Search Unity

"controller.isGrounded" doesn't work reliably.

Discussion in 'Scripting' started by jpdavis81, May 31, 2011.

  1. jpdavis81

    jpdavis81

    Joined:
    Apr 1, 2011
    Posts:
    33
    Hello all,

    I have beat my head against a wall for almost a MONTH on this one problem. So far, no solid help in the forums... I thought I would post here one more time, just in case I was lucky to find someone who can tell me what I'm doing wrong. Basically, I stripped my charactercontroller script to bare-bones so you can see what my code looks like. As you will see in the debug window, the status keeps switching from "GROUNDED" to "NOT GROUNDED" back and forth... in other words, with the player standing completely still in the game world, "NOT GROUNDED" keeps coming up as his status over and over again:

    Code (csharp):
    1.  
    2. // Stores the "forward" vector for the player, so we always know which way he is facing.
    3. private var playerDirection : Vector3;
    4. // Is the user pressing any keys?
    5. private var isMoving : boolean = false;
    6. private var currentAnalogStickMagnitude : float = 0.0;
    7. private var moveSpeed : float = 1.6;
    8. // The gravity for the character
    9. private var playerGravity : float = 8.0;
    10. // How high do we jump when pressing jump and letting go immediately
    11. private var jumpHeight : float = 0.5;
    12. // We add extraJumpHeight meters on top when holding the button down longer while jumping
    13. private var extraJumpHeight : float = 2.5;
    14. // Are we jumping? (Initiated with jump button and not grounded yet)
    15. private var isJumping : boolean = false;
    16. private var jumpingReachedApex : boolean = false;
    17. // The height we jumped from (Used to determine for how long to apply extra jump power after jumping.)
    18. private var lastJumpStartHeight : float = 0.0;
    19. // Last time the jump button was pressed
    20. private var lastJumpButtonTime : float = -10.0;
    21. // Last time we performed a jump
    22. private var lastJumpTime : float = -1.0;
    23. // Jump timeout variable
    24. private var jumpTimeout = 0.15;
    25. // Jump repeat time variable
    26. private var jumpRepeatTime = 0.05;
    27. // Last time we were grounded
    28. private var lastGroundedTime : float = -1.0;
    29. // The current vertical speed
    30. private var verticalSpeed : float = 0.0;
    31. // The player's character controller object
    32. private var controller : CharacterController;
    33.  
    34. function Awake()
    35. {
    36.     controller = gameObject.GetComponent(CharacterController) as CharacterController;
    37. }
    38.  
    39. function Update()
    40. {
    41.     MovePlayer();
    42.     ApplyGravity();
    43. }
    44.  
    45. function MovePlayer()
    46. {
    47.     // Variable to store the movement information for each frame.
    48.     var movement : Vector3 = Vector3.zero;
    49.  
    50.     // Make sure the isMoving variable is true before processing player movement.  The isMoving variable is set to true
    51.     // whenever the user is using the movement controls on the joypad or keyboard.
    52.     if (isMoving)
    53.     {
    54.         movement = currentMoveDirection.normalized * Time.deltaTime * 2;
    55.         var targetSpeed : float = 0.0;
    56.         var analogStickMagnitude : float = Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")).magnitude;
    57.         // Pick speed modifier
    58.         if (analogStickMagnitude > 0.9)
    59.             targetSpeed = moveSpeed;
    60.         movement *= targetSpeed;
    61.         currentAnalogStickMagnitude = analogStickMagnitude;
    62.     }
    63.     else
    64.     {
    65.         currentAnalogStickMagnitude = 0.0;
    66.     }
    67.  
    68.     // See if the player is freshly pressing the jump button
    69.     if (jumpButtonIsPressed  !jumpButtonWasPressed)
    70.     {
    71.         StartJump();
    72.     }
    73.  
    74.     if (IsGrounded())
    75.     {
    76.         lastGroundedTime = Time.time;
    77.         if (isJumping)
    78.         {
    79.             isJumping = false;
    80.         }
    81.     }
    82.  
    83.     // Make the player fall if there is no floor underneath him.
    84.     if (!IsGrounded())
    85.         movement = movement + (Vector3(0, verticalSpeed, 0) * Time.deltaTime * 2);
    86.  
    87.     controller.Move(movement);
    88.  
    89.     if (IsGrounded())
    90.         Debug.Log("GROUNDED");
    91.     else
    92.         Debug.Log("NOT GROUNDED");
    93. }
    94.  
    95. function GetAnalogStickMagnitude()
    96. {
    97.     return currentAnalogStickMagnitude;
    98. }
    99.  
    100. function ApplyGravity()
    101. {
    102.     // When we reach the apex of the jump we perform the appropriate operations.
    103.     if (isJumping  !jumpingReachedApex  verticalSpeed <= 0.0)
    104.     {
    105.         jumpingReachedApex = true;
    106.     }
    107.  
    108.     // When jumping up we don't apply gravity for some time when the user is holding the jump button.
    109.     // This gives more control over jump height by pressing the button longer.
    110.     var applyingExtraJumpHeight : boolean = false;
    111.     if (IsJumping()
    112.         verticalSpeed > 0.0
    113.         jumpButtonIsPressed
    114.         transform.position.y < lastJumpStartHeight + extraJumpHeight)
    115.     {
    116.         applyingExtraJumpHeight = true;
    117.     }
    118.     else
    119.     {
    120.         applyingExtraJumpHeight = false;
    121.     }
    122.  
    123.     if (applyingExtraJumpHeight)
    124.     {
    125.         return;
    126.     }
    127.     else if (IsGrounded())
    128.     {
    129.         verticalSpeed = 0.0;
    130.     }
    131.     else
    132.     {
    133.         verticalSpeed -= playerGravity * Time.deltaTime;
    134.     }
    135. }
    136.  
    137. function IsGrounded()
    138. {
    139.     return controller.isGrounded;
    140. }
    141.  
    142. function IsJumping()
    143. {
    144.     return isJumping;
    145. }
    146.  
     
  2. U7Games

    U7Games

    Joined:
    May 21, 2011
    Posts:
    943
    +1

    Same problem, i don't know why.. i have your same problem.. if anyone has a solution it will be highly welcome

    i test this problem by just adding a debug line :
    Code (csharp):
    1.  
    2. var controller : CharacterController;
    3.  
    4. function Update(){
    5. controller = GetComponent(CharacterController);
    6. Debug.Log(controller.isGrounded);
    7. }
    8.  
    and the console prints true , then false and changing continuously .. since the player is not moving...
     
  3. jpdavis81

    jpdavis81

    Joined:
    Apr 1, 2011
    Posts:
    33
    I would absolutely hate to resort to using raycasts downward to find out if the player is really "grounded" or not. Since the logic for determining the player's collisions are already built in, I don't understand why we couldn't use that instead of putting the addition raycasting load on the processor. Raycasting is relatively expensive on the processor.
     
    marcospgp likes this.
  4. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Again, like I said in the other thread, setting vertical speed to zero will never work, for reasons I detailed further up in that thread. Please detail what you are not understanding about that concept, and I will try to help.

    Here are some other things in your code I noticed, which may help you out in the future:
    The conditional operator.
    Code (csharp):
    1. Debug.Log(controller.isGrounded ? "GROUNDED" : "NOT GROUNDED");
    SerializeField can be used to keep a variable private, but still have it editable in the Inspector, or you can add HideInInspector if you just want to store it, but not edit it with the GUI.
    Code (csharp):
    1. @SerializeField @HideInInspector private var controller : CharacterController;
    2. function Reset () {
    3.     controller = GetComponent.<CharacterController>();
    4. }
    5.  
    That code also showed that GetComponent does not require gameObject., and has a generic form, for less clutter.
     
    legoblaster1234 likes this.
  5. jpdavis81

    jpdavis81

    Joined:
    Apr 1, 2011
    Posts:
    33
    Jessy, thank you for the feedback. However, I made my sample code simple enough so that everyone wouldn't have a hard time following it (for the post), so I didn't want to overly complicate things.

    Furthermore, I kept asking (on the other post) for you to clarify what you were saying. And all you could say were things like "please tell me what you don't understand about that concept" which sounds quite condescending and rude when you never specified what was wrong with my code to begin with. The vertical speed IS set to zero in the Third Person tutorial code (which is a project you can download off of Unity's website) and it works just fine.

    I started another thread to see if anyone else could chime in on this since I felt you didn't sincerely want to lend any assistance in the other thread. Maybe I'm wrong, but that's how I took it and how it felt.
     
    marcospgp, AlejMC and neonblitzer like this.
  6. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    It sounds like you've got some cynicism issues! :eek:

    Can you attach the script here? I can't download the project at the moment.
     
    Last edited: May 31, 2011
  7. jpdavis81

    jpdavis81

    Joined:
    Apr 1, 2011
    Posts:
    33
    I apologize if that sounded offensive, I didn't mean to offend you at all. I was a bit confused from your other responses before in tone and demeanor, so I'm sorry if I jumped to any incorrect conclusions.
     
  8. jpdavis81

    jpdavis81

    Joined:
    Apr 1, 2011
    Posts:
    33
    Jessy, the unity package you shared on the other threat you referenced made it real to me, and I could see exactly what you were talking about. You have converted me, and turned me into a believer. THAT WORKED!!!!!!!!!!!!!!!!!!!!!!!!!! After a month of not understanding how to fix this, you have become my hero... THANK YOU!!!!!!!
     
  9. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Mostly you want to swap this;

    Code (csharp):
    1.  
    2. else if (IsGrounded())
    3.     {
    4.         verticalSpeed = 0.0;
    5.     }
    6.     else
    7.     {
    8.         verticalSpeed -= playerGravity * Time.deltaTime;
    9.     }
    10.  
    for this

    Code (csharp):
    1.  
    2. else if (IsGrounded())
    3.     {
    4.         verticalSpeed = -playerGravity * Time.deltaTime;
    5.     }
    6.     else
    7.     {
    8.         verticalSpeed -= playerGravity * Time.deltaTime;
    9.     }
    10.  
    or - even better - this

    Code (csharp):
    1.  
    2. else if (IsGrounded())
    3.     {
    4.         verticalSpeed =  -controller.stepOffset / Time.deltaTime;
    5.     }
    6.     else
    7.     {
    8.         verticalSpeed -= playerGravity * Time.deltaTime;
    9.     }
    10.  
     
  10. DaveA

    DaveA

    Joined:
    Apr 15, 2009
    Posts:
    310
    I know this is an old thread, but the problem is still there. Using Jessy's code, it will ground (most of the time), BUT:
    1. I can't jump anymore
    2. If I do get airborne, when I land again I get the alternating grounded problem again.
    3. Just walking around, I glitch non-grounded/grounded

    Is there a work-around which accounts for jumping? or even just walking?
     
    Last edited: Feb 27, 2014
  11. Radivarig

    Radivarig

    Joined:
    May 15, 2013
    Posts:
    121
    Hi, I made a workaround for the toggling of controller.isGrounded value. I used a timer to count how long the value keeps being false, which happens consistently when the body is in the air, so if the value turns true that means we are colliding with something, we then reset the timer. My IsGrounded() function returns true only if the return value of controller.isGrounded does not change to true for some duration value.
    That works great when the player is on flat surfaces but the controller.isGrounded is taking too long (sometimes more then a second!) to calculate and return true when the character is on a terrain that has bumps, so I've combined it with a raycast when the value was false.

    Here is the project with separated scenes: GroundCheck.unitypackage

    $isGrounded.png

    the final code:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. //this is combined version of both previous scripts, one that was using raycasts
    6. //for detecting colliders and terrains, and other that was using character controllers controller.isGronded value.
    7. //adding raycast becase it takes too long for controller.isGrounded to
    8. //finnish with calculations while the object was on terrain parts that had very low angled surface
    9.  
    10. [RequireComponent (typeof (CharacterController))]
    11. public class GroundCheckRayCastAndCControllerCombinedScr : MonoBehaviour {
    12.    
    13.     //simply getting controller.isGrounded was not working properly because the state from true to false was r
    14.     //apidly swithing, solved it with a timer
    15.  
    16.     private float startTime;
    17.     public float duration = 0.08f;
    18.     private bool counterStarted = false;
    19.     public bool isGrounded = false;
    20.    
    21.     public RaycastHit hit;                                  //use this if you want to acces objects that are hit with the raycast
    22.     public float   distance   = 1.2f;                       //set this to go beyond your collider
    23.     public Vector3 direction= new Vector3(0f, -1f, 0f);
    24.    
    25.     void Update(){
    26.         isGrounded = IsGrounded();
    27.     }
    28.    
    29.     void OnGUI(){
    30.         float scrW = Screen.width, scrH = Screen.height;
    31.         GUI.Label(new Rect(scrW/10f, scrH/3f   , 250f, 100f), "Grounded State:\t\t\t " + isGrounded.ToString()
    32.                                                             + "\nIn Air Time:\t\t\t " + (CountTime() <= duration ? "0" : CountTime().ToString() ));
    33.         GUI.Label(new Rect(scrW/10f, scrH*2f/3f, 250f, 100f), "We use one raycast in addition to .isGrounded, check scene window in playmode for debug raycast line");
    34.     }
    35.    
    36.     public float CountTime(){
    37.         return Time.time - startTime;
    38.     }
    39.  
    40.     public bool IsGrounded(){
    41.         return IsGroundedByCController() || IsGroundedByRaycast();      //this also doesn't call raycast if we know we are grounded
    42.  
    43.     }
    44.  
    45.     public bool IsGroundedByCController()
    46.     {
    47.         CharacterController controller = GetComponent<CharacterController>();
    48.         if (controller.isGrounded == false){
    49.             if(counterStarted == false){
    50.                 startTime = Time.time;
    51.                 counterStarted = true;
    52.             }
    53.         }
    54.         else counterStarted = false;
    55.  
    56.         if(CountTime() > duration){
    57.             return false;
    58.         }
    59.         return true;
    60.     }
    61.    
    62.     public bool IsGroundedByRaycast(){
    63.         Debug.DrawRay(transform.position, direction * distance, Color.green);       //draw the line to be seen in scene window
    64.        
    65.         if(Physics.Raycast(transform.position, direction, out hit, distance)){      //if we hit something
    66.             return true;
    67.         }
    68.         return false;
    69.     }
    70. }
    71.  
     
    dkim19375, karsai106 and Shibli like this.
  12. SchmermundP

    SchmermundP

    Joined:
    Mar 20, 2014
    Posts:
    2
    I am having a problem very similar to this, In the end I need a reliable way to tell if the character is touching the terrain. The current character controller isGrounded variable is set on the previous frame, which is causing me most of my issues. Whats the best way to rewrite this object variable into a function that will check if the controller is currently (as in this frame) touching the ground? Any suggestions? Also, I know that this will not be nearly as fast as the built in way to check grounded, but it just not reliable enough for me.
     
  13. Gorov

    Gorov

    Joined:
    Feb 28, 2012
    Posts:
    10
    I was having the same problem but then I put controllers Move function in FixedUpdate and it works as it should.
     
    Squishymonster likes this.
  14. madzilla

    madzilla

    Joined:
    Dec 23, 2014
    Posts:
    5
    I know this thread is older and Jessy's help is amazing, but I just thought I'd share something that I learned while studying this mechanic.
    I noticed that when I added a movement component to Jessy's code that the color was still flickering.
    It looked like it was losing the ground over vertices - but that simplemove didnt have this issue.
    I ended up using the SimpleMove() method for linear movement and Move() for jumping. I have a Walk Method that calls the simplemove() and a jump method that calls move(). Currently I'm using gravity while not grounded in a Gravity() method and able to set the velocity to 0 in the Walk() method, so that the opposing force to jump force is constant.


    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class PlayerController : MonoBehaviour {
    5.  
    6.     CharacterController controller;
    7.     float x,z;
    8.     public Vector3 input, velocity;
    9.     public float jumpForce, walkSpeed, runSpeed;
    10.     bool jump, run;
    11.     protected float gravity = -9.8f;
    12.     float moveSpeed;
    13.  
    14.     void Start () {
    15.         controller = GetComponent<CharacterController>();
    16.     }
    17.  
    18.     void FixedUpdate () {
    19.         x = Input.GetAxis("Horizontal");
    20.         z = Input.GetAxis("Vertical");
    21.         jump = Input.GetButtonDown("Jump");
    22.         run = Input.GetButton("Fire1");
    23.  
    24.         input = new Vector3 (x,0,z);
    25.         input.Normalize();
    26.         moveSpeed = run ? runSpeed : walkSpeed;
    27.         input *= moveSpeed;
    28.  
    29.         velocity = new Vector3 (input.x, velocity.y, input.z);
    30.  
    31.         Move();
    32.         if(jump)
    33.             Jump();
    34.         Gravity();
    35.  
    36.         controller.renderer.material.color = controller.isGrounded ? Color.blue : Color.red;
    37.  
    38.     }
    39.     void Move(){
    40.         if(controller.isGrounded){
    41.             velocity.y = 0;
    42.             controller.SimpleMove(velocity);
    43.         }
    44.     }
    45.     void Jump(){
    46.         if(controller.isGrounded){
    47.             jump = false;
    48.             velocity.y += jumpForce;
    49.             controller.Move(velocity * Time.deltaTime);
    50.         }
    51.     }
    52.     void Gravity(){
    53.         if(!controller.isGrounded){
    54.             velocity.y += gravity;
    55.             controller.Move(velocity * Time.deltaTime);
    56.         }
    57.     }
    58. }
     
    Stardog likes this.
  15. WestSydeWilly

    WestSydeWilly

    Joined:
    Nov 23, 2013
    Posts:
    1
    I had a similar problem: my character controller would almost never return 'true' for isGrounded, even when I was moving it down by as much as 10 units per frame.

    The bizarre solution I found after experimenting a bit was this: I had to call CharacterController.Move TWICE per frame, first solely for gravity, and second for all other movement (without gravity).

    I know calling Move more than once per frame is advised against, but it fixed my problem, and I haven't run in to any issues so far...

    Note that the gravity calculation uses the 'magic number' technique which I got from Jessy. The idea is to move the CharacterController downward a tiny amount every frame when it's grounded. This accounts for the slight upward shift that occurs when the collision system moves the two colliders apart:

    Code (JavaScript):
    1. var magicNumber : float=0.0001;
    2. var controller;
    3. private var moveVector : Vector3;
    4.  
    5. controller=GetComponent.<CharacterController>();
    6.  
    7. function Update () {
    8.  
    9.     if (controller.isGrounded) moveVector.y = magicNumber;
    10.     else moveVector += Physics.gravity * Time.deltaTime;
    11.        
    12.     controller.Move(moveVector);
    13. }
    hopefully this will save someone from wasting all the time I spent on this problem:p
     
    linamad likes this.
  16. bjoerninger

    bjoerninger

    Joined:
    Oct 28, 2016
    Posts:
    1

    yep, that movement Vector (vert Speed) = 0 thing.... thx dude, you just saved me out of a loop of despetation :p

    had exactly the same prob, was the = 0 thingie. kudos.
     
  17. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    I have the same problem with character controller and based on my tests it's really weird. It looks like there occurs some race condition or something.
    The above solution with magic number doesn't work for me.
    But.... - when adding just one line (Debug.Log) to the Update method, it slows down the game tiny bit but sufficiently enough and the problem with jumping is gone. Every jump command is executed.
    However when I remove the Debug.Log method call, then I cannot occassionaly jump.
    This means I'm also unable to find out if the problem is really in the Grounded method as when using Debug.Log, everything works properly.
    Also the problem is definitely much worse when running built version of the game. Then it's of course running much faster than when run in Unity environment. And then jumping is completely impossible. 0 jumps out of 30 succeed.
    Have anybody found clue? Yes, to avoid character controller and write own. Hovever IsGrounded method won't be as simple as it seems. It requires plenty of cases and math behind the scenes. It's really not just about linecasting / raycasting or spherecasting as it won't be accurate and edge cases will break everything.
    I guess I should probably report this as a bug.
     
    AlejMC likes this.
  18. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    I reported a bug a while ago.
    Setting V Sync helps to solve the issue. It seems it's really framerate dependant.
    Without VSync set in Quality Settings I could easily re-create the issue. Once switching V Sync on, the problem is solved.
    I can always re-create it in a clean project as well.
    Debug.log also decreases the framerate slightly so yes I see the similarity here.
     
    Last edited: Mar 11, 2017
  19. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    Sorry, I changed my mind and won't display the case number as I realized it would expose whole the previous and further communication for all test cases in my account to public forever which I don't want.
    I'll try to put a note once I know some detail regaring to the problem.
    Also the fix with magic number seems to be a bit weird to me. Ensure you guys don't use non uniform scale for any object that takes place in the interaction (ground + player).
    Does this happens also with uniform scale of all the objects involved in the collision (e.g. scale 1,1,1 or 3,3,3 but not 1, 4, 2)?
     
  20. kubajs

    kubajs

    Joined:
    Apr 18, 2015
    Posts:
    58
    This is not a bug of Unity. The problem occurs on high frame rates because I have set “Min Move Distance” to 0.001 and called _characterController.Move() method with velocity that was multiplied by Time.deltaTime which will give very small value on high frame rates (smaller then 0.001). When character is not moving isGrounded returns false.
     
  21. hdchieh

    hdchieh

    Joined:
    Mar 9, 2018
    Posts:
    1
    I think this is a bug,not much crucial.but when you move by a vector and the character is on the ground now,if the vector is exactly parallel to the ground then isGrounded should return true.
     
    pajowa15 likes this.
  22. newjerseyrunner

    newjerseyrunner

    Joined:
    Jul 20, 2017
    Posts:
    966
    I noticed mine going between true and false each frame too and I remedied it by introducing coyote time, which made the character's movement feel better anyway. Rarely in games do you actually want to start falling the very frame you leave the ground.


    Code (csharp):
    1. class myCharacter {
    2.     private float coyoteTime = 0;
    3.     private const float const_maxCoyoteTime = 0.1f;
    4.     void Update(){
    5.         controller.Move(moveVector);
    6.         if (controller.isGrounded){
    7.             coyoteTime = 0;
    8.         } else {
    9.             coyoteTime += Time.deltaTime;
    10.         }
    11.     }
    12.  
    13.     public bool isGrounded(){
    14.         return coyoteTime < const_maxCoyoteTime;
    15.     }
    16. }
     
    Last edited: Mar 13, 2018
    k1hnr, evieepy, xpoDeveloper and 3 others like this.
  23. Akathon

    Akathon

    Joined:
    Sep 24, 2016
    Posts:
    2
    Thank you for this. Am working on a project that is still in the proof of concept phase, and with this I now have controls that are reasonably satisfying without having to write my own character controller. Both jumping and moving down steep slopes are now fixed.
     
  24. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,913
    AlejMC likes this.
  25. assassinshadow

    assassinshadow

    Joined:
    May 22, 2017
    Posts:
    1
    Using the character controller to detect collision seems like a quick hack.

    Code (CSharp):
    1. if (!controller.collisionFlags.ToString().Equals("None")) {
    2. //player is grounded
    3. grounded = true;
    4. airHeight = 0;
    5.  
    6. }
    7. else {
    8. //player not grounded
    9. }
     
    Miranda-03 likes this.
  26. ENAYSOFT

    ENAYSOFT

    Joined:
    Aug 22, 2012
    Posts:
    13
    Gorov had this right in his post. Any kind of Physics, including Character Controllers are updated in Unity using FixedUpdate. You're calling controller.Move( in Update()

    You should NOT be using Update, since Update is called at different times to FixedUpdate. If you update in two different Universes then of course things are going be randomly glitchy.

    Put "Debug.Log("1"); in Update()
    and "Debug.Log("2") in FixedUpdate() and watch what happens in the inspector

    Putting extra checks and fixes in Update to "fix" isGrounded, it's probably just a losing battle and a similar problem might crop up again later. Perhaps your game is fixed now with your additional code in Update. Try changing Update to FixedUpdate() and see what happens.
     
    LauroFilho and RickshawDerpyDerp like this.
  27. Taylor-Howell

    Taylor-Howell

    Joined:
    Feb 8, 2018
    Posts:
    2
    Hey guys, this is a simple solution that works for at least for FPS games. I noticed that my vertical velocity when grounded would bounce a bit between 0 and - .2 when grounded. All I did was check to see if the vertical velocity decreased to less than -1 (actually falling) and then played my falling loop. Hope this helps!
     
  28. Berkeaksoy

    Berkeaksoy

    Joined:
    Oct 26, 2019
    Posts:
    1
    Hello,

    I fix the isGrounded problem by subtracting a groundFixer value from my vector3.y. I hope it help to you :)



    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Player : MonoBehaviour
    6. {
    7.  
    8.     private CharacterController CharacterController;
    9.     private UIManager uiManager;
    10.     [SerializeField]
    11.     private float speed = 6, gravity = 1, yVelocity = 0, jumpHeight = 12, groundFixer = 0.0001f;
    12.     private int coins;
    13.     private bool canDoubleJump = false;
    14.     // Start is called before the first frame update
    15.     void Start()
    16.     {
    17.         CharacterController = GetComponent<CharacterController>();
    18.         uiManager = GameObject.Find("Canvas").GetComponent<UIManager>();
    19.  
    20.         if(uiManager == null)
    21.         {
    22.             Debug.LogError("UIManager is null!");
    23.         }
    24.     }
    25.  
    26.     // Update is called once per frame
    27.     void Update()
    28.     {
    29.         float horizontalInput = Input.GetAxis("Horizontal");
    30.  
    31.         Vector3 direction = new Vector3(horizontalInput, 0, 0);
    32.         Vector3 velocity = direction * speed;
    33.  
    34.         if (CharacterController.isGrounded)
    35.         {
    36.             canDoubleJump = true;
    37.             yVelocity = 0;
    38.             if (Input.GetKeyDown(KeyCode.Space))
    39.             {
    40.                 yVelocity = jumpHeight;
    41.             }
    42.         }
    43.         else
    44.         {
    45.             if(yVelocity < -20)
    46.             {
    47.                 yVelocity = -20;
    48.             }
    49.             else
    50.             {
    51.                 yVelocity -= gravity;
    52.             }
    53.  
    54.             if (Input.GetKeyDown(KeyCode.Space) && canDoubleJump)
    55.             {
    56.                 canDoubleJump = false;
    57.                 yVelocity = transform.position.y + jumpHeight;
    58.             }
    59.         }
    60.  
    61.         velocity.y = yVelocity - groundFixer;
    62.         CharacterController.Move(velocity * Time.deltaTime);
    63.     }
    64.  
    65.  
    66.  
    67.     public void giveCoins(int coinValueIn)
    68.     {
    69.         coins += coinValueIn;
    70.         uiManager.updateCoinsText(coins);
    71.     }
    72. }
    73.  
     
    Cani23 likes this.
  29. imacommenterat

    imacommenterat

    Joined:
    Dec 8, 2019
    Posts:
    1
    Hello! I fixed mine by not setting the vertical velocity to 0 but instead to
    Mathf.Epsilon
    since
    CharacterController.isGrounded
    according to the documentation "Was the CharacterController touching the ground during the last move?" so if it the vertical velocity is set to 0 then the CharacterController is technically hovering. Here's a sample code:

    Code (CSharp):
    1. if (!controller.isGrounded)
    2. {
    3.     // The variable gravity is negative
    4.     verticalVelocity += gravity * Time.deltaTIme;
    5. }
    6. else
    7. {
    8.     verticalVelocity = -Mathf.Epsilon;
    9. }
    10.  
    11. // Your code for movement and etc. //
    12.  
    13. // movementVelocity is the velocity from your code assuming it's Vector3
    14. controller.Move(movementVelocity * Time.deltaTIme + Vector3.up * verticalVelocity * Time.deltaTIme)
    NOTE: Use
    CharacterController.Move()
    once since
    CharacterController.isGrounded
    relies on the last move.

    NOTENOTE: See https://stackoverflow.com/questions/30216575/why-float-epsilon-and-not-zero if you don't know what Epsilon is.
     
    iagoccampos, ShadYueh and AlejMC like this.
  30. Astrokatz

    Astrokatz

    Joined:
    Sep 16, 2012
    Posts:
    1
    Sorry for ressuscitating this thread, but I saw this post and none of the solutions worked for me. So I wanted to share my solution:

    Code (CSharp):
    1.  
    2.     private CharacterController controller;
    3.     private Vector3 playerVelocity;
    4.     private float playerSpeed = 5.0f;
    5.     private float jumpHeight = 1.0f;
    6.     private float gravityValue = -9.81f;
    7.  
    8.     private void Start() => controller = GetComponent<CharacterController>();
    9.  
    10.     void Update()
    11.     {
    12.         var horizontalInput = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
    13.         var horizontalVelocity = horizontalInput * playerSpeed;
    14.  
    15.         if (horizontalVelocity != Vector3.zero) transform.forward = horizontalVelocity;
    16.  
    17.         playerVelocity = new Vector3(horizontalVelocity.x, playerVelocity.y, horizontalVelocity.z);
    18.  
    19.         if (controller.isGrounded)
    20.         {
    21.             if (Input.GetButtonDown("Jump"))
    22.                 playerVelocity.y += Mathf.Sqrt(jumpHeight * -playerSpeed * gravityValue);
    23.  
    24.             if (playerVelocity.y < 0) playerVelocity.y = 0; // Prevent player from getting negative potential
    25.         }
    26.         playerVelocity.y += gravityValue * Time.deltaTime; // Make gravity always act on the player
    27.  
    28.         controller.Move(playerVelocity * Time.deltaTime); // Move a single time on the frame. More than once gives weird results
    29.     }
    30.  
    Besides that I set the Min Move Distance on the Character Controller (on Unity's UI) to 0.

    Hope this helps someone withthe same problem!
     
    AlfieB, apotema and Gamerman12 like this.
  31. Twilight_18

    Twilight_18

    Joined:
    Feb 23, 2020
    Posts:
    3
    Simply add this line of code to your Update function. By forcing a slight downward velocity whenever the player is grounded, isGrounded works every time. Now mash that jump button and have fun!

    if(controller.isGrounded && playerVelocity.y < 0){
    playerVelocity.y = -0.1f;
    }
     
    Taylor-Howell and Joe-Censored like this.
  32. RageByte

    RageByte

    Joined:
    Jul 2, 2017
    Posts:
    34
    The real culprit here that's causing the
    Code (CSharp):
    1. if (controller.isGrounded && velocity.y < 0)
    2. {
    3.     velocity.y = 0;
    4. }
    5.  
    to not keep the controller grounded is the CharacterController Min Move Distance being set to 0.001 by default. Look in the CharacterController inspector and change it to 0. ;)
     
    Last edited: Mar 12, 2021
  33. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,735
    Neat! A ten-year-old thread... but I suppose it's still relevant since we're are all still struggling with this object. Here was my findings and my work-around for the broken example code provided by Unity:

    CharacterController CharMover broken:

    I wrote about this before: the Unity example code in the API no longer jumps reliably. I have reported it. Here is a work-around:

    https://forum.unity.com/threads/how...racter-movement-in-unity.981939/#post-6379746
     
    RageByte and PutridEx like this.
  34. sshpcl2

    sshpcl2

    Joined:
    Nov 4, 2020
    Posts:
    1
    Many thanks .. that's solve mine
     
    RageByte likes this.
  35. RageByte

    RageByte

    Joined:
    Jul 2, 2017
    Posts:
    34
    An ago old issue. Here's what I do now.
    Code (CSharp):
    1.     void Start()
    2.     {
    3.         controller = GetComponent<CharacterController>();
    4.         controller.minMoveDistance = 0f;
    5.     }
     
  36. theduke906

    theduke906

    Joined:
    Apr 4, 2021
    Posts:
    4
    -Oh my goodness thank you! This actually worked!!!
     
  37. bolibob2

    bolibob2

    Joined:
    Feb 15, 2013
    Posts:
    1
    i know i'm not the one who asked this, but THANK YOU SO MUCH! i've been wrestling with a simillar piece of code and this turned out to be the solution for mine.
     
  38. Squishymonster

    Squishymonster

    Joined:
    Jan 16, 2018
    Posts:
    1
    This is the answer. Thanks!
     
  39. thorns111

    thorns111

    Joined:
    Feb 22, 2019
    Posts:
    5


    could it be a bug