Skip to content

Instantly share code, notes, and snippets.

@artygrand
Created September 18, 2019 08:43
Show Gist options
  • Save artygrand/cc82e1b161ff43288b7c9cd9789f1042 to your computer and use it in GitHub Desktop.
Save artygrand/cc82e1b161ff43288b7c9cd9789f1042 to your computer and use it in GitHub Desktop.
Wrong dungeon generator
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Dungeon : MonoBehaviour
{
public static float[] MapSize;
[Header("World Generation Values")]
public GameObject BlockPrefab;
public GameObject ChunkPrefab;
public float BlocksPerTexture = 5f;
public float MapWidth;
public float MapHeight;
public string Seed;
public bool UseRandomSeed;
private string[,] map;
private Color[,][] lightMap;
private string[] blockTypes;
private GameObject blocksParent;
private Transform chunksParent;
private GameObject[,] chunks;
private GameObject blockPlaced;
private Vector3 rootPos;
private Vector2[][] blockBorders = new Vector2[256][];
private readonly int chunkSize = 32;
private readonly Dictionary<string, Vector2> textureOffset = new Dictionary<string, Vector2> {
{ "gold", new Vector2(0, 0.5f) },
{ "iron", new Vector2(0.5f, 0.5f) },
{ "copper", new Vector2(0.5f, 0) },
{ "gravel", new Vector2(0, 0) },
{ "dirt", new Vector2(0, 0) },
};
//private byte[] blockTypes;
void Start()
{
MapSize = new float[] { MapWidth, MapHeight };
rootPos = new Vector3(-MapWidth / 2, 0, 0);
map = new string[(int)MapWidth, (int)MapHeight];
lightMap = new Color[(int)MapWidth, (int)MapHeight][];
chunksParent = transform.Find("Chunks");
blocksParent = GameObject.Find("Dynamic");
blockPlaced = Instantiate(BlockPrefab, Vector3.zero, Quaternion.identity, blocksParent.transform);
blockPlaced.SetActive(false);
blockTypes = new string[textureOffset.Keys.Count];
textureOffset.Keys.CopyTo(blockTypes, 0);
FindBlockBordersTexture();
GenerateMap();
// or LoadMap();
CalcLightMap();
SpawnMap();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
var pos = Cursor.Position;
RemoveBlock((int)(Mathf.Round(pos.x) + (MapWidth / 2)), -Mathf.RoundToInt(pos.y));
}
}
void GenerateMap()
{
if (UseRandomSeed)
{
Seed = System.DateTime.Now.ToString();
}
System.Random random = new System.Random(Seed.GetHashCode());
string type;
string mainBlock;
for (int i = 0; i < MapWidth; i++)
{
mainBlock = "dirt";
for (int j = 0; j < MapHeight; j++)
{
if (random.Next(0, 100) > 20)
{
if (mainBlock != "gravel" && j > 24 && random.Next(0, 100) > 60 - i/3)
{
mainBlock = "gravel";
}
type = random.Next(0, 100) > 60 ? blockTypes[random.Next(0, blockTypes.Length-2)] : mainBlock;
}
else
{
type = null;
}
map[i, j] = type;
}
}
for (int i = 0; i < 1; i++)
{
SmoothMap();
}
}
private void SmoothMap()
{
for (int x = 0; x < MapWidth; x++)
{
for (int y = 0; y < MapHeight; y++)
{
int neighbourWallTiles = GetSurroundingCellCount(x, y);
if (neighbourWallTiles > 6)
map[x, y] = "dirt"; // TODO need same as surrounding
else if (neighbourWallTiles < 4)
map[x, y] = null;
}
}
}
private int GetSurroundingCellCount(int x, int y)
{
int near = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (i == x && j == y)
{
continue;
}
if (i < 0 || i > MapWidth-1 || j < 0 || j > MapHeight-1 || null != map[i, j])
{
near++;
}
}
}
return near;
}
private void CalcLightMap()
{
// floodfill from light source to 14 blocks
// https://minecraft.gamepedia.com/Light#Light_Spread
var lightlevel = 0.9f;
for (int i = 0; i < MapWidth; i++)
{
for (int j = 0; j < MapHeight; j++)
{
if (null == map[i, j])
{
continue;
}
bool found = false;
for (int x = i - 1; x <= i + 1; x++)
{
if (x < 0 || x >= MapWidth)
{
continue;
}
for (int y = j - 1; y <= j + 1; y++)
{
if (y < 0 || y >= MapHeight || (i == x && j == y))
{
continue;
}
if (null == map[x, y])
{
found = true;
}
}
}
lightlevel = found ? 0.8f : 0.3f;
Color[] vertColors = new Color[20];
for (int n = 0; n < 20; n++) // for each vertex
{
vertColors[n] = new Color(0, 0, 0, lightlevel);
}
lightMap[i, j] = vertColors;
}
}
/*vertColors[18] = new Color(0, 0, 0, lightlevel); // tl
vertColors[19] = new Color(0, 0, 0, lightlevel); // tr
vertColors[17] = new Color(0, 0, 0, lightlevel); // bl
vertColors[16] = new Color(0, 0, 0, lightlevel); // br */
}
void SpawnMap()
{
var combine = new CombineInstance();
int cw = (int)MapWidth / chunkSize;
int ch = (int)MapHeight / chunkSize;
List<CombineInstance>[,] combineLists = new List<CombineInstance>[cw, ch];
chunks = new GameObject[cw, ch];
for (int i = 0; i < cw; i++)
{
for (int j = 0; j < ch; j++)
{
combineLists[i, j] = new List<CombineInstance>();
}
}
for (int i = 0; i < MapWidth; i++)
{
for (int j = 0; j < MapHeight; j++)
{
if (null == map[i, j])
{
continue;
}
var (mesh, matrix) = SpawnBlock(i, j);
mesh.colors = lightMap[i, j];
combine.mesh = mesh;
combine.transform = matrix;
combineLists[i / chunkSize, j / chunkSize].Add(combine);
}
}
for (int i = 0; i < cw; i++)
{
for (int j = 0; j < ch; j++)
{
chunks[i, j] = CombineToChunk(combineLists[i, j].ToArray());
}
}
}
public void PlaceBlock(int x, int y)
{
map[x, y] = "dirt";
int cx = x / chunkSize;
int cy = y / chunkSize;
var (mesh, matrix) = SpawnBlock(x, y);
var chunk = chunks[cx, cy];
var combine = new CombineInstance[2];
combine[0].mesh = chunk.GetComponent<MeshFilter>().mesh;
combine[0].transform = chunk.transform.localToWorldMatrix;
combine[1].mesh = mesh;
combine[1].transform = matrix;
chunks[cx, cy] = CombineToChunk(combine);
}
public void RemoveBlock(int x, int y)
{
map[x, y] = null;
DelBlockFromMesh(x, y);
AdjustBordersAround(x, y);
}
#region Block adding stuff
private GameObject CombineToChunk(CombineInstance[] toCombine)
{
Mesh newMesh = new Mesh();
newMesh.CombineMeshes(toCombine);
GameObject chunk = Instantiate(ChunkPrefab, Vector3.zero, Quaternion.identity, chunksParent);
chunk.GetComponent<MeshFilter>().mesh = newMesh;
return chunk;
}
private (Mesh mesh, Matrix4x4 matrix) SpawnBlock(int x, int y)
{
var pos = new Vector3(rootPos.x + x, rootPos.y - y, rootPos.z);
blockPlaced.transform.position = pos;
var mesh = GetShiftedMesh(blockPlaced.GetComponent<MeshFilter>().mesh, pos, x, y);
return (mesh, blockPlaced.transform.localToWorldMatrix);
}
#endregion
#region Block removing stuff
private void DelBlockFromMesh(int x, int y)
{
MeshFilter chunkMesh = chunks[x / chunkSize,y / chunkSize].GetComponent<MeshFilter>();
List<Vector3> chunkVertices = chunkMesh.mesh.vertices.ToList<Vector3>();
List<int> chunkTriangles = chunkMesh.mesh.triangles.ToList<int>();
List<Vector2> chunkUv = chunkMesh.mesh.uv.ToList<Vector2>();
List<Vector2> chunkUv2 = chunkMesh.mesh.uv2.ToList<Vector2>();
List<Vector2> chunkUv3 = chunkMesh.mesh.uv3.ToList<Vector2>();
int vertsToRemove = 20;
int trisToRemove = 30;
var startVertPos = rootPos + new Vector3(x - 0.5f, 0.5f - y, -0.5f);
int startVertex = FindStartVertex(startVertPos, chunkVertices, vertsToRemove);
chunkVertices.RemoveRange(startVertex, vertsToRemove);
chunkTriangles.RemoveRange(chunkTriangles.Count - trisToRemove, trisToRemove);
chunkUv.RemoveRange(startVertex, vertsToRemove);
chunkUv2.RemoveRange(startVertex, vertsToRemove);
chunkUv3.RemoveRange(startVertex, vertsToRemove);
chunkMesh.mesh = new Mesh
{
vertices = chunkVertices.ToArray(),
triangles = chunkTriangles.ToArray(),
uv = chunkUv.ToArray(),
uv2 = chunkUv2.ToArray(),
uv3 = chunkUv3.ToArray(),
};
}
private void AdjustBordersAround(int x, int y)
{
int step = 20;
for (int i = x - 1; i <= x + 1; i++)
{
if (i < 0 || i >= MapWidth)
{
continue;
}
for (int j = y - 1; j <= y + 1; j++)
{
if (j < 0 || j >= MapHeight || (i == x && j == y) || null == map[i, j])
{
continue;
}
MeshFilter chunkMesh = chunks[i / chunkSize, j / chunkSize].GetComponent<MeshFilter>();
List<Vector2> chunkUv2 = chunkMesh.mesh.uv2.ToList<Vector2>();
var startVertPos = rootPos + new Vector3(i - 0.5f, 0.5f - j, -0.5f);
int startVertex = FindStartVertex(startVertPos, chunkMesh.mesh.vertices.ToList<Vector3>(), step);
Vector2[] edited = AdjustBorders(chunkUv2.GetRange(startVertex, step).ToArray(), i, j);
chunkUv2[startVertex + 16] = edited[16];
chunkUv2[startVertex + 17] = edited[17];
chunkUv2[startVertex + 18] = edited[18];
chunkUv2[startVertex + 19] = edited[19];
chunkMesh.mesh.uv2 = chunkUv2.ToArray();
}
}
}
private int FindStartVertex(Vector3 pos, List<Vector3> vertices, int step)
{
for (int i = 0; i < vertices.Count; i += step)
{
if (vertices[i] == pos)
{
return i;
}
}
return 0;
}
#endregion
public void Highlight(GameObject obj, bool enable)
{
var mat = obj.GetComponent<MeshRenderer>().material;
if (enable)
{
mat.SetFloat("_ShowOutline", 1f);
mat.EnableKeyword("SHOW_OUTLINE");
}
else
{
mat.SetFloat("_ShowOutline", 0f);
mat.DisableKeyword("SHOW_OUTLINE");
}
}
#region Place texture in right position
private Mesh GetShiftedMesh(Mesh mesh, Vector3 pos, int x, int y)
{
Vector2[] uv = mesh.uv; // main
Vector2[] uv2 = mesh.uv; // borders
Vector2[] uv3 = mesh.uv; // ore
for (int i = 0; i < uv.Length; i++)
{
if (map[x, y] == "gravel") // TODO find right underlay
{
uv[i] = (uv[i] + new Vector2(pos.x, 0 - pos.y % BlocksPerTexture)) / (BlocksPerTexture * 2);
}
else
{
uv[i] = (uv[i] + new Vector2(pos.x, BlocksPerTexture - pos.y % BlocksPerTexture)) / (BlocksPerTexture * 2);
}
uv3[i] = uv3[i] / 2 + textureOffset[map[x, y]];
}
Mesh newMesh = new Mesh
{
vertices = mesh.vertices,
triangles = mesh.triangles,
uv = uv,
uv2 = AdjustBorders(uv2, x, y),
uv3 = uv3,
normals = mesh.normals,
colors = mesh.colors,
tangents = mesh.tangents
};
return newMesh;
}
private Vector2[] AdjustBorders(Vector2[] uv, int x, int y)
{
int mask = 0;
if (y > 0 && null == map[x, y - 1])
{
mask += 2;
}
else
{
if (x > 0 && y > 0 && null == map[x - 1, y - 1])
{
mask += 1;
}
if (x < MapWidth - 1 && y > 0 && null == map[x + 1, y - 1])
{
mask += 4;
}
}
if (y < MapHeight - 1 && null == map[x, y + 1])
{
mask += 32;
}
else
{
if (x > 0 && y < MapHeight - 1 && null == map[x - 1, y + 1])
{
mask += 64;
}
if (x < MapWidth - 1 && y < MapHeight - 1 && null == map[x + 1, y + 1])
{
mask += 16;
}
}
if (x > 0 && null == map[x - 1, y])
{
mask += 128;
mask = ZeroBit(mask, 0);
mask = ZeroBit(mask, 6);
}
if (x < MapWidth - 1 && null == map[x + 1, y])
{
mask += 8;
mask = ZeroBit(mask, 2);
mask = ZeroBit(mask, 4);
}
uv[16] = blockBorders[mask][0];
uv[17] = blockBorders[mask][1];
uv[18] = blockBorders[mask][2];
uv[19] = blockBorders[mask][3];
return uv;
}
private int ZeroBit(int value, int position)
{
return value & ~(1 << position);
}
private void FindBlockBordersTexture()
{
blockBorders[0b00000001] = GetTexPart(0, 0);
blockBorders[0b00000100] = GetTexPart(0, 1);
blockBorders[0b00010000] = GetTexPart(0, 2);
blockBorders[0b01000000] = GetTexPart(0, 3);
blockBorders[0b00000101] = GetTexPart(1, 0);
blockBorders[0b00010100] = GetTexPart(1, 1);
blockBorders[0b01010000] = GetTexPart(1, 2);
blockBorders[0b01000001] = GetTexPart(1, 3);
blockBorders[0b00010001] = GetTexPart(2, 0);
blockBorders[0b01000100] = GetTexPart(2, 1);
blockBorders[0b01000101] = GetTexPart(3, 0);
blockBorders[0b00010101] = GetTexPart(3, 1);
blockBorders[0b01010100] = GetTexPart(3, 2);
blockBorders[0b01010001] = GetTexPart(3, 3);
blockBorders[0b01010101] = GetTexPart(4, 0);
blockBorders[0b00000010] = GetTexPart(5, 0);
blockBorders[0b00001000] = GetTexPart(5, 1);
blockBorders[0b00100000] = GetTexPart(5, 2);
blockBorders[0b10000000] = GetTexPart(5, 3);
blockBorders[0b00100010] = GetTexPart(6, 0);
blockBorders[0b10001000] = GetTexPart(6, 1);
blockBorders[0b00001010] = GetTexPart(7, 0);
blockBorders[0b00101000] = GetTexPart(7, 1);
blockBorders[0b10100000] = GetTexPart(7, 2);
blockBorders[0b10000010] = GetTexPart(7, 3);
blockBorders[0b01010010] = GetTexPart(8, 0);
blockBorders[0b01001001] = GetTexPart(8, 1);
blockBorders[0b00100101] = GetTexPart(8, 2);
blockBorders[0b10010100] = GetTexPart(8, 3);
blockBorders[0b01001010] = GetTexPart(9, 0);
blockBorders[0b00101001] = GetTexPart(9, 1);
blockBorders[0b10100100] = GetTexPart(9, 2);
blockBorders[0b10010010] = GetTexPart(9, 3);
blockBorders[0b10100010] = GetTexPart(10, 0);
blockBorders[0b10001010] = GetTexPart(10, 1);
blockBorders[0b00101010] = GetTexPart(10, 2);
blockBorders[0b10101000] = GetTexPart(10, 3);
blockBorders[0b10101010] = GetTexPart(11, 0);
blockBorders[0b01000010] = GetTexPart(12, 0);
blockBorders[0b00001001] = GetTexPart(12, 1);
blockBorders[0b00100100] = GetTexPart(12, 2);
blockBorders[0b10010000] = GetTexPart(12, 3);
blockBorders[0b00010010] = GetTexPart(13, 0);
blockBorders[0b01001000] = GetTexPart(13, 1);
blockBorders[0b00100001] = GetTexPart(13, 2);
blockBorders[0b10000100] = GetTexPart(13, 3);
blockBorders[0b00000000] = GetTexPart(15, 0);
}
private Vector2[] GetTexPart(int num, int rotate = 0)
{
float x1 = (float)((num % 4)) / 4;
float y1 = (float)(4 - num / 4) / 4;
float x2 = x1 + 0.25f;
float y2 = y1 - 0.25f;
var place = new Vector2[4] {
new Vector2(x2, y2),
new Vector2(x1, y2),
new Vector2(x1, y1),
new Vector2(x2, y1),
};
if (rotate != 0)
{
Vector2[] take = new Vector2[4];
place.CopyTo(take, 0);
for (int i = 0; i < take.Length; i++)
{
place[i] = take[(4 + i - rotate) % 4];
}
}
return place;
}
#endregion
void Dump(List<int> uv3)
{
Debug.Log(System.String.Join(" ",
new List<int>(uv3)
.ConvertAll(i => i.ToString())
.ToArray()));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment