Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts
Monday, November 24, 2008
XNA Series - 3d textures from Blender
A quick note, if you are exporting an object with uv images, you need to add the images to a subfolder of your Content folder called fbx (under the folder you are putting your fbx files) This is where the fbx exporter for blender is saying that the image files related to your object are. Now you don't actually have to create this folder in your solution explorer, just in your directory tree, or you can do it in the solution explorer, but you will need to then right click on the image and say "exclude from project". This is because the fbx import process in XNA does the texture loading behind the scenes. If you leave the file in the content pipeline, it will try to import the image 2 times and you will get unneeded warnings.
Saturday, November 22, 2008
XNA Series - Begining 3D - Part 1
I have been wanting to get some posts on here about 3D. But since it is a much more complex subject than 2D, I have been holding off as I am still getting my head around it. Perhaps we can work through this together and see what we come up with.
To start drawing in 3D, we need a model. I recommend something like Blender3D, since it has all the tools you need to create a working 3d model that you can import into XNA. This is not a tutorial about modeling in blender, perhaps I'll do that later. But once you have a model created in Blender, you want to export it in Autodesk FBX format. You can load that into your content pipeline in the same way you import a sprite. The difference is in the type of C# object we create to load it into. Rather than a texture2d object, we load in this model as a Model object. We will also create a Vector3 object to set it's position rather than a Vector2, since we need to put this model in 3d space.
Project Files
Vector3 camPos will store the location of our 3d camera. Our 3d camera can be thought of as a video camera placed into the 3d world at a given location.
camLookAt is a point at which our camera will be pointed in 3d space.
the 2 Matrices camProj and camView are somewhat more complicated and we will set them up later.
To load in our Model's data, in LoadContent we will do this
tower = Content.Load<model>("tower");
that should look familiar, since it is the same way we loaded in images. Except we make sure to tell the Load function it is dealing with Model objects rather than Texture2D.
Next we initialize the camView matrix
camView = Matrix.CreateLookAt(camPos, camLookAt, Vector3.Up);
We create a 4x4 matrix from the position of the camera and the point it is looking at. This matrix gives us a way to alter the way we see the model so that it looks like we are seeing it from the camera. Then we set up the projection matrix
camProj = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f),
graphics.GraphicsDevice.Viewport.AspectRatio,
1.0f,
10000.0f);
which will alter the way we see the model in perspective. We pass this method the width of our view field (45 degrees). the aspect ratio of our graphics device and the near and far limits to what we can see (clipping planes). If you don't fully understand these matrix operations, its ok, just press on for now.
We are almost there, we need to create a method that applies all these things to our model. Since we will want to draw more than one model in the future, we will make a generic function. We call it DrawModel and will pass it a model to be drawn, it's position and a scale (to make it bigger or smaller). In a model object we can have many meshes. So we need to loop through them all. Then each mesh has a list of "BasicEffects" which control how that mesh will be drawn, so we loop through those too. For each basic effect we will do the following
To start drawing in 3D, we need a model. I recommend something like Blender3D, since it has all the tools you need to create a working 3d model that you can import into XNA. This is not a tutorial about modeling in blender, perhaps I'll do that later. But once you have a model created in Blender, you want to export it in Autodesk FBX format. You can load that into your content pipeline in the same way you import a sprite. The difference is in the type of C# object we create to load it into. Rather than a texture2d object, we load in this model as a Model object. We will also create a Vector3 object to set it's position rather than a Vector2, since we need to put this model in 3d space.
Project Files
The next 4 objects in our game class declarations are a little more complex.
Model tower;
Vector3 pos = Vector3.Zero;
Vector3 camPos = new Vector3(0.0f, 60.0f, 160.0f);
Vector3 camLookAt = new Vector3(0.0f, 50.0f, 0.0f);
Matrix camProj;
Matrix camView;
Vector3 camPos will store the location of our 3d camera. Our 3d camera can be thought of as a video camera placed into the 3d world at a given location.
camLookAt is a point at which our camera will be pointed in 3d space.
the 2 Matrices camProj and camView are somewhat more complicated and we will set them up later.
To load in our Model's data, in LoadContent we will do this
tower = Content.Load<model>("tower");
that should look familiar, since it is the same way we loaded in images. Except we make sure to tell the Load function it is dealing with Model objects rather than Texture2D.
Next we initialize the camView matrix
camView = Matrix.CreateLookAt(camPos, camLookAt, Vector3.Up);
We create a 4x4 matrix from the position of the camera and the point it is looking at. This matrix gives us a way to alter the way we see the model so that it looks like we are seeing it from the camera. Then we set up the projection matrix
camProj = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f),
graphics.GraphicsDevice.Viewport.AspectRatio,
1.0f,
10000.0f);
which will alter the way we see the model in perspective. We pass this method the width of our view field (45 degrees). the aspect ratio of our graphics device and the near and far limits to what we can see (clipping planes). If you don't fully understand these matrix operations, its ok, just press on for now.
We are almost there, we need to create a method that applies all these things to our model. Since we will want to draw more than one model in the future, we will make a generic function. We call it DrawModel and will pass it a model to be drawn, it's position and a scale (to make it bigger or smaller). In a model object we can have many meshes. So we need to loop through them all. Then each mesh has a list of "BasicEffects" which control how that mesh will be drawn, so we loop through those too. For each basic effect we will do the following
- enable lighting
- set lighting preferences
- set the World, Projection and View transformation matrices for the effect
void DrawModel(Model model, Vector3 modelPosition,float scale)
{
foreach(ModelMesh mesh in model.Meshes){
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.PreferPerPixelLighting = true;
effect.World =
Matrix.CreateTranslation(modelPosition)*
Matrix.CreateScale(scale)
;
effect.Projection = camProj;
effect.View = camView;
}
mesh.Draw();
}
}
All that is left at this point is to call DrawMesh from our Draw method with appropriate parameters.
DrawModel(tower, pos,10f);
I've added a couple extra calls to DrawModel in the project file so you can see a little "scene"
Friday, November 07, 2008
A Simple but Good Game Creation Article
I came across a good article on a path to Game development. It seems the original site is now non responsive, but the web archive of it still exists. This short article was a recommendation from an indie game developer about a series of game clones to try to complete in an effort to learn by doing. The article is from 1999, but I think it has some good suggestions. The idea is that you create these clones from your knowledge of the games listed and to make a complete game out of each. The games he suggests are
A Tetris type game
A Breakout type game
A Pac-Man type game
A Platformer (Super Mario type game)
He gives reasons for each and this seems like a good path. He really stresses the importance of making each of these into a finished working game, so that you have the experience of FINISHING a game rather than starting one, over and over.
The original article is here on the web archive.
A Tetris type game
A Breakout type game
A Pac-Man type game
A Platformer (Super Mario type game)
He gives reasons for each and this seems like a good path. He really stresses the importance of making each of these into a finished working game, so that you have the experience of FINISHING a game rather than starting one, over and over.
The original article is here on the web archive.
Wednesday, November 05, 2008
XNA Series - Game Components
Ok, I know I said the next article would be about steering, but the next article for that topic is not ready yet, so I thought I would talk about a pretty cool class in XNA called GameComponent and its child Class DrawableGameComponent. These classes are made to add components to your game in a modular way. A component is something that needs to update with your game. DrawableGameComponent inherits GameComponent and adds functionality so that the component is also drawn at proper times as well. You could actually re-design our GameObject class as a DrawableGameComponent or at least inherit a class from DrawableGameComponent to give us the added functionality. Now from my reading, it seems that a lot of people think GameComponents are are really aimed at single game objects like a tank, but to add a TankManager class that would deal with the updating and drawing of all the tank objects in our game. But it certainly doesn't hurt to learn the type in an easy way.
We will create a UserTank class that is derived from DrawableGameObject. We just need to implement the basic constructor which takes a Game Object. So our UserTank will get all the functions (except private ones) of the DrawableGameObject class.
To begin this is actually all we need to add this tank to our game. In our game class we would add
and in our constructor we would initialize it in the same old way
but we do something a little different after that
Since the hero tank is a UserTank which is derived from DrawableGameObject which is Derived from GameComponent, we can put it in the GameClasses list of GameComponents. These GameComponents all get called on updates and if they are drawable, they get their draw methods called.
So now even though there is nothing "in" our UserTank class, it is being updated with our game. Lets add a couple bits of info to the UserTank class that will help us out. To save time and space, I will implement the fields of the component as public rather than making them Properties. This is lazy, don't follow my lead in this.
So we can store our location, sprite and a spritebatch to draw to. Then we simply override LoadContent, Update and Draw.
So as you see we initialize the spritebatch and load it's sprite. Then we call the base classes LoadContent method. In the Draw method we Begin our spritebatch, make a Draw call and End the SpriteBatch and then call the base classes Draw method. Then finally in the Update method we look at the KeyboardState and make some updates to the location and once again call the base classes Update method. So now that you have this component we could add it and it's logic in a very simple manner.
Here are the files for this project.
We will create a UserTank class that is derived from DrawableGameObject. We just need to implement the basic constructor which takes a Game Object. So our UserTank will get all the functions (except private ones) of the DrawableGameObject class.
public class UserTank : DrawableGameObject
{
public UserTank(Game g)
: base(g)
{
}
}
To begin this is actually all we need to add this tank to our game. In our game class we would add
UserTank hero;
and in our constructor we would initialize it in the same old way
hero = new UserTank();
but we do something a little different after that
Components.Add(hero);
Since the hero tank is a UserTank which is derived from DrawableGameObject which is Derived from GameComponent, we can put it in the GameClasses list of GameComponents. These GameComponents all get called on updates and if they are drawable, they get their draw methods called.
So now even though there is nothing "in" our UserTank class, it is being updated with our game. Lets add a couple bits of info to the UserTank class that will help us out. To save time and space, I will implement the fields of the component as public rather than making them Properties. This is lazy, don't follow my lead in this.
public Vector2 location = Vector2.zero;
public Texture2D sprite;
SpriteBatch spriteBatch;
So we can store our location, sprite and a spritebatch to draw to. Then we simply override LoadContent, Update and Draw.
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(this.GraphicsDevice);
sprite = this.Game.Content.Load("tank");
base.LoadContent();
}
public override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
spriteBatch.Draw(sprite, location, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
public override void Update(GameTime gameTime)
{
KeyboardState ks = Keyboard.GetState();
if (ks.IsKeyDown(Keys.Up))
{
location.Y -= 1;
} else if (ks.IsKeyDown(Keys.Down))
{
location.Y += 1;
}
if (ks.IsKeyDown(Keys.Left))
{
location.X -= 1;
} else if (ks.IsKeyDown(Keys.Right))
{
location.X += 1;
}
base.Update(gameTime);
}
So as you see we initialize the spritebatch and load it's sprite. Then we call the base classes LoadContent method. In the Draw method we Begin our spritebatch, make a Draw call and End the SpriteBatch and then call the base classes Draw method. Then finally in the Update method we look at the KeyboardState and make some updates to the location and once again call the base classes Update method. So now that you have this component we could add it and it's logic in a very simple manner.
Here are the files for this project.
Monday, November 03, 2008
XNA Series - AI - Better Steering - Part 1
I have been reading Steering Behaviors For Autonomous Characters by Craig Reynolds and after several reads I have decided to try to implement it in my own code and I think I am ready to explain it to you my loyal reader.
In his paper Mr Reynolds take a different approach that we have been taking thus far. Rather than having our steering be a simple rotation and moving forward at a set speed. He takes several other aspects into consideration, making a much more detailed simulation. To start we think about it in these terms: First we decide what direction and "magnitude" we need to move in that direction to steer us toward our goal. This is called our "Steering Vector". If we applied the steering vector all at once, we would immediately be put on the right path, but that would make for a very non-realistic simulation. Instead, rather, in the application of the steering vector, we take into consideration our current velocity, direction, mass and maximum force we can exert. We then factor these items against our steering vector to get an acceleration in a new direction to be added to our current velocity, we truncate this new velocity to our maximum speed and in that timestep we take a step towards steering to our goal.
The benefit of this method really lies in the Steering Vector. Since vectors can be scaled, added, subtracted, etc. We could apply several steering vectors. Say an object was following waypoints, we could say if an enemy is within a given distance, add the Flee steering vector times 50% and the Waypoint vector times 50%. We would then apply this new steering vector rather than just Waypoint or Flee, so that the object is still trying to do the waypoints, while evading.
In my next post, I will start looking at how we can adapt this method into our GameObject class.
The paper to which I am referring.
In his paper Mr Reynolds take a different approach that we have been taking thus far. Rather than having our steering be a simple rotation and moving forward at a set speed. He takes several other aspects into consideration, making a much more detailed simulation. To start we think about it in these terms: First we decide what direction and "magnitude" we need to move in that direction to steer us toward our goal. This is called our "Steering Vector". If we applied the steering vector all at once, we would immediately be put on the right path, but that would make for a very non-realistic simulation. Instead, rather, in the application of the steering vector, we take into consideration our current velocity, direction, mass and maximum force we can exert. We then factor these items against our steering vector to get an acceleration in a new direction to be added to our current velocity, we truncate this new velocity to our maximum speed and in that timestep we take a step towards steering to our goal.
The benefit of this method really lies in the Steering Vector. Since vectors can be scaled, added, subtracted, etc. We could apply several steering vectors. Say an object was following waypoints, we could say if an enemy is within a given distance, add the Flee steering vector times 50% and the Waypoint vector times 50%. We would then apply this new steering vector rather than just Waypoint or Flee, so that the object is still trying to do the waypoints, while evading.
In my next post, I will start looking at how we can adapt this method into our GameObject class.
The paper to which I am referring.
Saturday, November 01, 2008
XNA Series - A Simple Particle System
Sometimes you need to create a an effect like smoke or an explosion. This is when you can turn to a particle system. You can think of a particle system as an emitter. It emits points on given velocities, and each of those points has a lifetime. Once that lifetime is over they are no longer updated or drawn. This example will be a simple "fountain"
First, we will create a class to represent each particle. We will call it Particle
So each particle has a field that shows if it is to be drawn an updated. A current position and velocity as well as a counter for how many updates it has been alive.
In our particle system class we keep track of how many particles are in the system, the position of the emitter, the texture of the particles, an array of particles, a counter of how many updates the system has been through and if the system is running.
Here is an init function. This will instantiate the particle array and set each member to new particle. I was also lazy here and set the initial velocity to [0,-3] with each component getting a number between -1 and 1 added to it for some randomness.
This method simply starts the system
The update method first checks to see if the system is started. If so, it increments the total updates for the system and if we still have particles left, we set the next one to alive and set it's position to that of the emitter. Then for each alive particle, we increment it's life, if its been alive too long we kill it, if not, we add some gravity to it's velocity (taking into account elapsed time, and then add it's velocity to it's position.
The draw method is very simple. I have added a couple extra things in it to make it more interesting. First we simply iterate over the particles and if they are alive, we draw them to the spritebatch. Now to make it more interesting I divide the life of that particle by it's total time to live. Then use that as a percentage to alter the alpha value of the drawing color and also to alter the scale of the object. I just to them in reverse so that the scale goes from 0 to 1 and the then the alpha goes from 1 to 0 (then I multiply it by 255 to convert it to a byte).
So yes, this is a VERY simple particle system, In the project files you will see that I update the position of the system based on keyboard controls, so you can move the emitter around.
The Project Files
First, we will create a class to represent each particle. We will call it Particle
public class Particle
{
public bool alive = false;
public Vector2 position = Vector2.Zero;
public Vector2 velocity = Vector2.Zero;
public int lifeTime = 0;
}
So each particle has a field that shows if it is to be drawn an updated. A current position and velocity as well as a counter for how many updates it has been alive.
public class ParticleSystem
{
public int totalParticles = 500;
public Vector2 position = Vector2.Zero;
public Texture2D sprite;
public Particle[] parts;
public int current = 0;
public int end = 60;
public bool started = false;
In our particle system class we keep track of how many particles are in the system, the position of the emitter, the texture of the particles, an array of particles, a counter of how many updates the system has been through and if the system is running.
public void Init(){
Random r = new Random();
parts = new Particle[totalParticles];
for (int i = 0; i < totalParticles; i++)
{
parts[i] = new Particle();
parts[i].velocity = new Vector2(0 +(float)r.Next(-100,100)/100f,
-3 + (float)r.Next(-100,100)/100f);
}
}
Here is an init function. This will instantiate the particle array and set each member to new particle. I was also lazy here and set the initial velocity to [0,-3] with each component getting a number between -1 and 1 added to it for some randomness.
public void Start()
{
started = true;
}
This method simply starts the system
public void Update(GameTime gameTime)
{
if (started)
{
current++;
if (current < totalParticles)
{
parts[current].alive = true;
parts[current].position = position;
}
for (int i = 0; i < totalParticles; i++)
{
if (parts[i].alive)
{
parts[i].lifeTime++;
if (parts[i].lifeTime > end)
{
parts[i].alive = false;
} else
{
parts[i].velocity.Y += 9.8f/50f;
parts[i].position = parts[i].position + parts[i].velocity;
}
}
}
}
}
The update method first checks to see if the system is started. If so, it increments the total updates for the system and if we still have particles left, we set the next one to alive and set it's position to that of the emitter. Then for each alive particle, we increment it's life, if its been alive too long we kill it, if not, we add some gravity to it's velocity (taking into account elapsed time, and then add it's velocity to it's position.
public void Draw(SpriteBatch sb)
{
for (int i = 0; i < totalParticles; i++)
{
if (parts[i].alive)
{
Color c = Color.White;
c.A = (byte)((1-(parts[i].lifeTime / (float)end))*255f);
sb.Draw( sprite,
parts[i].position,
null,
c,
0f,
new Vector2(12, 12),
parts[i].lifeTime / (float)end,
SpriteEffects.None,
0
);
}
}
}
}
The draw method is very simple. I have added a couple extra things in it to make it more interesting. First we simply iterate over the particles and if they are alive, we draw them to the spritebatch. Now to make it more interesting I divide the life of that particle by it's total time to live. Then use that as a percentage to alter the alpha value of the drawing color and also to alter the scale of the object. I just to them in reverse so that the scale goes from 0 to 1 and the then the alpha goes from 1 to 0 (then I multiply it by 255 to convert it to a byte).
So yes, this is a VERY simple particle system, In the project files you will see that I update the position of the system based on keyboard controls, so you can move the emitter around.
The Project Files
Friday, October 31, 2008
XNA - Simulating Gravity
Now that we have talked a little about vectors and acceleration and such, lets put together a simple 'game' that takes a few of these things together. We will create a Game Object like we have been doing, but we are going to add an Update method to it that will move the updating of our object inside of it rather than outside.
So we pass the current gameTime into the object and check our location. If we are above 400 all we will do is acellerate based on gravity. Now Real gravity is -9.8 meters per second squared. So since we move "down" in XNA terms by adding to Y rather than subtracting, our Gravity velocity will be 9.8. Now 9.8 is relative in pixels So we are basically saying that 1 pixel is one meter. We could of course scale this. If our sprite is a person and is 25 pixels high and "in real life" that person is 2 meters tall, then really we want 12.5 pixels to represent a meter. So in that case we would scale any acellerations by 12.5 to get a more physically accurate simulation. You will also notice that my acceleration vector is not just [0,9.8] but instead I am multiplying by something you have not yet seen. gameTime.ElapsedTime.TotalSeconds. This is a representation in factional seconds of how long it has been since the last update. So since 9.8 is supposed to be per one second, if we multiply that by how long it has actually been (say 0.02 seconds) we will just get enough for the time that has past. That way no matter how fast or slow our program runs, the gravity acceleration should stay constant.
The only other thing that I am doing here is if my object falls below the 400 pixel mark. I make it bounce. I do this by negating it's Y velocity and scaling it back a little. The closer I scale the Y velocity to 0 the less it bouces back. If I scale it higher that 1 it will bounce higher then when it started in the first place.
Here are the project files.
public void Update(GameTime gameTime)
{
if (position.Y < 400)
{
velocity += new Vector2(0f, (float)(9.8 * gameTime.ElapsedGameTime.TotalSeconds));
}
else
{
velocity.Y = velocity.Y * -0.8f;
}
position += velocity;
}
So we pass the current gameTime into the object and check our location. If we are above 400 all we will do is acellerate based on gravity. Now Real gravity is -9.8 meters per second squared. So since we move "down" in XNA terms by adding to Y rather than subtracting, our Gravity velocity will be 9.8. Now 9.8 is relative in pixels So we are basically saying that 1 pixel is one meter. We could of course scale this. If our sprite is a person and is 25 pixels high and "in real life" that person is 2 meters tall, then really we want 12.5 pixels to represent a meter. So in that case we would scale any acellerations by 12.5 to get a more physically accurate simulation. You will also notice that my acceleration vector is not just [0,9.8] but instead I am multiplying by something you have not yet seen. gameTime.ElapsedTime.TotalSeconds. This is a representation in factional seconds of how long it has been since the last update. So since 9.8 is supposed to be per one second, if we multiply that by how long it has actually been (say 0.02 seconds) we will just get enough for the time that has past. That way no matter how fast or slow our program runs, the gravity acceleration should stay constant.
The only other thing that I am doing here is if my object falls below the 400 pixel mark. I make it bounce. I do this by negating it's Y velocity and scaling it back a little. The closer I scale the Y velocity to 0 the less it bouces back. If I scale it higher that 1 it will bounce higher then when it started in the first place.
Here are the project files.
Labels:
C#,
Games,
Programming,
Reference,
Simulation,
Tutorial,
XNA
Thursday, October 30, 2008
XNA - Version 3.0
XNA Game Studio 3.0 is scheduled for release today. I'm not sure what extra features it will have versus the Beta I've been using, but now that it is in it's stable release version, you should take the time to upgrade! I'm sure once the site is back up, there will be download links available at the XNA creators club website.
EDIT
So the site is back up with a 62.5mb download for XNA Game Studio 3. When I logged in, I had to edit my profile info again. There is also a survey that they want you to take about your involvement in making video games. Be aware though it is a lengthy survey.
EDIT
So the site is back up with a 62.5mb download for XNA Game Studio 3. When I logged in, I had to edit my profile info again. There is also a survey that they want you to take about your involvement in making video games. Be aware though it is a lengthy survey.
Wednesday, October 29, 2008
XNA Sidebar - More Vectors
Another thing that vectors can do for us is handle acceleration. We already talked about moving at a certain velocity (distance and direction) but what happens if we want to continue on that path or speed up or turn in a new direction. We have to accelerate or slow down or have force applied to us in a new direction. If our object has a position P and a current velocity of [3,3] which means at every timestep we move 3 in the x direction and 3 in the y direction. If we want to speed up to [10,10] we could of course just set our velocity to [10,10], but that is very unrealistic. That would be like getting in your car and going 30 miles per hour, pressing the gas and in one microsecond be going 100 mph! The sheer force of that acceleration alone would probably kill you. Depending on your mode of transportation you have limits to how fast you can accelerate. This is defined by how much mass you have and how much force you can exert. Because if you took high-school physics, you may remember this.
F=ma
Which means: Force = Mass x Acceleration. This is Newton's Second Law of Motion. We would know the mass of our object ( say your vehicle weighs ton in english measurements. Which is approx 907kg) and we decide it should be able to go from 0 to 30 meters per second (which is about 60 miles per hour, use google to do your conversions!) in 10 seconds. Now we will assume for our simulation that acceleration is constant, that means every second we would need to go 3 meters per second faster. So we want to acellerate at a maximum of 3 m/s^2. So our maximum force is
907kg * 3 m/s^2 = 2721 Newtons or (kg*m/s^2)
But how do we apply this to vectors?
Well first we need to know our desired velocity. In my car example before we were going 30 mph and our desired velocity was 100mph (naughty speeder) That is velocity in 1 direction. Now lets think in 2. our current velocity is the vector [0,0] and we want to go [10,10] we cannot just jump from one to the other. In this case we can actually use the length of the vector to represent the force we would have to apply to go from one to the other. In this case the length of the vector is 14.14 well, say we determine that our maximum force is a vector length of 2, we truncate our desired vector to a length of 2 (we leave it alone if it is less than that already) and that gives us a vector which is the maximum force vector which we can apply to our object. So now that we have force, we divide that by the mass of our object say 10kg and that gives us our acceleration vector. Now for each timestep we do this calculation and add our acceleration vector to our current velocity to get our new accelerated velocity. We do have to truncate our velocity to our maximum speed since the length of our velocity vector is equal to our speed. Then we simply add our velocity vector to our position point and we have our new position for that timestep.
So some things we learned about vector lengths...
Length of the Vector between 2 velocity vectors is the total force required to accelerate from one to the other.
Then length of the velocity vector is the current speed
The Acceleration vector is gained by Dividing the Force vector by the mass of the object
The Acceleration vector is added to the velocity vector each time step to gain a new velocity.
Did I get something wrong? If you know more about this than me, please contribute by commenting below!
F=ma
Which means: Force = Mass x Acceleration. This is Newton's Second Law of Motion. We would know the mass of our object ( say your vehicle weighs ton in english measurements. Which is approx 907kg) and we decide it should be able to go from 0 to 30 meters per second (which is about 60 miles per hour, use google to do your conversions!) in 10 seconds. Now we will assume for our simulation that acceleration is constant, that means every second we would need to go 3 meters per second faster. So we want to acellerate at a maximum of 3 m/s^2. So our maximum force is
907kg * 3 m/s^2 = 2721 Newtons or (kg*m/s^2)
But how do we apply this to vectors?
Well first we need to know our desired velocity. In my car example before we were going 30 mph and our desired velocity was 100mph (naughty speeder) That is velocity in 1 direction. Now lets think in 2. our current velocity is the vector [0,0] and we want to go [10,10] we cannot just jump from one to the other. In this case we can actually use the length of the vector to represent the force we would have to apply to go from one to the other. In this case the length of the vector is 14.14 well, say we determine that our maximum force is a vector length of 2, we truncate our desired vector to a length of 2 (we leave it alone if it is less than that already) and that gives us a vector which is the maximum force vector which we can apply to our object. So now that we have force, we divide that by the mass of our object say 10kg and that gives us our acceleration vector. Now for each timestep we do this calculation and add our acceleration vector to our current velocity to get our new accelerated velocity. We do have to truncate our velocity to our maximum speed since the length of our velocity vector is equal to our speed. Then we simply add our velocity vector to our position point and we have our new position for that timestep.
So some things we learned about vector lengths...
Length of the Vector between 2 velocity vectors is the total force required to accelerate from one to the other.
Then length of the velocity vector is the current speed
The Acceleration vector is gained by Dividing the Force vector by the mass of the object
The Acceleration vector is added to the velocity vector each time step to gain a new velocity.
Did I get something wrong? If you know more about this than me, please contribute by commenting below!
Tuesday, October 28, 2008
XNA Sidebar - Intro To Vector Math
I am not a math wiz, but I know enough to get around. I wrote my Sidebar article on Trigonometry to explain a few concepts that people might want to know to better program in XNA. Well according to my site metrics, it quicky became one of my most viewed pages. So apparently it was info people wanted to know! Well in that same spirit, I give you my intro to vectors post.Vectors in 2 dimensions are relatively easy concepts, especially when you can visualize them. They have 2 parts, a direction and a magnitude. We can write them as a simple coordinate like [7,8]. Now, although this looks more like a point (and you are right) if we consider this as relative to another point, say (0,0) we now have a vector. As you can see the vector in the image has a direction and a length. In this case the length could be found by the Pythagorean theorem. where A is the x length and B is the Y length. So the length of this vector is the square root of 7 squared + 8 squared, which is 10.63.
One of the simplest operations we can do to a vector is addition. Say we take our vector [7,8] and add [1,3]. If we look at example B, we see the 2 vectors. But when we add them, we simply add their 2 components. Thus resulting in a new vector [8,11]. Which makes more sense if we look at example C where we have stacked the first
2 vectors. That is went from the endpoint of the first vector and moved 1 in the x direction and 3 in the Y. The same goes for subtraction, just minus instead of plusMultiplication is another useful tool for Vectors. when we multiply a vector times a scaler (a single number) it "scales" the vector to a new length. So if we say [3,4] * 2 we end up with the vector [6,8] which is 2 times as long as the first vector. See Example D. Want some proof? The length of the vector [3,4]
LENGTH [3,4]
= SQRT[3^2 + 4^2]
= SQRT[9+16]
= SQRT[25]
= 5
[3,4] * 2 = [6,8]
LENGTH [6,8]
= SQRT[6^2 + 8^2]
= SQRT[36 + 64]
= SQRT[100]
= 10
and 10 is 2 times 5.
So we have double the length of the vector, but kept the same direction. Much like if we think of a vector as a force being applied to an object in a given direction it will go a certain distance. If we double that force, it will go twice as far. This works the same for division. dividing a vector by 2 will cut it's length in half.
One of the things that we may want to do is find out what the vector that goes in the same direction as this vector, but has a length of 1 is. This is called a unit vector and is gained when we normalize a vector. Luckily for us the XNA Vector2 class has a Normalize method which converts a Vector2 into a unit vector. This is really handy when we have a vector that we want to follow, but it is longer than our current velocity can carry us. Say we want to move from (0,0) to (5,5) we would add the vector (5,5) to position, but say our maximum speed for that movement is only 2. The vector [5,5] would take us aprx 7.07 in that direction. We need to find the vector that is 2 units long in the same direction. We can do that by Normalizing the vector [5,5] and then multiplying it by 2. So we scale it up or down to a length of 1 and then multiply it by the length we actually want it to be. So in c# that would look like this
float maxSpeed = 2;
Vector2 canGo;
Vector2 wantToGo = new Vector2(5,5);
wantToGo.Normalize();
canGo = wantToGo * maxSpeed;
When we multiply this out we get a point that is at [1.14,1.14] This is the vector [5,5] Normalized and multiplied by 2. You are going to see these vector method a lot in our new steering code, because a lot of the time we want to steer further than we are able to move so we have to scale back our vector to a size that we can actually move.
Monday, October 27, 2008
XNA Series - Refactoring GameObject
By now, our GameObject and GameAgent classes are getting out of hand, we have kept expanding them and now they are not very cohesive. There are several problems I see as I look at the code in front of me at this point:
Next I want to move my variables from the access level of public to protected. This means that only objects that are derived from this class can see these variable, everyone who just uses an instance of the class has to get at them through either a method or a property (or not at all).
So for all the variables that need to be viewed outside the class (location, rotation, etc) I am creating a public Property.
Then to top things off, I am adding XML style comments to all my class members. Visual C# has a great shortcut for doing comments, on the line before a class or class member, type /// and it will prefill a template for a XML style comment.
for example, the code
on the line before it, I typed in /// and got
now I can just fill in my description and the decription for the point parameter. That makes life a little easier. Visual C# also uses XML comments in tooltips when you access that class member in some other area of your code.
You may have also noticed that I moved GameObject out of the class of the current game and put it in my own namespace johnnyGizmo. So when I want to use it in a new game, I just need to add a class from the solution explorer and in my file say
Later when I am happy with how the class looks, i.e. it is finished, I can compile it to it's own library for simple use in other programs. You can see the refectored GameObject files Here
- Code Documentation is Pitiful
- Lots of Public variables
- Poor class planning
Next I want to move my variables from the access level of public to protected. This means that only objects that are derived from this class can see these variable, everyone who just uses an instance of the class has to get at them through either a method or a property (or not at all).
So for all the variables that need to be viewed outside the class (location, rotation, etc) I am creating a public Property.
Then to top things off, I am adding XML style comments to all my class members. Visual C# has a great shortcut for doing comments, on the line before a class or class member, type /// and it will prefill a template for a XML style comment.
for example, the code
public void SetPosition(Vector2 point)
{
this.position = point;
}
on the line before it, I typed in /// and got
/// <summary>
///
/// </summary>
/// <param name="point"></param>
public void SetPosition(Vector2 point)
{
this.position = point;
}
now I can just fill in my description and the decription for the point parameter. That makes life a little easier. Visual C# also uses XML comments in tooltips when you access that class member in some other area of your code.
You may have also noticed that I moved GameObject out of the class of the current game and put it in my own namespace johnnyGizmo. So when I want to use it in a new game, I just need to add a class from the solution explorer and in my file say
using johnnyGizmo;
Later when I am happy with how the class looks, i.e. it is finished, I can compile it to it's own library for simple use in other programs. You can see the refectored GameObject files Here
Sunday, October 26, 2008
XNA Series - Modular AI - Flee and Arrival
To continue with our modular AI discussion, we will first look at a "Flee" operation. Fleeing is the opposite of seeking. Rather than turning towards the current position of an object and moving forward, we will turn away from it and move forward. So first we will create a way to turn away from an object. You may remember we had a TurnTowards method, we will copy that method and call it TurnAway and simply make our target rotation Pi Radians (180 degrees) different from the rotation of TurnTowards
and then we move forward. Since we are doing this as modular as possible, we can reuse the previous Move method.
So our Flee method is simply
We also want to implement an "Arrival" behavior. Now so far TurnTowards and TurnAway have effected rotation and Move has effected position. Arrival will effect speed. We will pass Arrival a speed and it will be scaled based on position.
Now target is your target position, speed is your top speed, minDistance is the closest you want to get and maxDistance is the distance where you start slowing down.
so we check our distance from the target and if we are outside of slowing range, we just move on ahead otherwise we check what percentage of the slowing range we are then apply smoothstep between our desired speed and 0 using the percent to pick our new speed. Now of course if you have an object with variable speed, you will need to tweak how you use this, but this is bare bones, what can I say. To use this method, I can do this to combine it with Seek
In the example code, you will see that I injected it into FollowPath to make the tank slow down at each stopping point.
Here are the project files
public void TurnAway(Vector2 target, float turnSpeed)
{
Vector2 difference = target - position;
float objectRotation = (float)Math.Atan2(difference.Y, difference.X)
+ (float)Math.PI ;
float deltaRotation = MathHelper.WrapAngle(objectRotation - rotation);
rotation += MathHelper.Clamp(deltaRotation, -turnSpeed, turnSpeed);
return;
}
and then we move forward. Since we are doing this as modular as possible, we can reuse the previous Move method.
So our Flee method is simply
public void Flee(Vector2 target, float speed, float turnSpeed)
{
TurnAway(target, turnSpeed);
Move(speed);
}
We also want to implement an "Arrival" behavior. Now so far TurnTowards and TurnAway have effected rotation and Move has effected position. Arrival will effect speed. We will pass Arrival a speed and it will be scaled based on position.
Now target is your target position, speed is your top speed, minDistance is the closest you want to get and maxDistance is the distance where you start slowing down.
public float Arrival(Vector2 target, float speed, float minDist, float maxDist)
{
float dist = Vector2.Distance(position, target);
if (dist > maxDist)
{
return speed;
}
else
{
float percent = (dist - minDist) / (maxDist - minDist);
return MathHelper.SmoothStep(0,speed, percent);
}
}
so we check our distance from the target and if we are outside of slowing range, we just move on ahead otherwise we check what percentage of the slowing range we are then apply smoothstep between our desired speed and 0 using the percent to pick our new speed. Now of course if you have an object with variable speed, you will need to tweak how you use this, but this is bare bones, what can I say. To use this method, I can do this to combine it with Seek
Seek(target, Arrival(target,speed,17,80), turnSpeed);
In the example code, you will see that I injected it into FollowPath to make the tank slow down at each stopping point.
Here are the project files
Saturday, October 25, 2008
XNA Series - Showing the Mouse
Here is a quick one for today. You may have noticed that when using your mouse with XNA that it disappears when entering the game area. This is because XNA wants you to determine what your mouse looks like. If you are programming a game with a mouse or cursor type interface (remember that XBOX 360 and Zune gamers do not have mice!) you probably want a custom mouse cursor. Well, it is really very simple. If we take our basic GameObject class (our custom one we have been using, this is not part of the XNA package) and create a instance lets call it mouseObject.
Then in Update we can update it's position
The only other thing that can be helpful here is if your mouse "hotspot" is not in the top left corner, you may want to add an offset value to your gameObject and then in your GameObject.Draw method, do position-offset for the image position. See the attached code sample to see what I did with that (it was an afterthought).
Now you should have your cursor drawn at your mouse's location. You could of course have various cursors in a spritesheet then simply change the source rectangle based on your need.
The source for this post
GameObject mouseObject;and load it up in LoadContent
mouseObject = new GameObject(Content.Load<Texture2D>("mouse"),
0,0,
new Rectangle(0,0,25,25));
Then in Update we can update it's position
MouseState ms = Mouse.GetState();Then simply call our custom drawing method in Draw
mouseObject.position.X = ms.X;
mouseObject.position.Y = ms.Y;
mouseObject.Draw(spriteBatch);
The only other thing that can be helpful here is if your mouse "hotspot" is not in the top left corner, you may want to add an offset value to your gameObject and then in your GameObject.Draw method, do position-offset for the image position. See the attached code sample to see what I did with that (it was an afterthought).
Now you should have your cursor drawn at your mouse's location. You could of course have various cursors in a spritesheet then simply change the source rectangle based on your need.
The source for this post
Friday, October 24, 2008
XNA Series - Modular AI - Waypoints
So in our last AI lesson we talked about 3 methods, TurnTowards, MoveForward and Seek. Where TurnTowards turned our rotation towards a point by an maximum amount, Move Forward moved us at a certain distance based on our rotation and Seek combined the 2 to let us move our GameAgent towards a goal.
In this post we will create a path made from Waypoints that we want our object to follow. We will start by adding a Waypoint class to our project. It will contain a position vector, and a bool indicating if we have visited it yet.
and then declaring a List collection of Waypoint objects in our game class
and then initialize it in our constructor
we can also add Waypoints to our path either here or in LoadContent like this
in Update we can do this to allow for adding new Waypoints during gametime.
Now we need to create a new method in our GameAgent class called FollowPath. It will take a List<Waypoint> object, a speed and a turn speed as parameters.
Now for each waypoint, we will check if we are within an certain distance and if so we will mark that Waypoint as visited
then if the current Waypoint is not visited, we will seek it and return
So now in our Update method we can assign this action to our Agent
and another action to another
So we have a following a path and b Seeking a. With the ability to add new points by clicking. Please be aware though that the mouse is not shown on the screen during gametime, so you have to guess where your mouse is (silly you may think, but I'll post on showing the mouse soon) So this example works out pretty well, You can tinker with the speed and turnSpeeds to see how they effect the movement.
So until next time...
The Project Files for this post
In this post we will create a path made from Waypoints that we want our object to follow. We will start by adding a Waypoint class to our project. It will contain a position vector, and a bool indicating if we have visited it yet.
public class Waypoint
{
public Vector2 position;
public bool visted = false;
public Waypoint(Vector2 v){
position = v;
}
}
and then declaring a List collection of Waypoint objects in our game class
List<Waypoint> path;
and then initialize it in our constructor
path = new List<Waypoint>();
we can also add Waypoints to our path either here or in LoadContent like this
path.Add(new Waypoint(new Vector2(10,10)));
path.Add(new Waypoint(new Vector2(400,10)));
path.Add(new Waypoint(new Vector2(20,100)));
path.Add(new Waypoint(new Vector2(120,300)));
in Update we can do this to allow for adding new Waypoints during gametime.
MouseState ms = Mouse.GetState();
if (ms.LeftButton == ButtonState.Pressed)
{
path.Add(new Waypoint(new Vector2(ms.X, ms.Y)));
}
Now we need to create a new method in our GameAgent class called FollowPath. It will take a List<Waypoint> object, a speed and a turn speed as parameters.
public void FollowPath(List<Waypoint> p, float speed, float turnSpeed)
{
Now for each waypoint, we will check if we are within an certain distance and if so we will mark that Waypoint as visited
foreach (Waypoint w in p)
{
if (Vector2.Distance(position, w.position) < 20){
visted = true;
then if the current Waypoint is not visited, we will seek it and return
if (w.visted == false)
{
Seek(w.position, speed, turnSpeed);
return;
}
}
return;
}
So now in our Update method we can assign this action to our Agent
a.FollowPath(path,2.5f,0.1f);
and another action to another
b.Seek(a.position,2f, 0.02f);
So we have a following a path and b Seeking a. With the ability to add new points by clicking. Please be aware though that the mouse is not shown on the screen during gametime, so you have to guess where your mouse is (silly you may think, but I'll post on showing the mouse soon) So this example works out pretty well, You can tinker with the speed and turnSpeeds to see how they effect the movement.
So until next time...
The Project Files for this post
Thursday, October 23, 2008
XNA Sidebar - Trigonometry
One area that you may find yourself lacking is in the area of math. Especially when it comes to trig. In this post I want to talk a little about trig and how we can use it in our game programming.
First of it is very good to understand the concept of radians. The trig functions that you will be using in XNA deal with radians, and while you can convert radians to degrees, it is easier to just understand them.
We are used to the idea that a circle is 360 degrees, in radians that is equal to 2π. So all the way around the circle is equal to 0 to apx 6.28 radians, or 0 to 2π radians. Once you go past 2π, you are going around the circle again and can start measuring again. There is a helper function in the MathHelper class called WrapAngle(float angle) that takes a radian measurement and constrains it to -π to π (that is 2π total). So if your Radian Measurment was 3π, it would return a value of π since those 2 angles are equivelent.

So as you can see from this image, we can look at either making a full circuit around the circle from 0 to 2π or going half a circle π in either direction.
This way we can spin like this in our code and stay within these bounds.
and although it looks like rotation would end up at 100, it actually ends up at -0.5309677 since the WrapAngle maps its value into the -π to π space.
The next question to deal with is this: I am pointing in a given direction and want to move forward 10. How do I determine how much I want to move in the X and Y directions to let me move 10 along my current path. The answer lies in Sin and Cos. If we take a right triangle we can discover the length of a side or an angle if we know 2 of the others. So in this case, we are pointing along the hyp and want to move 10 in that direction. So we want to find the length of the opposite side and the adjacent side. We do this in 2 steps. 1st to find the opposite side (which will be the Y value we add to our current location, we use the Sine function.
Sin(angle) = opposite / hypotenuse
We know the angle from our object's current rotation and we know we want the hyp to be 10 we get this
Sin(rotation) = Y / 10
and if we multiply both sides by 10 we get
10Sin(rotation) = Y
So our Y coordinate for the addition to our current position is 10 times the Sine of our rotation. To get the X coordinate, we do the exact same thing except use the Cosine function. Since
Cos(angle) = adjacent/hyp
In C# it looks like this...

Now how do you remember which is which? Well if you take the image above with the 3 arrows, left arrow, up arrow, down-left arrow and think sin,cos,tan if we superimpose these arrows over 3 triangles you will see that the arrow shows you what order to put the sides in. The left pointing arrow goes over the opposite then the hyp, that is sine. The up arrow goes over adjacent then hyp, that is cosine, the the left-down arrow goes over opposite to adjacent that is tangent.
Now if you already know the 2 sides and need to find the angle instead, you can use the "Arc" versions of sine, cosine and tangent. They look like this
angle = arcsine(opposite/hyp)
angle = arccosine(adjacent/hyp)
angle = arctangent(opposite/adjacent)
this last one ArcTangent is very helpful if we need to determine at what angle one object is to another. If you look at the example below, if we know the x and y distance from the yellow star to the red star, we can take the arctangent of y/x to get the angle in radians. The 'arc' functions are found in the Math library as Atan, Asin and Acos

So this is a little crash course in Trig. Did it answer your questions? Are there other things you would like me to cover? Let me know, talk back below!
First of it is very good to understand the concept of radians. The trig functions that you will be using in XNA deal with radians, and while you can convert radians to degrees, it is easier to just understand them.
We are used to the idea that a circle is 360 degrees, in radians that is equal to 2π. So all the way around the circle is equal to 0 to apx 6.28 radians, or 0 to 2π radians. Once you go past 2π, you are going around the circle again and can start measuring again. There is a helper function in the MathHelper class called WrapAngle(float angle) that takes a radian measurement and constrains it to -π to π (that is 2π total). So if your Radian Measurment was 3π, it would return a value of π since those 2 angles are equivelent.

So as you can see from this image, we can look at either making a full circuit around the circle from 0 to 2π or going half a circle π in either direction.
This way we can spin like this in our code and stay within these bounds.
for(int i = 0; i< 1000;i++){
rotation = MathHelper.WrapAngle(rotation + 0.1f);
}and although it looks like rotation would end up at 100, it actually ends up at -0.5309677 since the WrapAngle maps its value into the -π to π space.
The next question to deal with is this: I am pointing in a given direction and want to move forward 10. How do I determine how much I want to move in the X and Y directions to let me move 10 along my current path. The answer lies in Sin and Cos. If we take a right triangle we can discover the length of a side or an angle if we know 2 of the others. So in this case, we are pointing along the hyp and want to move 10 in that direction. So we want to find the length of the opposite side and the adjacent side. We do this in 2 steps. 1st to find the opposite side (which will be the Y value we add to our current location, we use the Sine function.
Sin(angle) = opposite / hypotenuse
We know the angle from our object's current rotation and we know we want the hyp to be 10 we get this
Sin(rotation) = Y / 10
and if we multiply both sides by 10 we get
10Sin(rotation) = Y
So our Y coordinate for the addition to our current position is 10 times the Sine of our rotation. To get the X coordinate, we do the exact same thing except use the Cosine function. Since
Cos(angle) = adjacent/hyp
In C# it looks like this...
float moveDist = 10;
Vector2D movement = new Vector2D();
movement.Y = moveDist * Math.Sin(rotation);
movement.X = moveDist * Math.Cos(rotation);

Now how do you remember which is which? Well if you take the image above with the 3 arrows, left arrow, up arrow, down-left arrow and think sin,cos,tan if we superimpose these arrows over 3 triangles you will see that the arrow shows you what order to put the sides in. The left pointing arrow goes over the opposite then the hyp, that is sine. The up arrow goes over adjacent then hyp, that is cosine, the the left-down arrow goes over opposite to adjacent that is tangent.
Now if you already know the 2 sides and need to find the angle instead, you can use the "Arc" versions of sine, cosine and tangent. They look like this
angle = arcsine(opposite/hyp)
angle = arccosine(adjacent/hyp)
angle = arctangent(opposite/adjacent)
this last one ArcTangent is very helpful if we need to determine at what angle one object is to another. If you look at the example below, if we know the x and y distance from the yellow star to the red star, we can take the arctangent of y/x to get the angle in radians. The 'arc' functions are found in the Math library as Atan, Asin and Acos

So this is a little crash course in Trig. Did it answer your questions? Are there other things you would like me to cover? Let me know, talk back below!
XNA Series - Modular AI
In our last installment, we created a very specific AI interaction. We moved toward another object and then as we approached, we slowed down. In today's post we will look at breaking that out into more granular, reusable steps.
First Thing we need to do is to turn towards our target. Which may be an object, an agent or just some arbitrary location, like a waypoint on a track. Then no matter why we need to turn, we have a way to do a targeted turn. For the sake of flexibility, we will add a turnSpeed to the method so that depending on the call we can determine how fast we will turn.
and then we want to be able to move forward. So we create a move forward method.
where we determine what to add to our current location by getting a unit circle offset from us based on our rotation and then multiply it by our speed.
Now that we have these 2 behaviors, we can construct a Seek method, where we turn towards our target at our turnspeed and then move towards our target using our speed.
So with this in our Update method if we had to GameObjects that implemented this Seek method we could call
To set our a object seeking b. But like I said, we don't have to stop there, there are other things we can do with these methods. Check back next time and I'll talk about how we can make our objects follow a path that we add to by mouse clicking. (Code for this and the next project will be in the next post).
First Thing we need to do is to turn towards our target. Which may be an object, an agent or just some arbitrary location, like a waypoint on a track. Then no matter why we need to turn, we have a way to do a targeted turn. For the sake of flexibility, we will add a turnSpeed to the method so that depending on the call we can determine how fast we will turn.
public void TurnTowards(Vector2D target, float turnSpeed){
Vector2 difference = target-position;
float objectRotation = (float)Math.Atan2(difference.Y, difference.X);
float deltaRotation = MathHelper.WrapAngle(objectRotation - rotation);
rotation += MathHelper.Clamp(deltaRotation, -turnSpeed, turnSpeed);
}and then we want to be able to move forward. So we create a move forward method.
public void MoveForward(float speed)
{
position.X += (float)Math.Cos(rotation)*speed;
position.Y += (float)Math.Sin(rotation)*speed;
}
where we determine what to add to our current location by getting a unit circle offset from us based on our rotation and then multiply it by our speed.
Now that we have these 2 behaviors, we can construct a Seek method, where we turn towards our target at our turnspeed and then move towards our target using our speed.
public void Seek(Vector2 target, float speed, float turnSpeed)
{
TurnTowards(target, turnSpeed);
Move(speed);
}
So with this in our Update method if we had to GameObjects that implemented this Seek method we could call
a.Seek(b.position,2f, 0.02f);
To set our a object seeking b. But like I said, we don't have to stop there, there are other things we can do with these methods. Check back next time and I'll talk about how we can make our objects follow a path that we add to by mouse clicking. (Code for this and the next project will be in the next post).
Wednesday, October 22, 2008
XNA Series - Basic AI
For today's post I want to take our first step towards 'AI'. Now please understand this and the following posts are not truly AI, but at least get us moving in a direction towards it. The Wikipedia entry for AI states the following:
So our idea is that some element or 'agent' in our game that can take data from it's surroundings and choose an action based on that data to take it toward it's goal. One of the MAJOR components of an AI is that it has a 'goal' something it is trying to accomplish. For instance an AI controlling a car in a racing game's goal is to get across the finish line first while staying on the course. While an AI for a target in a shooting game might want to avoid being hit. Or a "bad guy" AI who want to seek out a target. If you are serious about game AI, I would suggest reading this paper http://www.red3d.com/cwr/steer/gdc99/ on Steering Behaviors.
To start in this post, we will choose a very basic task. Agent A will start at a random location with a random rotation. Object B will start at a random location and do nothing. Agent A will rotate until it is facing Object B and will move forward until it reaches object B. So our agent has 3 tasks: turn, move and stop. Object
B just takes up space.
So what do we need for our example? An Agent A and an Object B. They will both be GameObjects. We can reuse our most recent GameObject Class from XNA Part 10 to start with. We will add a couple things to the game class to make this work.
Rotation will hold the current rotation of our object and origin will hold a "pivit point" for our object to rotate on. Then, update your draw call to use these parameters
Another addition is a property called Center
So here is a tank and a missle for it to seek to. They are on one sprite sheet. We will add it to our project and create a game object for our missle, but hold off on the tank for a moment
Now in our GameObject file, after our GameObject declaration we are going to create a new
class derived from GameObject called GameAgent
So our new class GameAgent will have all the GameObject Stuff, but we can add to it and give it extra variables and methods. First we need to give it a constructor
Basically we pass everything on to our base class and let the GameObject set everything up. We will give GameAgent a couple fields
Now for each update, we will make a call to a method of our GameAgent for it to seek. So in our GameAgent class we will create a Method called Seek that takes a GameObject as a parameter.
I've added comments to the function code so you can see what is happening. Now this may of course not be the most optimal way for us to do steering, but it does work. I have been reading the work of Craig Reynolds and it does seem that he has done extensive work and research in this area. He uses a method in which the key points are more accurate to how objects might behave. I am currently trying to implement his concepts in my code and will get back to you when I accomplish something. But moving on.
Then in our LoadContent method we can initialize our agent.
Then in our Update method
I also added this in our Update Class so I can move the target.
When all is compiled, you end up with a missle that you can move and a tank that turns and drive to it and slows down and stops when it arrives. If you were a little lost by the trigonometry, don't fear, I'll have a XNA sidebar on that soon.
Code for this Post
References:
http://creators.xna.com/en-us/sample/aiming
http://www.red3d.com/cwr/steer/
...The study and design of intelligent agents,"where an intelligent agent is a system that perceives its environment and takes actions which maximize its chances of success. John McCarthy, who coined the term in 1956, defines it as
"the science and engineering of making intelligent machines."
So our idea is that some element or 'agent' in our game that can take data from it's surroundings and choose an action based on that data to take it toward it's goal. One of the MAJOR components of an AI is that it has a 'goal' something it is trying to accomplish. For instance an AI controlling a car in a racing game's goal is to get across the finish line first while staying on the course. While an AI for a target in a shooting game might want to avoid being hit. Or a "bad guy" AI who want to seek out a target. If you are serious about game AI, I would suggest reading this paper http://www.red3d.com/cwr/steer/gdc99/ on Steering Behaviors.
To start in this post, we will choose a very basic task. Agent A will start at a random location with a random rotation. Object B will start at a random location and do nothing. Agent A will rotate until it is facing Object B and will move forward until it reaches object B. So our agent has 3 tasks: turn, move and stop. Object
B just takes up space.
So what do we need for our example? An Agent A and an Object B. They will both be GameObjects. We can reuse our most recent GameObject Class from XNA Part 10 to start with. We will add a couple things to the game class to make this work.
float rotation = 0;
public Vector2 origin = Vector2.Zero;
Rotation will hold the current rotation of our object and origin will hold a "pivit point" for our object to rotate on. Then, update your draw call to use these parameters
public void Draw(SpriteBatch sb)
{
sb.Draw(sprite, position, texSource, Color.White, rotation,
origin,1,SpriteEffects.None,0);
}
Another addition is a property called Center
public Vector2 CenterWhich returns a screen based coordinate of the rotational center of our object.
{
get
{
return position + origin;
}
}
So here is a tank and a missle for it to seek to. They are on one sprite sheet. We will add it to our project and create a game object for our missle, but hold off on the tank for a moment

//In our game class
GameObject b;
//In our Load Content Method
sprites = Content.Load<Texture2D>("tank-missle");
b = new GameObject(sprites,100,100,new Rectangle(50,0,50,50));
b.origin = new Vector2(15, 15);
Now in our GameObject file, after our GameObject declaration we are going to create a new
class derived from GameObject called GameAgent
class GameAgent : GameObject
{
}
So our new class GameAgent will have all the GameObject Stuff, but we can add to it and give it extra variables and methods. First we need to give it a constructor
public GameAgent(Texture2D inSprite, float x, float y, Rectangle src) :
base(inSprite,x,y,src)
{
}
Basically we pass everything on to our base class and let the GameObject set everything up. We will give GameAgent a couple fields
const float MAX_SPEED = 2;
public float speed = 0;
Now for each update, we will make a call to a method of our GameAgent for it to seek. So in our GameAgent class we will create a Method called Seek that takes a GameObject as a parameter.
public float Seek(GameObject target)
{
//First Move ahead along current angle
// The distance between the 2 elements
float distance = Vector2.Distance(position, target.Center);
//if we are far away, speed up to MAX_SPEED
if (distance > 100)
{
speed = MathHelper.Clamp(speed + 0.1f, 0, MAX_SPEED);
}
// If we are getting close, use the SmoothStep method to slow us down
else if (distance <= 100 && distance > 40)
{
speed = MathHelper.SmoothStep(0,MAX_SPEED, (distance - 30) / 70);
}
// If we are closer than 40, stop!
else
{
speed = 0;
}
// Use our current rotation + some trig to set our new location
// I'll explain later
position.X += (float)Math.Cos(rotation) * speed;
position.Y += (float)Math.Sin(rotation) * speed;
// Rotate Towards the Object
// Find Distance between this object and the target's center
float o = target.Center.Y - position.Y;
float a = target.Center.X - position.X;
// Find the angle between our unrotated object and the target
float theta = (float)Math.Atan((double)o / (double)a);
// If we are on the right of the object, we need to think a little backwards
if (position.X > target.Center.X)
{
theta = MathHelper.WrapAngle(theta + MathHelper.Pi);
}
// Add to our Rotation to point to the object
// theta-rotation gives us the difference between our current rotation and the
// offset of the target object to our 0 rotation point. This is ideally
// how much we want to rotate, but our agent can only turn so fast,
// So we need to clamp that change to our maximum turning amount.
rotation += MathHelper.Clamp(MathHelper.WrapAngle(theta - rotation), -0.05f, 0.05f);
// I return theta here so I can output it to the screen later, you don't really need to.
return theta;
}
I've added comments to the function code so you can see what is happening. Now this may of course not be the most optimal way for us to do steering, but it does work. I have been reading the work of Craig Reynolds and it does seem that he has done extensive work and research in this area. He uses a method in which the key points are more accurate to how objects might behave. I am currently trying to implement his concepts in my code and will get back to you when I accomplish something. But moving on.
Then in our LoadContent method we can initialize our agent.
a = new GameAgent(sprites, 300,300, new Rectangle(0, 0, 50, 50));
a.origin = new Vector2(25, 25);
Then in our Update method
a.Seek(b);and Finally in our Draw method
spriteBatch.Begin();
a.Draw(spriteBatch);
b.Draw(spriteBatch);
spriteBatch.End();
I also added this in our Update Class so I can move the target.
KeyboardState ks = Keyboard.GetState();
if (ks.IsKeyDown(Keys.NumPad4))
{
b.position.X -= 3f;
}
if (ks.IsKeyDown(Keys.NumPad6))
{
b.position.X += 3f;
}
if (ks.IsKeyDown(Keys.NumPad8))
{
b.position.Y -= 3f;
}
if (ks.IsKeyDown(Keys.NumPad2))
{
b.position.Y += 3f;
}
When all is compiled, you end up with a missle that you can move and a tank that turns and drive to it and slows down and stops when it arrives. If you were a little lost by the trigonometry, don't fear, I'll have a XNA sidebar on that soon.
Code for this Post
References:
http://creators.xna.com/en-us/sample/aiming
http://www.red3d.com/cwr/steer/
Tuesday, October 21, 2008
XNA Series - Animation Part 2
In our last installment we created a sprite based animation and today I want to extend that a little. We are going to add a velocity to our character and scale his animation based on it. This is actually very simple. First we create 2 floats
Then in our Update method, when we detect our key press, rather than updating the position, we will update the velocity + 0.1 for right and -0.1 for left. Then after the keyboard stuff we will update the position by adding the current velocity to the location.
Then we also check to see if our velocity is plus or minus to choose our direction. Now that we have a velocity, we can use that to scale our framerate. So rather than just adding 0.4 to our framecount whenever our key was down we will always add 0.4 scaled by our velocity each update
as you can see we scale it by velocity/maxVelocity so when velocity is 0, nothing is added. We also take the Absolute value of the scale so that we are always adding to our value (otherwise our animation would run backwards, which may be desired at times). So now as we press our buttons we will see our character speed up and the animation speed up in relation to it.
Here is the source code: SpriteAnimation2.zip
public float velocity = 0;
public float maxVelocity = 1;
Then in our Update method, when we detect our key press, rather than updating the position, we will update the velocity + 0.1 for right and -0.1 for left. Then after the keyboard stuff we will update the position by adding the current velocity to the location.
KeyboardState ks = Keyboard.GetState();
if (ks.IsKeyDown(Keys.Left))
{
a.velocity -= 0.1f;
a.frameNumber += 0.4f;
}
if (ks.IsKeyDown(Keys.Right))
{
a.velocity += 0.1f;
a.frameNumber += 0.4f;
}
a.position.X += a.velocity;
if (a.velocity > 0) a.direction = 1;
else if (a.velocity < 0) a.direction = 0;
Then we also check to see if our velocity is plus or minus to choose our direction. Now that we have a velocity, we can use that to scale our framerate. So rather than just adding 0.4 to our framecount whenever our key was down we will always add 0.4 scaled by our velocity each update
a.frameNumber += 0.4f * (float)Math.Abs(a.velocity / a.maxVelocity);
as you can see we scale it by velocity/maxVelocity so when velocity is 0, nothing is added. We also take the Absolute value of the scale so that we are always adding to our value (otherwise our animation would run backwards, which may be desired at times). So now as we press our buttons we will see our character speed up and the animation speed up in relation to it.
Here is the source code: SpriteAnimation2.zip
Monday, October 20, 2008
XNA Series - Animation Part 1
Now, we may not always be using static images as our characters in our games, and sometimes we may want to have an animation, like when our character moves, his legs actually go back and forth. In 3d graphics, we would build these actions into our models, but in 2d we can pre-render these animations in frames and create a sprite sheet with all the different images of our animation.
Here is an example of such a sheet. It has 20 images of the same size, laid out in a strip.
I did this simple animation in Blender3D and then stuck it together using a little program called SpriteStripComposer that I someone put together for this very purpose. I wish Blender had a built in method for doing this, perhaps I should hit up some of my old dev pals for such a feature (or they would say "Code it Yourself!") Anyway,I am going to extend my GameObject class (see example code) with a derived class called AnimGameObject and I will add a few member variables to it
First we build a new source rectangle that takes the float frame number and does a % frames on it so that we have an int between 0 and 20, we then pull the height and width from our original source rectangle. Next we create an object of type SpriteEffects and set it to None. then if direction == 1 we set it to FlipHorizontally. This will render our sprite facing the other way. Then finally we call the sb.Draw function and pass it the new source rectangle rather than the original and later in the call we pass it the SpriteEffects parameter.
Now in our game class we do things alot like we have before we declare an object of type AnimGameObject and load it up in our LoadContent method
and draw it the same in the Draw method
the main difference is in Update where we check our keyboard state and update our position, we also add to the frameNumber of the object. Now keep in mind that by default XNA runs at 60 frames per second, so you would not want to set this to update 1 frame per update, or this 20 frame animation would go all the way through 3 times per second. Instead we will set it to advance the frame 0.4f per update, that will run 24 frames per second or a little under 1 time per second (if we are holding down one of the keys) since we chop off the fractional part, when we use that number as an index, it will only make the frame change after it adds up past a whole number.
Next time we will talk about frame rates and we will extend this example to make it more interesting.
Here is an example of such a sheet. It has 20 images of the same size, laid out in a strip.
I did this simple animation in Blender3D and then stuck it together using a little program called SpriteStripComposer that I someone put together for this very purpose. I wish Blender had a built in method for doing this, perhaps I should hit up some of my old dev pals for such a feature (or they would say "Code it Yourself!") Anyway,I am going to extend my GameObject class (see example code) with a derived class called AnimGameObject and I will add a few member variables to itprivate float frames = 0;The frames will hold the total number of frames in the spritesheet, the frameNumber will hold the current frame to be displayed and the direction will tell the Draw method to flip the image or not. We also add a new constructor
public float frameNumber = 0;
public byte direction = 0;
public AnimGameObject(Texture2D inSprite, float x, float y, Rectangle src,float frm)Which sends most of the construction to the base constructor, but also sets the frames to the number of frames passed in. Also note the when we pass in the src rectangle that we will point to the first frame of the animation and then use that data to find the rest. We also add an overridden version of the Draw method
: base(inSprite, x, y, src)
{
frames = frm;
}
public override void Draw(SpriteBatch sb)
{
Rectangle source = new Rectangle(((int)frameNumber)%(int)frames * texSource.Width,
0,
texSource.Width,
texSource.Height);
SpriteEffects se = SpriteEffects.None;
if (direction == 1)
{
se = SpriteEffects.FlipHorizontally;
}
sb.Draw(sprite, position, source, Color.White,0f,new Vector2(0,0),1f,se, 0);
}
First we build a new source rectangle that takes the float frame number and does a % frames on it so that we have an int between 0 and 20, we then pull the height and width from our original source rectangle. Next we create an object of type SpriteEffects and set it to None. then if direction == 1 we set it to FlipHorizontally. This will render our sprite facing the other way. Then finally we call the sb.Draw function and pass it the new source rectangle rather than the original and later in the call we pass it the SpriteEffects parameter.
Now in our game class we do things alot like we have before we declare an object of type AnimGameObject and load it up in our LoadContent method
a = new AnimGameObject(Content.Load<texture2d>("test"),0,0,new Rectangle(0,0,128,128),20);and draw it the same in the Draw method
spriteBatch.Begin();
a.Draw(spriteBatch);
spriteBatch.End();
the main difference is in Update where we check our keyboard state and update our position, we also add to the frameNumber of the object. Now keep in mind that by default XNA runs at 60 frames per second, so you would not want to set this to update 1 frame per update, or this 20 frame animation would go all the way through 3 times per second. Instead we will set it to advance the frame 0.4f per update, that will run 24 frames per second or a little under 1 time per second (if we are holding down one of the keys) since we chop off the fractional part, when we use that number as an index, it will only make the frame change after it adds up past a whole number.
if(ks.IsKeyDown(Keys.Left)){
a.position -= 1f;
a.frameNumber += 0.4f;
}Here is a link to the source code. SpriteAnimation.zipNext time we will talk about frame rates and we will extend this example to make it more interesting.
Sunday, October 19, 2008
XNA Sidebar - SmoothStep and Lerp
Here is another bit of information for you when you are coding. There is a small class called MathHelper that you should become familiar with. It is in the Microsoft.Xna.Framework namespace. It contains 11 methods and 7 fields. The fields are 3 values of E (E, Log2E, Log10E) and 4 versions of Pi (Pi, Pi/2, Pi/4 and 2Pi) , but the things we want to talk in this post I want to
mention 2 methods in the class.
Lerp and SmoothStep are 2 methods in the MathHelper class that can assist us when trying to change from one value to another. If an object is going at one speed and needs to slow down to another there should be be a smooth transition between the 2 speeds. Or perhaps something needs to change from one value to another at a constant rate. These are the functions for you.
Lerp is short for Linear Interpolation, it makes a straight line between 2 values that you provide and gives you the value on that line at the percentage you pass it. It is the red line in the graphic.
The second method is SmoothStep, you invoke it the same way as Lerp, by passing a low, high and percent (0.0-1.0) value. The difference is in the value it returns. As it's name implies, the value steps down smoothly from the first value to the second using a cubic function. It's example is the blue line in the graphic.
So the next time you need to go between 2 values and you can determine how far into the transition you are, you can use one of these 2 methods to make a better transition.
mention 2 methods in the class.
Lerp and SmoothStep are 2 methods in the MathHelper class that can assist us when trying to change from one value to another. If an object is going at one speed and needs to slow down to another there should be be a smooth transition between the 2 speeds. Or perhaps something needs to change from one value to another at a constant rate. These are the functions for you.Lerp is short for Linear Interpolation, it makes a straight line between 2 values that you provide and gives you the value on that line at the percentage you pass it. It is the red line in the graphic.
Lerp(LowValue,HighValue,Percentage);
The second method is SmoothStep, you invoke it the same way as Lerp, by passing a low, high and percent (0.0-1.0) value. The difference is in the value it returns. As it's name implies, the value steps down smoothly from the first value to the second using a cubic function. It's example is the blue line in the graphic.
SmoothStep(LowValue,HighValue,Percentage);
So the next time you need to go between 2 values and you can determine how far into the transition you are, you can use one of these 2 methods to make a better transition.
EDIT
If you are interested in the code that I generated this example from, it is not in XNA (although it does reference the Microsoft.XNA.Framework assembly to get the Lerp and SmoothStep methods. Here is the file
LerpandSmoothStep.zip
Subscribe to:
Posts (Atom)