Showing posts with label Games. Show all posts
Showing posts with label Games. Show all posts

Monday, April 08, 2013

New fun Games for my Birthday!

In addition to everything else, I love to play a good a good board/strategy game. So here are 2 games that I got from my awesome wife and kids for my birthday. Star Fluxx and the Expansion "Inns and Cathedrals" for Carcassonne.

    


Star Fluxx is a stand alone card game, based on the Fluxx (Google Affiliate Ad) card game. But instead of common items, it uses Sci-Fi tropes as the theme. We played it and it's really fun and has some good dynamics that set it apart from the original. It's gonna get played a bunch I'm sure.

Inns and Cathedrals is an expansion pack for Rio Grande's Carcassonne (Google Affiliate Ad) which adds a coulpe new dynamics to the original game (which you need in order to use this pack). It made the game a bit more cutthroat. It was fun. It also makes it so that you can play 6 players instead of 5, which is always nice for game night. Carcassonne is a great game because it takes a little strategy, but even my 8 year old can play it and enjoy himself.

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


Model tower;
Vector3 pos = Vector3.Zero;
The next 4 objects in our game class declarations are a little more complex.

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
Then for each mesh, we draw the mesh

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.

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.


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.

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.


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.

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.

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:
  • Code Documentation is Pitiful
  • Lots of Public variables
  • Poor class planning
So a little restructuring is in order. First, I am going to copy all the extra methods out of GameAgent and bring them into GameObject. I can live with smart objects. Of course I'll need to change all the references to GameAgent to GameObject, but that is not too bad.

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

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.

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();
mouseObject.position.X = ms.X;
mouseObject.position.Y = ms.Y;
Then simply call our custom drawing method in Draw
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.

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.


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.
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:

