Sprite Sheets in XNA: Auto-Movement
You may want to start from my previous article on Keyboard Movement
If you begin from my previous article then you should already have a decent template for what we’re about to create, which is auto-movement! We’ve all played games such as Final Fantasy or Pokemon that have sprites that walk around say, in a circle, or back and forth along a path. Of course, they don’t just glide around, they walk like you do when you move. Therefore, in order to create a non-playable character (NPC) that uses the same animation method we do, lets add the ability to create an auto-moving character.
To start let’s have a think about what exactly we need to achieve:
- We’ll definitely need a path for our character to travel on – We will use an array of Vector2 coordinates for our character to travel along (waypoints)
- Because our character only travels in four directions we need to ensure when defining our waypoints that they are always 90 degrees to one another
- When our character starts we probably need to be able to define which direction they are facing to begin with
- Still need to pass in the game time for animation purposes
So now let’s have a think about how exactly we move our character towards each waypoint in the array:
- First we need to check to see if we’re traveling towards the first point in the array
- If we are then we’re obviously beginning our patrol and are not going backwards
- We then grab the direction we supplied as a parameter and set the direction we are facing according to this
- If we are at the last point in the array then we need to turn around and go back!
- Set a flag saying we are going in reverse
- Get the distance from the sprite to the point
- If the distance is <1 then we are at the point and we need to head towards the next point. We also need to check if this new point is in a new direction and face the character accordingly
- We move the angle as before, 90 degrees
- We check which direction the character is facing and animate accordingly
This might be a little hard to visualize from text but the only real complexity is the fact that we travel through our route forwards, then travel through backwards allowing for an infinite patrol.
The animation is still handled by our previous animation functions, though we have added a new concept of direction. For my implementation I have used the concept of North, South, East and West so I use a single character N,S,E, or W to represent the direction my character is facing.
I also use a new function to determine which way my character is facing by checking if the previous waypoint is north, south, east or west of us (it will only ever be one of the four due to 90 degree turning).
The code to get the direction is as follows:
private void getDirection(Vector2[] points,GameTime gameTime) { float currentX; float currentY; float previousX; float previousY; m_previousDirection = m_direction; try { previousX = points[m_count - 1].X; previousY = points[m_count - 1].Y; currentX = points[m_count].X; currentY = points[m_count].Y; } catch { return; } switch (m_reverse) { case false: if (currentX > previousX) { m_direction = 'E'; return; } if (currentX < previousX) { m_direction = 'W'; return; } if (currentY > previousY) { m_direction = 'S'; return; } if (currentY < previousY) { m_direction = 'N'; return; } break; case true: if (currentX > previousX) { m_direction = 'W'; return; } if (currentX < previousX) { m_direction = 'E'; return; } if (currentY > previousY) { m_direction = 'N'; return; } if (currentY < previousY) { m_direction = 'S'; return; } break; } }
Obviously it does a sequence of very simply less than/greater than checks to see which direction the character should be facing on the turn.
The complete code for the auto-movement is as follows:
public void autoMove(Vector2[] points,Char initialDirection,GameTime gameTime) { if (m_count == 0) { m_reverse = false; switch (initialDirection) { case 'N': animateUp(gameTime); break; case 'E': animateRight(gameTime); break; case 'S': animateDown(gameTime); break; case 'W': animateLeft(gameTime); break; } } if (m_count == points.Length && m_reverse != true) { getDirection(points, gameTime); m_count--; m_reverse = true; } float dist_x = (float)(points[m_count].X - m_position.X); float dist_y = (float)(points[m_count].Y - m_position.Y); float distance = (float)(Math.Sqrt(Math.Pow(dist_x, 2) + Math.Pow(dist_y, 2))); if (Math.Floor(distance) <= 1) { switch (m_reverse) { case false: m_count++; getDirection(points, gameTime); break; case true: getDirection(points, gameTime); m_count--; break; } } float angle = (float)Math.Atan2(dist_y, dist_x); m_position.X = (float)(m_position.X + 2 * Math.Cos(angle)); m_position.Y = (float)(m_position.Y + 2 * Math.Sin(angle)); if (m_direction.Equals('E')) { animateRight(gameTime); } if (m_direction.Equals('S')) { animateDown(gameTime); } if (m_direction.Equals('W')) { animateLeft(gameTime); } if (m_direction.Equals('N')) { animateUp(gameTime); } m_sourceRect = new Rectangle(m_currentFrame * m_spriteWidth, 0, m_spriteWidth, m_spriteHeight); m_origin = new Vector2(m_sourceRect.Width / 2, m_sourceRect.Height / 2);
You can check out the complete code if any of this is unclear, and make sure to ask if you don’t understand what I’ve done. Integrating the two packages should be quite simple now to have one complete sprite sheet class!
Please note that the source is compiled for windows, and is also compiled for XNA 3.0! If you need it to run in XNA 2.0 then just make a new XNA 2.0 project and import the Game1.cs file and the texture. (Remember to change the namespace to the name of your project!)
hello
i want o ask something .. how to make the sprite moving 1 pixel/ frame ? can u show me how?
You have a splendid post. I deem it. Thanks for keep me update. I have the benefit of stay.