- Home /
rigidbody2D : X velocity is slowed down when I jump.
Hi everybody! First question here. :)
From the code I made for a 2D game or even in the Unity 2D demo project, when I jump the velocity.x is maxed at 2 and if I release the arrow key in middle air, the character seems to speed up. Don't know what cause that. At first I tough the problem was on my side but when I tested again the Unity 2D demo project, I noticed the same problem. You'll especially notice it when you release the move key middle air. Cans someone explain this to me or have fixed it yet? Thanks!
Here is my code : using UnityEngine; using System.Collections;
public class PlayerControlBonhomme : MonoBehaviour {
public float moveForce = 365f;
public float maxSpeed = 5f;
public LayerMask groundLayerMask;
public float jumpForce = 1000f;
private bool faceRight = true;
private Animator anim;
private Transform groundCheck;
private bool jump;
private bool grounded = false;
// Use this for initialization
void Start () {
anim = GetComponent<Animator>();
groundCheck = transform.Find("groundCheck");
}
void FixedUpdate () {
float h = Input.GetAxis("Horizontal");
anim.SetFloat("speed", Mathf.Abs(h));
if(h * rigidbody2D.velocity.x < maxSpeed)
rigidbody2D.AddForce(Vector2.right * h * moveForce);
// Si la velocité est trop grande, on se créé un nouveau vector2 qui la limite
if(Mathf.Abs(rigidbody2D.velocity.x) > maxSpeed)
rigidbody2D.velocity = new Vector2(Mathf.Sign(rigidbody2D.velocity.x) * maxSpeed, rigidbody2D.velocity.y);
if (h < -0.01f && faceRight) {
Vector3 scale = transform.localScale;
scale.x *= -transform.localScale.x;
transform.localScale = scale;
faceRight = !faceRight;
}
if (h > 0.01f && !faceRight) {
Vector3 scale = transform.localScale;
scale.x = Mathf.Abs (transform.localScale.x);
transform.localScale = scale;
faceRight = !faceRight;
}
if(jump)
{
rigidbody2D.AddForce(new Vector2(0, jumpForce));
jump = false;
}
}
// Update is called once per frame
void Update () {
grounded = Physics2D.Linecast(transform.position, groundCheck.position, groundLayerMask);
if(Input.GetButtonDown("Jump") && grounded)
jump = true;
print (rigidbody2D.velocity.x);
}
}
I have this exact same problem. In my own game, as well as thee Tower Bridge Defence example. I would upvote the question, but I can't, since I don't have 15 reputation.
Answer by HappyMoo · Jan 03, 2014 at 04:16 AM
Damn... answer length is limited.. my answer got cut - I try to distribute on comments...
Oh, the code is not that nice... that's from an official Tutorial? I see multiple problems... and also where the speedup comes from.
Ok, lets try to fix that. I won't check any code in unity, so I might have syntax errors, but you should be able to fix them if you understand the code above.
Here's the first Problem:
if(h * rigidbody2D.velocity.x < maxSpeed)
rigidbody2D.AddForce(Vector2.right * h * moveForce);
This is problematic in multiple ways... First you think that this just checks for movement to the right, because it only checks for vel.x not being too big, but what happens if we're moving to the left? Well then h is also negative and a negative vel.x multiplied with a negative h makes it positive again, so you can check against maxSpeed... well so you thought. Because actually, we can still slide/move to the left and then press right to get a positive h, which makes the whole thing negative... which passes the check and doesn't lead to a wrong movement, but the designer clearly didn't think about that case.
Anyway... the real problem starts when you are at maxSpeed and let go of the buttons. Then h isn't 0 immediately, but is smoothed for keyboard and if you would use an analog stick, you also get values like 0.9 etc.... So you're moving at maxSpeed and h is something like 0.9... well, then
0.9*vel.x<maxSpeed
and you accelerate although you are already at maxSpeed.
You then try to clamp back speed that is too big, but you already added the force and that force isn't applied as an impulse, but as a continuous force - that'S why the AddForce method has an optional Force$$anonymous$$ode parameter - so this means that some of the force is applied over the next frames and moves the character somwhat too far, even when the speed is cut back between frames.
Your direction changing code is also confusing, because once you copy the vector into "scale", you have all values in there, why do you access the localscale again? And why do you use two methods to turn the -1 to 1 and 1 to -1? The first time you multiply and the other time you do $$anonymous$$athf.Abs. When you surely know that that value can only be 1 or -1, why not just multiply by -1 both times?
Also the overall Idea, that you throw a force into a black box physics system, but then mess with the system, by not correcting with forces again, but manipulating the velocity directly. You shouldn't manipulate forces and velocity as they are connected. That's like you don't know what you're doing... pushing at A and watching what the system does and then push back at B.
You clearly controlling the character over the velocity (at least the horizontal movement), so you should stay within the velocity domain and do the velocity changes yourself directly, not by using forces or rather apply the horizontal force yourself and not throw it into the physics system. Then for the vertical movement it's ok to use forces here, as we don't need to mess with the velocity again and gravity is also an acceleration/force that gets applied in the same direction.
Also, the grounded variable can also be potentially problematic if the jump force isn't enough to get the player outside of the grounded state within a frame, because if it's not enough force, the player could press jump on two consecutive frames and perform an extra high jump. But I can't say if it's really problematic, because I don't know where the groundcheck position exactly is, so this may only be a potential problem, but not show up concretely, so I won't change anything there.
Well, let's try to fix this mess... ;)
float horizVel = 0f;
static Vector3 mirrorX = new Vector3(-1,1,1);
void FixedUpdate () {
float h = Input.GetAxis("Horizontal"); // our wish speed between -100% and 100%
anim.SetFloat("speed", $$anonymous$$athf.Abs(h));
// calculate adding the force ourselves: acceleration = Force/mass ... and clamp the velocity
horizVel = $$anonymous$$athf.Clamp(horizVel + h*(moveForce/rigidbody2D.mass)*Time.deltaTime, -maxSpeed, maxSpeed);
Vector3 vel = rigidbody2D.velocity;
vel.x = horizVel;
rigidbody2D.velocity = vel;
if ( (h<-0.01f && faceRight) || (h>0.01f && !faceRight) ){
transform.localScale = Vector3.Scale(transform.localScale, mirrorX);
faceRight = !faceRight;
}
if(jump)
{
rigidbody2D.AddForce(new Vector2(0, jumpForce));
jump = false;
}
}
Thank you for this! :D I have two comments, though: 1) Correct me, if I'm wrong (I'm very new to Unity), but we don't need Time.deltaTime when we're working in FixedUpdate()? 2) You forgot to use the h-variable with the speed, which means there will be no deceleration, unless you change direction with the input. $$anonymous$$y workaround was to multiply it in the Clamp as such: horizVel = $$anonymous$$athf.Clamp((horizVel + (moveForce/rigidbody2D.mass))*h, -maxSpeed, maxSpeed);
but I'm not too good with physics, so this might not be the best approach.
Once again, thank you for your reply and explanation of the problem! :D