AddForce instead of modyfing Velocity
I just realized that if i want to use physics with rigidbodies2d i cant move the player or character by modyfing Rigidbody2D.velocity.
So how do i do it?
Im getting the movement input from GetAxis("Horizontal") and i want the character to move left or right depending on this. Its easy if i want to use velocity but how would i convert this to AddForce?
Answer by robertbu · Jan 17, 2014 at 04:14 PM
'velocity' and AddForce() are very different. Imagine a space ship heading in some direction and you add some force from the side (perpendicular to the direction the ship is traveling). The ship does not make an immediate 90 degree turn. Instead the force turns the ship towards the new direction some. The more the force, the more the angle of the new direction is different from the old. The current move vector and the force are added together to get the new direction.
With velocity, you get an immediate change in direction. So if your ship was headed directly up in your 2D game and you did:
rigidbody2D.velocity = Vector3.right;
Your ship would make an immediate turn towards the right. Note you can split the difference somewhat. Rigidbody2D.drag makes AddForce() act more line an immediate velocity assignment. The higher the drag, the faster an object sheds old velocity and takes on new velocity. Of course you have to add more force and continue to add force each frame to overcome drag. As for "Horizontal" and velocity, In FixedUpdate() you can put:
rigidbody2D.velocity = Vector2(GetAxis("Horizontal") * maxSpeed, 0.0);
Thanks for the explanation! Very usefull. :) However, i think i read that i should not set the velocity directly if i want physics to work properly. Is this not correct?
This is how it is now and i thought that is what causes the problems?
rigidbody2D.velocity = new Vector2 (movement * $$anonymous$$axSpeed, rigidbody2D.velocity.y);
Setting velocity does not break the Physics engine with respect to things like collisions. But the behaviors of objects will be less realistic if you set the velocity directly. Cars or space ships do not make immediate 90 degree turns when we attempt to change directions for example.
I'm not sure what problem you are trying to solve with AddForce() vs. Rigidbody.velocity. As a starting point, you can do:
rigidbody2D.AddForce(Vector2.right * GetAxis("Horizontal") * amount);
You will have to play with 'amount' to see what works for your mass and situation.
Tanks! That moves the character and the physics still work as they should.
But it gives me other problems i dont know how to fix. The speed seems acumulative so it just keeps adding upp. When i stop pressing a key the player takes time to slow down so it seems to be skating on ice.
How could i control how long it takes to reach max velocity? How could i make sure a max velocity is not passed? How could i make the slow down faster so the player doesnt skate?
As mentioned in the original answer, Rigidbody.drag controls how long it takes to slow down. Edit 'Drag' in the inspector or it can dynamically be changed in script. As for creating a maximum velocity, you use Vector3.Clamp$$anonymous$$agnitude():
rigidbody.velocity = Vector3.Clamp$$anonymous$$agnitude(rigidbody.velocity, topSpeed);
Problem is that drag also affects the speed with which the player falls.
There has to be some better way. I guess problem is that i want physics to apply, but when walking i dont want to have a an acceleration time or deceleratiion time. I guess im looking for best of both worlds...
I have a strong feeling that somewhere there is someone that knows about this problem or knows exactly what i am thinking wrong. Its like im missing something that i havent learned yet. Thanks for your answer though!
Answer by Riak157 · Nov 11, 2014 at 02:46 AM
I ran into similar problems as you OP and I tried the Accepted answer. While it worked, manually clamping the velocity is problematic and can make for a confusing applied physics system (it seems bad to mix force and velocity management in practice). An approach that allowed me to maintain one system of speed management was to scale the force applied in terms of the current velocity as a percentage of max velocity.
float xcap = (rigidbody.velocity.x / maxWalkingSpeed)*moveHorizontal;
if (xcap > 0) {
maxVelocityScalerX = Mathf.Clamp( 1.0f - xcap, 0.0f,1.0f);
movement.x *= maxVelocityScalerX;
}
float zcap = (rigidbody.velocity.z / maxWalkingSpeed)*moveVertical;
if (zcap > 0) {
maxVelocityScalerZ = Mathf.Clamp( 1.0f - zcap, 0.0f,1.0f);
movement.z *= maxVelocityScalerZ;
}
rigidbody.AddForce(movement*speed*Time.deltaTime, ForceMode.VelocityChange);
This also has an added benefit of allowing your game actors to pass your manually set velocity cap due to outside forces. This helps maintain a more realistic feel to your movement.
@Riak157 Hey man, I tried to use this, but I can't get it to work. You've got a ton of variables and I dunno which one is which. I know this is an old post, but you seem to have solved the answer to my problem. I just can't get your code to work. If you could help that would be great.
Sorry man, I wrote this before my first job and getting hounded on my horrible coding styling. I'll clean it up so its a little more understandable.
Answer by paledust · Oct 15, 2015 at 07:12 AM
I got the same problem and fixed it, NARROWLY since some remains to be test more. As for that you can't use the velocity or the force added to it will be in vain. So my solution is to set a limitation speed for CONTROL, meaning that you can only control the character within some speed using "addForce" function and since I can only control the horizontal direction, the limitation should just be for "x Axis", instead of the magnititude of the velocity. Here is the code.
if(Input.GetButton(m_HorizontalInput))
{
if(Mathf.Abs( GetComponent<Rigidbody2D>().velocity.x )<= MoveSpeed)
GetComponent<Rigidbody2D>().AddForce (Vector2.right*Input.GetAxis(m_HorizontalInput)*Accelerate, ForceMode2D.Impulse);
else
{
GetComponent<Rigidbody2D>().velocity = Vector2.Lerp(GetComponent<Rigidbody2D>().velocity,Vector2.up * GetComponent<Rigidbody2D>().velocity.y,Time.deltaTime * 5.0f);
}
}
else
{
GetComponent<Rigidbody2D>().velocity = Vector2.Lerp(GetComponent<Rigidbody2D>().velocity,Vector2.up * GetComponent<Rigidbody2D>().velocity.y,Time.deltaTime * 5.0f);
}
why don't you define something like Rigidbody2D pete = GetComponent<Rigidbody2D>();
in the beginning and politely ask pete to change his velocity ins$$anonymous$$d of bugging the compiler with the too many fetch and forget GetComponent calls? ;-)
Answer by mgreene1 · Mar 19, 2016 at 05:13 AM
This is similar to how Mario handles physics-based movement:
rigidbody2D.velocity = new Vector2(CrossPlatformInputManager.GetAxis("Horizontal"); * yourMoveSpeed* Time.deltaTime, rigidbody2D.velocity.y);
what this does is slowly accelerate up to your movement speed, then decelerate when you stop moving left or right. You can see that the deltaTime is accessed to make sure this is handled precisely. Don't try to use a '+=' with velocity, either, it will make you move insanely fast, as it will keep increasing your velocity forever. (remember += means that the new value is being added to the old, then set as the new value)
Unity would really benefit from creating a series of tutorials where they teach you how to emulate controls in other games, rather than giving you the most basic way to accomplish something. I'd appreciate it, anyway.