Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Entering certain triggers causes my player rotation to "snap"?

Discussion in 'Scripting' started by ResonantWilterUni, Aug 20, 2023.

  1. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    44
    I have created a small form of ledge grabbing where the player can jump onto a wall via entering a trigger and then hold onto the ledge and then move sideways while basically dangling from the wall. During the ledge grab the horizontal mouse rotation is clamped to prevent the player from looking too far left or right. But this clamp effect only works on ledge objects that have a rotation of (0,0,0), otherwise my player rotation will "snap" to different directions.

    In my scene I have three ledge objects which all share the same tag 'LedgeObject' and have both a box collider and rigidbody, and the first ledge has a default rotation of (0,0,0), the second ledge has a default rotation of (0,-90,0), and the third ledge has a default rotation of (0,180,0). When I enter the trigger of the first ledge I have no issues and my code works. However if for example I enter the trigger of the second ledge, my player will immediately rotate from (0,-90,0) to (0,58,0). A similar issue also occurs when entering the trigger of the third ledge object. At the beginning of runtime, my players default rotation is (0,0,0).

    Code (CSharp):
    1. public void OnTriggerEnter(Collider other) //LedgeGrab script
    2. {
    3.     if (!other.CompareTag("Ledge")) return;
    4.    
    5.     isOnLedge = true;
    6.     ledgeObject = other.transform.parent;
    7. }
    Code (CSharp):
    1. void Start()
    2. {    
    3.     // Store the initial x-axis rotation when the script starts
    4.     initialXRotation = rotation.x;
    5. }
    6.  
    7. void Update() // Camera script
    8. {
    9.     Vector2 mouse = Input.GetAxis(xAxis) * Vector2.right + Input.GetAxis(yAxis) * Vector2.up;
    10.  
    11.     rotation.x += mouse.x * mouseSens;
    12.     rotation.y += mouse.y * mouseSens;
    13.  
    14.     rotation.y = Mathf.Clamp(rotation.y, -90f, 90f);
    15.  
    16.     if (ledgeGrab.isOnLedge) // reference the isOnLedge bool from the LedgeGrab script
    17.     {
    18.         // Calculate the clamped x-axis rotation based on the initial value
    19.         float clampedXRotation = Mathf.Clamp(initialXRotation + rotation.x - initialXRotation, -45f, 45f);
    20.         rotation.x = clampedXRotation;
    21.     }
    22.  
    23.     var xMouseQuaternion = Quaternion.AngleAxis(rotation.x, Vector3.up);
    24.     var yMouseQuaternion = Quaternion.AngleAxis(rotation.y, Vector3.left);
    25.  
    26.     transform.localRotation = yMouseQuaternion;
    27.     player.transform.rotation = xMouseQuaternion;
    28. }
    I currently try to store the default x-axis rotation and then use that to get my updated rotation so that I can clamp properly based on whichever angle I'm entering the trigger from but it causes even more snapping. I have tried many other methods and debugging has gotten me to this point but no matter how many adjustments I make, only one ledge object will ever clamp properly.
     
  2. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    I'm sure there's a better way, but I think I'd use my function:
    Code (CSharp):
    1. public Vector3 FindAlongLine(Vector3 start, Vector3 end, float percent)
    2.     {
    3.         return (start + percent * (end - start));
    4.     }
    As the "start" would be one end of the "ledge", and "end" the other. Then "percent" obviously percentage of where my Vector3 "playerGrabPosition" is between those two vectors.

    Then would need the direction "forward" of ledge to reference and determine what is players zero reference, that now uses -90 to 90 to then clamp the camera by. It's a little too early to deep thinking on this as far as more code snippets to explain, but hopefully what I just said makes more sense to you, than it does to me. :)
     
    ResonantWilterUni likes this.
  3. ResonantWilterUni

    ResonantWilterUni

    Joined:
    Jul 20, 2023
    Posts:
    44
    I see what you did there with that snippet. I would never have thought of that.

    Do you know why y-axis clamp is so much easier than x-axis clamp? Even when I remap and try to normalize the angle and use that to clamp my x-axis, it still has the issue that I described in my post. If maybe I understood the reason for the issue in my post, I could probably try to work to resolve it but in the last few weeks of researching I have absolutely no idea why I can't simply just use
    Mathf.Clamp(rotation.x, -35f, 35f)
    and that's it. Every online solution that I've seen for x-axis clamp has required so many lines of code

    Code (CSharp):
    1. public static float NormalizeAngle(float angle)
    2.     {
    3.         angle = (angle + 180) % 360;
    4.         if (angle < 0)
    5.         {
    6.             angle += 360;
    7.         }
    8.         return angle - 180;
    9.     }
    I have also tried your other suggestion about getting the direction "forward" to determine the players zero reference, but that also causes the same problem. I don't want to overload this post with so much code but I've got so many different solutions that I've tried and they all only partially work.
     
    Last edited: Aug 20, 2023
  4. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    Not exactly sure, but I'm thinking if the camera changes it's Y then now the X is probably giving all kinds of weird numbers to compensate, since it's technically no longer a "clean" X. Which is thanks to Quaternions, and probably why every example is many lines of code to handle it, lol..

    But a simple fix I can think, would be to have a parent object of camera, that only handles Y rotation and world position. Then have camera, which is a child of said object, handle it's localRotation of X. That way Unity internals handle all the math, and you just have to reference 2 different objects/transforms.

    I normally do this with a first person camera setup, main object is movement and (left/right)rotation, and player camera is up/down.
     
    Last edited: Aug 20, 2023