- Home /
Move an object through a set of positions
Okay, I know this should be much more straightforward than I'm finding but my brain is mush today, sorry.
In the level i'm making I want characters to patrol back and forth over an area repeatedly - specifically travel through a set of waypoints. I've been reading up on Angry Ant's Path and Aron Granberg's A* Pathfinding for a future project, but for this I don't need actual pathfinding since the characters don't have to go anywhere other than along their set path. They will have a vision cone or torchlight that, when it touches the player, will reset the players position. Basically a really simple stealth game.
Last year I modified the SeekSteer script on the wiki to have a character move along a set of waypoints but it was a bit of a rushed job and it had some problems. I'm returning to it now and starting from scratch. I'm using the Lerp function as described in this post to move my object to the new position and i've combined that script with my waypoint one (it looks nothing like SeekSteer now by the way).
I rewrote my old script since I had the unwanted effect of easing in and out of positions - where that came from I don't know - but aside from that it worked. I'm using the same code here, the only real difference is the method to move the character and the old script had a radius for each waypoint. There are currently 3 transforms in the waypoints array to make an 'L' shape. But after the character reaches the first waypoint I get an Index Out of Range error.
As far as I can tell, the part where the targetwaypoint variable is being incremented is suspect but this wasn't a problem in the other script despite being in the Update function.
// array to hold waypoint locations var waypoints : Transform[];
// variable to control time taken to travel between points var duration : float = 1.0;
private var startPoint : Vector3; private var endPoint : Vector3; private var startTime : float;
// the array index number of the current target waypoint private var targetwaypoint : int;
function Start() { startPoint = transform.position; startTime = Time.time;
if(waypoints.Length <= 0){
Debug.Log("No waypoints found");
enabled = false;
}
targetwaypoint = 0;
endPoint = waypoints[targetwaypoint].position;
}
function Update () {
transform.position = Vector3.Lerp(startPoint, endPoint, (Time.time - startTime) / duration);
if(transform.position == endPoint){
targetwaypoint++;
endPoint = waypoints[targetwaypoint].position;
// if this new target is the last item in the array
if(targetwaypoint>=waypoints.Length){
targetwaypoint = 0;
}
}
}
I've just read through my post again and realised I don't actually change the value of startPoint at any point after it is initially set. Adding startPoint = transform.position; into the Update function has slowed it down enough that I can see all the waypoints are being used but after the first one it's super fast and I still get an Index Out of Range error at the end. Am I going about this completely the wrong way?
Answer by duck · Mar 10, 2010 at 05:09 PM
I think your "super fast" problem is occuring because you're not resetting the startTime variable when you increment the targetwaypoint counter. This means that for every transition after the first one, the duration has already elapsed.
As for the 'index out of range' error, I would guess that's occuring because you're assigning the new endPoint before you check whether the targetwaypoint variable should be wrapped back to zero.
Move the line:
endPoint = waypoints[targetwaypoint].position;
...to after the check where you test if it's the last item in the array. (remember, array access is zero-indexed, so if targetwaypoint == waypoints.Length, then targetwaypoint isn't a valid index!
(If your array is 8 items long, the first item is at position zero, and the last item is at position 7!)
Also, you might be interested in a neat way of "wrapping" your integer index counter using the modulus operator.
Also, I've moved the Lerp interpolation value to a separate variable for shorter line length, and fixed the indentation :)
So, with these fixes, your could might look like this.
// array to hold waypoint locations var waypoints : Transform[];
// variable to control time taken to travel between points var duration : float = 1.0;
private var startPoint : Vector3; private var endPoint : Vector3; private var startTime : float;
// the array index number of the current target waypoint private var targetwaypoint : int;
function Start() {
startPoint = transform.position;
startTime = Time.time;
if(waypoints.Length <= 0){
Debug.Log("No waypoints found");
enabled = false;
}
targetwaypoint = 0;
endPoint = waypoints[targetwaypoint].position;
}
function Update () {
var i = (Time.time - startTime) / duration;
transform.position = Vector3.Lerp(startPoint, endPoint, i);
if(i >= 1){
startTime = Time.time;
// increment and wrap the target waypoint index
targetwaypoint++;
targetwaypoint = targetwaypoint % waypoints.Length;
// assign the new lerp waypoints
startPoint = endPoint;
endPoint = waypoints[targetwaypoint].position;
}
}
Thanks for the detailed response Duck=) I knew about arrays being zero indexed but didn't see the flaw in my logic, and yeah, I should have noticed the problems with startTime and endPoint. I looked at the modulus operator you mentioned and, while I understand what it does with two values, i'm not sure I understand what it's doing here (that and I get an unexpected token error when using it). I'm assu$$anonymous$$g the result wraps around back to '0' each time? How would I write this without the shorthand? Ultimately I want the character to move through the waypoints in reverse when they reach the end.
I was a bit confused at first with the line if(i >= 1) but am I right in thinking that this is doing a similar thing as my line if(transform.position == endPoint) since the '1' is the end value returned by the Lerp function (or at least, as I understand it, the value of i varies between 0 and 1)? Is there any benefit to writing it this way ins$$anonymous$$d apart from shorter code?
I also couldn't assign the temporary variable 'i' like you have - in the end I had to create a variable for it - as it kept asking for a semicolon.
Oh, yes - the i>=1 is mainly for shorter code, but it's also generally good practice to not rely on comparing floats (or structs which contain floats) for equality, because of floating point imprecision problems: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
I'm surprised that %= doesn't work - it seems to be yet another thing missing from the JS syntax. Basically: "a %= b" is the same as "a = a % b", in the same way as "a += b" is the same as "a = a + b". And the "float i" thing was a result of me accidentally mixing c# and JS. sorry! (both have been fixed in the code now)
Thanks Duck, that fixes the errors and i'm getting there with understanding % (maths was never my strong point - back to school I think!) Cheers.
Answer by SwapnilRane · Aug 01, 2016 at 02:19 PM
Use this code add as many waypoint you want.
using UnityEngine; using System.Collections; using System.Collections.Generic;
[RequireComponent(typeof(Rigidbody2D))] public class SquareMovement : MonoBehaviour {
[SerializeField]
private List<Transform> wayPoints;
public float speed = 10f;
private Transform target;
private int waypointIndex = 0;
// Use this for initialization
void Start ()
{
target = wayPoints[0];
}
// Update is called once per frame
void Update ()
{
Movement();
}
//methode which decide which axis to move
void Movement()
{
Vector3 dir = target.position - transform.position;
transform.Translate(dir.normalized * speed * Time.deltaTime , Space.World);
if (Vector3.Distance(transform.position, target.position) <= 0.2f)
{
GetNextWayPoint();
}
}
void GetNextWayPoint()
{
if (waypointIndex >= wayPoints.Count - 1)
{
waypointIndex = 0;
}
else
{
waypointIndex++;
}
target = wayPoints[waypointIndex];
}
}
Answer by straydogstrut · Mar 11, 2010 at 02:36 AM
I've been looking into stepping back through the array when I reach the end. I didn't achieve that yet (seemed to have become a bit distracted in the process!) but I've modified my code quite a bit so thought I would post it here as an alternate solution.
Now i'm using a normal Javascript array rather than an in-built one (although i've read in-built ones are faster). This approach allows me to use the Shift and Add functions to achieve the same effect as Duck's modulus approach (as far as I can tell) and also means that the array is resizeable. Since Javascript arrays can't be modified from the editor i've added some script to automatically add the waypoints to the array at initialisation (much more efficient than dragging them into the editor).
Currently I have an empty object called waypoints with child cubes for the individual waypoints. The script is attached to the NPC and so has to find the waypoint object's children (took me a while to get that working!). I'd be happy to hear and comments/criticism, and thanks once again to you Duck for your help.
// array to hold waypoint locations var waypoints = new Array();
// variable to control time taken to travel between points var duration : float = 1.0;
private var startPoint : Vector3; private var endPoint : Vector3; private var startTime : float;
private var wp : Transform;
// the array index number of the current target waypoint private var targetwaypoint : int;
function Start() { startPoint = transform.position; startTime = Time.time;
// store a reference to the transform of the waypoints object
wp = GameObject.Find("waypoints").transform;
// for each of the child objects of the waypoints object
for (var child : Transform in wp) {
// add them to the waypoints array
waypoints.Push(child);
}
if(waypoints.length <= 0){
Debug.Log("No waypoints found");
enabled = false;
}
targetwaypoint = 0;
// set the initial end point to the first item in the array
endPoint = waypoints[targetwaypoint].position;
}
function Update () {
var i = (Time.time - startTime) / duration;
transform.position = Vector3.Lerp(startPoint, endPoint, i);
// if we have reached the target
if(i >= 1){
// store the current time as the new start time
startTime = Time.time;
// store the value of the current item in the array and remove it from the array
currentwaypoint = waypoints.Shift();
// add the previously removed item to the end of the array
waypoints.Add(currentwaypoint);
// set our start point to our current position
startPoint = endPoint;
//set our new endpoint to the position of the new target waypoint
endPoint = waypoints[targetwaypoint].position;
}
}