Using sandbags to control avatar movement

When designing a game, it is important to define what areas the player’s avatar can access. This lets you define the outer bounds for the map, as well as mark off blocked areas, such as spaces occupied by a chest, a building or a tree.

In my work developing Armadillo, I experimented with the concept of a sandbag grid to aid me in this. In this multi-dimensional array, each cell represents an X,Y coordinate, with a value indicating whether or not that square is accessible in the game.

At it’s most basic, this grid only needs to store a boolean value, but I am re-using this grid for other purposes that require more granularity and chose to use a byte instead. This generic grid object can be used to determine movement range for characters, targeting zones for spells, or splash damage for big attacks.

public struct Grid
{
    public Rectangle Size;
    
    public byte[,] Weight;
    
    public Grid(int x, int y, byte defaultValue = 0)
    {
        // build the grid according to the sizes specified
        Size = new Rectangle(0, 0, x, y);
        Weight = new byte[x, y];
        
        // prepopulate the grid with the default byte value
        for(var i = 0; i < x; i++)
        {
            for (var j = 0; j < y; j++)
            {
                Weight[i, j] = defaultValue;
            }
        }
    }
}

You may be running the numbers in your head - a 3000 pixel wide, 2000 pixel high zone, with a byte per cell, takes up 5.7mb just to know if you can enter a space! While 5.7mb is not a lot on modern systems, using 5.7mb for this purpose is hard to justify.

How can we trim that down? By scaling the sandbag, we can drastically reduce the memory footprint. How often do we need to know about every single pixel on screen? The avatar is more than a pixel in size, afterall. By scaling it down to a 1:6 ratio, we cut that size down by 98%.

In a 2D game like Armadillo, the sandbag is just a miniature, chopped up version of the basic zone image. White areas represent moveable spots, and non-white areas represent blocked off spots. In my testing of Armadillo, I have been using Kakariko Village from The Legend of Zelda: Link to the Past for the SNES. You can see the full map and the sandbag map to the side.

And since this sandbag is just an image, a simple function can convert this image into a sandbag grid, allowing you to quickly create sandbag images from maps, and sandbag grids from images.

public static Grid FromBitmap(string bitmapName)
{
    // use XNA's content manager to load the bitmap
    var texture = Game.Content.Load<Texture2D>(bitmapName);
    var grid = new Grid(texture.Width, texture.Height);

    // loop through the cells in the bitmap
    for (var i = 0; i < grid.Size.Width; i++)
    {
        for (var j = 0; j < grid.Size.Height; j++)
        {
            // get the average RGB value at this point
            var c = new Color[1];
            texture.GetData(0, new Rectangle(i, j, 1, 1), c, 0, 1);
            grid.Weight[i, j] = (byte)((c[0].R + c[0].G + c[0].B)/3);
        }
    }

    return grid;
}

Now you just need a function to test if your avatar can enter the section on the sandbag to determine if they are trying to step into a valid spot. All of this combines to give you a very easy way to mark off the parts of a map that your avatar can access. With only a little bit of work, you can turn a map into a fully traversable zone without a large memory footprint.

private bool IsValidLocation(Rectangle rect)
{
    // scan through the x values of the submitted rectangle
    for(var x = rect.X; x < rect.X + rect.Width; x += 6)
    {
        // scan through the y values
        for(var y = rect.Y; y < rect.Y + rect.Height; y += 6)
        {
            // sandbag grids have a 1:6 scaling
            // check if this spot is acceptable
            if (Zone.Sandbag.Weight[x / 6, y / 6] < 255)
            {
                return false;
            }
        }
    }

    // all areas were acceptable
    return true;
}

The last thing that needs to be done, but I will not cover here, is taking parts of the map and breaking them down into other layers so that the avatar can walk beneath them. For instance, the Kakariko Village arch in the bottom right has to be pulled into a separate image so that my character can appear to be walking underneath it.