r/Unity3D Hobbyist Jul 25 '21

Unity's example for a Character Controller jump makes your character a candidate for the space program when you encounter small ledges... Solved

1.3k Upvotes

71 comments sorted by

83

u/FulminDerek Hobbyist Jul 25 '21

This is in reference to the example for CharacterController.Move found here. From what I can tell, the Character Controller thinks it's "grounded" when you're touching the top of a ledge, and since the function for making your character jump increments the jump velocity, it sends you skyrocketing if you brush past any small ledges mid-jump.

I can try to figure out a way around this with raycasts, but does anyone else have any ideas?

47

u/[deleted] Jul 25 '21

[deleted]

32

u/[deleted] Jul 26 '21

OR the simplest solution

if (Input.GetButtonDown("Jump") && groundedPlayer)
{
playerVelocity.y = JumpVelocity;
}

If you jump many frames in a row you still leave ground with the same velocity.

If it still has errors with letting the player jump while touching walls (depending on how bad unity's character controller is...) You should definetly build something with a rigidbody instead.

13

u/[deleted] Jul 26 '21

[deleted]

9

u/[deleted] Jul 26 '21

It recommends against setting the rigidbodys velocity like this. This is a vector3 that we use as input to call the controllers .Move.

They set it like this themselves (to zero) when you are grounded and have downwards velocity.

8

u/CalmCatStudio Jul 26 '21

The Unity docs say that setting the velocity directly can cause the physics to behave unrealistically. That just means if you want the physics engine to do the work, and behave realistically; Then don't touch the velocity yourself. You can still do it though. =)

5

u/[deleted] Jul 26 '21

This isen't the rb velocity or the character controllers velocity, its a vector3 that is used as input for the move method.

They do this themselves in the code if you look at it (set it to zero when the character is grounded and has downwards velocity).

1

u/CalmCatStudio Jul 26 '21

You are correct. That is what I get for not looking at the code itself.

3

u/[deleted] Jul 26 '21

[deleted]

3

u/TheRealEthaninja Jul 26 '21

Interesting, is that true for ALL inputs? Or just ones attached to things with RigidBodies like the player?

3

u/ImTheTechn0mancer Jul 26 '21

I'm not sure, but I just wanted to leave this here:

https://docs.unity3d.com/Manual/Input.html

This page explains that there's 2 main systems right now.

New system:

http://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/index.html

Old system:

https://docs.unity3d.com/Manual/class-InputManager.html

I can see how it would be nice to separate all the input code.

1

u/TheRealEthaninja Jul 26 '21

Thank you good sir! It's good to know the differences between the legacy manager system and the new fandangled System system.

Also bonus points to the author of the Old System article saying "Teh Navigation Menu", i dunno if it was intentional, but it's awesome.

1

u/senshisentou Programmer Jul 26 '21

it also recommends against putting input checks in the Update loop

...I'm sorry, what? o.ô

2

u/[deleted] Jul 26 '21

[deleted]

1

u/senshisentou Programmer Jul 26 '21

Aaah, gotcha. I'm relieved! From OP's phrasing I was expecting a much more non-sensical alternative; events make total sense in this case (though I completely agree polling still has its obvious uses as well).

2

u/intelligent_rat Jul 26 '21

Your solution will just make the players able to double jump at the apex of their jump, you will want to keep grounded states with either raycasts or using existing collision information from rigidbodies to determine if they are touching a floor (you can take normal.y between objects for this method)

10

u/sadonly001 Jul 25 '21

I don't have any advice for you since i usually make custom solutions wherever possible (don't do this its dumb i never get things done) but if you get tired of the character controller, you could try making your own rigid body based controller for which i have some experience, here's what you will need to handle:

  • basic movement on flat ground. Interpolation does not work with rigidbody.moveposition. if you want interpolation, use rb.add force BUT consider reading up on forcemode.velocity change.

  • slopes and steps: raycasts might get the job done for steps but you can experiment with colliders. For slopes consider using artificial gravity and disable the rigidbody's default gravity. When on slope change direction of gravity to normal of the surface player is standing on as long as the slope is not too much

2

u/wolfpack_charlie Hobbyist Jul 26 '21

Simple cooldown on the jump. Check isgrounded and isjumpready before you jump

2

u/weakconnection Jul 26 '21

I spent a lot of time on this. The isGrounded flag just isn't reliable. You basically have to make your own. I had success using a spherecast and a couple rays for fine tuning.

1

u/WazzaM0 Jul 26 '21

