- Home /
Stopping a rigidbody at target
Hi,
we're currently developing a dungeon siege style top down game where the player moves his character with mouse clicks. Since physics will have to take full effect on the character we decided to move the avatar by rotating its rigidbody towards the mouse and then use AddForce to move it along it's forward axis.
void Move()
{
Vector3 dir = rigidbody.transform.forward * _fCurrentSpeed;
Vector3 v3TargetPos = new Vector3(v3MouseTarget.x, rigidbody.transform.position.y, v3MouseTarget.z);
if(Vector3.Distance(rigidbody.transform.position, v3TargetPos) <= .5f )
{
_fCurrentSpeed = 0f;
}
else
{
_fCurrentSpeed = fSpeed;
transform.LookAt(v3TargetPos);
}
rigidbody.AddForce(dir);
}
The problem here is that the rigidbody will usually overshoot, turn around, walk back, and then stop. If i try to set the stop condition to transform.position == v3TargetPos the avatar will turn endlessly as the added force will always make it overshoot the target position.
Does anyone have an idea how to effectively stop the character once he's reached the target position? Alternate methods are also welcome :)
Thanks
Answer by aldonaletto · Dec 16, 2011 at 01:29 AM
Applying a force to reach some position is hard to control: the distance is a complicated function (integral) of force intensity, time and mass.
A simple solution is to just zero rigidbody.velocity when the target is reached, but this produces a very weird and unnatural result - the rigidbody just stops at the target position.
A better alternative is to use a feedback control algorithm, where the force is applied to reach a target velocity, and this velocity (clamped to some max value) is proportional to the distance to the target point - this way it will fall to zero when the target point is reached, stopping the rigidbody:
public float toVel = 2.5f;
public float maxVel = 15.0f;
public float maxForce = 40.0f;
public float gain = 5f;
Rigidbody rbody;
void Start(){
rbody = GetComponent<Rigidbody>();
}
void FixedUpdate(){
Vector3 dist = targetPos - transform.position;
dist.y = 0; // ignore height differences
// calc a target vel proportional to distance (clamped to maxVel)
Vector3 tgtVel = Vector3.ClampMagnitude(toVel * dist, maxVel);
// calculate the velocity error
Vector3 error = tgtVel - rbody.velocity;
// calc a force proportional to the error (clamped to maxForce)
Vector3 force = Vector3.ClampMagnitude(gain * error, maxForce);
rbody.AddForce(force);
}
This controller makes the rigidbody go in the targetPos direction - just set targetPos to the desired position, and the rigidbody goes there. It doesn't rotate the rigidbody to the target direction, however - you may do it at Update.
The parameters maxVel, maxForce, toVel and gain adjust the controller characteristics, and may be tweaked to reach a stable and precise operation: maxVel is the max speed the rigidbody will reach when moving; maxForce limits the force applied to the rigidbody in order to avoid excessive acceleration (and instability); toVel converts the distance remaining to the target velocity - if too low, the rigidbody slows down early and takes a long time to stop; if too high, it may overshoot; the last parameter, gain, sets the feedback amount: if too low, the rigidbody stops before the target point; if too high, it may overshoot and oscillate (like your current algorithm).
Thanks a lot!
I've implemented your approach and it works like a charm :) To further prevent the oscillation I've also added a distance check that will make the avatar simply look ahead of himself ins$$anonymous$$d of at the target position once he is close enough.
Very helpful. Thank you again :D
I'm using this basic functionality to create a sort of gravity gun for a first person game, but the objects I'm controlling seem to have a bit of a jittery movement when moving quickly. Is there something that is missing from this script that might improve the smoothness?
Hiya @aldonaletto, I was messing around with this a bit and decided to make a little scene to test different values out quickly. Here's a webplayer link if anyone else wants to have a play. http://www.peterleary.com/ForumFiles/Attract/index.html
@aldonaletto iam doing a game(drone type). in that i have to stop the player at some target position. and one more condition it should not stop by calculating the distance. it should consider the mass of the object. apply force(up) also should consider mass after gets mass zero it should stop.
"theoretically if a object is going up and up and up their mass will reduse. how can we come to know here"
@aldonaletto Thanks so much for your answer, this solved my problem. I'd like to understand it more though, where did you learn about this technique?
This code implements a kind of Proportional Derivative Control, where the force applied is proportional to the distance to the target and also to its current velocity (velocity is the derivative of distance over time, hence the name Proportional Derivative). This algorithm first calculates the velocity needed proportionally to the current distance to the target position, then estimates the force necessary to reach the desired velocity - this way it automatically accelerates during most of the time and decelerates when getting near to the target.
I've kind of invented this algorithm based on previous experiences with real world servo controllers, but you can obtain more technical information from Wikipedia (PID controller)
NOTE: PID stands for Proportional Integral Derivative, and it's a little more complex than PD (Proportional Derivative). The Integral (I) term helps to zero the difference to the target due to friction in the real world, but in Unity we definitely don't need it - the simpler PD control does the job.
Thanks for the explanation! I did end up finding some articles about PID Controllers, but had no idea what to search for initially. To experiment further, I've implemented this example with poor results (oscillation and crazyness).
I'll try to think through your PD approach more. It's very interesting and works a treat, I just have to sit down and write out the maths to properly wrap my head around it. $$anonymous$$aths does not come naturally to me.
From there I think your PD algorithm could be encapsulated into a class/library and then used across several applications.