Why does writing to rigidbody.velocity after AddForce stop my rigidbody moving?

I’ve not spotted this issue documented, so hoping someone can help out.

My aim:

  1. Add input force ‘correctly’ to my rigidbody (via AddForce)
  2. Adjust the rigidbody velocity to stop it going too fast (e.g. by capping the max)

[as an aside, I know for #2, adjusting the rigidbody velocity directly isn’t recommended, but I still see it mentioned in Unity Answers and forums and it is even in some Unity demo scripts, I tried to only use it for limiting max velocity]

I didn’t even get as far as step #2 though - what I discovered was:
After I use AddForce, then making any change to the rigidbody velocity, even just doing a get/set, seems to cancel out any movement made with AddForce.

Any idea what I’m doing wrong? Have I missed something obvious? I’ll paste my code below, you can repro by adding a cube and assigning this script to it. See the line marked #THEPROBLEM for whereabouts the issue occurs.

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Rigidbody))]
public class TestMover : MonoBehaviour {

	private Transform cameraTransform;
	
	void Start ()
	{
		cameraTransform = Camera.main.transform;
	}
	
	
	public float charMoveInputForceXZ = 25f;
			
	void moveCharacter (Vector3 inputVector)
	{
		rigidbody.AddForce(inputVector * charMoveInputForceXZ * Time.deltaTime , ForceMode.VelocityChange);


		// #THEPROBLEM
		// The below should have no effect, yet...
		// * If commented out, the rigidbody can be moved
		// * If compiled in, the rigidbody cannot be moved
		// * If compiled in and placed prior to the AddForce line, the rigidbody can be moved
		Vector3 v;
		
		v = rigidbody.velocity;
		
		rigidbody.velocity = v;
	}
	

	void FixedUpdate ()
	{
		Vector3 cameraForward;
		Vector3 moveInput = Vector3.zero;


		float h = Input.GetAxis("Horizontal");
		float v = Input.GetAxis("Vertical");
		
		if (cameraTransform != null)
		{
			// calculate camera relative direction to move:
			cameraForward = Vector3.Scale (cameraTransform.forward, new Vector3(1,0,1)).normalized;
			moveInput = v * cameraForward + h * cameraTransform.right;	
		}
		
		if (moveInput.magnitude > 1)
		{
			moveInput.Normalize();
		}
			
		moveCharacter(moveInput);
	}
}

In your case, easiest way is probably to not use AddForce. Change line 19 to rigidbody.velocity+=inputVector...

AddForce is merely a front-end to a direct velocity change. The four versions do/don’t divide by mass and multiply by deltaTime. Your first line “should” omit time.deltaTime and instead use ForceMode.Acceleration (which applies T.dT for you.)

The “never use velocity directly” advice is really just a warning to people starting out with programming and physics systems. Try to explain what Time.deltaTime does to a non-mathy person, and you find yourself saying “know what – forget I mentioned it. Use AddForce.”

I use direct velocity changes for everything, in dozens of ways including a velocity limit, and never had a problem like that. But then I never use AddForce, so don’t use them together.

For fun, I’d print velocity after each line (print each x,y,z by itself, otherwise it’s cut off to the tenths place.) It’s gotta be some crazy bug elsewhere, or a compiler glitch.

Its worth noting that this is not a bug. There is a reason the docs say not to set velocity every frame, and that is because it overrides the physics engine velocity calculations.

To explain further AddForce does not actually change the velocity of the RigidBody. AddForce queues a force to the physics engine to be applied during the next physics update step. The physics update happens after FixedUpdate. During the physics update all queued forces are applied, and the RigidBody is assigned the new velocity. Then the RigidBody is moved. Direct changes to velocity are applied after the forces are calculated. This is by design, otherwise direct velocity changes would be overridden by the physics engine.

In terms of your script that means v is reading the velocity before the force is applied. In your case 0. Then the physics update is applied, which applies your force. Then you set the velocity back to v, which is zero.

Note: I’m not 100% certain that velocity changes are applied after forces by PhysX. But this does seem to be the most logical cause to your problem. The other possibility is that your force simply isn’t large enough. If I get bored enough I might run a few tests to confirm.

I was intrigued by BoredMormon’s post, so I did a quick test in 4.6.1 to confirm/explore when AddForce indeed happens.

Compare version 1:

   void FixedUpdate()
    {
        print("Enter FixedUpdate. rigidbody v: " + this.rigidbody.velocity);
        this.rigidbody.velocity += Vector3.forward;
        print("Exit FixedUpdate. rigidbody v: " + this.rigidbody.velocity);
    }

to version 2:

   void FixedUpdate()
    {
        print("Enter FixedUpdate. rigidbody v: " + this.rigidbody.velocity);
        this.rigidbody.AddForce(Vector3.forward, ForceMode.VelocityChange);
        print("Exit FixedUpdate. rigidbody v: " + this.rigidbody.velocity);
    }

With Version 1 you get what you might expect, namely if you update rigidbody.velocity, the change is visible right away, just as with a “normal variable”:

Enter FixedUpdate. rigidbody v: (0.0, 0.0, 0.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 1.0)
Enter FixedUpdate. rigidbody v: (0.0, 0.0, 1.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 2.0)
Enter FixedUpdate. rigidbody v: (0.0, 0.0, 2.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 3.0)
Enter FixedUpdate. rigidbody v: (0.0, 0.0, 3.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 4.0)

With Version 2, in contrast, we see don’t see the rigidbody’s velocity being increased until we get to the next FixedUpdate:

Enter FixedUpdate. rigidbody v: (0.0, 0.0, 0.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 0.0)
Enter FixedUpdate. rigidbody v: (0.0, 0.0, 1.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 1.0)
Enter FixedUpdate. rigidbody v: (0.0, 0.0, 2.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 2.0)
Enter FixedUpdate. rigidbody v: (0.0, 0.0, 3.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 3.0)
Enter FixedUpdate. rigidbody v: (0.0, 0.0, 4.0)
Exit FixedUpdate. rigidbody v: (0.0, 0.0, 4.0)

So AddForce is indeed being delayed until after FixedUpdate

My guess would be that there’s a bug in which the AddForce is updating the velocity, but the old velocity is still being stored in the physics engine for certain reasons, getting rigidbody.velocity is getting the old value before you changed it, and setting it is setting the value that had been updated.

As for why you shouldn’t alter velocity, it’s basically because it messes with physics. You could do it if you’re really really careful, but all the checks you’re going to have to make to avoid unintended consequences will be awkward.

Take this instance for example - using force to move a character, but changing velocity to cap the speed. The trouble is that this may also cap the speed not only when the player is trying to move, but when a fast object is pushing the player. And if the players velocity is capped in that instance, you may have an irresistible force pushing an immovable (or immovable past a certain speed) object, which will cause big problems, such as the two objects starting to overlap.
A better solution to cap the player movement speed would be to just check the current movement speed first, and reduce the amount of force applied accordingly. Here is the code I use in my 2D-platformer so that the user can’t make the player run faster than 3 units per second. (The commands will be slightly different for 3D-rigidbodies.)

	tempvel = rigidbody2D.velocity;
	tempvel.x = 3f * hInput;
	rigidbody2D.AddForce(0.1f * (tempvel - rigidbody2D.velocity),ForceMode2D.Impulse);

(The multiplication by 0.1 is so that the character slowly accelerates rather than immediately running at the requested speed.) All movement here is by added force rather than setting the velocity, but the speed the user can move the player is still capped, but the character can move at greater speeds if moved by other forces in the level.