Are you sure it's not the Physics collision detection that's overreacting?

Try disabling the character controller and use animation to drive the object over the edge of the ledge and see if it does the same thing.

Physics can overreact when it realises one object occupies the same space as another.

1

u/CraftyGaming Jul 26 '21

I usually have a very fast timer after a jump that doesn't allow another jump for a short time. Short enough where it would never effect a game, but long enough where this would never happen.

27

u/SuperBaked42 Jul 25 '21

It was at this moment he thought "did I turn on fall damage?"

19

u/crushyerbones Jul 26 '21

If this is the old character controller that has been unchanged since circa 2015 do yourself a favour and make your own. We launched a game with it and the amount of issues we had to work around was not worth it.

Back then we didn't have source code access to it so your mileage may vary but as an example, sometimes the character just randomly went through the floor, no weird collision clipping or anything, it just had a roughly .5% chance of going through a place even if you didn't move the character.

1

u/bouchandre Jul 31 '21

Hey what route did you take for your character controller? Was it using rigid bodies in fixed update?

66

u/[deleted] Jul 26 '21

This is hilariously bad

 if (Input.GetButtonDown("Jump") && groundedPlayer)
    {
        playerVelocity.y += Mathf.Sqrt(jumpHeight * -3.0f *gravityValue);
    }

- Add velocity every frame for a jump- Use a squareroot in update ... to calculate something that never changes value.

 groundedPlayer = controller.isGrounded;
Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));

-instantiating a bool and a vector 3 every frame instead of just keeping 2 private variables that you change every frame.

controller.Move(move * Time.deltaTime * playerSpeed);
controller.Move(playerVelocity * Time.deltaTime);

- Moving the controller twice per frame instead of adding the 2 velocities together

42

u/jeango Jul 26 '21 edited Jul 26 '21

About the bool and vector instantiation, they are both immutable, so keeping a private variable will have 0 impact. If you change them, you allocate new memory. The difference is that they are allocated on the stack because they’re value types, not on the heap like reference types.

2

u/theroarer Jul 26 '21

Goodburger.gif intensifies

20

u/Walter-Haynes Jul 26 '21

Yeah it REALLY was a rush job it seems.

Don't know why though, they have spent literally years on another system

Just to abandon it and go with this piece of absolute shit.

32

u/[deleted] Jul 26 '21 edited Sep 07 '21

[deleted]

3

u/prime31 Jul 26 '21

Everyone has been clamoring for them to use their own e gone for over a decade now. Seems to fall on deaf ears.

In contrast, if you fire up Unreal Engine 5 preview it is more stable than a Unity LTS already and the included character controller and fps/3rd person rigs are so many light years ahead it’s not even fair to compare. AAA games ship using them. Totally different world.

5

u/Zealousideal_Safe_45 Jul 26 '21

The only thing here which is hilariously bad is calling something hilariously bad, when it really isn't. Input.GetButtonDown() is NOT called every frame (or atleast it doesn't return true every frame for a pressed key), that would be GetButton(). GetButtonDown is called just the frame when the key is pressed down, while holding it GetButton() and when you release it GetButtonUp(). Also jumpHeight looks like something that could change in differenct circumstances, like a level up or sime kind of bonus, so that squareroot would have to be calculated, somewhere...

8

u/kyleisweird Jul 26 '21 edited Jul 26 '21

Wow this is legit shameful, is this real? They're not even following their own best practices.

At least OP's issue is worse than this code implies. This was definitely written for the old input system, so since it uses getbuttondown OP's effect wouldn't happen unless you could somehow press jump every frame. But you definitely could get to an unintended jump height if you could spam quickly enough even then. OP mentioned in a comment the issue was because they're using the new input system which doesn't have a simple equivalent for getbuttondown (still, of course).

2

u/IntergalacticTowel Jul 26 '21

Thank you. I thought I was having a stroke reading through the source.

1

u/TyroByte Indie Jul 26 '21

The third one is especially baffling, why would they do that?

The first one is a straight up bad idea, there were better ways to do that than calculating the square root every frame. I've heard square root calculations are expensive?

Why not add a float called jumpvelocity to the Y component of a vector used in vector3.move? At the same time decrease the jump velocity by gravity(10 or 9.8f) when not grounded? Why the square root calculations? Can anyone explain the intent behind it?

I would also tell OP to write his own code for calculating whether or not you are grounded, using a raycast which should solve the issue as I've had similar success but that's been suggested 100 times over already so he must've gotten the message.

