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 :slight_smile:

Thanks

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).