I promised two blog posts today, and for once I've exceeded a target!
I just want to post an update on DBP 2011. I've decided, rather than try to rush and finish Sphero in time for the June deadline, I took up an offer of a collaboration from one of the devs I follow on twitter.
I'm pretty excited about it, as it'll be the first collaborative project I've worked on.
I won't give anymore details about the collaboration at this stage, as its really my new team mates gig, but hopefully I'll be able to post an update in the future.
I'll still be working on Sphero and Alta (in fact we'll probably use Alta's editors in some form for the game), and another project I'll talk about in a second, but the collaboration will be the focus. You can still expect Sphero to make an appearance at DBP 2012! :)
One final note, I've started work on a side project, which I've dubbed 'Xnity'. As the name suggests, its a bridge between XNA and Unity, and will consist of some (but not all) of the core xna classes.
The aim is to minimize the number of code changes xna devs need to port their games to Unity, essentially just using Unity as a compatibility layer to reach more platforms than xna offers alone.
It will be an open source project, and I'm hoping other xna/ unity devs will get involved as well.
This is very much a side project, but I'd still like to get through a few classes a week.
Well, that's plenty of blogging for now, I have work to do!
SyntaxHighlighter
Saturday, 5 March 2011
Quick 2d terrain with Farseer
This is going to be a quick tutorial on one approach to implementing 2d terrain in Farseer. I'm going to assume you already have a reasonable grasp of xna and at least a basic grasp of Farseer, and that you either have access to an extensible map editor, you can build one, or that you're happy to create the coords for your terrain in some other way (perhaps in ASCII in a text file, or hard coded values).
However you get your data, for this tutorial you'll need to get it in the following format:
• Your terrain will need to be divided up into 'ledges', or continuous solid lines.
• Your ledges will need to be divided up into 'edges', a straight line between 2 'nodes'. The end node of one edge must be the beginning node of the next.
• Each ledge must be stored as a List of Vector2, each containing the coordinates in screenspace of the nodes (in the order they appear from one end of the ledge to the other).
Obviously you can adapt this, (array instead of list, Point instead of Vector2 etc) but for now I'll assume we are using the above.
We can now plug into the following method:
public CreatePhysicsLedge(List<Vector2> vectors, float friction, float restitution) { body = BodyFactory.CreateBody(game.world); body.BodyType = BodyType.Static; body.IsStatic = true; fixtures = new List<Fixture>(); for (int i = 1; i < vectors.Count; i++) { Vector2 tempVec1 = ConvertUnits.ToSimUnits(vectors[i - 1]); Vector2 tempVec2 = ConvertUnits.ToSimUnits(vectors[i]); EdgeShape shape = new EdgeShape(tempVec1, tempVec2); if (i != vectors.Count - 1) { shape.HasVertex3 = true; shape.Vertex3 = ConvertUnits.ToSimUnits(vectors[i + 1]); } if (i != 1) { shape.HasVertex0 = true; shape.Vertex0 = ConvertUnits.ToSimUnits(vectors[i - 2]); } fixtures.Add(body.CreateFixture(shape)); } for (int i = 0; i < fixtures.Count; i++) { fixtures[i].Friction = friction; fixtures[i].Restitution = restitution; } }
This makes use of the ConvertUnits class from the Farseer samples.
And that's it! Simple as that.
Hopefully that will save some other devs some time here or there. Enjoy!
Promised update!
Two posts today. First of all, a Sphero update. I originally intended to spend about a day implementing wall-crawling. In reality its taken me over a week, mostly due to me not understanding how Farseer works.
Needless to say I got there in the end, and I have a better understanding of Farseer as a result, which I'll go into in a second. First though, a video of wall-crawling in action:
Not much to look at, I know, but a lot of blood sweat and tears (ok, coffee, pondering, and swearing at my monitor) went into getting it working, so I feel I should share some of the lessons I've learned.
My 2d terrain is made up of EdgeShapes, which are polygons with 2 vertices and a single edge. In other words, lines!
Needless to say I got there in the end, and I have a better understanding of Farseer as a result, which I'll go into in a second. First though, a video of wall-crawling in action:
Not much to look at, I know, but a lot of blood sweat and tears (ok, coffee, pondering, and swearing at my monitor) went into getting it working, so I feel I should share some of the lessons I've learned.
My 2d terrain is made up of EdgeShapes, which are polygons with 2 vertices and a single edge. In other words, lines!
In order to stick to the various surfaces, I decided to look at all of the Fixtures that the character is touching each frame, check to see if they're terrain, and if they are, add the collision normals between the character and each fixture together, and apply a force to the character in the opposite direction.
That probably wasn't very clear, so here's a handy diagram to demonstrate the idea:
The first problem I hit was, well, it didn't work. My character, instead of sticking to surfaces, was instead floating above them.Debugging showed that although most of the normals were reporting correctly, some were coming out as straight up.
The first problem I hit was, well, it didn't work. My character, instead of sticking to surfaces, was instead floating above them.
Let me back track a bit, and show you how I implemented this:
I grab a reference to the characters contact list, which is essentially a doubly linked list of Contacts. I iterate through it, looking for Contacts that contain fixtures that are in my terrain collision category, and then for those that are, I call the contact's GetWorldManifold() method, which gives me back a collision normal and the manifoldPoints of the collision in world coordinates. I've included a quick diagram to illustrate what a manifold is below, just in case you're not familiar with it:
I keep a cumulative sum of the normals as well as a count of how many there are, and then take an average at the end. Then all that remains is to normalize the average (just in case), and apply a force on at the centre of our character in the opposite direction.
So you can see that random additions of normals pointing in the wrong direction could cause problems!
After some digging inside Farseer I discovered that GetWorldManifold() returns Vector2.UnitY when called on a contact which has manifold.pointCount = 0 (the points it is referring to are those in the diagram above).
This confused me no end. My understanding of the engine was that a contact being included in an object's contact list meant that the two objects had collided. Turns out that this isn't quite the case.
After posting on the Farseer forums (a great bunch, just make sure you search before asking!) to see if this was a bug, I was put straight. A contact is formed when two AABBs (think of them as large bounding boxes) intersect. Each shape or fixture has a AABB to quickly check for possible collisions. The engine can then check whether the two Fixtures associated with the AABBs have collided and make them react appropriately. If you'd like an illustration of AABBs and what they do, download the Testbed solution from the Farseer codeplex page and hit F4 while its running.
So whenever my character got to the end of an EdgeShape, it would hit the AABB of the next EdgeShape, but wouldn't yet be in contact with the shape itself, causing manifold.pointCount to be zero, and giving me my floaty behaviour.
This was easy to fix, I just had to make sure manifold.pointCount > 0 and it was fixed. Or so I thought.
It worked just fine for concave edges and shapes, I.e. the inside of a room, which was exactly what my test map happened to be.
However, when I then built a map with an island in the middle, the character would get to a convex (or 'pointy') join between edges, and then fall off.
If we look back at the algorithm I described, it should be fairly obvious what was happening: the character would lose contact with all fixtures, and my code then reapplied gravity until it touched another fixture, I.e the floor.
So my solution was to attach a second, larger circle fixture to the character body with zero density, and set isSensor to true.
The idea was to use this as a backup, so that when the main character fixture loses contact with all fixtures, we can use the sensor to check if there are any other fixtures near by, and we can use that to give us the direction of our wall crawling force.
The final issue I came across was that sensors don't generate manifolds, so I had to manually calculate the normals on the EdgeShapes.
After all that, I ended up with the video above. *Phew*
That was a bit long winded, so for those of you that prefer code, I've added the finished code snippet below.
That's it for this post, but I'll be putting another post up in a bit with a short tutorial on 2d terrain in Farseer.
{
{
ContactEdge tempContactEdge = circle.Body.ContactList;
Contact tempContact;
Vector2 normalSum = Vector2.Zero;
int count = 0;
if (tempContactEdge != null)
{
while (tempContactEdge != null)
{
if (tempContactEdge.Contact.FixtureA.CollisionFilter.IsInCollisionCategory(Category.Cat11) || tempContactEdge.Contact.FixtureA.CollisionFilter.IsInCollisionCategory(Category.Cat12))
{
tempContact = tempContactEdge.Contact;
count++;
}
else if (tempContactEdge.Contact.FixtureB.CollisionFilter.IsInCollisionCategory(Category.Cat11) || tempContactEdge.Contact.FixtureB.CollisionFilter.IsInCollisionCategory(Category.Cat12))
{
tempContact = tempContactEdge.Contact;
count++;
}
else
{
tempContactEdge = tempContactEdge.Next;
continue;
}
if (tempContact.Manifold.PointCount != 0)
{
Vector2 tempNormal;
FixedArray2<Vector2> tempPoints;
tempContact.GetWorldManifold(out tempNormal, out tempPoints);
normalSum += tempNormal;
}
else
{
count--;
}
tempContactEdge = tempContactEdge.Next;
}
if (count > 0)
{
circle.Body.IgnoreGravity = true;
normalSum /= count;
normalSum.Normalize();
normalSum *= (-gravForce);
circle.Body.ApplyForce(normalSum);
}
}
}I keep a cumulative sum of the normals as well as a count of how many there are, and then take an average at the end. Then all that remains is to normalize the average (just in case), and apply a force on at the centre of our character in the opposite direction.
So you can see that random additions of normals pointing in the wrong direction could cause problems!
After some digging inside Farseer I discovered that GetWorldManifold() returns Vector2.UnitY when called on a contact which has manifold.pointCount = 0 (the points it is referring to are those in the diagram above).
This confused me no end. My understanding of the engine was that a contact being included in an object's contact list meant that the two objects had collided. Turns out that this isn't quite the case.
After posting on the Farseer forums (a great bunch, just make sure you search before asking!) to see if this was a bug, I was put straight. A contact is formed when two AABBs (think of them as large bounding boxes) intersect. Each shape or fixture has a AABB to quickly check for possible collisions. The engine can then check whether the two Fixtures associated with the AABBs have collided and make them react appropriately. If you'd like an illustration of AABBs and what they do, download the Testbed solution from the Farseer codeplex page and hit F4 while its running.
So whenever my character got to the end of an EdgeShape, it would hit the AABB of the next EdgeShape, but wouldn't yet be in contact with the shape itself, causing manifold.pointCount to be zero, and giving me my floaty behaviour.
This was easy to fix, I just had to make sure manifold.pointCount > 0 and it was fixed. Or so I thought.
It worked just fine for concave edges and shapes, I.e. the inside of a room, which was exactly what my test map happened to be.
However, when I then built a map with an island in the middle, the character would get to a convex (or 'pointy') join between edges, and then fall off.
If we look back at the algorithm I described, it should be fairly obvious what was happening: the character would lose contact with all fixtures, and my code then reapplied gravity until it touched another fixture, I.e the floor.
So my solution was to attach a second, larger circle fixture to the character body with zero density, and set isSensor to true.
The idea was to use this as a backup, so that when the main character fixture loses contact with all fixtures, we can use the sensor to check if there are any other fixtures near by, and we can use that to give us the direction of our wall crawling force.
The final issue I came across was that sensors don't generate manifolds, so I had to manually calculate the normals on the EdgeShapes.
After all that, I ended up with the video above. *Phew*
That was a bit long winded, so for those of you that prefer code, I've added the finished code snippet below.
{
{
ContactEdge tempContactEdge = circle.Body.ContactList;
Contact tempContact;
Vector2 normalSum = Vector2.Zero;
int count = 0;
if (tempContactEdge != null)
{
while (tempContactEdge != null)
{
if (tempContactEdge.Contact.FixtureA.CollisionFilter.IsInCollisionCategory(Category.Cat11) || tempContactEdge.Contact.FixtureA.CollisionFilter.IsInCollisionCategory(Category.Cat12))
{
tempContact = tempContactEdge.Contact;
count++;
}
else if (tempContactEdge.Contact.FixtureB.CollisionFilter.IsInCollisionCategory(Category.Cat11) || tempContactEdge.Contact.FixtureB.CollisionFilter.IsInCollisionCategory(Category.Cat12))
{
tempContact = tempContactEdge.Contact;
count++;
}
else
{
tempContactEdge = tempContactEdge.Next;
continue;
}
if (tempContact.Manifold.PointCount != 0)
{
Vector2 tempNormal;
FixedArray2<Vector2> tempPoints;
tempContact.GetWorldManifold(out tempNormal, out tempPoints);
normalSum += tempNormal;
}
else if (tempContactEdge.Contact.FixtureA.CollisionFilter.IsInCollisionCategory(Category.Cat12))
{
EdgeShape eShape = (EdgeShape)tempContactEdge.Contact.FixtureA.Shape;
Vector2 v = eShape.Vertex2 - eShape.Vertex1;
Vector2 w = sensorCircle.Body.Position - eShape.Vertex1;
float t = Vector2.Dot(w, v) / Vector2.Dot(v, v);
v = eShape.Vertex1 + (t * v) - sensorCircle.Body.Position;
v.Normalize();
v *= -1;
normalSum += v;
}
else if (tempContactEdge.Contact.FixtureB.CollisionFilter.IsInCollisionCategory(Category.Cat12))
{
EdgeShape eShape = (EdgeShape)tempContactEdge.Contact.FixtureB.Shape;
Vector2 v = eShape.Vertex2 - eShape.Vertex1;
Vector2 w = sensorCircle.Body.Position - eShape.Vertex1;
float t = Vector2.Dot(w, v) / Vector2.Dot(v, v);
v = eShape.Vertex1 + (t * v) - sensorCircle.Body.Position;
v.Normalize();
v *= -1;
normalSum += v;
}
else
{
count--;
}
tempContactEdge = tempContactEdge.Next;
}
if (count > 0)
{
circle.Body.IgnoreGravity = true;
normalSum /= count;
normalSum.Normalize();
normalSum *= (-gravForce);
circle.Body.ApplyForce(normalSum);
}
}
}That's it for this post, but I'll be putting another post up in a bit with a short tutorial on 2d terrain in Farseer.
See you in a bit.
Wednesday, 2 March 2011
Updates soon!
I've been reinstalling my operating system recently, so I haven't been able to post, but I have an update that I'm prepping for the weekend, plus hopefully a quick tutorial on 2d terrain with Farseer.
More soon!
More soon!
Saturday, 5 February 2011
Sphero progress update
The first of many updates on Sphero. My plan is to blog all the way through the development of the game to try and keep myself on track. Anyone who's gone back and read the previous entries in this blog will see that keeping on track is something that I struggle with!
So without further ado, here is the first progress update on Sphero.
So far I've implemented creating 2D terrain inside my map editor in the form of ledges or solid paths. You simply click where you would like the next node to go. The nodes can also be dragged and moved around to alter the shape of the ledge.
I've also implemented this ledges into Alta, having the engine create EdgeShapes using Farseer to create solid terrain.
Finally, I've created the first prototype of the basic movement mechanics for the main character. They're very rough at the moment, but as I'm going for a rapid prototyping approach, they're good enough at this point.
Finally, I whipped up a quick map and put together a demo:
http://www.youtube.com/watch?v=69jQG3r5I88
That's all for now. I'm hoping to get some more of the movement mechanics done today, so I may have another video for you soon!
So without further ado, here is the first progress update on Sphero.
So far I've implemented creating 2D terrain inside my map editor in the form of ledges or solid paths. You simply click where you would like the next node to go. The nodes can also be dragged and moved around to alter the shape of the ledge.
I've also implemented this ledges into Alta, having the engine create EdgeShapes using Farseer to create solid terrain.
Finally, I've created the first prototype of the basic movement mechanics for the main character. They're very rough at the moment, but as I'm going for a rapid prototyping approach, they're good enough at this point.
Finally, I whipped up a quick map and put together a demo:
http://www.youtube.com/watch?v=69jQG3r5I88
That's all for now. I'm hoping to get some more of the movement mechanics done today, so I may have another video for you soon!
Thursday, 3 February 2011
2D Rendering with SpriteBatch: My approach
I'll be posting an update (along with hopefully a video of the first prototype) of my progress on Sphero at the weekend, but today I wanted to take a second to share my approach to 2D rendering using SpriteBatch.
SpriteBatch itself if an optimisation feature of XNA, grouping and/or sorting sprites to reduce the number of draw calls or textures swaps that need to take place to render the scene. However, the performance benefit from using a SpriteBatch can vary depending on how its used.
The approach I took was to try and figure out what the optimal way of using sprite batch was, and then try and work the rest of my engine around that. The following is the approach I came up with.
***HEALTH WARNING***
This method has not been tested on an Xbox, so I can give no guarentees about it's performance.
At the centre of the set-up are a class, a struct, and 2 dictionarys.
The key to it all is the RenderManager class. However, I won't say too much about this class just yet.
I'm going to try and go through these in a logical order. First of all, we have a dictionary which contains all of the textures we use in the game, with their names as keys (you could use unique ID number if you prefer, I
just find strings easier to work with and remember). All textures are loaded through the RenderManager class, which loads the texture from the content pipeline, and stores it in the Texture dictionary with its name as its key.
The next piece of the puzzle is the RenderObject2D struct. A better name might have been the 'sprite' class, as this is essentially what it is. It holds all of the information needed for the RenderManager to draw the sprite that it describes. This includes a string representing the texture of the sprite.
The penultimate item that makes up this system is the Layer Dictionary.
This is not quite as it sounds. This is not a Dictionary, but rather a Dictionary. The int refers to the order of the layers, the string is used by the game code to tell the RenderManager which layer to add the new sprite to.
Finally we get to the RenderManager.
The RenderManager does 3 main jobs. It loads and stores textures (as we've already seen). It keeps track of all of the sprites to be drawn this frame, and it does the actual drawing.
Essentially the way all of this fits together is this:
Each frame, the game code calls RenderManager.AddToScene() in place of its normal draw calls. This (heavily overloaded) method takes all of the parameters you would need in a SpriteBatch.Draw() call, with two big differences. The first is that instead of a reference to a texture, it passes the name of the texture, and the second is that instead of a depth parameter, it passes the name of a layer.
Inside the RenderManager, every time AddToScene() is called, a RenderObject2D struct is created and filled out with all of the parameters barring the layer name.
Next, the RenderObject2D Struct is stored inside what is effectivey a 3 dimensional array (List
SpriteBatch itself if an optimisation feature of XNA, grouping and/or sorting sprites to reduce the number of draw calls or textures swaps that need to take place to render the scene. However, the performance benefit from using a SpriteBatch can vary depending on how its used.
The approach I took was to try and figure out what the optimal way of using sprite batch was, and then try and work the rest of my engine around that. The following is the approach I came up with.
***HEALTH WARNING***
This method has not been tested on an Xbox, so I can give no guarentees about it's performance.
At the centre of the set-up are a class, a struct, and 2 dictionarys.
The key to it all is the RenderManager class. However, I won't say too much about this class just yet.
I'm going to try and go through these in a logical order. First of all, we have a dictionary which contains all of the textures we use in the game, with their names as keys (you could use unique ID number if you prefer, I
just find strings easier to work with and remember). All textures are loaded through the RenderManager class, which loads the texture from the content pipeline, and stores it in the Texture dictionary with its name as its key.
The next piece of the puzzle is the RenderObject2D struct. A better name might have been the 'sprite' class, as this is essentially what it is. It holds all of the information needed for the RenderManager to draw the sprite that it describes. This includes a string representing the texture of the sprite.
The penultimate item that makes up this system is the Layer Dictionary.
This is not quite as it sounds. This is not a Dictionary
Finally we get to the RenderManager.
The RenderManager does 3 main jobs. It loads and stores textures (as we've already seen). It keeps track of all of the sprites to be drawn this frame, and it does the actual drawing.
Essentially the way all of this fits together is this:
Each frame, the game code calls RenderManager.AddToScene() in place of its normal draw calls. This (heavily overloaded) method takes all of the parameters you would need in a SpriteBatch.Draw() call, with two big differences. The first is that instead of a reference to a texture, it passes the name of the texture, and the second is that instead of a depth parameter, it passes the name of a layer.
Inside the RenderManager, every time AddToScene() is called, a RenderObject2D struct is created and filled out with all of the parameters barring the layer name.
Next, the RenderObject2D Struct is stored inside what is effectivey a 3 dimensional array (List
- >> scene). This is indexed by layer and texture.
In plain english, the scene is a List of layers. Each layer is a list of sub-layers grouped by texture. Each sub-layer is a list of RenderObject2Ds.
After all other components have had 'draw' called on them, RenderManager's draw() method is called. The RenderManager then starts a spriteBatch in 'immediate' mode, and then loops through the scene, starting with the backmost layer, looping through each Texture sub-layer to ensure the minimum number of texture swaps.
Essentially this means the sprites never need sorting, because they were filed away in the right place when they were added to the RenderManager's scene.
There are some optimisations around the Lists to ensure that they dont alloate memory each frame from resizing, and it could be made more efficient by drastically reducing the size of a RenderObject2D by using a numeric identifier for textures, but that should give you a flavour of how I've approached dealing with SpriteBatch.
Now the question is, have I got this completely wrong? Is there a glaringly obvious reason not to go down this route? What are the pros and cons?
I'd love to hear your thoughts!
Sunday, 23 January 2011
Updating the Farseer Platformer tutorial to Farseer 3.2 (footnote)
Because I'm forgetful, and blogger doesn't like me editing blog posts with code in them, I've added a seperate post on updating the tutorial from Farseer 3.0 and 3.2.
There are only 2 issues you should encounter upgrading the 3.0 tutorial to 3.2. The first is that CollisionEventHandler has been renamed as OnCollisionEventHandler (but the only the name appears to have changed).
The second is that fixture.IgnoreCollisionWith() is now fixture.CollisionFilter.IgnoreCollisionWith(). And that's pretty much it! There are other changes between the two librarys, but these are the only ones we'd encounter.
If you need help updating the rest of the code from XNA 3.1 to 4.0, I suggest you check out the upgrade cheat sheet on xdsk2: http://www.nelxon.com/blog/xna-3-1-to-xna-4-0-cheatsheet/
There are only 2 issues you should encounter upgrading the 3.0 tutorial to 3.2. The first is that CollisionEventHandler has been renamed as OnCollisionEventHandler (but the only the name appears to have changed).
The second is that fixture.IgnoreCollisionWith() is now fixture.CollisionFilter.IgnoreCollisionWith(). And that's pretty much it! There are other changes between the two librarys, but these are the only ones we'd encounter.
If you need help updating the rest of the code from XNA 3.1 to 4.0, I suggest you check out the upgrade cheat sheet on xdsk2: http://www.nelxon.com/blog/xna-3-1-to-xna-4-0-cheatsheet/
Subscribe to:
Posts (Atom)

