Why does ForceMode.VelocityChange work but not Rigidbody.velocity = ... in script?

I have a FPS movement script that I created that has a jump function that looks like this

public void Jump()
{
if (jumpAble && grounded)
{
jumpAble = false;
gameObject.GetComponent<Rigidbody>().velocity = (Vector3.up * jumpForce * Time.deltaTime);
}
}


For some reason, this does not make the player jump. However, replacing the .velocity =... to

gameObject.GetComponent<Rigidbody>().AddForce(Vector3.up * jumpForce * Time.deltaTime, ForceMode.VelocityChange);


makes the player jump. Why is this? I'm under the impression that the two are essentially the same thing, but is there a slight difference?

Also, not sure if it affects the function, but this is the whole script

public class Player_Movement : MonoBehaviour


{ public Transform camera;

public Transform groundCheck;
public float groundDistance = 0.4f;
public bool grounded;

public GameObject joystick;
public bool isWalking = false;
public bool isRunning = false;
public float walkForce = 12f;
public float runForce = 30f;

public float jumpForce;
public bool jumpAble = true;

public GameObject playerRotationHandler;
public float cameraTouchSensitivity;

public Animator animator;

// Update is called once per frame
void Update()
{

if (grounded)
{
jumpAble = true;
}

Vector3 direction = joystick.GetComponent<Joystick>().inputDirection.x * transform.right + joystick.GetComponent<Joystick>().inputDirection.y * transform.forward;

if (isRunning)
{
gameObject.GetComponent<Rigidbody>().velocity = (direction * runForce * Time.deltaTime);
}

else if (isWalking)
{
gameObject.GetComponent<Rigidbody>().velocity = (direction * walkForce * Time.deltaTime);
}

else
{
gameObject.GetComponent<Rigidbody>().velocity = Vector3.zero;
}
}

public void Jump()
{
if (jumpAble && grounded)
{
jumpAble = false;
gameObject.GetComponent<Rigidbody>().velocity = (Vector3.up * jumpForce * Time.deltaTime);
gameObject.GetComponent<Rigidbody>().AddForce (Vector3.up * jumpForce * Time.deltaTime, ForceMode.VelocityChange);
}
}

public void Rotate(float xMoveValue, float yMoveValue)
{
float xRotation = Mathf.Atan2(yMoveValue, cameraTouchSensitivity) * Mathf.Rad2Deg;
float yRotation = Mathf.Atan2(xMoveValue, cameraTouchSensitivity) * Mathf.Rad2Deg;

playerRotationHandler.transform.Rotate(xRotation, 0, 0);
transform.Rotate(0, yRotation, 0, Space.World);

if (playerRotationHandler.transform.localEulerAngles.x < 300 && playerRotationHandler.transform.localEulerAngles.x > 60)
{
if (xRotation < 0)
{
playerRotationHandler.transform.localEulerAngles = new Vector3(300, playerRotationHandler.transform.localEulerAngles.y, 0);
}

if (xRotation > 0)
{
playerRotationHandler.transform.localEulerAngles = new Vector3(60, playerRotationHandler.transform.localEulerAngles.y, 0);
}
}
}


}

• It looks to me like your update method is setting your vertical velocity back to zero every frame. You generally don't want to multiply an instantaneous effect like the launch of a jump by deltaTime either — that will make the speed/height of your jump depend on your framerate. Dec 1, 2020 at 12:24
• By the way: You don't need to multiply by time.deltaTime when you change the velocity or use AddForce. The physics engine will do so automatically when changing the position by the current velocity. Dec 1, 2020 at 15:37

The difference is that while velocity = value sets the velocity to a new value, AddForce(value) adds to the existing velocity. The rigidbody will retain any existing momentum it received from velocity changes and forces it received previously. However, in your case it does not, because you overwrite the velocity in every update, causing the rigidbody to "forget" any previous forces and velocity changes.
However, I am a bit confused how your code allows your player to jump at all. Because your Update function will in every situation replace the velocity with a new value, canceling any velocity the rigidbody might have received from the Jump() method. Not sure where that method gets called from, though, because there is no reference to it in the code you posted. Perhaps the method gets called from a different script on every single frame while the player holds the jump button? In that case what happens is that the script which called Jump and the Update method of the script here will compete about who can set the velocity to its preferred value. Whatever script gets executed last wins. Apparently this is the script which does the updating when you use .velocity = and the script which does the jumping when you use .AddForce.
The answer is that AddForce does not actually change the velocity immediately. What it actually does is order the physics engine to change the velocity during the next physics update but before applying the velocity to the position. This would happen after all your Updates were processed. So the physics engine will add that force on top of the velocity you set in Update.
• Avoid replacing the velocity with velocity =. Only add to the velocity directly (velocity += value) or indirectly through forces.