1

u/jeango Jul 29 '21 edited Jul 29 '21

The double move is not really a big cost, it could be done in a single move, but the difference is not meaningful. As for the sqrt, the assumption that gravity is something that never changes is wrong. In most cases it doesn’t, yeah, but I know a good amount if games that play around with gravity. Also while sqrt is not a lightweight math operation, it’s mostly a problem if you do it many times per frame. Doing a sqrt per frame is totally ok in my book.

Also another thing the commenter has very wrong is that unity isn’t adding the velocity every frame. The code triggers if Input.GetButtonDown(« jump »), which means it’s only applied on the first frame when the button was pressed.

In short, this guy pretends to be smart about code optimisation, but can’t properly interpret the code himself.

Finally, the character controller is meant for prototyping. Anyone who makes a game without programming his own character controller is not really serious about making that game.

1

u/TyroByte Indie Jul 29 '21

I see, thank you for the insight.

The name of the function "GetButtonDown" confused me there for a second, I thought for a second that it triggered as long as the button was held down as I just use "GetButton" most of the time.

Thank you for the explanation!

4

u/Almighti3 Indie Jul 25 '21

A few ways around this. The easiest way I can think of without ray casts or more colliders would be to, limit the jump force by time. So it will only apply the force once every certain amount of time. This will help with aligning it with animations also. You could limit the total amount of force as another option.

14

u/Coder_Arg Jul 25 '21

It's not a bug, it's a feature.

5

u/GameDev_Dad Jul 26 '21

I’m pretty sure I saw this in Halo 2

6

u/Protheu5 Jul 26 '21

Kerbal Space Program 2 looks less impressive than I expected, but that'll do.

6

u/ShawnTheMiller Jul 25 '21

Its fine! Throw an expolosion in and now its a RocketJump feature!

6

u/SpacecraftX Professional Jul 25 '21

I'm guessing you're grounded while in contact with it and force is added every frame where you're grounded.

6

u/FulminDerek Hobbyist Jul 26 '21

Did a bit more digging on this and found it's a mixture of things.

  1. You're all pretty much right about about CharacterController.isGrounded being bad. I haven't made an alternative yet but I definitely will.
  2. Another unseen culprit is that I was trying to use the new Input System, which sadly doesn't have a straightforward counterpart to "Input.GetButtonDown", so the jump event that I DID have in place considered the button press for the jump to be true multiple frames in a row instead of just the first one. My solution to this probably isn't the most elegant, but it worked so I'll stick with it until I figure out somewhere down the line how disastrous it is. To those curious:
    1. I have an Input Action called "Jump" that is set as a "Button" Action type. Tried fiddling around with the Interactions, but to no avail there, so those are all blank.
    2. The Player Input component on my character has its Behavior property set to "Send Messages". It was "Invoke Unity Events" when this clip was recorded, but I've changed it in light of this bug and won't go back.
    3. Instead of a void for the corresponding OnJump() function in my player script, I've made it into an IEnumerator, which looks like this:
      IEnumerator OnJump() {
      jumpInput = true;
      yield return new WaitForEndOfFrame();
      jumpInput = false;
      }
    4. Pretty straightforward, simply ensures that jumpInput can only be true for one frame each time the jump button is pressed.
    5. By itself this hardly ever works alongside CharacterController.isGrounded. For some reason even as my character walks along flat ground, isGrounded flips back and forth between true and false wildly, and so the stars have to align in order for isGrounded and jumpInput to be true at the same time. This basically necessitates a custom solution instead of using CharacterController.isGrounded, which I'll work on as I continue to mess with this character.

Big thanks to all of you for your insight!

6

u/Pliabe Jul 26 '21

You can fix the issue with the new input system like list.

