Skip to content

Instantly share code, notes, and snippets.

@MerliMejia
Created June 27, 2021 16:46
Show Gist options
  • Save MerliMejia/26a9a61efc9cea80155ea129f83619d0 to your computer and use it in GitHub Desktop.
Save MerliMejia/26a9a61efc9cea80155ea129f83619d0 to your computer and use it in GitHub Desktop.
2D Tiled Based Map Generator For Unity3D
/**
This is a humble approach for creating a random map generator for my game.
Please notice that I did not try my best here and this could definitely be way better...
I just wanted to have some fun while creating a random map generator.
*/
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
//What kind of "point or tile" it is.
//A Point represents a single square or tile.
//It starts at 1 because 0 it's the very first tile layer or "dirt".
public enum PointType
{
rock1 = 1,
rock2,
tree1,
tree2,
}
//Represents a single tile or "square".
[System.Serializable]
public class Square
{
//What kind of obstacle it is.
public Obstacles obstacle;
//How much of it will be drawn.
public int maxCount;
}
public class MapGenerator : MonoBehaviour
{
//Reference to the tilemap in which we're drawing.
public Tilemap tilemap;
//So I can work with different configurations.
public List<Square> obstacles;
//I'm using this one because I don't want to lose the "max_count" of obstacles added in the inspector.
List<Square> obstaclesToWork;
//Map width
public int widht;
//Map height.
public int height;
//To save all the generated data and use it later.
public int[,] mapData;
//Just to know when it should generate stuff.
[HideInInspector]
public bool isInitialized = false;
//This is usefull for getting the obstacle's tile faster.
private Dictionary<int, Tile> tileDictionary = new Dictionary<int, Tile>();
//This method shuffles an array of integers.
//Not sure if it's the best Suffle approach but it gets the job done.
public void Shuffle(System.Random rng, int[,] array)
{
for (int i = 0; i < array.GetLength(0); i++)
{
for (int a = 0; a < array.GetLength(1); a++)
{
int randI = rng.Next(i);
int randA = rng.Next(a);
int temp = array[i, a];
array[i, a] = array[randI, randA];
array[randI, randA] = temp;
}
}
}
//Let's start the process.
public void Init()
{
//We initialize stuff.
mapData = new int[widht, height];
tileDictionary = new Dictionary<int, Tile>();
tilemap.ClearAllTiles();
obstaclesToWork = new List<Square>();
obstacles.ForEach((o) =>
{
Square s = new Square();
s.obstacle = o.obstacle;
s.maxCount = o.maxCount;
obstaclesToWork.Add(s);
});
//We fill the map with random obstacles.
for (int i = 0; i < widht; i++)
{
for (int a = 0; a < height; a++)
{
//Random point type;
int data = Random.Range(1, System.Enum.GetNames(typeof(PointType)).Length + 1);
mapData[i, a] = data;
}
}
//We can know how many tiles we need to keep based on the maxCount of each obstacle.
int objectsToKeep = 0;
foreach (Square s in obstaclesToWork)
{
objectsToKeep += s.maxCount;
tileDictionary.Add((int)s.obstacle.type, s.obstacle.tile);
}
//Here we work with the dirt objects. Wich is normally the ground and first layer of tiles.
int dirtAdded = 0;
int[,] dirtArray = mapData;
//We fill the dirt array with... dirt.
for (int i = 0; i < mapData.GetLength(0); i++)
{
for (int a = 0; a < mapData.GetLength(1); a++)
{
if (dirtAdded < mapData.Length - objectsToKeep)
{
//But we make sure to not add "dirt" if there's still more obstacles in the maxCount.
bool shouldAddDirt = true;
foreach (Square square in obstaclesToWork)
{
if (mapData[i, a] == (int)square.obstacle.type && square.maxCount > 0)
{
square.maxCount--;
shouldAddDirt = false;
break;
}
}
//Once we have skiped all the obstacles we can add "dirt".
if (shouldAddDirt)
{
dirtArray[i, a] = 0;
dirtAdded++;
}
}
else
{
break;
}
}
}
//At this point the data have all the dirt and obstacles we need, but only the first positions
//Of the array have obstacles and this is a *RANDOM* Map Generator... So we shuffle the data.
Shuffle(new System.Random(), dirtArray);
//And finally, we pass that random data to our mapData array.
mapData = dirtArray;
//Now, we use this random data to draw stuff like the tiles in the tilemap.
int x = 1;
int y = 1;
for (int i = 0; i < mapData.GetLength(0); i++)
{
y = 0;
for (int a = 0; a < mapData.GetLength(1); a++)
{
try
{
tilemap.SetTile(new Vector3Int(x, y, 0), tileDictionary[mapData[i, a]]);
}
catch (System.Exception)
{
}
y++;
}
x++;
}
isInitialized = true;
}
//To help me visualize stuff in the development process.
private void OnDrawGizmos()
{
if (isInitialized)
{
int x = 1;
int y = 1;
for (int i = 0; i < widht; i++)
{
y = 0;
for (int a = 0; a < height; a++)
{
int point = mapData[i, a];
DrawPoint(new Vector3(x, y), (PointType)point);
y++;
}
x++;
}
}
}
void DrawPoint(Vector3 position, PointType type)
{
if (type == 0)
{
Gizmos.color = Color.yellow;
}
if (type == PointType.rock1 || type == PointType.rock2)
{
Gizmos.color = Color.black;
}
if (type == PointType.tree2 || type == PointType.tree1)
{
Gizmos.color = Color.green;
}
Gizmos.DrawSphere(position, 0.1f);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(MapGenerator))]
[CanEditMultipleObjects]
public class MapGeneratorEditor : Editor
{
MapGenerator map;
private void OnEnable()
{
map = (MapGenerator)target;
map.isInitialized = false;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if(GUILayout.Button("INIT DATA"))
{
map.Init();
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
[CreateAssetMenu(fileName = "Obstacles", menuName = "Obstacles", order = 1)]
public class Obstacles : ScriptableObject
{
public PointType type;
public Tile tile;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment