Archive for January, 2013

2D terrain generation for XNA

Saturday, January 5th, 2013

This is a small program for generating tilebased landscapes in XNA.

The logic behind the random world

Even though this program only saves maps as 2D, the generation has some 3D data in it.

The map is stored as a double array of bytes:

byte[,] _map = new byte[50,50];    //the map (stores height for each tile)

Each tile stores the height of the tile (a value between 0 and 255), and to begin with they are all zero.

To begin the creation of the world, about one fifth of the cells receive a random height between zero and 255. This simulates a young planet dotted with tall mountains. All tiles below the ocean level (which is 10 - see lower left corner of program) are colored blue.

image

This initial generation is done using this bit of code

//create the new map
byte[,] map = new byte[width, height];

int allCells = width * height;  //figure out how many dots to begin with
int amountOfFirstFillCells = (int)( allCells * percentageFillAtStart);

//add random heights to all the wanted cells
for (int i = 0; i < amountOfFirstFillCells; i++)
{
    Point p =  map.GetRandomCellPosition();
    map[p.X, p.Y] = (byte)_rnd.Next(255);
}

The byte[,].GetRandomCellPosition() is an extensionmethod I've created to help with the mapgeneration. You can get all the code at the end of this article.

Smoothing

As time passes (the user right-clicks), the mountains are worn down by smoothing them, so hills are created at their feet. This is done by calculating the average of the height of all neighbors for every tile and then elevating or lowering the tile accordingly. Here you can see the landscape from initial values over six iterations of smoothing:

mapmaker

The smoothing is performed with this bit of code:

//for calculating the average value of neighbors of a tile:
float tileSum = 0;              //the sum of the height of the neighbors
float numberOfNeighbors = 0;    //the number of neighbors for this tile

//to store the smoothed version of the map
byte[,] destinationMap = new byte[50,50];

//get the neigbors and go through them
foreach (var neighbor in sourceMap.GetNeighborContents(x, y))
{
    numberOfNeighbors++;                        //increase the number of neighbors found
    tileSum += neighbor;                        //and store the new sum of heights
}

//calculate the average of all neighbors
float averageForNeighbors = tileSum / numberOfNeighbors;

//find out what the difference between this tile and the neighbor average is
float difference = averageForNeighbors - sourceMap[x, y];

//introduce a little randomness
float randomPct = Math.Abs(difference * .1f ) * (_rnd.Next(6) -2);

//use a fifth of the difference to raise/lower + the randomness
destinationMap[x, y] = (byte) MathHelper.Clamp( (sourceMap[x, y] + difference * .2f + randomPct), 0, 255);

The most important thing to notice is that we are using a new doublearray for storing the values of the smoothed tiles as we go over the map. The reason for this is that if we updated the values of each tile in the original doublearray as we went along, then the neighbortiles would get an average calculated from the already smoothed tiles, and we would skew the distribution of smoothing. In the code I swap back and forth, calculating from one doublearray and putting into the other, and then vice versa.

Drawing

When drawing the map, all tiles that are higher than the current sealevel get a shade of green, the taller they are, the lighter it is.

The result

With CTRL+S the map is saved into a textfile for easy access from other platforms.

The width and height is stored at the top.

All the tiles that are higher than the current sealevel are stored as Xs and the rest as spaces.

mapmaker_notepad

Where to go from here?

This mapmaker only has the contours of the map. To populate the map with forests, swamps, deserts, etc. it would be pretty easy to make an algorithm that spreads these types of tiles out from random points on the map using flood fill algorithm. That would require that you store the different tiletypes, for example as integers.

Here's the code

The complete application (XNA 4.0, VS 2010).

Only the doublearray extensionmethods. (right-click > "save as...")

An extended version which can save heightmap and includes code for loading (but no support for it in the application)

"Small space shooter" – a Dogme 95 inspired game

Saturday, January 5th, 2013

Director Lars von Trier and Thomas Vinterberg once created a set of rules called "Dogme 95" for making a specific type of movies.

One of the rules is:

"Filming must be done on location. Props and sets must not be brought in."

I regularly browse through the new art at www.OpenGameArt.org and I came across the work of Kenney (particularly his very elegant Space Shooter Art).

image

The thought struck me: "I'll make a Dogme Game where I'm only allowed to use this set of artwork and nothing else".

I have allowed myself the following slack:

  • I am allowed to use text
  • I am allowed to draw Kenneys graphics using another color in the SpriteBatch.Draw() call
  • I am allowed to scale the artwork

So far, I am very happy with the result. I am getting quite a bit of feedback from friends' kids on what needs to be added, so every once in a while I update the project.

What it looks like so far

image

 

How to play

Steer this

player

using the arrow keys,

while firing these

laserGreen

using the space key, to hit these

enemyShip

who are firing these

laserRed

