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. Dismiss Notice

Question Why does clamping only work on some objects?

Discussion in 'Scripting' started by androidac67, Aug 13, 2023.

  1. androidac67

    androidac67

    Joined:
    Jan 3, 2023
    Posts:
    75
    My clamping when in a trigger of an object that has a default (0,0,0) rotation works as it should, but any object with any angle at all such as (0,45,0) will cause my camera to rotate forward based on my clamp degrees. I use a trigger like this to allow me to use as many of the same object as I want for my specific 'wall running' mechanic:

    Code (CSharp):
    1. public void OnTriggerEnter(Collider other)
    2. {
    3.     if (!other.CompareTag("Wall")) return;
    4.  
    5.     canTurn = true;
    6.     wallObject = other.transform.parent;
    7. }
    8.  
    9. void Update()
    10. {
    11.    
    12.     if (canTurn == true && Input.GetKeyDown(KeyCode.Z))
    13.         {
    14.             movement.enabled = false; // disable player movement script so it doesn't overlap with this script
    15.             if (currentRoutine != null) StopCoroutine(currentRoutine);
    16.             currentRoutine = StartCoroutine(Turn(wallObject));
    17.         }
    18.  
    19.     if (isWallRunning)
    20.     {
    21.         // Do movements here
    22.     }
    23. }
    24.  
    25.  
    In my video, the first trigger that I enter has perfect clamping and I can only turn left or right a small amount. But when I step into the second trigger my camera instantly rotates forward based on my clamp value, and as a result the clamp doesn't work properly because the camera had just rotated when it shouldn't have.


    My entire camera code just incase, is here: https://pastebin.com/nh2zGwbA some of it is my own code and I found the general script on github, but everything else in it works perfectly.

    I tried to fix this by resetting the rotation when entering the trigger using a vector
    positionForward = transform.forward
    and
    float Angle = Vector3.Angle(Vector3.forward, positionForward)
    , then using 'Angle' inside my clamp to readjust the rotation of the player and wall, but that didn't have any effect.
     
    Last edited: Aug 14, 2023
  2. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    One trick I did for a complicated camera setup, was have an empty parent object, and child the camera to it. That way, the parent only needs to focus on a position, and possibly even a left/right modification. And the camera itself only needs to worry about a look up/down.

    However it seems you may also be adding in a tilt/roll, which would further complicate the cameras rotation.

    But the hard part is learning that if the camera tilts left(z90), if you now change the cameras left/right value(y90), it would now look up or down, because of it's current rotation.

    So having a hierarchy setup allows you to keep normal rotations, in a general sense. Which technically makes it more complex in one way, but much easier to understand in another. There are other ways of doing the same thing, like just adding a rotation.LookAt(), and just modifying it's axis to rotate on as the perspective changes.

    Now however, I already hear Kurt yelling "Cinemachine!", so if you just want simple functionality I would look into that. But if you truly want to make your own camera setup, then brew up another pot of coffee, and get some aspirin on standby... Because you've now entered debug hell.. lol

    But personally I like to learn how it all works, and how I can modify things myself.. So yes I have a lot of headaches. ;)
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    INCOMING!!

    Camera stuff is pretty tricky... you may wish to consider using Cinemachine from the Unity Package Manager.

    There's even a dedicated forum: https://forum.unity.com/forums/cinemachine.136/

    But check this out: I got a new blurb I'm working on: how to make your own camera controller:

    If you insist on making your own camera controller, the simplest way to do it is to think in terms of two Vector3 points in space: where the camera is LOCATED and where the camera is LOOKING.

    Code (csharp):
    1. private Vector3 WhereMyCameraIsLocated;
    2. private Vector3 WhatMyCameraIsLookingAt;
    3.  
    4. void LateUpdate()
    5. {
    6.   cam.transform.position = WhereMyCameraIsLocated;
    7.   cam.transform.LookAt( WhatMyCameraIsLookingAt);
    8. }
    Then you just need to update the above two points based on your GameObjects, no need to fiddle with rotations.


    Finally:

    Notes on clamping camera rotation and NOT using .eulerAngles because of gimbal lock:

    https://forum.unity.com/threads/implement-a-limit-in-camera-movement.1100038/#post-7082380
    https://forum.unity.com/threads/implement-a-limit-in-camera-movement.1100038/#post-7083097

    How to instantly see gimbal lock for yourself:

    https://forum.unity.com/threads/confusing-bug.1165789/#post-7471441

    All about Euler angles and rotations, by StarManta:

    https://starmanta.gitbooks.io/unitytipsredux/content/second-question.html
     
    wideeyenow_unity likes this.
  4. androidac67

    androidac67

    Joined:
    Jan 3, 2023
    Posts:
    75
    Is Cinemachine not a lot more difficult and time consuming to set up? I've had it suggested a lot but all I can find are several third person videos and no first-person camera setups minus a brief official Unity tutorial
     
  5. androidac67

    androidac67

    Joined:
    Jan 3, 2023
    Posts:
    75
    I think I understand what both of you mean by Lookat(). I used it before for another feature so I'm going to try to mess around with it now.

    So is that the only issue when it comes to my camera rotating based on my clamp value, that I don't have something like LookAt?
     
  6. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    well.. the thing you need to understand is, any function(or package), does all the same things as I mentioned by hard coding the values yourself. You put in one parameter or variable, and it simply returns the results you want.

    So true, things can be much more simplified by using another's already created method, but you'll find that they lack in certain aspects that you might want to have in your methods. One trick I do is find the hard code on things, and find the meat and potatoes of what they do, and make my own function(which is time consuming and hard).

    But I just like to understand how things tick, and how they operate so I understand it on a baser level. As I said, that's just me...

    I've never played with Cinemachine, so I can speak to it's ease of use, or what it does great, or where it lacks.

    And for LookAt(), you'll have to dig into it more, and fully understand how it operates. Once you do, it takes out a lot of the leg work from doing it yourself(as does any pre-made function or package).

    That's the best part! or well for me it is, the debugging and learning how and why it works. I would also look into using
    Debug.DrawLine
    and it's brother
    Debug.DrawRay
    . As I never would of grasped anything, without physically seeing what rotations and directions are actually doing. :)
     
  7. androidac67

    androidac67

    Joined:
    Jan 3, 2023
    Posts:
    75
    Thanks for the reply.

    I tested something overnight but it does nothing but cause a jittery effect until I exit trigger. Debugging shows me that it also still tries to rotate forward rather than towards my object.. it makes no sense. I've used similar code elsewhere and it works. I just took that and expanded upon it:

    Code (CSharp):
    1. IEnumerator Turn(Transform wallObject)
    2.     {
    3.         Quaternion targetRotation = Quaternion.identity;
    4.         do
    5.         {
    6.             Debug.Log("Rotate towards wall.");
    7.             Vector3 targetDirection = wallObject.position - transform.position;
    8.             targetRotation = Quaternion.LookRotation(targetDirection);
    9.             Quaternion nextRotation = Quaternion.RotateTowards(
    10.                     transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
    11.             targetDirection.y = 0;
    12.             targetRotation = Quaternion.LookRotation(targetDirection);
    13.             transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
    14.             yield return null;
    15.  
    16.         } while (transform.rotation != targetRotation);
    17.         transform.rotation = targetRotation;
    18.  
    19.         currentRoutine = null;
    20.         isWallRunning = true;
    21.         canTurn = false;
    22.     }
    I also edited my post to better show my trigger script code. I put this inside my trigger (which I have in my post above) that is in my other script. Basically what I do is, when I first enter the trigger of the wall, I need to press "Z", which will then activate this coroutine through the
    canTurn
    bool. Once the coroutine ends,
    canTurn
    bool turns false, and
    isWallRunning
    turns true, which should allow for clamp. But the coroutine never ends.
     
  8. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    That is the major problem, this happens all the time, and is a very common problem within these forums.

    The thing you need to understand about coroutines, is they technically create their own script, and they run separately on their own each time they're called. Which gives a compounding effect, and often will cause Unity to crash since so many of them can be running and called each frame.

    So always make sure your coroutines are for something you want to be separate from your script, to run on their own, and that they only get called once.

    I personally don't use coroutines, nor do I think I ever will. If I need something to happen constantly within my script while the script is alive, it would simply be in my update method. And if I knew there's a chance the script would disable, and need something to keep thinking after the fact, it would be handled in another script, whether existing or creating a new one(best example would be a separate death animation, if using object pooling).

    And testing for run-away-coroutines is a little tricky, you'd need a frame counter, and have your print() debug mention it:
    Code (CSharp):
    1. public class AlwaysExists
    2. {
    3.    public static int frameCounter;
    4.    void Update()
    5.    {
    6.        frameCounter++;
    7.    }
    8. }
    9. // in problem area:
    10. print($"value 1 now {value1} at frame {AlwaysExists.frameCounter}");
    11. //print= value 1 now 37 at frame 136
    12. //print= value 1 now 37 at frame 136
    13. //print= value 1 now 37 at frame 136
    Something like that for example ^
     
  9. androidac67

    androidac67

    Joined:
    Jan 3, 2023
    Posts:
    75
    Is it just preference that you don't use coroutines or is it generally advised to try and move away from them? I've also heard this about while loops and even using LateUpdate().
     
  10. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    Coroutines are just a tool. They do certain things REALLY well.

    But most of the time they only add complexity, not actual useful utility.

    Here's more scribblings to help you feel comfy with them:

    Coroutines in a nutshell:

    https://forum.unity.com/threads/des...en-restarting-the-scene.1044247/#post-6758470

    https://forum.unity.com/threads/proper-way-to-stop-coroutine.704210/#post-4707821

    Splitting up larger tasks in coroutines:

    https://forum.unity.com/threads/bes...ion-so-its-non-blocking.1108901/#post-7135529

    Coroutines are NOT always an appropriate solution: know when to use them!

    "Why not simply stop having so many coroutines ffs." - orionsyndrome on Unity3D forums

    https://forum.unity.com/threads/things-to-check-before-starting-a-coroutine.1177055/#post-7538972

    https://forum.unity.com/threads/heartbeat-courutine-fx.1146062/#post-7358312

    Our very own Bunny83 has also provided a Coroutine Crash Course:

    https://discussions.unity.com/t/coroutines-ienumerator-not-working-as-expected/237287/3

    Unity will lock up 100% of the time EVERY millisecond your scripting code is running.

    Nothing will render, no input will be processed, no Debug.Log() will come out, no GameObjects or transforms will appear to update.

    Absolutely NOTHING will happen... until your code either:

    - returns from whatever function it is running

    - yields from whatever coroutine it is running

    As long as your code is looping, Unity isn't going to do even a single frame of change. Nothing.

    No exceptions.

    "Yield early, yield often, yield like your game depends on it... it does!" - Kurt Dekker
     
  11. wideeyenow_unity

    wideeyenow_unity

    Joined:
    Oct 7, 2020
    Posts:
    728
    For me, it's just a personal preference.

    But you can use them however you want, I'm just mentioning how they create problems, that are sometimes hard to debug.

    Best way is to make sure it only runs once, so any call/invoke of a coroutine should be handled by a one-time bool, or something like it. That way it only gets called one time, and you're sure it won't be called multiple times. :)