Tagged: xna Toggle Comment Threads | Keyboard Shortcuts

  • danielsaidi 10:22 pm on February 22, 2010 Permalink | Reply
    Tags: , ai, , , random path, shortest path, , xna   

    Find the shortest path between two tiles in a grid in XNA 

    I am currently developing an adventure board game in XNA, in which the players can play various missions that take place on dynamic boards that are made up of tiles.

    In the game, players should be able to select to which tile they want to move their game piece. The game should then suggest the shortest possible path to reach that tile.

    The path finding algorithm will also be used by the game to move the various enemies that are to be controlled by the computer, thus providing a primitive form of AI.

    To improve the AI illusion a bit, the algorithm should also be able to select a random path if multiple options exist, which will give the enemies a random, unpredictable (well, sort of) behavior.

    Before I describe the method, let’s just recap a bit.

    Board movement

    In the game, players and computer controlled enemies can move horizontally and vertically over the board.

    Board movement example

    Factors that limit whether or not a game piece can move from one tile to another (tile A to tile B) are (so far):

    • Tile B simply does not exist (the piece would move outside of the board boundaries)
    • Tile B is marked as a None or a Nonwalkable tile (see below)
    • Tile B belongs to another room and is separated from tile A by a wall (covered in the previous post)
    • Tile B is occupied by another piece or furniture (another piece cannot stop at the same tile)
    • Tile B is occupied by a player or enemy (players and enemies cannot walk past eachother)

    All these rules are (in my game) handled by a single Tile class function – x.CanMoveTo(Tile y). This function is not presented here, since it is very specific to my game.

    Tile types

    I have chosen to limit myself to three different tile types:

    • None (tile has no properties and is ignored)
    • Nonwalkable
    • Walkable

    The None type is not really needed, but I have kept it to separate tiles that are not to be considered as part of the game board from tiles that just cannot be entered.

    Path finding overview

    Consider the following map, where a player stands on the green tile and wants to move to the red tile:

    A player on the green tile wants to move to the red tile

    In the example above, multiple “shortest paths” exist. As we will see later, the method presented below will find a random path each time it is executed.

    The method presented below has the following main steps:

    1. Beginning at the start tile, set its “path length” to zero.
    2. Recursively handle each sibling, according to the following:
      • If the sibling has not been handled yet, set its path length
      • If the sibling has already been handled, do it again if the new path length is shorter
    3. When no tile can be improved, start at the end tile and find the shortest path to the start tile

    I call the two main procedures spreading (a tile spreads its path length to its siblings) and tracing (trace the shortest path that has been found during the spreading operation).

    Step 1: Spreading

    In my game, the path finding operation is started with the following Board class function :

       //Placeholder for the calculated path (not thread safe :)
       int[,] pathLengths;
       public List<Tile> FindPath(Tile startTile, Tile endTile)
          //Abort if start or end tile is null
          if (startTile == null || endTile == null)
             return new List<Tile>();
          //Abort if the end tile is non-stoppable
          if (!endTile.IsStoppable)
             return new List<Tile>();
          //Initialize the path length array
          pathLengths = new int[Tiles.GetLength(0), Tiles.GetLength(1)];
          for (int y = 0; y < pathLengths.GetLength(1); y++)
             for (int x = 0; x < pathLengths.GetLength(0); x++)
                pathLengths[x, y] = int.MaxValue;
          //Begin at the start tile
          pathLengths[startTile.BoardPosition.X, startTile.BoardPosition.Y] = 0;
          //Once done, backtrack from the end tile
          List<Tile> result = FindPath_Trace(endTile);
          //Only return the path if it contains the start tile
          if (result.Contains(startTile))
             return result;
          return new List<Tile>();

    This function corresponds to the first part of the numbered list above. We do not proceed if any tile is null or if the end tile cannot be stopped at. We then initialize the placeholder array with max values, then begin our spread operation at the start tile.

    Once the spread operation is done, we trace the path, but let’s return to that part later.

    As you can see, the function only returns a path if it contains the start tile. If the trace operation cannot reach the start point, we cannot reach the end point from the start point, and thus no path should be returned.

    The spread operation is a recursive one that eventually will handle each tile at least once. It consists of two functions:

       private void FindPath_Spread(Tile tile)
          FindPath_Spread(tile, tile.TopSibling);
          FindPath_Spread(tile, tile.LeftSibling);
          FindPath_Spread(tile, tile.RightSibling);
          FindPath_Spread(tile, tile.BottomSibling);
       private void FindPath_Spread(Tile tile, Tile target)
          //Abort if any tile is null
          if (tile == null || target == null)
          //Abort if no movement is allowed
          if (!tile.CanMoveTo(target))
          //Get current path lengths
          int tileLength = FindPath_GetPathLength(tile);
          int targetLength = FindPath_GetPathLength(target);
          //Use length if it improves target
          if (tileLength + 1 < targetLength)
             pathLengths[target.BoardPosition.X, target.BoardPosition.Y] = tileLength + 1;

    Beginning at the start tile (which has a path length of zero), the path info is spread out to the tile siblings, but is only regarded if the sibling’s path length would be improved. Thus, the recursive operation will stop once all tiles are “as good as they can be”. When this happens, the spread operation will automatically stop.

    Step 2: Tracing

    Once the spread operation is done, the recursive trace operation will be started, which we could see in the FindPath function above.

    The trace operation will start at the end tile and follow the shortest way back in an attempt to reach the start tile. If the start tile cannot be reached, no path exists between the two tiles. The operation will then return an empty list.

    The trace operation consists of a single function:

       private List<Tile> FindPath_Trace(Tile tile)
          //Find the sibling paths
          int tileLength = FindPath_GetPathLength(tile);
          int topLength = FindPath_GetPathLength(tile.TopSibling);
          int leftLength = FindPath_GetPathLength(tile.LeftSibling);
          int rightLength = FindPath_GetPathLength(tile.RightSibling);
          int bottomLength = FindPath_GetPathLength(tile.BottomSibling);
          //Calculate the lowest path length
          int lowestLength =
             Math.Min(rightLength, bottomLength))));
          //Add each possible path
          List<Tile> possiblePaths = new List<Tile>();
          if (topLength == lowestLength)
          if (leftLength == lowestLength)
          if (rightLength == lowestLength)
          if (bottomLength == lowestLength)
          //Continue through a random possible path
          List<Tile> result = new List<Tile>();
          if (possiblePaths.Count() > 0)
             result = FindPath_Trace(possiblePaths[RandomHelper.GetInt32(0, possiblePaths.Count())]);
          //Add the tile itself, then return
          return result;

    The FindPath_GetPathLength function is only a small function that I added in order to avoid duplicate code:

       private int FindPath_GetPathLength(Tile tile)
          if (tile == null)
             return int.MaxValue;
          return pathLengths[tile.BoardPosition.X, tile.BoardPosition.Y];


    If you consider the map that I presented at the beginning of this blog post, my game will parse it into a game board as is described in the previous blog post.

    This is how my game (for now) displays the tiles (walls are missing)

    In the image above, all tiles are walkable, to increase the number of “shortest” paths. However, it is not possible to walk to a dark tile from a light one, which means that the path must be concentrated to light grey tiles only. Note that the green and red tile are just highlighted display the start and end tile – in fact, they are also light grey!

    When I run my game (well, game-to-be) and press the A button, I generate a path between the green and red tiles. Below are displayed three example of resulting path suggestions:

    Three paths returned by the function

    The path finding operation is fast and can handle large board games. However, I doubt that it would be suitable for more complex games, where the “world” is not made up of square tiles of equal size.

    • brian 3:38 pm on February 23, 2010 Permalink | Reply

      as you said, brute force will work with a small board, but not one larger. this reminds me of problem 67 of projecteuler…


      (20 billion years!!)

      • danielsaidi 4:10 pm on February 23, 2010 Permalink | Reply

        Well…computers ARE getting faster, so maybe the 20 billion years can be reduced by half in a couple of years? 😉

        No, but you are absolutely correct. This algorithm will get the work done for my boards, but it can be optimized a LOT…much like the LOS/FOV problem I am working on right now. As the boards grow in size, better algorithms will be needed.

        Thanks for the link! Very interesting.

    • Jens 1:30 pm on March 12, 2010 Permalink | Reply

      Vilken jättepost, snyggt!

    • Flash Game License 11:20 am on December 27, 2011 Permalink | Reply

      This is a really useful explanation and a good practical example of such algorithm. Thanks!

    • Charles 3:28 am on April 17, 2012 Permalink | Reply

      Do you mind if I use your pathfinding and bitmap parsing code in a game I’m making? I’ll be sure to credit you.

  • danielsaidi 10:08 pm on February 21, 2010 Permalink | Reply
    Tags: bitmap, , image, texture, , xna   

    Generate a game board map from a bitmap in XNA 

    I am currently developing an adventure board game in XNA, in which the players can play various missions that take place on boards that are made up of horizontal tiles.

    This entry will describe how a bitmap file can be used to quickly create a mission map, but first some relevant side information.

    Board movement

    In the game, players and monsters can move horizontally and vertically over the board:

    Board movement example

    Game pieces can move horizontally and vertically

    Factors that limit whether or not a game piece can move from one tile to another (tile A to tile B) are (so far):

    • Tile B simply does not exist (the piece would move outside of the board boundaries)
    • Tile B is marked as a None or a Nonwalkable tile (see below)
    • Tile B belongs to another room and is separated from tile A by a wall
    • Tile B is occupied by another piece or furniture (another piece cannot stop at the same tile)
    • Tile B is occupied by a monster (players cannot pass through such a tile)

    Tile types

    I have chosen to limit myself to three different tile types:

    • None (tile has no properties and is ignored)
    • Nonwalkable
    • Walkable

    When a tile is drawn later on, it can use any available texture, so the three tile types are more than sufficient.

    The None type is not really needed, since such a tile cannot entered. I have kept it to separate tiles that are not to be considered as part of the game board from tiles that just cannot be entered. There is an obvious conceptual difference between the two situations.

    Mission data

    In the game, players can chose to play one of a large set of missions. To make it easy to create a large number of missions, I have chosen to define them as such:

    • The mission board map is represented in a bitmap file
    • The mission data is specified in an XML file

    When a mission is loaded, the XML file is parsed into a mission object. The file points out the bitmap file, which is then parsed into a mission board map.

    The board map only specifies the board tiles and thus the look of the board, while the XML file specifies everything else, like the name of the mission, the mission targets, board items etc.

    The XML file content is the topic of a future post.

    Bitmap parsing overview

    Consider the following bitmap:

    An example of how a mission map (really small, though) can look.

    When the mission is initialized, it goes through the following steps:

    1. Parse the XML file (not covered in this post)
    2. Parse the bitmap into a mission board
    3. Initialize each tile using the corresponding color in the bitmap
    4. Divide the board tiles into rooms

    I have chosen to handle bitmap colors as such:

    • Black corresponds to the None tile type
    • The light grey at position (0,2) and (0,3) – #c3c3c3 – is specified in-code to be handled as a Nonwalkable tile
    • All other colors are regarded to be Walkable tiles

    The colors will later be used to determine which image that will be used for each tile:

    • Black tiles are not handled at all (they are None, remember?), thus they have no image
    • All other colors are converted into hex code (e.g. #ffffff instead of White) handled as such:
      • If one or several content textures have the hex code in their names (e.g. ffffff_1.png, ffffff_2.png etc.)  one is selected by random
      • If no corresponding image exists, the color is used to tint a random ffffff_x.png image

    Finally, when all tiles have been initialized with a type, an image and, perhaps, a tint, they are divided into rooms. Adjacent tiles that have the same color are considered to belong to the same room. When the board is then drawn, I make the game draw walls between all rooms.

    The process is really straightforward, as can be followed below. I have chosen not to include code for the Board and Tile classes that are mentioned below, since my classes are quite complex and I just want to describe the brief concept of how I parse the bitmap.

    Step 1: Initialize the mission board

    In my data model, the Board class has an Initialize function that takes an image as a parameter.

            public void Initialize(Texture2D image)

    As you can see, the function only consists of two sub-operations. First, all tiles are fully initialized, then the board is divided into rooms.

    Naturally, the bitmap has already been imported into the game content collection, which is why it is available here 🙂

    Step 2: Initialize each tile

    The InitializeTiles function really does nothing more than converting the texture into an array of colors, then applies each color the corresponding tile, as such:

            private void InitializeTiles(Texture2D image)
                //Convert image to colors
                Color[] colors = new Color[image.Width * image.Height];
                //Initialize the board tile matrix
                Tiles = new Tile[image.Width, image.Height];
                //Initialize each tile in the grid
                for (int y = 0; y < Tiles.GetLength(1); y++)
                    for (int x = 0; x < Tiles.GetLength(0); x++)
                        Tiles[x, y] = new Tile(colors[x + y * image.Width]);

    In this example, I have reduced the numbers of parameters in the Tile constructor, to make the code easier to read.

    In my game, the Tile constructor uses the provided color to set the Image, Type and Tint of the tile. However, since this is really specific to my game, I have decided to leave the constructor code out of this post. Just drop a comment if you want to take a look at it.

    Step 3: Divide the board tiles into rooms

    Once the board has a grid of tiles, where each tile has a color, my game divides the tiles into rooms, using three functions.

    The parameterless InitializeRooms function makes sure that all tiles are handled, at least once:

            private void InitializeRooms()
                for (int y = 0; y < Tiles.GetLength(1); y++)
                    for (int x = 0; x < Tiles.GetLength(0); x++)
                        InitializeRooms(Tiles[x, y]);

    The InitializeRooms function that takes a tile as a parameter, makes sure that the tile fetches the room index from any already initialized adjacent tile that belongs to the same room (if any), sets a new room index if needed then finally spreads the room index to all non-initialized adjacent tiles that belong to the same room (once again…if any).

    For now, my function uses two sub-functions that cleans up the code a bit:

            private void InitializeRooms(Tile tile)
                //Abort if no tile or if already checked
                if (tile == null || tile.RoomIndex.HasValue)
                //Set negative room index if no tile or non walkable
                if (tile.TileType == TileType.None || tile.TileType == TileType.Unwalkable)
                    tile.RoomIndex = -1;
                //Fetch room number from similar siblings
                InitializeRooms_Fetch(tile, tile.TopSibling);
                InitializeRooms_Fetch(tile, tile.LeftSibling);
                InitializeRooms_Fetch(tile, tile.RightSibling);
                InitializeRooms_Fetch(tile, tile.BottomSibling);
                //Set room number if none has been set
                if (!tile.RoomIndex.HasValue)
                    tile.RoomIndex = roomIndex++;
                //Spread room number to similar siblings
                InitializeRooms_Spread(tile, tile.TopSibling);
                InitializeRooms_Spread(tile, tile.LeftSibling);
                InitializeRooms_Spread(tile, tile.RightSibling);
                InitializeRooms_Spread(tile, tile.BottomSibling);
            private void InitializeRooms_Fetch(Tile tile, Tile sibling)
                //Abort if either tile is null
                if (tile == null || sibling == null)
                //Fetch index if the tiles have the same color
                if (sibling.RoomIndex.HasValue && tile.Color == sibling.Color)
                    tile.RoomIndex = sibling.RoomIndex;
            private void InitializeRooms_Spread(Tile tile, Tile sibling)
                //Abort if either tile is null
                if (tile == null || sibling == null)
                //Spread by initializing the sibling
                if (tile.RoomIndex.HasValue && tile.Color == sibling.Color)


    The steps above are all you really need to parse a bitmap into an image.

    In the game that generated the final result below, I have 12 “ffffff” images and 4 “c3c3c3” images. I have no images for the red color and no images for the darker grey rooms, which is why they use the same images as is used in the hallway and apply a tint above them.

    In the image, I also display the room index. As you can see, all non and non-walkable tiles are given the room index -1, while all other tiles are divided into room 1-4.

    Mission map with a 6×6 grid of tiles with images, types and room indices

    If I want to create a totally different map for another mission, or simply edit the map above, this approach makes it reaaaaally easy to do so.

    I will return to the XML file in another blog post, as well as how to draw walls between the various rooms, how to use different colors/images for the same rooms etc. etc. etc. But all that will have to wait for another day 🙂

    I have chosen to limit myself to three different tile types:

    • None
    • Nonwalkable
    • Walkable
  • danielsaidi 5:18 pm on February 21, 2010 Permalink | Reply
    Tags: , , , , xna   

    A board game with tiles in XNA 

    During my experimenting with the XNA framework, I am currently developing a board game where the game board is made up of a grid of square shaped tiles, over which player and monster pieces can move horizontally and vertically.

    Board movement example

    Game pieces can move horizontally and vertically

    So far, the board/tile model have presented these three main problem areas:

    • What is the best way to parse a bitmap into a game board, to make it easy to create dynamic mission maps?
    • Given a set of walkable and non-walkable tiles, how do I find the shortest path from tile A to tile B?
    • How can I determine if tile A is visible from tile B?

    I have successfully solved the first two problems, of which the first is presented in the next post.

  • danielsaidi 1:46 am on February 21, 2010 Permalink | Reply
    Tags: , , xna   

    A* algorithm implemented for XNA 

    I have implemented a dynamic A* algorithm, which will find the shortest way through a grid of tiles. If multiple possible paths exist, the algorithm will choose a random path, which adds to the random behavior of any computer character that is to be given an illusion of intelligence.

    I will publish the algorithm source code here as soon as I have rewritten it a bit. I will then begin working on an algorithm that will determine the line of sight from a tile in the grid…that is, which tiles can I see if I stand on a certain tile?

    If anyone have implemented such an algorithm, feel free to help me out.

  • danielsaidi 11:57 pm on February 16, 2010 Permalink | Reply
    Tags: , pathfinding, , xna   

    A* implementation for XNA 

    For a while now, I have played around with the XNA framework to get a grasp at how to develop games for the XBOX 360. It is great fun, but quite different from the development I usually do.

    I now have a working game stub with an object model that grows and grows and that feels really good. I can generate missions from an image/text file tuple, where the image describes the game board of the mission and the text file describes the mission, items on the board, goals etc.

    This setup makes it really easy to quickly develop a large amount of missions that can be divided into multiple campaigns. By making the base model solid, new missions will be content management than programming.

    When my game imports the various game board maps, it generates a grid of walkable and unwalkable tiles, of which the game board consists. The next step is now to be able to find the shortest way from one tile to another.

    I found this great tutorial and will use it to implement the A* algorithm, which hopefully will take care of this for me:


  • danielsaidi 2:33 am on January 26, 2010 Permalink | Reply
    Tags: assets, content, folder, xna   

    Load all content files in a folder in XNA 

    After playing around with XNA for a little while, I quickly realized how tedious it is to load all content manually, especially if the project uses a lot of images, sounds, textures etc.

    The method below is an extension method that can load all files within a folder into any content type. The method requires that the specified folder is relative to the Content.RootDirectory folder.

    public static Dictionary<String, T> LoadContent<T>(this ContentManager contentManager, string contentFolder)
       //Load directory info, abort if none
       DirectoryInfo dir = new DirectoryInfo(contentManager.RootDirectory + "\\" + contentFolder);
       if (!dir.Exists)
          throw new DirectoryNotFoundException();
       //Init the resulting list
       Dictionary<String, T> result = new Dictionary<String, T>();
       //Load all files that matches the file filter
       FileInfo[] files = dir.GetFiles("*.*");
       foreach (FileInfo file in files)
          string key = Path.GetFileNameWithoutExtension(file.Name);
          result[key] = contentManager.Load<T>(contentManager.RootDirectory + "/" + contentFolder + "/" + key);
       //Return the result
       return result;

    The function extends the ContentManager class, and can, for instance, be used by the main Game class like this:

    • var textures = Content.LoadContent<Texture2D>("Textures");
    • var models = Content.LoadContent<Model>("Models");
    • var songs = Content.LoadContent<Song>("Songs");
    • ...

    The method returns  a dictionary, so if you want to access the “warrior” model in the models dictionary, you just have to access it as such:

    • var warriorModel = models["warrior"];

    I am having some problems with pasting in code fragments into this blog, so if anyone can give me some tips for good plugins that can be used at wordpress.com, feel free to do so 🙂

    • EriX 1:17 am on August 10, 2010 Permalink | Reply

      This is great! thanks soooo much for that info 🙂

    • Gradius V 10:32 pm on February 8, 2011 Permalink | Reply

      Thanks for this tip =)

    • Cpt Fresh 7:06 pm on March 23, 2011 Permalink | Reply

      Thanks, this was actually exactly what I needed.

    • TXG1152 5:20 am on April 27, 2011 Permalink | Reply

      Thanks for the great solution. One note though, for XNA 4 it seems that the asset name in ContentManager.Load(asset) is relative to the content loader root directory. You will need to take contentManager.RootDirectory + “/” + out of the call or you will get an error. Try this instead:

      result[key] = contentManager.Load(contentFolder + “/” + key);

    • Andy 8:59 am on October 26, 2011 Permalink | Reply

      Thanks, nice tip, but i have a question:
      How can i convert this result (Dictionary) into Dictionary for example?
      Dictionary buttonList;
      buttonList = result; <— Error because of the conversion
      thank in advance =)

    • Andy 9:02 am on October 26, 2011 Permalink | Reply

      Hmm there was a problem displaying my question properly:
      “Dictionary(String, Texture2D) buttonList;”
      “Dictionary(String, T) result;”
      “buttonList = result;”

      • danielsaidi 9:37 am on October 26, 2011 Permalink | Reply

        Wow, I have not looked at this for a long while 🙂 However, let’s begin with – what is T in your example? Can T be cast to Texture2D?

    • Andy 10:32 am on October 26, 2011 Permalink | Reply

      T can be a Texture2D, SpriteFont or Sound or whatever
      here another example:
      buttonList is a Dictionary with String, Texture2D
      fontList is a Dictionary with String, SpriteFont
      and result is a Dictionary with String, T and T can be: SpriteFont, Texture2D, Sound
      case “Other”: buttonList = result;
      case “Font”: fontList = result;

    • danielsaidi 10:37 am on October 26, 2011 Permalink | Reply

      I may be wrong, but in order to make your code work, you need to constraint T so that it always can be cast to an Texture2D…at least for the example you provided. You can do this by adding “where T : Texture2D”. If T can be anything in your code, then buttonList will not be compatible with result.

      • Andy 11:29 am on October 26, 2011 Permalink | Reply

        “If T can be anything in your code, then buttonList will not be compatible with result.”
        yeah and i wanted to implement a qucik and nice solution for this problem,
        so that i dont have to look for the type of T , if its a Texture or Sound or a Font….
        do u have any idea how to convert this without using if or switch?

    • Victor 2:50 pm on November 3, 2011 Permalink | Reply

      Humm, i had to make the following changes before it works for me:

      result[key] = contentManager.Load(contentFolder + “\\” + key);

      • Victor 2:50 pm on November 3, 2011 Permalink | Reply

        my folder structure: Content\Set1\myFile.png

    • Steven 5:45 pm on March 6, 2012 Permalink | Reply

      This is exactly what I need, but I have a problem. I added the code as you wrote it and get no compilation errors. When I run the program I get a ContentLoadException, file not found, with result[key] = content.load &lt t>(contentManager.rootDirector + “/” + contentFolder + “/” + key);. I’ve tried making changes listed here but the same error comes up. I can’t figure it out. And the thing that baffles me the most is the fact that when I look at the file in the foreach loop, it shows that the file exists at that location.

      • Steven 10:54 pm on March 6, 2012 Permalink | Reply

        Thanks for the help that would have been given. I finally managed to figure out my issue. I has some weird things going on with my content directory, but a new project and it works fine. Thanks for the code.

        • danielsaidi 10:15 pm on March 9, 2012 Permalink

          Glad that it worked out! This was such a long time ago that it is great to hear that it is still a working way to handle content 🙂

  • danielsaidi 6:47 am on January 25, 2010 Permalink | Reply
    Tags: xna   

    Doing my first XNA tutorial 

    After looking at my friend Jens’ XNA game framework, which really helps you get started with your XNA games, I think I’d benefit from reading a tutorial or two. The game project type is quite different from other types of software I usually develop, so I’d better read something before getting my hands dirty.

    I will start off with the Microsoft tutorial for getting a small 2D game up and running, with a bouncing sprite:


    After that, I will probably do a couple of more tutorials with 3D models and a bit more advanced functionality, before continuing with my “which-I-thought-would-be-quite-easy” game project.

  • danielsaidi 8:35 am on January 4, 2010 Permalink | Reply
    Tags: kobingo, , xna   

    XNA – A first glance… 

    Two days ago, I started looking at XNA, which I’ve been longing to do for quite som time now. After installing Visual C# Express 2008 and XNA 3.1, my friend Jens came over to guide me some of the basics.

    After two minutes or so, we happily loaded a texture by pressing space. Although I’m sure that XNA has more to offer than this, I’m glad to be on my way to creating a crappy game that no one will ever download 😉

    Jens, on the other hand, has created quite a few nice games for XNA on his profile – kobingo. Make sure to check them out if you have an XBOX – especially the nice Painting Party 🙂

Compose new post
Next post/Next comment
Previous post/Previous comment
Show/Hide comments
Go to top
Go to login
Show/Hide help
shift + esc