- Home /
[bug] Velocity set to 0 by Unity
Hi,
I have a curious bug in my project.
My project is simple, I have a rigidbody who run indefinitly. I ve made a player controller attached to my rigidbody with the following update :
A fixed update to move my rigidbody and flip it if necessary :
void FixedUpdate () {
if (grounded)
{
rigidbody2D.velocity = new Vector3(xSpeed * direction, rigidbody2D.velocity.y, 0f);
}
//flip sprite player
Vector3 theScale = transform.localScale;
theScale.x = direction;
transform.localScale = theScale;
leftGroundCheckTransform.position = new Vector2 (collider2D.bounds.min.x+0.05f, collider2D.bounds.min.y-0.05f);
rightGroundCheckTransform.position = new Vector2 (collider2D.bounds.max.x-0.05f, collider2D.bounds.min.y-0.05f);
leftSideCheckTransform.position = new Vector2 (collider2D.bounds.min.x - 0.02f, collider2D.bounds.center.y);
rightSideCheckTransform.position = new Vector2 (collider2D.bounds.max.x + 0.02f, collider2D.bounds.center.y);
}
And an Update that make the following thing :
void Update(){
//check player position
UpdateGroundedStatus();
//check jump
GestionPlayerJump ();
//check speed
GestionXSpeed ();
}
So my problem is :
Everything works fine, but randomly my rigidbody.velocity is set to 0 after an update with no reasons. I've test it Many many time, when the bug comes out, the velocity is set to the right value as the update finish, but when it enter the update again the velocity is set to 0.
It's totally random. I have just a rigibody who run along the scene and run back when there's an obstacle.
I read that Unity sometime bugs when addforce is used at the same time we directly change the velocity, so I deleted all the reference to addforce. But the bug stays here.
I want to know if someone have an idea if it is really a bug or if i make something wrong ? Thanks in advance.
PS sorry for my bad english ! :)
Hi, it's difficult to spot he problem. I guess you've checked that you never set the velocity to zero with your code (i.e. xSpeed = 0 or direction = 0 before you compute it).
Could it be because sometimes there are 2 Update() called before a FixedUpdate() is called (if the game runs faster than the FixedUpdate rate) or the other way around: 2 FixedUpdate can be called before an Update() is called when the framerate is low. I mean, I don't know what you do in the code called in the Update, just don't assume that there is 1 call to FixedUpdate followed by 1 call to Update, this is not always the case.
_dns is right, but it;s impossible to help more without seeing the contents of your
UpdateGroundedStatus();, GestionPlayerJump (); GestionXSpeed (); routines.
Answer by DamDamDuSixZero · Jan 07, 2015 at 11:51 PM
Hi and Thx for your answer.
In fact, I never set the velocity to zero in my code. Sometimes xSpeed is reduced but never set to 0. The same goes for direction.
It's very curious as the rigidbody travels a little space and it can stop the movement after 10sec or after 5mn, it's totaly random. And the position of my rigidbody is never the same when the bug occurs.
I didnt post all my code because I didnt want to make a huge post but i'm going to post it here :
sing UnityEngine;
using System.Collections;
public class MuleController : MonoBehaviour {
public float xSpeed = 0.0f;
public int direction = -1;
public float xSpeedMax = 100.0f;
public float xSpeedMin = 40.0f;
public float xSpeedAcceleration = 0.008f;
public float xSpeedDecceleration = 0.05f;
//le step d acceleration lors du premier appui sur l acceleration
public float xSpeedAccelerationStep1 = 7.0f;
public float jumpForce = 500.0f;
private Transform leftGroundCheckTransform;
private Transform rightGroundCheckTransform;
private Transform leftSideCheckTransform;
private Transform rightSideCheckTransform;
private bool grounded;
private bool rebond;
private bool doubleJump;
private bool rouleboule;
private bool rightCol ;
private bool leftCol ;
private bool bottomLeftCol ;
private bool bottomRightCol ;
public int coinsCollected = 0;
public LayerMask groundCheckLayerMask;
public LayerMask wallCheckLayerMask;
// Use this for initialization
void Start () {
//On associe les checks de la hitbox du player
leftGroundCheckTransform = transform.Find ("leftGroundCheck");
rightGroundCheckTransform= transform.Find ("rightGroundCheck");
leftSideCheckTransform = transform.Find ("leftSideCheck");
rightSideCheckTransform = transform.Find ("rightSideCheck");
//On place les checks à la bonne position
leftGroundCheckTransform.position = new Vector2 (collider2D.bounds.min.x+0.05f, collider2D.bounds.min.y-0.05f);
rightGroundCheckTransform.position = new Vector2 (collider2D.bounds.max.x-0.05f, collider2D.bounds.min.y-0.05f);
leftSideCheckTransform.position = new Vector2 (collider2D.bounds.min.x - 0.02f, collider2D.bounds.center.y);
rightSideCheckTransform.position = new Vector2 (collider2D.bounds.max.x + 0.02f, collider2D.bounds.center.y);
}
// Update is called once per frame
void FixedUpdate () {
//gestion du deplacement
if (grounded || rebond)
{
rigidbody2D.velocity = new Vector3(xSpeed * direction, rigidbody2D.velocity.y, 0f);
//on desactive le rebond
rebond = false;
}
//flip sprite player
Vector3 theScale = transform.localScale;
theScale.x = direction;
transform.localScale = theScale;
// //flip les collid check
leftGroundCheckTransform.position = new Vector2 (collider2D.bounds.min.x+0.05f, collider2D.bounds.min.y-0.05f);
rightGroundCheckTransform.position = new Vector2 (collider2D.bounds.max.x-0.05f, collider2D.bounds.min.y-0.05f);
leftSideCheckTransform.position = new Vector2 (collider2D.bounds.min.x - 0.02f, collider2D.bounds.center.y);
rightSideCheckTransform.position = new Vector2 (collider2D.bounds.max.x + 0.02f, collider2D.bounds.center.y);
}
//update utilisé quand il y a un input button
void Update(){
//on verifie si le player est au sol
UpdateGroundedStatus();
//gestion du saut
GestionPlayerJump ();
//delimitation de la vitesse
GestionXSpeed ();
}
void GestionXSpeed(){
bool startAcceleration = Input.GetButtonDown ("Fire1");
bool acceleration = Input.GetButton ("Fire1");
bool stopAcceleration = Input.GetButtonUp ("Fire1");
//On ne peut accelerer et decellerrer que lors d un rebond ou au sol
if (grounded || rebond)
{
if (startAcceleration){
if (xSpeed < xSpeedAccelerationStep1) xSpeed = xSpeedAccelerationStep1;
}
else if (stopAcceleration){
xSpeed -= xSpeedDecceleration * 10.0f;
}
else {
if (acceleration && xSpeed <= xSpeedMax)
xSpeed += xSpeedAcceleration;
if (!acceleration && xSpeed >= xSpeedMin)
xSpeed -= xSpeedDecceleration;
}
}
//regulation de la vitesse min et max
xSpeed = Mathf.Clamp (xSpeed, xSpeedMin, xSpeedMax);
}
void OnCollisionEnter2D(Collision2D collisionInfo){
//debug
Debug.Log("Points colliding: " + collisionInfo.contacts.Length);
Debug.Log("First point that collided: " + collisionInfo.contacts[0].point);
Debug.Log("second point that collided: " + collisionInfo.contacts[1].point);
//ON VERIFIE LES COLLISIONS DE CHAQUE COTE
rightCol = Physics2D.Linecast (transform.position, rightSideCheckTransform.position, wallCheckLayerMask);
leftCol = Physics2D.Linecast (transform.position, leftSideCheckTransform.position, wallCheckLayerMask);
bottomLeftCol = Physics2D.Linecast (transform.position, leftGroundCheckTransform.position, groundCheckLayerMask);
bottomRightCol = Physics2D.Linecast (transform.position, rightGroundCheckTransform.position, groundCheckLayerMask);
if (rightCol)
{
if (grounded)
{
direction = -direction;
xSpeed -= xSpeed/3;
}
Debug.Log ("Col right");
}
if (leftCol)
{
if (grounded)
{
direction = -direction;
xSpeed -= xSpeed/3;
}
Debug.Log ("Col left");
}
if (bottomLeftCol || bottomRightCol)
{
//on verifie si il y a collision sur les coté
//au moment du retour au sol
if (rightCol) {direction = -1; xSpeed = xSpeed/2;}
else if (leftCol) {direction = 1; xSpeed = xSpeed/2;}
//si au retour sur le sol et face à un mur
//on perd la moitié de sa vitesse
grounded = true;
//marchepasgrounded = true;
Debug.Log ("Col bottom");
}
}//fonction
void OnTriggerEnter2D (Collider2D col){
if (col.gameObject.CompareTag ("Coins")) {
coinsCollected += 1;
Destroy (col.gameObject);
}
}
void GestionPlayerJump(){
bool jump = Input.GetButtonDown ("Fire2");
//if jump
if (jump) {
//on check les collisions
rightCol = Physics2D.Linecast (transform.position, rightSideCheckTransform.position, wallCheckLayerMask);
leftCol = Physics2D.Linecast (transform.position, leftSideCheckTransform.position, wallCheckLayerMask);
bottomLeftCol = Physics2D.Linecast (transform.position, leftGroundCheckTransform.position, groundCheckLayerMask);
bottomRightCol = Physics2D.Linecast (transform.position, rightGroundCheckTransform.position, groundCheckLayerMask);
//if grounded= jump normal
if (grounded){
//rigidbody2D.AddForce (new Vector2 (0f, jumpForce));
//rigidbody2D.velocity+=(new Vector2 (0f, jumpForce));
Debug.Log("Jump");
}
//else if !grounded
else {
//si il y a une collision a ce moment c un rebond
if (rightCol || leftCol) {
rebond = true;
//on reinitialise le double jump
doubleJump = false;
//on midifie la direction
if (rightCol) direction = -1;
else if (leftCol) direction = 1;
//on reiinitialise la force du saut
Vector2 newVelocity = rigidbody2D.velocity;
newVelocity.y = 0f;
rigidbody2D.velocity = newVelocity;
//rigidbody2D.AddForce (new Vector2 (0f, jumpForce));
Debug.Log("Jump");
}//si collision
//sinon c un double jump
else if (!doubleJump){
//on reiinitialise la force du saut
Vector2 newVelocity = rigidbody2D.velocity;
newVelocity.y = 0f;
rigidbody2D.velocity = newVelocity;
//rigidbody2D.AddForce (new Vector2 (0f, jumpForce));
Debug.Log("DoubleJump");
//le double jump est en cours, il repassera false uniquement en
//cas de contact avec le sol ou lors d un rebond
doubleJump = true;
}
}
}
}//fonction
void UpdateGroundedStatus()
{
// The player is grounded if a linecast to the groundcheck position hits anything on the ground layer.
//grounded = Physics2D.Linecast(transform.position, groundCheckTransform.position, 1 << LayerMask.NameToLayer("Ground"));
//grounded = Physics2D.Linecast(transform.position, groundCheckTransform.position, wallCheckLayerMask);
bool bottomLeftCol = Physics2D.Linecast (transform.position, leftGroundCheckTransform.position, groundCheckLayerMask);
bool bottomRightCol = Physics2D.Linecast (transform.position, rightGroundCheckTransform.position, groundCheckLayerMask);
grounded = bottomLeftCol;
if (!grounded) grounded = bottomRightCol;
//on reinitialise le double jump
if (grounded)doubleJump = false;
}
Dont laugh on my code i'm not a pro developper ;)
And what are the values of xSpeed$$anonymous$$in, xSpeed$$anonymous$$ax set in the inspector?
xSpeed$$anonymous$$in = 5.0f; xSpeed$$anonymous$$ax = 10.0f;
Thx for looking at my problem :)
Have you tried clamping xSpeed at the end of OnCollisionEnter2D as it may be called multiple times if multiple objects are collided (or if you have multiple colliders components on 1 gameobject). It should not set xSpeed to 0 though, but maybe very close.
Also, did you try some edge cases with the physics2d project settings, like changing velocity & position iteration to 1 and next to 20. This would be to see if it helps reproduce the bug more often or remove it. You could also play with the time settings to increase & decrease the frequency of fixedupdates: it may also trigger the bug more or less often and give new leads.
Hmm like i say in my first post, when the bug occurs my velocity is set to the right value at the end of the update, but when the update start again the velocity is set to 0. So clamping value doesn't do anything.
But Ill give a try for your second idea. I'm not totaly satisfied tough because it doesn't solve the bug, but it's a good idea for the moment.
Do you think I can re-use addforce for Jump, and direct change to velocity for the x movement ? The bug still occurs without addforce but I think it makes it appearing less often.
AddForce will only do some math to compute a new velocity, just like you do, so there is no problem using it. It will also take mass and deltaTime into account depending on the force mode.
The important thing is when to call it and to make it work with the other parts of your code. This page : http://docs.unity3d.com/$$anonymous$$anual/ExecutionOrder.html has very interesting information about that. You'll see that the physics engine will do it's computations just after the FixedUpdate and call the OnCollision/Trigger then. So, the best time to add forces is to do it during the FU because there will be as many FU as physics engine call. Inputs are updated during Update though, making things a bit more complicated if you want to be accurate and all framerate proof (= set a playerWantsToJump = true in Update, and use+clear it in FU)
For the bug you have, it must be the physics engine that sets the velocity to 0, possible cause is a collision that would stop the object. There must be a special situation to trigger that, multiple collisions at the same time or sometime a collision with an object that is 0.0001 further/upper/lower than it should be, or some math imprecision. $$anonymous$$y experience is that physics always need lots of tweaking, special case handling, faking reality etc... Your code seems correct so it may be difficult to track this bug. $$anonymous$$aybe you could backup all values like positions and velocity of your player at the end of the FU. Then, if you detect a 0 velocity in the update, you can use those backuped values to reproduce the exact situation that lead to the bug. This may help you to "pause" the game at the bug moment and inspect things to find the cause.
Also, it's totally normal to have this kind of problem while developing a game, spending lots of time debugging, even for the most experienced of us :-)
Your answer