It was like 2-3 weeks ago when I got bored and decided to check out some gaming forums. Gaming forums are always fun after all. After a few hours of skimming / reading / talking ; I remembered about XNA, the gaming framework of Microsoft using C# , and decided to give it a shot.
So I’m playing with XNA for that last 2 weeks and it has been incredibly fun! It’s extremely easy if you’re somewhat decent at C# and you can create experimental stuff extremely quick. Also there are big huge professional games using XNA out there , like Terraria , Bloodline Champions and Bastion. I’m not really experienced in game programming and don’t know much about other game development frameworks either but XNA really looks like the perfect choice for indie game developers to me now.
I’ve been making small experimental stuff all those 2 weeks and I’ll slowly blog about them , stargin with background management and creation in 2D games.
But please note that , I’m not experienced in XNA or game development , not one bit. So my samples here might bad practices or wrong or something. Beware.
So , about background in 2D games. First thing I wanted to try was a simple platformer , actually I think I started it after checking out one of the Xna Platformer samples. And after recreating what is already in that sample , I decided to improve it a bit myself and the first thing came to my mind was the backgroun since background is the heart and soul of a platformer. It takes like %90 of the screen and you stare it almost all game long. Also it’s the most important part of creating the mood and showing off your art style etc etc.
In this post , we’ll create the two simplest background types , static and tiled background.
What I call static background is just putting an image into background and no matter where does the player character moves , it doesn’t change one bit. It just stays there , pretty much like “Fixed Background” in Cascading Style Sheets.
On the other hand , tiled background is craeted by repeating same tile texture until it fills the whole screen , like grass or water in good old 2D games. It’s a bit repetitive of course but with good symmetrical textures , it looks pretty good and much more usable then one huge background image.
OK here we go , first of all I’ll create some services to take care of background elements. This might not be the best practice in XNA but I just like to work with my custom services to do stuff. I guess “Components” is the XNA way of doing the same thing but I prefer my good old simple service classes for now. I’ll read & learn about component later.
PlayerService = new PlayerService(this); EnvironmentService = new EnvironmentService(this); CameraService = new CameraService(this, EnvironmentService, PlayerService);
These are the first 3 services I like to use in every project. Obviously Player service creates and takes care of player , EnvironmentService handles background stuff and the level , CameraService takes care of camera position , where camera looks ( for 3D ) and stuff like that.
Constructor of the EnvironmentService is also simple and not much there for now;
public EnvironmentService(TheGame game) : base(game) { _background = new Background(game, MapWidth, MapHeight); }
tangle.Height))
Now the real content is all in Background object.
public class Background { public List<IBackground> BackgroundLayers { get; set; } public Background(TheGame game, int mapWidth, int mapHeight) { BackgroundLayers = new List<IBackground>(); BackgroundLayers.Add(new FixedBackground(_game.Content.Load<Texture2D>("space"))); BackgroundLayers.Add(new TiledBackground(_game.Content.Load<Texture2D>("astroid"), _game.GraphicsDevice.Viewport.Width, _game.GraphicsDevice.Viewport.Height)); } public void Update(GameTime gameTime) { foreach (IBackground bg in BackgroundLayers) { bg.Update(_game.CameraService.CameraRectangle); } } public void Draw(SpriteBatch spriteBatch, Rectangle screenRectangle, Rectangle nextPosition) { foreach (IBackground bg in BackgroundLayers) { bg.Draw(spriteBatch); } } }
OK , what do we got here. We have a background layers list , we add our 2 background layers to that list in constructor method , update them in generic Update Method and Draw them on screen in generic Draw Method , nothing fancy.
As you can guess , Game Update method calls , EnvironmentService Update method which calls Background Update method. And same thing for Draw method too.
Oh and I got an interface for Backgrounds so I can keep them all in one list ;
public interface IBackground { void Update(Rectangle screenRectangle); void Draw(SpriteBatch spriteBatch); }
Now , let’s have a look at Fixed Background ;
public class FixedBackground : IBackground { private Texture2D _texture; private Rectangle _screenRectangle; public FixedBackground(Texture2D texture) { _texture = texture; } #region Implementation of IBackground public void Update(Rectangle screenRectangle) { _screenRectangle = screenRectangle; } public void Draw(SpriteBatch spriteBatch) { spriteBatch.Draw(_texture, new Rectangle(0,0,_screenRectangle.Width,_screenRectangle.Height), Color.White); } #endregion }
That’s all and hardly anything there really. We just set texture in constructor method , update screen data in Update method and draw a simple rectangle in Draw method. One thing you may ask tho , do we really need to update screen data , it’s not like screen will change in the middle of game after all. But then , what if we want to change our camera position and Viewport? You may remove it too of course but I prefer having it there , just feels like it’s working properly checking out where to draw stuff every time.
In the draw method , we’re using generic spriteBatch.Draw method to draw an image using the texture set in contructor , starting from the top left of the screen (0,0) and ending at bottom right corner. ( Color.White is the standart input for no tinting , not really important for now ) Easy Peasy.
Now it should look something like this ;
Yea I’m using that ship , from one of those Xna samples , as player and a random space image from internet.
Now even tho our “space” is 4000×960 , no matter where player goes , background will stay the same.
And now let’s have a look at Tiled Background and add an astroid field to our game. But before the code , let’s see what do we really need.
This is simply how tiled backgrounds work. We create screenWidth / tileWidth + 1 amount of tiles ( because one tile will be used to fill empty space created by scrolling ) and start drawing them one by one , starting from a point close to top left ( which always changes according to player movement ). And if we assume our tile width is 100px , starting point X must be somewhere between 0 and -100. If it’s below -100 then there is no need to draw that tile , player can’t see it anyway and if it’s above 0 , then we’ll have empty space at the left of that tile , right?
Now let’s have a look at code ;
public class TiledBackground : IBackground { private readonly Texture2D _texture; readonly int _horizontalTileCount; readonly int _verticalTileCount; public Vector2 _startCoord; public TiledBackground(Texture2D texture, int environmentWidth, int environmentHeight) { _texture = texture; _horizontalTileCount = (int) (Math.Round((double)environmentWidth / _texture.Width) + 1); _verticalTileCount = (int) (Math.Round((double)environmentHeight / _texture.Height) + 1); _startCoord = new Vector2(0, 0); } public void Update(Rectangle _cameraRectangle) { _startCoord.X = ((_cameraRectangle.X / _texture.Width) * _texture.Width) - _cameraRectangle.X; _startCoord.Y = ((_cameraRectangle.Y / _texture.Height) * _texture.Height) - _cameraRectangle.Y; } public void Draw(SpriteBatch spriteBatch) { for (int i = 0; i < _horizontalTileCount; i++) { for (int j = 0; j < _verticalTileCount; j++) { spriteBatch.Draw(_texture, new Rectangle( (int)_startCoord.X + (i * _texture.Width), (int)_startCoord.Y + (j * _texture.Height), _texture.Width, _texture.Height), Color.White); } } } }
It’s pretty close to that simpler Fixed Background actually. We have a texture , a starting point and tile counts for each dimension. We set them all in constructor and then update starting coordination in the update method. I didn’t update tile count ( which depends on screen size ) here as an example. As I said in Fixed Background , it’s optional. Now if we change screen size during game , this background won’t be able to resize according to new screen.
Anyway , as I said, in update method we update starting coordination. Note that _cameraRectangle is the place where camera is looking at that given moment. We have a 4000×960 environment and our game screen is just 766×500 , so yes starting points of the camera rectangle changes as player moves.
(_cameraRectangle.X / _texture.Width) will give you the required tile count from absolute 0,0 to the top left of camera.
((_cameraRectangle.X / _texture.Width) * __texture.Width) will give you the X position of our first tile.
(((_cameraRectangle.X / _texture.Width) * __texture.Width) – _cameraRectangle.X) will give you the X position of our first tile relative to camera ( or relative to the (0,0) you have on your screen ) , or in other words it’s where we should draw the tile.
Draw method is pretty straight forward , it just draws a 2D matrix on screen.
And now it should look something like this;
Yes I know it looks horrible but it’s all because of the terrible textures I’m using , nothing related to the code. Hopefully I’ll have time and patience to find better textures for future blog posts. Who would want to try out such a bad looking sample right? It’s not really encouraging…
What’s next? Hmm I guess something like Flowing Background or I guess people call it Parallaxing Background eh? Anyway hope you like this one!
Visual Studio 2010 Solution
Oh and in case you’re curious about the solution name ; Code Monkey
To my understanding, your formulas in update will always return (0,0), right? Let's look at the formula:
(((_cameraRectangle.X / _texture.Width) * _texture.Width) – _cameraRectangle.X)
If you look at the first three terms, you get this:
cameraRectangle.X * (_texture.Width/_texture.Width) = cameraRectangle.X
So then by subtracting the same thing you'll go back to 0.
I'm really sorry for the late reply, there must be something wrong with my notification system 🙁
Anyway, I believe the point you're missing here is that (_cameraRectangle.X / _texture.Width) will return an integer, not a double. So (5/3) will return 1.
Considering that, (5/3)*3 – 5 will yield -2, not 0.
And that means, if you tile texture width is 3 and your cam is at 5, you should start drawing your first tile at -2.
I hope it was clear, please let me know if it's not
I'm really sorry for the late reply, there must be something wrong with my notification system Frown
Anyway, you're right I had to clarify it but (_cameraRectangle.X / _texture.Width) will return an integer, not a double. So (5/3) will return 1.
Considering that, (5/3)*3 – 5 will yield -2, not 0.
And that means, if you tile texture width is 3 and your cam is at 5, you should start drawing your first tile at -2.
I hope it was clear, please let me know if it's not