Collision Textures and 2DRenderTargets in XNA
I started making a Tower Defense Game which I will be sharing soon, including all my trials and tribulations and the theory behind it. Whilst making it I came across an interesting problem, which was how to tell whether or not the user could place the tower where they currently have the cursor. I thought about it for a while and then decided that the best method would be a sort of collision texture whereby you have a texture ‘underneath’ the main texture which outlines the areas where a tower can and cannot be placed (thought this texture is never actually rendered).
I started by considering exactly how I could do this. Well in XNA we have these handy things called RenderTargets, which is a buffer where the video card draws pixels for a scene that is being rendered by an Effect Class. The default render target is called the back buffer – this is the part of video memory that contains the next frame to be drawn. But we also have the RenderTarget2D class in effect, which reserves new regions of video memory for drawing. So the best way to create this collision texture is to grab the portion of the collision texture around the tower you are trying to place, then check if it is an ‘allowable’ area. I chose to do this by having the collision texture as being white for disallowed areas and black otherwise. We then iterate through and check each pixel of the collision texture to see if any pixel is white, and if it is then you can’t place the tower there!


The collision texture is never rendered, the map shown here is rendered, but you will find in the example I cut the map in half and rendered the collision texture so you can see where the allowed areas are.
colTex = new RenderTarget2D(GraphicsDevice, tileWidth, tileHeight, true, SurfaceFormat.Color, DepthFormat.Depth24); |
First we need to initialize the render target, giving it the default size of the width and height of a tile. We then need to grab the current area the mouse is over and render it to the render target.
private Texture2D CreateCollisionTexture(float X, float Y) { GraphicsDevice.SetRenderTarget(colTex); GraphicsDevice.Clear(ClearOptions.Target, Color.Red, 0, 0); spriteBatch.Begin(); spriteBatch.Draw(path, new Rectangle(0, 0, tileWidth, tileHeight), new Rectangle((int)(X - tileWidth / 2), (int)(Y - tileHeight / 2), tileWidth - 1, tileHeight - 1), Color.White); spriteBatch.End(); GraphicsDevice.SetRenderTarget(null); return colTex; } |
This function essentially sets the render target, clears it and then draws the current tile sized area under the mouse using the spriteBatch. We then have the meat of the algorithm which does the calling of ‘CreateCollisionTexture’ and loops through the pixels
public void checkPlacement() { TileX = (int)Math.Floor((float)(Mouse.GetState().X / tileWidth)); TileY = (int)Math.Floor((float)(Mouse.GetState().Y / tileHeight)); float aXPosition = ((TileX * tileWidth) + (tileWidth / 2)); float aYPosition = ((TileY * tileHeight) + (tileHeight / 2)); Texture2D CollisionCheck = CreateCollisionTexture(aXPosition, aYPosition); int pixels = tileWidth * tileHeight; Color[] myColors = new Color[pixels]; CollisionCheck.GetData(0, new Rectangle(0,0, tileWidth, tileHeight), myColors, 0, pixels); foreach (Color aColor in myColors) { if (aColor == Color.White) { towerActive = false; break; } } } |
Here we do a few of things:
- Find what tile we’re on and translate it into an X and Y coordinate
- We generate the collision texture
- We get the pixel data and then loop through each pixel to see if its white, if it isn’t then we’re good to place the tower
Now if you check the example you can see that if the tower is over the path then it turns grey, otherwise it’s red! Technically we could make a bit of an optimization here by only checking the pixels around the edge of the collision texture because we’re using tiles, we don’t really have to care about any of the pixels within the middle because if it intersects the path then the edge check will find this. I haven’t implemented this because I’ve tried to keep this algorithm more general purpose. But try it if you like! Even send it to me and i’ll post it
Please note that the source is compiled for windows, and is also compiled for XNA 4.0! You will also require Visual Studio 2010
This is bad, the rendertarget will surely be stored on the video card, why not use a 2D array in memory? If the gameplay area is generally sparse a hashmap of used squares may be even better… Also using colours is 32 bits for essentially 1 bit of storage thats required, if you use bool type then the compiler will optimise…
But good thing someone in DoC is making games and not educational software!
Yup, I think in the same solution and you validate my conclusion, this technique works better because if you have a large map is so difficult to generate the array, using a graphic you can simply use paint to define areas, besides the graphic is automatically compressed and the array not (at least in disk). Is impossible to use a b/w only graphic? why the graphics must use 32 bits colors? The content pipeline convert everything to 32 bit color or something?
As long as performance is no problem, don’t care about performance. He just does the best as he can and he explains rendertarget and perpixel collision not that bad. He is just a person who does his best, why would you go and nag about performance why it isn’t a problem in this case?
A similar problem that can be solved via a collision-texture would be a racing game. Since racing games tend to have smooth curved corners – something that can’t be modelled easily as an array, a per-pixel collision solution such as this is probably your best bet.