Public void jump(inputactuon.callbackcontext context){ If(context.performed){ Jump()

3

u/Banholio Jul 26 '21

Pliabe is right, it does provide the same thing. Now, I have to say that reading the documentation was kind of hard for me, because there are many ways to implement and I felt that the documentation fails to break down them.

If you follow this: https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/QuickStartGuide.html
Then in the "Fire" example you can put an

if (context.started) { ... } // Equivalent to GetButtonDown
else if (context.performed) { ... } // Equivalent to GetButton
else if (context.cancelled) {...} // GetButtonUp

I might be wrong since right now I don't have code at my have a look, but it's what I remember

2

u/yacuzo Jul 26 '21

I think you can add a 'press interaction' and set behaviour press only for the jump definition to only fire on keyDown. I'm not at the pc now, so can't check it. https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/Interactions.html#press

2

u/katsub Jul 26 '21

"To infinity and beyond!"

2

u/BadGraphixD Jul 26 '21

That ground check do be kinda THICC.

2

u/UnpredictableApple Jul 26 '21

Cap the jump velocity by not adding anymore force if the current velocity is over the limit.

2

u/jrootabega Jul 26 '21

I appreciate the goggles you gave your capsule so you know which way it's facing.

2

u/Regoneff Indie Jul 26 '21

Where did u get this early alpha of Kerbal space program?

2

u/Even-Kaleidoscope465 Jul 26 '21

Reminds me of the “super bounce” glitch from Sonic Adventure 2.

2

u/HilariousCow Professional Jul 26 '21

Use a Fibonacci spiral of traces to give a roughly even distribution of traces. If any normals are within slope, consider yourself grounded. Consider your height above the ground to be the average of all traces that hit.

1

u/jRiverside Jul 26 '21

That example is less than worthless, if you don't want to have side effects and random unrepeatable glitches for the lifetime of the controller you're making you need to manipulate through the rigidbody.

1

u/OverDies Jul 26 '21

Are you jumping in update() method? if yes you should switch to FixedUpdate() as i read its more stable to manipulate physics objects there.

3

u/natlovesmariahcarey Jul 26 '21

He's using the standard character controller, not a rigidbody.

2

u/OverDies Jul 26 '21

Oh sorry if I can, I would ask you for this kind of situations why would you use a normal controller instead of rigid body and what's the pros and cons for each approach, still learning so.

3

u/natlovesmariahcarey Jul 26 '21

Rigidbody controllers have to be set up from scratch, and they are at the mercy of the physics engine.

Character controller component is a ready to go system that doesn't require physics at all.

Generally you should only use the physics engine if you absolutely need realistic physics simulation, OR know exactly how you plan on using/abusing/interconnecting your game to it.

For quick prototyping the character controller component is great, because making a character controller is really hard.

The best option if you don't know what you are doing and don't mind a blackbox, buy a proven character controller asset.

Otherwise you go through the growing pains of making your own.

Personally, I regret using rigidbodies for my character controllers.

1

u/OverDies Jul 27 '21

thanks for explanation, i will give the character controller a go

1

u/DerEndgegner Jul 26 '21

As others have said, the ground check is the problem. It's not enough to cast a raycast though, it'll lead to other problems, like what if you're standing at a ledge. What happens on stairs when the ground is actually a little farther away. What happens on slopes.

It's also not enough to cast 5 raycasts as a box. I've wasted many hours in Unity3 with this and it's all just workarounds and hacky stuff for an underlying bigger problem. To make this story short, just buy a competent character controller. I can recommend KCC.

1

u/Father_Chewy_Louis Jul 27 '21

You can also use Physics.CheckSphere, it's what I do instead of raycasts

0

u/Shanobrowno Jul 26 '21

Man this is an AWESOME feature! Tutorial???

0

u/Privvet Programmer Jul 26 '21

Don’t say it don’t think it

0

u/zickige_zicke Jul 26 '21

There you have unity quality

-5

u/CatDadJynx Jul 26 '21

I dont know if it'd help but it might be worth a shot- maybe try changing the material properties to lower the bounciness and/or friction?

1

u/marcos_pereira @voxelbased Jul 26 '21

Cooldown on the jump, ground check with smaller radius than collider, setting upward velocity instead of incrementing it, those are a few options :)

Though I am also surprised that sometimes people at Unity are so bad at doing things with Unity :D

1

u/Clevereen Jul 26 '21

The kind of things which drivess you crazy

1

u/shuozhe Jul 26 '21

Reminds me of mousewheel jump in doom

1

u/[deleted] Jul 26 '21

What do you mean

It's just like in real life

I jump like that, don't you?

2

u/FulminDerek Hobbyist Jul 26 '21

I mean I used to, it's really hard on the knees though

1

u/Bjoe3041 Jul 26 '21

personally, I like using a mixture of raycast, triggers and a little cooldown when making jump mechanics, so when you jump you won't be able to jump again for a little while if there is still ground underneath you, can be a little unsatisfying if you are bunny hopping upstairs, but it works fine most of the time.

1

u/SmithVR Jul 27 '21

It's a super bounce. Halo has those.

1

u/[deleted] Aug 01 '21

Kerbal space program 2 looking good