Created
September 18, 2019 08:43
-
-
Save artygrand/cc82e1b161ff43288b7c9cd9789f1042 to your computer and use it in GitHub Desktop.
Wrong dungeon generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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