Spatial Hashing For Teh Win!!!
It totally worked. Sometimes when theory is followed by practice, things go wrong. But my previous blog post experiment has worked an absolute treat. I spent the afternoon implementing it into SBARG. Here’s a screenshot of the result.
WIN!!!!!
Spatial hashing implementation for fast 2D collisions
This is a sample prototype I wrote to fix a performance limitation with collision checking in SBARG. So what is spatial hashing? Here is a good 1 liner I will borrow from the source material i used.
“Spatial hashing is a process by which a 3D or 2D domain space is projected into a 1D hash table.” Optimization of Large-Scale, Real-Time Simulations by Spatial Hashing
So why do we need it?
Well, for me it was a problem in my collision code and my A.I code which was trying to find nearby objects to check collisions for. Due to my brute force nature if I had 10 monsters in the world there would be 10*10 = 100 collision checks in every update. Now ramp the number up to 100 to be a bit excessive and we end up with 100*100=10,000 collision checks. This makes the cpu cry like a little baby as I’m sure you can imagine. To rectify this we need to reduce the amount of collision checks we need to do in the first place. This is where spatial hashing comes in handy.
Imagine the game world in a flat 2d grid. 100 by 100 pixels and each cell was 25 by 25 pixels. Now number the cells from left to right, top to bottom 0 onwards. You will end up with something like this. The orange circles are the game objects, in my case, monsters.
Each cell is a bucket of game objects and a unique hash id. If we imagine the bucket as a list of 16 buckets, 0-15 cells and placed the game objects in that bucket. It might look something like this.
This is our 1D grid mentioned in the introduction.
It’s a simple premise really, any item in bucket 3 for example, cannot possibly collide with something in bucket 9. This reduces the amount of times we need to cycle the nearby objects but also dramatically reduce the amount of times we need to check if a collision is happening.
Ok, so the above implementation is fine as long as a game object only ever exists in 1 bucket. But what if it crosses a line and exists in more than 1 bucket. To resolve this I imagined a box around each game object, and I calculated the hash id for each corner of the box. I then populate a List<GameObject> going through each bucket the game object is in. Sounds simple ?
Let me show you some of this theory in code.
First we need a game object. For this sample all we need is a position and a radius.
public class GameObject
{
public Vector2 Position { get; set; }
public float Radius { get; set; }
}
We create a new class to store the grid data in including the buckets. I’m terrible at naming classes so I called it SpatialManager. I gave this class a Setup method which takes a full scenewidth, height and a cellsize. In our example this would be 100,100,25
public void Setup(int scenewidth, int sceneheight, int cellsize)
{
We can work out how many buckets we need by first calculating the rows and cols then simply create a new Dictionary of buckets to the tune of Rows*Cols. I also store these variables passed in for future use.
Cols= scenewidth / cellsize;
Rows= sceneheight / cellsize;
Buckets = new Dictionary<int , list><gameobject>(Cols * Rows);
for (int i = 0; i < Cols*Rows; i++)
{
Buckets.Add(i, new List());
}
SceneWidth = scenewidth;
SceneHeight = sceneheight;
CellSize = cellsize;
}
Each update, we need to clear out the buckets and re calculate the buckets each game object are in. I created a method called ClearBuckets to start fresh.
internal void ClearBuckets()
{
Buckets.Clear();
for (int i = 0; i < Cols * Rows; i++)
{
Buckets.Add(i, new List());
}
}
We now need a method to register a game object into the buckets it sits in.
internal void RegisterObject(GameObject obj)
{
List cellIds= GetIdForObj(obj);
foreach (var item in cellIds)
{
Buckets[item].Add(obj);
}
}
As you can see, the code retrieves a list of cellids to add the game object to.
In the GetIdForObj method it calculates the cell id for each corner of the game object.
If we were just checking the position of the game object the calculation would be.
float width = SceneWidth / CellSize; // 100 / 25
int hashid=(int)(
(Math.Floor(position.X / CellSize)) +
(Math.Floor(position.Y / CellSize)) * width);
We need to do this for each corner and add our game to each bucket.
The GetIdForObj method looks like this.
private List GetIdForObj(GameObject obj)
{
List bucketsObjIsIn = new List();
Vector2 min = new Vector2(
obj.Position.X - (obj.Radius),
obj.Position.Y - (obj.Radius));
Vector2 max = new Vector2(
obj.Position.X + (obj.Radius),
obj.Position.Y + (obj.Radius));
float width = SceneWidth / CellSize;
//TopLeft
AddBucket(min,width,bucketsObjIsIn);
//TopRight
AddBucket(new Vector2(max.X, min.Y), width, bucketsObjIsIn);
//BottomRight
AddBucket(new Vector2(max.X, max.Y), width, bucketsObjIsIn);
//BottomLeft
AddBucket(new Vector2(min.X, max.Y), width, bucketsObjIsIn);
return bucketsObjIsIn;
}
And here is the AddBucket method which uses the calculation described above and adds it to the list of bucket id’s to add to.
private void AddBucket(Vector2 vector,float width,List buckettoaddto)
{
int cellPosition = (int)(
(Math.Floor(vector.X / CellSize)) +
(Math.Floor(vector.Y / CellSize)) *
width
);
if(!buckettoaddto.Contains(cellPosition))
buckettoaddto.Add(cellPosition);
}
Now that we have our grid of buckets. It’s a very simple retrieval process. I added a method to get the nearby objects of a given object. This uses the GetIdForObj method and populates a list of GameObject’s and returns once complete. This is the key part to this solution, you only now need to check items which are actually nearby and not items the other side of the theoretical world.
internal List GetNearby(GameObject obj)
{
List objects = new List();
List bucketIds = GetIdForObj(obj);
foreach (var item in bucketIds)
{
objects.AddRange(Buckets[item]);
}
return objects;
}
So that’s it. Now you can do your normal collision checking by retrieving nearby GameObjects. And here’s a screenshot to prove it.
I’ve placed my mouse over one of the GameObjects and it’s highlighted all nearby items. Notice how they cross over cells. This is because the item i hover over is on the line and exists in multiple cells. Also notice the amount of checks it has to do. For brute force it has to do 250,000 bounding box collision checks. For spatial hashing, it checks only 4840 times. Wicked.
And what would a sample be without source code?
Enjoy
Simply RenderTargets
Somebody in the #xna IRC channel just asked how to use RenderTargets to only draw a portion of the screen. Here’s my answer.
I guessed from his brief description he might have a game scene which didn’t look like this
And the result he wanted, was something almost like … this ?
What you see here is the game scene drawn, with a triangle portion visible and the rest not so much. My favourite way to achieve this is to use 2 RenderTargets, A VERY simple Effect(shader) and good old SpriteBatch. So what do we use all this for?
First we need to draw the game scene. There is a way around this but to make things easier to understand we will draw this to a RenderTarget. Simply put, a RenderTarget is an imaginary “screen” to draw to which you can specify the size of. Then once done, you can use the texture like any normal Texture2D you might load from the Content Pipeline.
To draw the scene we first want to set the correct RenderTarget, then use spritebatch just like you would to draw any other sprite. Like this.
GraphicsDevice.SetRenderTarget(0, _gameRT);
//Clear the RT screen
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
//Draw the game
spriteBatch.Draw(
_gameBackground,
Vector2.Zero,
Color.White);
spriteBatch.End();
Simple uh?
Now we need to create another RenderTarget, and Draw a triangle to it. This will never be displayed in the final scene. Although with the RenderTarget it allows you to if you so desire, maybe for debugging purposes?
GraphicsDevice.SetRenderTarget(0, _lightRT);
//Clears the screen transparent.
GraphicsDevice.Clear(Color.TransparentBlack);
spriteBatch.Begin();
spriteBatch.Draw(
_triangle,
Vector2.Zero,
Color.White);
spriteBatch.End();
This code is almost identical to the last except we draw the _triangle Texture which looks like this.
And we use the color transparent black.
The final thing we need to do now is draw it to the scene.
If we just draw the triangle on top of the game scene you will see the game scene with a white triangle on top. This isnt what we want. So we use a shader to take the game scene, and change the alpha channel corresponding to the white triangle. Wherever there is a solid non transparent color pixel, this will be set to a fully non transparent pixel in the game result, however, a transparent pixel in the triangle image will result in a completely transparent pixel in the final result. Resulting in you not being able to see that pixel. The final result is like this.
The shader code is very simple. It is a pixel shader which takes a AlphaTexture parameter which is the texture taken from the triangle RT. It takes the alpha from that texture, and sets the coresponding pixel on the final image to be the same.
float4 PixelShader(float2 texCoord: TEXCOORD0) : COLOR
{
float4 Color = tex2D(ScreenS, texCoord);
float alphaLayerAlpha = tex2D(AlphaSampler, texCoord).a;
Color.a=alphaLayerAlpha;
return Color;
}
Cool?
To use this we just create a new Effect variable and load it in LoadContent. Call Begin/End in the appropriate places, set the parametar and hey presto. A RESULT!!!
_myEffect.Parameters["AlphaTexture"].SetValue(_lightRT.GetTexture());
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);
_myEffect.Begin();
_myEffect.CurrentTechnique.Passes[0].Begin();
spriteBatch.Draw(_gameRT.GetTexture(),
Vector2.Zero,
Color.White);
_myEffect.CurrentTechnique.Passes[0].End();
_myEffect.End();
spriteBatch.End();
To get a better grasp of the code you can download the sample project here.
As I said, Someone in IRC literally just asked. So I knocked this up. Apologies for the rushed post
BACK TO SBARG!!!
XBox Live Community Games Listings
A community created (Nick Gravelyn) website has existed for some time which set out to emulate the functionality of the Xbox Marketplace but until very recently, it kind of sucked to look at. The information was there, but it was a fairly dull and average affair. But now, wow. Take a look for yourself. It’s almost like a direct copy of the Xbox Marketplace but it has 2 major benefits. It’s not slow, so you can go to it today, and read it today, and the other is user ratings. Go check it out for yourself, oh a third thing, the Box Art Collage is pretty sweet too, oh, and Random Game, i could go on but here’s the link. I’m going to find some new games to play
Congratulations to Nick Gravelyn & Scott Wendt & Björn Graf for getting this out without too much of a hiccup, I can see why Nick was so excited to get it out now. Great work with the new site.
A Pinball Prototype
Last night I set myself and a friend a challenge. Make a game in 1 night. We’ve done it before with Rock Paper Scissors Live. This time we failed, miserably. It’s not a functional game by any stretch of the imagination, however it is a cool little prototype.
2D Pinball was the genre we chose, so I created a new IceCream project and off I went. Using the Farseer integration it meant I had something up and running in no time, but it was proving difficult and time consuming to get the physics settings to feel like a pinball game. IceCream support for Farseer isnt quite complete yet, so there was quite a bit of code to write surrounding Springs and Joints. After some quick tweakage after arriving home I’ve got it to feel much better and much more like a pinball game. Here’s what it looks like now. I wonder if I’ll come back to this project and turn it into a game
could be fun.
IceCream Release (Update)
After finally releasing a release of IceCream early this morning which was probably not my wisest move as I was rushing and it showed in the release. Firstly I just messed up but secondly there were a couple of bugs I’ve been coping with that I should not have inflicted on anyone who tried it out. These were when you tried to open a project file and it couldnt find the scene file or no scene file was to be opened, the loading progress screen would stay alive and look like the app had hung. It didn’t, you just close the progress window and it’s ok to use. The second bug was deeper, when you open a project while already having a different project open, it would die a horrible death. I’ve been working hard on SBARG so it’s been a single project for me for some time. Both these bugs are now fixed in this release, along with an appropriate version number along with being compiled in Release mode so that should eliminate any debug performance issues.
You can grab the latest download of IceCream release here.
Again, please feel free to leave feedback, suggestions, comments or if you want some help email me at conkerjo at hotmail dot co dot uk.
IceCream quick and dirty crash course.
So now that IceCream has been let out of the bag, how do you use it? well this post should give you a better idea of how a IceCream game is built and what are the basics of making your own game.
In the release there is a IceCreamPong demo sample, so let’s use this as our example.
This consists of a Visual Studio XNA Game 3.0 project, just like any other game project you would create. The installer adds a template for you which adds the reference and creates the default files. There is also a Game.icproj file. This file is the IceCream project file and is used by Milkshake to open your project.
Next is the global.ice file. This is a global data holder for template scene items and global assets such as materials.
Then you will have 1 or more scene files for your game, this sample contains TestScene.icescene. This is the main data store for any scene in your game. Both the scene files are xml and can be edited by hand, I highly recomend studying the data before attempting this though.
The game project contains a reference to the IceCream.dll. You will notice a lot of similarities with a standard XNA game project, we also gave a Game1.cs file which is our main game entry point. The difference is we inherit from IceCream.Game instead of the normal Game class from XNA.
public class Game1 : IceCream.Game
{
IceScene scene;
public Game1()
{
}
protected override void LoadContent()
{
base.LoadContent();
scene = SceneManager.LoadScene("Content/TestScene.icescene");
}
}
All it takes is 2 lines of code to get a scene up and running.
This is important the base.LoadContent(); method call must come before you LoadScene call as this is what loads the global content potentially required by your scene data.
When the LoadScene method is called it loads the specified file into the SceneManager.ActiveScene property if it’s the first scene loaded.
Next we wil take a look at the components. In IceCream there are 2 types of Components. A IceSceneComponent which is added to the Scene.Components collection and a IceComponent which is added to a SceneItem’s Components collections.
These components are updated on every game update much like the component system built into XNA except with more flexibility.
The IceComponent contains a property called Owner which is a reference to its Owning SceneItem, but a IceSceneComponent has a property called Owner too which is a reference to its owning Scene.
In the sample game we have an IceSceneComponent called PongEngine. This has been added to the scene in the TestScene.icescene file.
When the LoadScene method is called this Component is added to the scene and the OnRegister method is called.
public override void OnRegister()
{
Instance = this;
Enabled = true;
BottomWall = (Sprite)Owner.GetSceneItem("WallBottom");
TopWall= (Sprite)Owner.GetSceneItem("WallTop");
SetupPlayers();
SetupBall();
}
A couple of things to point out here, the Owner property is a SceneItem so it needs to be casted to a Sprite in this instance. But if you look more closely we have Owner.GetSceneItem method, this will retrieve the SceneItem if it’s registered in the scene with the specified name.
If we look at the decleration of the Component we see how IceCream & Milkshake utilize your custom components.
[IceComponentAttribute("PongEngine")]
public class PongEngine : IceSceneComponent
Some of the other types of SceneItem available are
One of the cool things about IceCream is you can add Components to the scene and SceneItems from within Milkshake by right clicking an item in the main tree.
I know this is only a brief overview of a tiny portion of what IceCream is. I will be writing more specific articles very soon.
If you have a specific task you want to achieve, please post a comment and I’ll gladly help.
Early IceCream Engine Release
I have decided to make a release of IceCream & Milkshake.
IceCream is a 2D game engine built for XNA and Milkshake is the 2D visual editor for IceCream.
Included in the download is a very simple sample of IceCreamPong.
It’s a little hacked together but take a look at how things work.
IceCream & Milkshake are still very young and this is a very early look at what it can do, it has some known issues, and I’m sure it will have some yet unknown issues so please use this with caution as I cannot take any responsibility for it yet.
I use IceCream & Milkshake with SBARG and it has made the development much easier. So once learning the quirks this is a very functional engine and editor.
Please feel free to try it out by downloading the self extracting exe here
Once installed open the IceCreamPong sample in the Samples directory and make sure it builds ok. (The reference maybe wrong if you installed to a different location). Then run to make sure it runs and plays a very simple version of pong.
Now run Milkshake.exe and open the Game.icproj file from withint the IceCreamPong directory, (edit) You will need to open the icescene file from the Content directory.
Try something crazy like…. adding bloom… mmmm. MORE BLOOM!!!
I’ll work on some “Getting started guides” but please feel free to try it out and let me know what you think in the comments or if it’s something more detailed or you want a bit of help I’ll try my best to respond to conkerjoe at hotmail dot co dot uk
Xbox Live Community Games Coverage
Wow… people bitch about coverage, and coverage is what they get.. keep bitching guys… it’s working.
Here’s a rundown of what’s been going on in the world of XNA and Community Games recently and some other random links XBLCG related.
‘Media Coverage Matters’: Weapon of Choice Dev
Analysis: XNACG Regional Sales And Conversion Rates
Best Of GamerBytes: GDC Round-Up, XNA Analysis
In-Depth: Xbox 360 Community Games Devs Talk Successes, Failures, What They Want
Exclusive: XNA Community Games Sales Figures Revealed
Joystiq interview: Halfbrick Studios
The brighter side of XNA sales
GamerBytes study shows disappointing sales for XNA Community Games
Some creators disappointed with XNA Community Games sales
GamerBytes picks top XNA Community Games
XNA Roundup episode no.13 bite sized reviews #1
Nick G’s awsome new XNA Game listings
