Unity is a great way of creating quick prototypes which can evolve into great games with enough care and attention. This and its ever evolving editor is probably why I like it so much. In this post I am going to show you one simple way of creating a completely random square (or rectangle) of tiles. While this may not be amazing, it does introduce some concepts of random generation which can be used across all different types (Cellular autonoma, Maze and more). These types will be written up in the future.

I am assuming some experience with unity and some experience of C#

Part One

This is the first part in a multipart post on Procedural Generation. Find below links to further posts on this subject
1. Basic 2D Procedural Generation
2. Making sense of this noise <– Bug me to get this done please.

Note

I have omitted using statements from the code blocks. Using a decent IDE you should be suggested which ones to use, if you are blindly copying and pasting (which I do not suggest) be aware.

The Map

The concept of procedural generation can seem quite overwhelming to start off with, however once split down into some basic parts it becomes manageable. Probably the Most important part of this is going to be the Map class.

The map is where we are going to store the data required to draw our map to screen as well as some helpers (There may not be many of them in this post however future posts will require more of them).

public class Map {

    // Public Accessor for width.
    public int Width 
    { 
        get { return _width; }
        private set { _width = value; }
    }
    
    // Public Accessor for height.
    public int Height 
    { 
        get { return _height; }
        private set { _height = value; }
    }
    

    private int _width;
    private int _height;
    private int[,] _map;

    // Default constructor for the map.
    public Map(int width, int height)
    {
         _width = width;
         _height = height;
         _map = new int[_width,_height];
    }
    
    //Indexer for this class, this allows us to get direct coordinates without using another method.
    public int this[int index1,int index2]
    {
        get 
        {
            return _map[index1,index2];
        }
        
        set
        {
            _map[index1,index2] = value;
        }


    }
}

There is currently nothing Unity specific in this class, this is a simple object and could theoretically be moved into any c# project

As you can see we have set up a few members and methods to start off the map class.

  • _width and Width are private and public accessors respectively for getting the width when we set it using the constructor.
  • _height and Height is the same as above however for the height of the map.
  • _map and int this[int index1,int index2] are the private and public accessors for the map object we will use to store the map data.

int this[int index1,int index2] ... is an indexer, this means when we access the object rather than creating methods to access the map all we simply need to do is _map[width,height] to get the value at the given coordinates.

  • public Map(int width, int height) ... is the constructor for this object, this allows us to set up a map based on dimensions in one line.

This is simply all we need for the map class at this moment.

Generator

To make this system as extensible as possible, In future posts we will be using polymorphism so that we can use generator objects to create the maps. For now we are literally going to fill the map with random values. We are still interacting with data however I will be using unity’s random class, for the sake of using this code elsewhere the only lines which need to be changed would be any reference to the random object.

The first generator we will make will not be a generator as much but an outline of how we would like the generators to work.


public class Generator 
{
    public static int Seed = 0;

    public static void Generate(Map map)
    {
        Random.seed = Seed;
        for(int x = 0; x < map.Width; x++)
            for(int y = 0; y < map.Height; y++)
                map[x,y] = Random.Range(0,3); //this will return 0-2 as the second parameter is exclusive.
    }

}

This is the most simple class in its state at the moment, however this is where all the heavy lifting will be done later on.

Now all that needs to be done is to turn this map data into a rendered object you can see.

Map Renderer

The map renderer class will be used to display the maps on our scene. This will need to be attached to a game object, it will also contain our bindings for the data to the sprite (for simplicity for now). We will also contain a progress variable for aesthetic reasons.

The algorithm we will use will be something like the following;

  1. Starting from 0,0 get the data for the tile.
  2. Create that tile at a given start position.
  3. Repeat through until we have reached the max map height and map width.

This is a nice and simple algorithm for now.


public class MapRenderer : MonoBehaviour 
{
    public GameObject[] Sprites;

    public int SpriteWidth = 32;
    public int SpriteHeight = 32;

    public float RenderProgress = 0.0f; //a value between 0 and 1 will be returned.

    public void Start()
    {
        if(Sprites == null)
            Debug.LogError("You have not set any sprites in the editor");
    }


    public void Render(Map map, bool timelapse)
    {
        if(timelapse)
            StartCoroutine(RenderMapTimeLapse(map));
        else
            RenderMap(map);

    }

    public void RenderMap(Map map)
    {
        for(int x = 0; x < map.Width; x++)
        {
            RenderProgress = (x / map.Width) * x;
            for(int y = 0; y < map.Height; y++)
            {
                RenderAtCoordinate(map,x,y);
            }
        }
    }

    public IEnumerator RenderMapTimeLapse(Map map)
    {
        for(int x = 0; x < map.Width; x++)
        {
            RenderProgress = (x / map.Width);
            for(int y = 0; y < map.Height; y++)
            {
                RenderAtCoordinate(map,x,y);
                yield return new WaitForSeconds(0.1f);
            }
        }

    }

    public void RenderAtCoordinate(Map map,int x, int y)
    {
        Vector3 position = new Vector3(x * SpriteWidth,y * SpriteHeight,0);
        GameObject go = DataToSprite(map[x,y]);
        
        if(!go)
            return;
 
        Instantiate(go,position,Quaternion.Euler(Vector3.zero));

    }

    
    

    
    private GameObject DataToSprite(int mapCoordinateData)
    {
        GameObject sprite;
        switch(mapCoordinateData)
        {
            case 0:
                sprite = null;
                break; //we don't want to draw anything on 0 spots.
            case 1:
                sprite = Sprites[0]; //for me this will be my ground texture.
                break;
            case 2:
                sprite = Sprites[1]; //for me this will be my wall texture.
                break;
            default:
                sprite = null;
                break; //if its an unknown value we can get away with drawing nothing.
 
        }

        return sprite;
    }

    
}

While this is quite a large class compared to the others, its quite simple in implementation and strictly follows the algorithm above.

  • SpriteWidth and SpriteHeight are required and used to work out the positioning of the tile in the unity engine.
  • RenderProgress is a float which we can use to contribute to a progress bar.

The rest of the methods are all used to gather resources needed to render the map.

I included the time-lapse option so that we could visually see the map being rendered.

Finishing up

Debug Generator

Now all that needs to be done is to write the methods inside of another object you want to house all this, include all your scripts on an object and then assign the variables. I am going to be using a class called DebugMapManager to generate for now (in your case this could be in your level class).

public class DebugMapManager : MonoBehaviour
{
    public int Width = 50;
    public int Height = 50;
    public MapRenderer Renderer;
    public bool TimeLapse;

    private Map map;


    public void Start()
    {
        if(!Renderer)
            Renderer = GetComponent<MapRenderer>();

    }

    public void Update()
    {
        if(Input.GetKeyDown(KeyCode.G))
        {
           map = new Map(Width,Height);

           Generator.Seed = Random.Range(0,int.MaxValue);
           Generator.Generate(map);
           Renderer.Render(map,TimeLapse);
           
        }
    }

}

Once you have added the MapRenderer and DebugMapManager onto a GameObject via the inspector, all you need to do is set the variables up and you are done!

Click play on the Editor and then hit G. You will watch your map generate random tiles in-front of your eyes.

Random Timelapse Generation

Next time…

A big thank you again to OpenGameArt for supplying the resources, specifically this post

Find the completed code and assets here. I have left you put them together…


164 Downloads

The next post I make on this subject will be to generate something which could potentially be a bit more useful than random data. The purpose of this post was to just lay down some foundations for more structured random generation. Thank you for reading.