Using LookAt to turn NPC to new coordinates

Hi guys,

I’ve been struggling for days to find a way to turn NPCs toward new world coordinates and have them translate to those points.

My goal is to have NPCs “patrol” by generating a random coordinate, smoothly turning the NPC toward it, then translating the NPC to it, then generating a new random coordinate and repeating the process.

I’ve tried using Lerp, but from what I can tell, it moves an object from point A to point B over a specific amount of time, which means that as the distance gets longer, the speed of the NPC will get higher. Not really what I want. So I tried Vector3.Distance to get the distance between point A and B and then dividing by time to get a steady speed, but that didn’t seem to work (probably because I’m not coding it properly. I’m new to this)

I’ve also tried simply using LookAt to have the NPC turn to the new coordinates and keep on moving until transform.position == new.position,then starting again but I can’t seem to get that to work properly either. (Once again, could simply be my inability to get the code right)

Could use a little guidance on this one if you don’t mind. Seems like it ought to be simple, but it’s eluding me. I can’t find a scripting reference that describes this sort of behavior.

Thanks in advance for the help. :roll:

Your idea about using Vector3.Lerp and then varying the time according to distance is definitely the right way to go. I generally wouldn’t recommend checking to see if two positions are equal because this is very susceptible to round-off errors.

If you can post the script you are using, I’ll try to sort out the problem you are having with the Lerp.

Thanks so much for your help. The code I’ve come up with is as follows:

var NPCspeed = 2;  // allows me to control the NPC speed

private var newposition = Vector3 (Random.Range (80, 910) ,0, Random.Range(200, 640)); // select random position on map coordinates

private var newpositiondistance = Vector3.Distance (newposition, transform.position); // get the distance from the current position to the new position






function Update ()

{
transform.position = Vector3.Lerp(transform.position, newposition, (newpositiondistance*NPCspeed/Time.time)); // moves NPC by distance over time to keep speed the same

if (transform.position == newposition)
{
newposition = Vector3 (Random.Range (80, 910) ,0, Random.Range(200, 640)); // select new random point on map coordinates
newpositiondistance = Vector3.Distance (newposition, transform.position); // start the process over again
}
}

Something is very wrong with this script because Unity won’t even let me attach the script to the NPC game object. It gives me a compile error. I’m just too new at this to figure out what’s going wrong.

Thanks again.

Erm… any chance you could mention which compile error you are getting?

Sorry. :sweat_smile: (I didn’t know there were several kinds of compilation errors)

The error message said that “all scripts must be compiled first!”

I realized after your note that it was not referring to the current script, but to one just sitting in my script folder. I didn’t know that Unity compiled scripts that weren’t attached to game objects. Anyway, I deleted the offending scripts and successfully attached the current script.

I’m not getting any error messages now, but the moment I hit “play”, the NPC shoots off the map and is visible at the lower left corner of the screen. It seems to happen instantaneously. There’s no translation time at all. First it’s on the map, then it’s not. I’m going to paste the script in again below because I tweaked it a bit.

Thanks so much for your patience and help. I’m learning.

var NPCspeed : float = 20;  // allows me to control the NPC speed


private var newposition = Vector3 (Random.Range (80, 910) ,0, Random.Range(200, 640)); // select random position on map coordinates

private var newpositiondistance = Vector3.Distance (newposition, transform.position);






function Update ()
{

transform.position = Vector3.Lerp(transform.position, newposition, (newpositiondistance*NPCspeed/Time.time)); // moves NPC by distance over time to keep speed the same

if (transform.position == newposition)
{
newposition = Vector3 (Random.Range (80, 910) ,0, Random.Range(200, 640)); // select new random point on map coordinates
newpositiondistance = Vector3.Distance (newposition, transform.position); // start the process over again
}
}

The main problem I can see is this line:-

transform.position = Vector3.Lerp(transform.position, newposition, (newpositiondistance*NPCspeed/Time.time));

Firstly, the idea with Vector3.Lerp is that you pass two points and a fraction from 0 to 1. If you pass 0.25, it will return a point a quarter of the way along the line between the first point and the second. If you want to animate along a line at a constant speed, you must use a fixed start and end point for the line. You are getting the current transform.position each frame and using that as the start point. You need to get the transform.position value in the Start method and then use that position as the starting point of the movement line all the way through:-

var startPosition: Vector3;

function Start() {
    startPosition = transform.position;
}

Secondly, I’m pretty sure the way you are calculating the interpolation fraction is not going to achieve what you intend. It’s often handy to break this down. First, calculate the total time that the movement should take:-

 var moveTime: float = newpositiondistance / NPCspeed;

To get the fraction of the movement time that has elapsed so far you would use:-

var fracTime: float = Time.time / moveTime;

Finally, the line where you handle the movement will look like this:-

transform.position = Vector3.Lerp(startPosition, newposition, fracTime);

Another useful thing here is that you can just check fracTime to see when the movement has finished - it will be >= 1 at the end of the line.

First of all, I want to thank you for all the help. You put me on the right track and cleared up my fundamental misunderstanding of Lerp (and a lot of other things). I put your code into my script and sure enough, the NPC moved randomly around the map. The only problem I had was that after the first leg, the NPC started bouncing around the map like a ping pong ball at high speed. After staring at the code for more time than I care to admit, I realized that FracTime has to be reset to zero each time a new position is established, otherwise it’s always greater than 1 after the first leg.

To do this, I created a new variable changetime += Time.deltatime and substituted it for Time.time, then reset it each time the If statement kicks in.

Now it works great. I also added a Quaternion.Slerp to make the NPC turn smoothly. I’ll paste the complete code below in case it’s useful to anyone. I’m sure it’s not the most elegant or efficient way of doing this, but it works. Perhaps someone can improve on it. Thanks again for the guidance.

var NPCspeed : float = 1.0;  // allows me to control the NPC speed
var turnspeed : float = 1;

private var newposition : Vector3;
private var newpositiondistance : float;
private var startPosition : Vector3;
private var moveTime : float;
private var changetime : float;

function Start() 
{
newposition = Vector3 (Random.Range (80, 910) ,0, Random.Range(200, 640));
newpositiondistance = Vector3.Distance (newposition, transform.position); 
startPosition = transform.position;
moveTime = newpositiondistance/NPCspeed;
} 

function Update ()
{

var targetRotation = Quaternion.LookRotation(newposition - transform.position);
       
// Smoothly rotate towards the target point.
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, turnspeed * Time.deltaTime); 

changetime += Time.deltaTime;

var fracTime: float = changetime/moveTime;
transform.position = Vector3.Lerp(startPosition, newposition, fracTime);


if (fracTime >=1)
{
changetime = 0;
startPosition = transform.position;
newposition = Vector3 (Random.Range (80, 910) ,0, Random.Range(200, 640));
newpositiondistance = Vector3.Distance (newposition, transform.position);
moveTime = newpositiondistance / NPCspeed;
}
}