towards you (avoid them, they're bad for you!)

You can also try to hit these

meteorBig

which break into these

meteorSmall

some of them even have powerups (shield/more guns).

As you progress through the levels, more enemies appear, shooting faster, eventually with targeting shots.

Try it - play or get code

You can play it with or without installing it (using click-once-deployment).
You can download the project here (xna 4.0, vs 2010), there aren't a lot of comments, but variable names are pretty descriptive Smiley

Shuffling lists in C# – an extensionmethod

Friday, January 4th, 2013

This is an extensionmethod for shuffling generic lists.

shuffling-anim

I absolutely LOVE generics!

Back in the days of .net 1.0 ("yaaawn" - I know, but bear with me...) there was a class called CollectionBase which had an ArrayList (like a "List<Object>") inside, and to make your own strongly typed collection you would inherit from CollectionBase and implement a typed Setter and Getter. No more of that - phew!!  Thank you Microsoft!!

The sample project  gives a demonstration of shuffling four different List types:

List<int> _numbers;
List<string> _names;
List<Color> _colors;
List<Card> _cards;

How to use it

When you've added a reference to the ListShufflerExtensionMethods class, all Lists get a Shuffle() method. So you can call Shuffle on any type of List.

_numbers.Shuffle();
_names.Shuffle();
_colors.Shuffle();
_cards.Shuffle();

The Shuffle() method has an optional parameter numberOfTimesToShuffle where you can specify how many times to shuffle. The default value is 5.

Demonstration

Complete code

Here you can see the implementation. Extensionmethods have to be public, static methods on a public static class. Using the "this" keyword on the first parameter passed to a method you effectively add the method to that type (see line 16 below).

/// <summary>
/// Class for shuffling lists
/// </summary>
/// <typeparam name="T">The type of list to shuffle</typeparam>
public static class ListShufflerExtensionMethods
{
    //for getting random values
    private static Random _rnd = new Random();

    /// <summary>
    /// Shuffles the contents of a list
    /// </summary>
    /// <typeparam name="T">The type of the list to sort</typeparam>
    /// <param name="listToShuffle">The list to shuffle</param>
    /// <param name="numberOfTimesToShuffle">How many times to shuffle the list
    /// by default this is 5 times</param>
    public static void Shuffle<T>(this List<T> listToShuffle, int numberOfTimesToShuffle = 5)
    {
        //make a new list of the wanted type
        List<T> newList = new List<T>();

        //for each time we want to shuffle
        for (int i = 0; i < numberOfTimesToShuffle; i++)
        {
            //while there are still items in our list
            while (listToShuffle.Count > 0)
            {
                //get a random number within the list
                int index = _rnd.Next(listToShuffle.Count);

                //add the item at that position to the new list
                newList.Add(listToShuffle[index]);

                //and remove it from the old list
                listToShuffle.RemoveAt(index);
            }

            //then copy all the items back in the old list again
            listToShuffle.AddRange(newList);

            //and clear the new list
            //to make ready for next shuffling
            newList.Clear();
        }
    }
}

Source code

Solution file as ZIP

ListShufflerExtensions.cs (right click > save as...)

ListShufflerExtensions.zip

Taking screenshots in XNA

Friday, January 4th, 2013

imageWouldn’t it be nice if it was simple to add screenshot functionality to your games? People who play your games could easily post pictures online, gathering even more interest in and publicity for your game? Smiley

I’ve gathered some screenshot code I found on the web (credits in the code) in a little GraphicsDevice extension method. If you aren't familiar with extension methods, basically they add new functionality to existing classes - in this case adds two new methods to Microsoft's GraphicsDevice class.

class diagram

The class is here C# (right click > "save as..."), or ZIP.
Complete codesample here.

To use it :

  1. add the GraphicsDeviceExtensions class to your project
  2. call the GraphicsDevice.PrepareScreenshot() before the spriteBatch.Begin()
  3. call the GraphicsDevice.SaveScreenshot() after spriteBatch.End()

If you pass the SaveScreenshot method a filename or path then that filename will be used. If you don’t then screendumps will be saved in a file called Screenshot_001.png, Screenshot_002.png, etc. in the running game’s folder.

In the code sample below, the screenshot is prepared on line 9 and the screenshot is taken on line 34.

bool doScreenshot = false;  //set this to true to perform screenshot

protected override void Draw(GameTime gameTime)
{

    //if necessary, we prepare for a screenshot 
    if (doScreenshot)
    {
        GraphicsDevice.PrepareScreenShot();
    }

    //Clear graphicsdevice with blue background
    GraphicsDevice.Clear(Color.Orange);

    //begin drawing
    spriteBatch.Begin();

    //write test string
    spriteBatch.DrawString(_defaultFont, "Screenshot test!", new Vector2(100, 100), Color.Red);

    //write whether we are running in HiDef or Reach graphics profile
    spriteBatch.DrawString(_defaultFont, "Profile: " + graphics.GraphicsProfile,
        new Vector2(100, 200), Color.DarkRed);

    //call superclass' Draw()
    base.Draw(gameTime);

    //end drawing 
    spriteBatch.End();

    //if necessary, we save the image to a screenshot
    if (doScreenshot)
    {
        GraphicsDevice.SaveScreenshot();
        doScreenshot = false;
    }
}