Search Unity

Character Controller - unexpected step offset behaviour

Discussion in 'Physics' started by marcpeyre, Mar 7, 2019.

  1. marcpeyre

    marcpeyre

    Joined:
    Jan 23, 2014
    Posts:
    15
    I have a Character Controller component attached to the same Game Object as a script that moves the Character Controller on the XZ plane without applying gravity:

    Code (CSharp):
    1. private void Update()
    2. {
    3.     // Vector3 _prevPos = transform.position;
    4.  
    5.     characterController.Move( GetMoveDirection() * speed * Time.deltaTime );
    6.  
    7.     // Debug.DrawLine( _prevPos, transform.position, (Time.frameCount % 2 == 0) ? Color.red : Color.blue, 5.0f );
    8. }
    I've included (but commented out) the code to draw the debug lines, just to show where that comes from. It draws debug lines in alternating colors to show the delta movement of that frame.

    The issue I'm having is that the Character Controller snaps up or down to unexpected heights in different scenarios:

    1. Moving into a box from the air that is higher than the stepOffset, but lower than stepOffset + radius will snap the Character Controller down. The box is of height 2 and the Character Controller moves from 1.08 to 0.305 on contact with the box as seen at the end of the following video. It first steps up some smaller boxes that are lower than the stepOffset of 0.8 to reach the height of 1.08 (the second last box is 1 unit high + 0.08 skinWidth), before it hits the box that moves it down.



    2. Moving around a corner of a box that is higher than the stepOffset, but lower than stepOffset + radius, will snap the Character Controller up.


    This documentation for the step offset states that it will only move it up, not down. So i'm wondering what happens under the hood with the stepOffset as the Character Controller moves down in case 1.
    Case 2 is also contradicting the documentation as the box is higher than the stepOffset.

    I'm running this with unity 2018.3.7f1 and the character controller settings are:



    The Slope Limit and Skin Width don't affect this unexpected behaviour, it seems to be only linked to Step Offset and Radius.

    It would be nice to get some more information on how the radius is affecting the height it can step up boxes that are higher than the Step Offset.
     
    Sungold and ollaxe like this.
  2. Zajoman

    Zajoman

    Joined:
    May 31, 2014
    Posts:
    15
    I'm having the same issue, exactly. I managed to iron everything out, but this one problem occurs. Did you solve it by any chance in the meantime?
     
  3. CptOblivion

    CptOblivion

    Joined:
    Mar 8, 2015
    Posts:
    9
    I've got the same issue (my character controller can climb the corner of a box that's higher than the step height), looks like it's still not fixed in Unity 2019.2. Anybody know of a solution?

    Best workaround I can think of is to track the position from the previous frame and if the height jumps up more than the step height in one frame, snap back to the previous position. That's just asking for something to break if there's anything else moving nearby, though.
     
  4. Feezen

    Feezen

    Joined:
    Mar 15, 2018
    Posts:
    7
    Having the exact same issue in version 2018.4.11f1 (LTS)
    Does anyone have a working solution?
     
  5. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    I have exactly the same problem as 2. Controller steps up on the corners of box collider with higher value than step offset...
     
  6. Aromyl

    Aromyl

    Joined:
    Aug 9, 2017
    Posts:
    1
    Has anyone had any experience dealing with these issues? A simple character controller (such as the one in Brackey's recent FPS Controller video) will have the same issues. The best way I've found to reproduce the issues is jumping into probuilder stairs from the side. It only seems to "jerk" or "snap" down when close to the point where the player would be able to step up via the step offset.

    Any leads on this would be wonderful.
     
    Benjaboy3141 likes this.
  7. SecretAnorak

    SecretAnorak

    Joined:
    Mar 19, 2014
    Posts:
    177
    I have a very similar issue as shown in the video below. The character controller works as expected until I put it near a corner:


    Submitting as a bug.
     
    Last edited: Apr 14, 2020
  8. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    Given the track record, I highly suspect that this will get addressed... This is yet another example of Unity's "builtin" feature that "looks" like it can be used for the game production, but when you try something a little deeper, you get this kind of issues.
     
    halley, jiraphatK, Sungold and 2 others like this.
  9. Sungold

    Sungold

    Joined:
    Jan 13, 2013
    Posts:
    9
    Is there any event handler from the Character Controller like an OnStepOffset or something that we can attach code to that would prevent this? Is there a better solution in the meantime?

    Any drawbacks to just using a capsule collider and trying to code these behaviors of the character controller manually? Does anyone have a script they recommend for a replacement to the character controller that just uses a capsule collider as mentioned?

    So far my jank solution is to just set Slope Limit to 90 and accept the fact that players will be able to scale every sloped surface, and just design my levels with vertical walls as the bounds. In some sense this isn't so bad because it provides a lot of freedom of movement, and its very easy for the player to internalize that any sloped surface can be scaled, rather than be interrupted in the middle of scaling a slope by not being able to move forward when they hit the arbitrarily decided slope limit.

    Setting slope limit to 90 will not cause the issue where when you jump and move forward next to a tall block that is just about too tall for you too climb with slope limit, you get pushed down mid-jump. https://answers.unity.com/questions/1422941/character-controller-jitters-when-jumping-next-to.html

    Not sure if it fixes the bug you've been experiencing with the corners of objects.
     
    Last edited: Dec 13, 2020
  10. castor76

    castor76

    Joined:
    Dec 5, 2011
    Posts:
    2,517
    My advice to you, so that you do not lose weeks of development time like I have. Write your own, Come up with your own solution for this. You will more likely to win that way than trying to over come this.
     
  11. Deleted User

    Deleted User

    Guest

    That is my solution too, but I wonder why, in all these new versions that have been released, nothing mentioned in this thread has been fixed. At all.
     
  12. robinsail

    robinsail

    Joined:
    Jul 18, 2018
    Posts:
    6
    Having this issue in 2020.3.11f1 too.
     
    DoomGuy-dot likes this.
  13. LaHoDev

    LaHoDev

    Joined:
    Jun 10, 2022
    Posts:
    8
    Damn, unity is almost losing it, still not fixed
     
  14. n_gon

    n_gon

    Joined:
    Oct 8, 2020
    Posts:
    10
    I am out of town for the holiday but I do not have this issue in my project. A few things --

    1) Set Minimum Move Distance to 0. I have no idea why Unity has this set to 0.001, their own documentation says that it should be 0 in most cases. It can cause problems. It only exists for performance reasons and the performance difference is minimal. When you start expanding your code with more complex mechanics, this minimum move distance will get in the way and you'll spend an entire day trying to figure out whats wrong with your code and realize Unity doesn't set the default values of things to their recommendations...

    2) Skin Width should be lower. Unity recommends 10% of the radius which would be 0.03.

    I don't know if either of these are related or will fix the issue. But they will cause problems in other areas. I'll do some more looking around when I get back. A simple thought - you could supplement your code with something that limits the y mobility of a move to the step height. Gimmicky fix, but shouldn't be difficult. I imagine this is a collision resolution issue so you would need to manually move the gameobject after the move (look at location before and after the move and if it moved up in the y direction by equal to or greater than the stepheight set it directly back to the old y height. Code would break if you had an obnoxiously long tick during a jump but if your frame rate is that bad you wouldn't expect the game to work anyways. May have issues with the player going through the floor if the floor is not flat around the corner of the step but considering this issue is probably only occurring in one tick as you're gliding across the step I would think this is unlikely. If it does happen I can think of a more complex algorithm that would account for this. Though thinking about it, it is actually fairly likely that this issue ONLY occurs with a flat ground against a 90deg angled object perfectly perpendicular to the player. In other words, you may never even notice this bug in an actual project.

    Nope. This kind of thing would involve writing your own collision detection and management algorithms. I have been down this road before and found myself reading scholarly white paper articles on it. This is possibly the single most complex thing to code in a game engine - up there with rendering.

    Really annoys me that Unity neglects the character controller so much. I also have no idea why so many unity users think that rigidbody's are the best way to handle character movement which is... objectively incorrect for 90% of games.
     
    Last edited: Jul 4, 2022
    PrecisionRender likes this.
  15. Wiesiolak

    Wiesiolak

    Joined:
    Jan 30, 2020
    Posts:
    2
    You need to turn off step offset in CC when it shouldn't work.

    Code (CSharp):
    1.         [SerializeField] CharacterController CharacterController;
    2.         [SerializeField] private float stepOffset = 0.3f;
    3.         private Vector3 _moveDir;
    4.      
    5.      
    6.         private void Update()
    7.         {
    8.             _moveDir = /*Calculate move direction using your solution with gravity etc.*/;
    9.          
    10.             HandleStepOffset();
    11.          
    12.             CharacterController.Move(_moveDir * Time.deltaTime);
    13.         }
    14.         private void HandleStepOffset()
    15.         {
    16.             //Ignore Y in movement calculations
    17.             Vector3 moveDirXZ = new(_moveDir.x, 0, _moveDir.z);
    18.             //Ignore movement speed
    19.             Vector3 normalizedMoveDirXZ = moveDirXZ.normalized;
    20.             //Distance for raycast, to detect objects closest to moveDirection
    21.             float distance = CharacterController.radius + CharacterController.skinWidth;
    22.             //Position of player's ground level
    23.             Vector3 bottom = transform.position - new Vector3(0f, CharacterController.height / 2f - CharacterController.center.y, 0f);
    24.             //Position of player's ground level + StepOffset
    25.             Vector3 bottomWithStepOffset = new(bottom.x, bottom.y + stepOffset, bottom.z);
    26.             //Raycast at player's ground level in direction of movement
    27.             bool bottomRaycast = Physics.Raycast(bottom, normalizedMoveDirXZ, out _, distance);
    28.             //Raycast at player's ground level + StepOffset in direction of movement
    29.             bool bottomWithStepOffsetRaycast = Physics.Raycast(bottomWithStepOffset, normalizedMoveDirXZ, out _, distance);
    30.             if (bottomRaycast && bottomWithStepOffsetRaycast)
    31.             {
    32.                 //Wall in move direction
    33.                 //Block stepping over object
    34.                 CharacterController.stepOffset = 0;
    35.             }
    36.             else if (bottomRaycast && !bottomWithStepOffsetRaycast)
    37.             {
    38.                 //Step in move direction
    39.                 //Allow stepping over object
    40.                 CharacterController.stepOffset = stepOffset;
    41.             }
    42.             else
    43.             {
    44.                 //Nothing in move direction
    45.                 //Block stepping over object
    46.                 CharacterController.stepOffset = 0;
    47.             }
    48.         }
     
    Last edited: Sep 7, 2023
    Huxii likes this.
  16. Huxii

    Huxii

    Joined:
    Sep 15, 2018
    Posts:
    37
    Hello @Wiesiolak, I had the same problem, so I tried your workaround and it works :). Thank you very much for providing it :). Huxi