
A while back, I posted a simple program on the XNA forums that showed how to define a path, and then have objects follow that path. I figured I'd copy the code from there onto this site, for easy reference. The program below is a command-line C# program; you can compile it from the command line with "csc flypath.cs" and run it to test it out. Or you can copy the "follower" class from the middle, and use it in your XNA project. (The additional code, including the declaration of Vector2, is just there to make the test program work stand-alone)
using System; namespace flypath { /// <summary> /// Create an instance of class Follow, passing in the initial position of the object /// and its speed in pixels per second. Also pass in an array of Vector2 that describe /// the relative movement of the object. Call Move() each frame, passing in the number /// of seconds since the last frame (typically 0.166667f for 60 Hz) and get back the /// new position of the object. If Move() returns false, it means that the path has /// been completed. /// </summary> public class Follow { Vector2[] path_; Vector2 offset_; float speed_; float t_; float len_; int segment_; public static Vector2[] SomePath = new Vector2[] { new Vector2(-100, -100), // start out going down/left new Vector2(0, -200), // then go down-right until you're right below where you started new Vector2(0, -300), // then go straight down new Vector2(100, -100), // then go right/up new Vector2(0, 0), // then return to "home" }; public Follow(Vector2[] path, Vector2 spawnPoint, float speed) { path_ = path; offset_ = spawnPoint; speed_ = speed; t_ = 0; // Make sure the internal state machine knows we're at the first segment start point EnterSegment(0); } void EnterSegment(int seg) { // the state machine has reached a new segment in the path to follow segment_ = seg; if (segment_ < path_.Length - 1) { // calculate length to go // calculate how long the path is, at our configured velocity len_ = (path_[segment_ + 1] - path_[segment_]).Length(); t_ = 0; // I have not started moving here yet, so I'm at time 0 of this segment } } /// <summary> /// Move the object along the path, given some amount of time has elapsed. /// </summary> /// <param name="dt">The number of seconds that has elapsed since the last call.</param> /// <param name="pos">Returns the new position of the object.</param> /// <returns>false if the path has finished</returns> public bool Move(float dt, out Vector2 pos) { // add to the current time along the path t_ += dt * speed_; // if I reached the end of this segment, move to a new segment if (t_ >= len_) { EnterSegment(segment_ + 1); } // calculate my position along the current segment (or, if complete, just return the start point) pos = (segment_ >= path_.Length - 1) ? offset_ : offset_ + path_[segment_] + (path_[segment_ + 1] - path_[segment_]) * (t_ / len_); // return true as long as I'm still following the path return segment_ < path_.Length; } } public class Program { static void Main(string[] args) { Vector2 initPos = new Vector2(300, 600); Follow f = new Follow(Follow.SomePath, initPos, 20); Console.WriteLine("First pos: {0}", initPos); while (f.Move(1, out initPos)) { Console.WriteLine("New pos: {0}", initPos); } Console.WriteLine("End pos: {0}", initPos); return; } } // In this test program, I define my own Vector2, so that I don't have to // link with the entire XNA framework. public struct Vector2 { public Vector2(float x, float y) { X = x; Y = y; } public float X; public float Y; public float Length() { return (float)Math.Sqrt(X*X + Y*Y); } public override string ToString() { return String.Format("{0:0.0},{1:0.0}", X, Y); } public static Vector2 operator +(Vector2 a, Vector2 o) { return new Vector2(a.X + o.X, a.Y + o.Y); } public static Vector2 operator -(Vector2 a, Vector2 o) { return new Vector2(a.X - o.X, a.Y - o.Y); } public static Vector2 operator *(Vector2 a, float s) { return new Vector2(a.X * s, a.Y * s); } } }
Comments
bug
The code is incorrect, what if I made path like this one ? It will calculate NaN position (length will be 0.0)
new Vector2(0, 0),
new Vector2(-5, 5),
new Vector2(-5, 5),
new Vector2(-5, 5),
Don't do that, then :-)
Don't do that, then :-)
Final position
I would have thought the code would be more useful/robust if it returned the offset + final point on path when it has finished following to allow for paths that don't always return to the start.
currently if you final point didn't happen to be 0,0 the sprite (or whatever would teleport back to 0,0 when finished.
pos = (segment_ >= path_.Length - 1) ? offset_+path_[path_.Length-1] : offset_ + path_[segment_] + (path_[segment_ + 1] - path_[segment_]) * (t_ / len_);
Sure, if that's the behavior
Sure, if that's the behavior you want, you should do that!
I dont really understand why
I dont really understand why they is a speed variable. I dont see where it is used!
Fixed.
Fixed.
Looping Sprites
Greetings,
Thanks for the tutorial, this is great info. I've added it to my Windows Phone project and really like how easy it is to use. I typically don't add comments to post I read, but thought I'd say thanks!
-Johnathon Scionwest
My Blog
My Twitter