...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 Center
{
get
{
return position + origin;
}
}
Which returns a screen based coordinate of the rotational center of our object.

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/

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
private float frames = 0;
public float frameNumber = 0;
public byte direction = 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 AnimGameObject(Texture2D inSprite, float x, float y, Rectangle src,float frm)
: base(inSprite, x, y, src)
{
frames = 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
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.zip

Next time we will talk about frame rates and we will extend this example to make it more interesting.

Saturday, October 18, 2008

XNA Part 10 - The Sprite Sheet


We have taken a little diversions with audio and collisions, but today I want to talk about a handy way to optimize your game. Up until now whenever we have wanted a sprite in our game we would just create an image and load it into our Content Pipeline and draw the whole thing. Now that is fine when you only have a couple...but say you have dozens of sprites. What is the best thing to do? Well here is one answer. A sprite sheet. this is basically a large image file that has your sprites separated into an organized grid. Here is an example of 4 sprites in a file, each in their own 100x100 spot in the file.

Now when we load in this sprite sheet we are not going to load it 4 times, that would be wasteful, instead we will load it once and pass it's reference to each game object that needs it. So I'm going to grab the GameObject class from my last project which keeps a Texture2D, a Position and the info needed for pixel based collisions. I can either extend that class or create a child class from it with new features. For now I will just extend the code for it. I am going to add a Rectangle object for the texture source. This will tell my object what part of the sprite image it needs to draw. I'll call it texSource. I will also add a member to the constructor to pass in a rectangle object to define the rectangle.

I will add a texture2D object to my main game class to hold the sprite sheet and in the LoadContent method, pull the sprite sheet into it, then pass that object to the new GameObjects along with the particular position of that GameObject's sprite in Rectangle format.

Now rather than creating 4 separate GameObjects, I have created an array of GameObjects


GameObject[] actors;

and after I load the sprite sheet in, I initialize the game objects.

actors = new GameObject[4];
actors[0] = new GameObject(spriteSheet, 0, 0, new Rectangle(0, 0, 100, 100));
actors[1] = new GameObject(spriteSheet, 0, 0, new Rectangle(0, 99 , 100, 100));
actors[2] = new GameObject(spriteSheet, 0, 0, new Rectangle(99, 0, 100, 100));
actors[3] = new GameObject(spriteSheet, 0, 0, new Rectangle(99, 99, 100, 100));

Now to siplify things when it comes time to draw these, I am going to make a GameObject draw itself (sort of). We will add a method to GameObject called Draw and it will take a SpriteBatch as a parameter.

public void Draw(SpriteBatch sb)
{
sb.Draw(sprite, position, texSource, Color.White);
}

So then in the Draw method of the Game class we can call

spriteBatch.Begin();
for (int i = 0; i < 4; i++)
{
actors[i].Draw(spriteBatch);
}
spriteBatch.End();

and handle the details of the Drawing call inside the object. Now of course, This will draw the 4 actors all stacked on top of each other, so for giggles, I'll add some Randomness to their initialization.

actors = new GameObject[4];

int x, y;
Random r = new Random();

x = r.Next(0, graphics.GraphicsDevice.Viewport.Width-100);
y = r.Next(0, graphics.GraphicsDevice.Viewport.Height - 100);
actors[0] = new GameObject(spriteSheet, x, y, new Rectangle(0, 0, 100, 100));

x = r.Next(0, graphics.GraphicsDevice.Viewport.Width - 100);
y = r.Next(0, graphics.GraphicsDevice.Viewport.Height - 100);
actors[1] = new GameObject(spriteSheet, x,y, new Rectangle(0, 99, 100, 100));

x = r.Next(0, graphics.GraphicsDevice.Viewport.Width - 100);
y = r.Next(0, graphics.GraphicsDevice.Viewport.Height - 100);
actors[2] = new GameObject(spriteSheet, x, y, new Rectangle(99, 0, 100, 100));

x = r.Next(0, graphics.GraphicsDevice.Viewport.Width - 100);
y = r.Next(0, graphics.GraphicsDevice.Viewport.Height - 100);

Now they should be in 4 different random locations.

From what I have read, the less Content loading you do the better, it is an "expensive" operation. So if you load and store 1 large image and reference parts of it, you are better off then loading many smaller images.

Project files for this post

Friday, October 17, 2008

XNA Part 9 - Pixel Based Collisions

Let me start by saying that the XNA Creators Club tutorials on collisions are really good and a lot of what I have learned so far has come from them. Check them out!

So on to pixel based collisions. You can of course collide on any data based in the pixels, but in our case we will look at the "Alpha" value. For those of you who are not familiar with the channels in a color, we represent color values with different components. There are several different ways to represent color, but in our case we will be using ARGB. The 'RGB' part should be familiar, this is the Red Green and Blue components of our color. The 'A' part stands for the Alpha, which is simply how opaque your color is. Where 0 is completely transparent and 255 is completely opaque. Notice that the values are between 0 and 255. This is because the 4 values are represented by the 'byte' data type which is an 8 bit integer type. 8 bits can hold 256 values and therefore in this case represent the numbers 0-255.

So the idea behind our pixel based collision is this: we look at the bounds of the 2 objects that we want to test and if they overlap, we examine the overlapping pixels in each image. If a pixel in both images is not transparent and overlapping, a collision has happened.

Now of course it will be up to you if you test for both pixels being 0 alpha for non-collisions or if you allow partial transparency to equal non-collision (i.e. both pixels have to be 255 alpha to collide rather than both being 0 to not collide).

Lets look at how we do this in our code.

We will store our pixel data in an array of Color objects for ease of use. So I'm going to add a Color[] to my GameObject.
public Color[] pixelData;

and in my constructor I am going to initialize it and load it up with my sprite's data.
pixelData = new Color[sprite.Width * sprite.Height]; 
sprite.GetData<color>(pixelData);


So we create the size of our pixelData array to be the sprite's height times width and then call the sprite's GetData method using the color type template and passing it the pixelData array to receive the data. Now we should have a lovely array filled will the pixel values of our sprite.

Now that we have pixel data to compare, we will rewrite our Intersects function to take it into account. We will take out our reference to the rectangle intersects() method. and start with our empty method.
public bool Intersects(GameObject b) { }


First we need to determine what the bounds of interection are. So we compare the tops and sides of the 2 rectangles. Remember that Y goes the opposite direction then we might think, so the top of a rectangle is a smaller number than the bottom. So we will compare the Top of a and b and see which one has a bigger value, meaning which one is LOWER on the screen. So in this case b's Top is a bigger number than a's Top since b's Top is lower on the screen than a's (confusing isn't it). So to find the lowest object Top on the screen we compare and choose the maximum Top between the 2 objects. You should also see that in this image b.Top is the top of our collision area.
int Top = Math.Max(Bounds.Top, b.Bounds.Top);


We also want the highest bottom on the screen so we look for the minimum value of a and b's Bottom.
int Bottom =  
Math.Min(Bounds.Bottom,
b.Bounds.Bottom);

Left and Right are easier since it moves in a more intuitive way. So we want the biggest Left and the smallest Right.
int Left = Math.Max(Bounds.Left,b.Bounds.Left);
int Right = Math.Min(Bounds.Right, b.Bounds.Right);

Now we can loop though using these values and extract the pixelData from the 2 GameObjects and compare them.
for (int y = Top; y < Bottom; y++)
{
for (int x = Left; x < Right; x++)
{

You will see the the for loops will not execute if top > bottom. That way, if the lowest top on the screen is below the highest bottom (ie the object are completely above and below each other) the loops will not happen. Then if they are vertically able to collide but horizontally not able to collide we don't execute the body of the inner loop. But if both work we get to the meat.

To discover where in the array of pixels we need to calculate where a particular pixel is. The pixels were stored by each row. So in this image we have our GameObjects textures as 8x8 grids. pixelData[0] through pixelData[7] would contain the first row. [8] though [15] would contain the next. So conviently we can do a little math to get us to our pixel. We take the row we want to access (starting at 0) and multiply by the width of the row (in this case 8) to give us the starting pixel in a row. Then we simply add the number of the pixel in the row we want to access (starting at 0) to that number and we have the index of the pixel we are looking for. Therefore our pixel index within a sprite becomes


pixelData [ rowNumber*rowWidth+colNumber ]


Now that we know how to access a given pixel how to we use the data we have to find the overlapping pixels and thier data. As we can see in this image, our x value would be starting at pixel 7 of the screen but only index 5 of object a and index 0 of object b. Since we can get the left value of object a (which is 2), we can subtract that from the value of x (7) and get the horizontal pixel index we need in object a (5). That would become our "colNumber" in our index formula. We determine our rowNumber in the same way with the y value. We get the distance of the y value from the "Top" of a by subtracting a's Top from the value of y. This gives us our rowNumber. We would then take the width of a as the rowWidth; so our formula for finding the pixel in a would be
Color colA = pixelData[(y - Bounds.Top) *
Bounds.Width + (x - Bounds.Left)];
Color colB = b.pixelData[(y - b.Bounds.Top) *
b.Bounds.Width + (x - b.Bounds.Left)];

Then we compare the colors and decide if they are a collision.
if (colA.A != 0 && colB.A != 0) 
{
return true;
}

Since it only takes one pixel for a collision, the first time we hit, we finish. After the loops you will want to add a return false if it makes it all the way through without a collision.

Now if you run this again with some objects that have transparency, you should find that they will only intersect when their pixels line up rather than their bounding boxes.

One caveat, this only applies to non-rotated, non-scaled textures. When we come back to collisions again, we will talk about how to handle those situations. But that will not be for a little while.

Thanks big time to the XNA creators club tutorial on Pixel based collisions, it was my primary learning source while preparing to write this post.

Thursday, October 16, 2008

XNA Part 8 - Simple Collisions

Most games that have any kind of movement need to detect object collision. There are many levels of object collision (as I am learning) that we can detect, but to start, we will look at simple rectangular collisions.

Here is the basic idea: First take 2 objects of type Rectangle which is defined this way

Rectangle a = new Rectangle(10, 10, 100, 100);

where we pass the X and Y coords of the top left corner of the rectangle and then the width and height of the rectangle. Then we create another one

Rectangle b = new Rectangle(20, 15, 100, 100);

Now we can call a member of the Rectangle class called Intersects() that will return a bool to tell us if if the 2 rectangles intersect.

So in this case since a and b do intersect,

a.Intersects(b)

would return true. We can use this type of information to help us out. In determining our game logic and behavior.

Here is an idea I came up with for putting this type of functionality into our GameObject Class. We can implement an Intersects method for our GameObject class that takes a GameObject as a parameter and then do our determining there. Here is the code for a simple GameObject

class GameObject
{
public Texture2D sprite;
public Vector2 position;

public GameObject(Texture2D inSprite,float x, float y)
{
sprite = inSprite;
position = new Vector2(x, y);
}

public Rectangle Bounds
{
get
{
return new Rectangle((int)position.X,
(int)position.Y,
sprite.Width,
sprite.Height);

}
}

public bool Intersects(GameObject b)
{
return Bounds.Intersects(b.Bounds);
}
}

We will hold only the texture and the position for now and we set them both in our constructor. Then we add a read only property called Bounds. This dynamically returns a Rectangle object that has been derived from the position vector and the size of the texture. Then in our Intersects() method we will ask our class for it's bounds and then see if it intersects with the bounds of our GameObject b. That way, we can later run code that looks like this.


GameObject a = new GameObject(Content.Load("test"), 10, 10);
GameObject b = new GameObject(Content.Load("test"), 300, 300);
if(a.Intersects(b)){
//Do something here
}

Now of course in this example, both GameObjects are hard coded to a location, so we would want to add code somewhere to get one of these puppies moving. So in our Update method I will add

a.position.X += 1.0f;
a.position.Y += 1.0f;

and flesh out my Draw method with

if(a.Intersects(b)){
graphics.GraphicsDevice.Clear(Color.Red);
} else {
graphics.GraphicsDevice.Clear(Color.Green);
}
spriteBatch.Begin();
spriteBatch.Draw(a.sprite, a.position, Color.White);
spriteBatch.Draw(b.sprite, b.position, Color.Blue);
spriteBatch.End();

So in the end, if a and b intersect, the background is cleared with red otherwise it is green.

This method works great when our Game Objects are rectangles, but if you recall, we are using PNG images which support transparency, so even the transparent part of your GameObjects would cause a collision since we don't care what is drawn in our GameObject we are just checking the bounds. Next we will be taking a closer look at our image to see if, pixel by pixel, we have collisions.

Wednesday, October 15, 2008

XNA Sidebar - Converting a single index into multiple

I was playing around with some coding ideas and thought about this. Say you have a 5x5 grid defined in a multidimensional array int[5,5] and then you create an array of object that will be placed in that grid. Say you want to refer to those grid entries as 0-24 rather than [0,0][0,1]...[4,3][4,4] a neat way to do it is with a little integer math. Division in integers is whole number division and we use the modulus operator to get remainder. So if we know the width of our grid (in this case 5), we can say

int x = 7;
int wid = 5;
grid[x/wid][x%wid];

would give us

grid[1][2]

which if we line things up

0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
20 21 22 23 24


we see that the index 7 is the second row, 3rd column, which in 0 based notation is 1,2. You may have already known this, but I thought it was pretty handy.

XNA Part 7 - Audio - Sound Effects

In our last installment, we talked a little about the Song object and how to load a music file and play it in our games. In this post we will talk about the SoundEffect Object. From my understanding the difference between these two types is in a. how they are stored and b. how they are played. Sound effects are considered shorter burts of sound that will be played from beginning to end rather than started, paused, stopped, etc. If there is a XNA genius here who wants to add to the finer details of this, please do so in the comments.

So to add a sound effect to our project, we will add it the same way in our content pipeline. We will add a folder under content called sounds and then add our sound file to that folder.

The difference comes in how we access that file. Rather than exposing that sound file with a Song object, we will load it into a SoundEffect object. So at the top of our class we will add a variable of type SoundEffect called fx.

SoundEffect fx;

Then in our LoadContent method, we will call
fx = Content.Load("sounds\\effect");

Now since this sound effect should be triggered by something happening in our game, we want to play it during our update method.

I am going to add it to the block of code that gets executed when we press the spacebar.

if (ks.IsKeyDown(Keys.Space)) { fx.Play(); }

As you can see we do not use the MediaPlayer class to play a SoundEffect file. They have a built in method to play themselves. Now the Play has 3 versions. the first takes no parameters and just plays the sound at the full volume. The second takes a float for volume control (between 0 and 1) and the 3rd takes a float for volume, a float between -1 and 1 for pitch to move the sound down or up an octave, a float for panning (between -1 left and 1 right, 0 center) and a bool to indicate if the sound should just keep looping.

If we just call fx.Play() it is sort of a set it and forget it sort of deal. It plays, ends and that is it. But if you need additional control over the sound effect after it is started, then you need to save the return value of fx.Play() which is an instance of type SoundEffectInstance. So you would do this.

SoundEffectInstance e = fx.Play(.1f, 0.0f, 0.0f, true);

now the variable e can be used to control this instance of the playing sound effect. It has methods such as Stop, Play, Resume and variables like volume, pan, pitch, islooped, and state (which tells us what the sound is currently doing). That way if you created a SoundEffect as looping, you could stop it with e.Stop();

So now as we move along, you can very easily add sound effects to your games and make them a lot more interesting.

One note, as you progress into 3d games, there are ways to place a sound within 3d space and the SoundEffect objects have methods for dealing with this as well.