Hey guys. I’m trying to generate some chunks for a voxel game I’m working on in my spare time. Currently I’m having an issue when I’m building my chunks. It takes forever. I’m not sure why but I think it has something to do with how I get the blocks when I’m building the terrain.But I’m not entirely sure. Any Ideas? Just so you guys know the BLockType is an enum I have set up. Below is the relavent code :
Mesh Manager :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeshManager : MonoBehaviour
{
public static ChunkManager chunkManager;
public static MeshObject CreateVisualMesh(int chunkSize, BlockType[,,] blocks, MeshObject meshOb, Vector3 chunkPos, Chunk chunk)
{
MeshManager.chunkManager = World.currentWorld.chunkManager;
Mesh visualMesh = meshOb.Mesh;
MeshFilter meshFilter = meshOb.MeshFilter;
MeshCollider meshCollider = meshOb.MeshCollider;
List<Vector3> verts = new List<Vector3>();
List<Vector2> uvs = new List<Vector2>();
List<int> tris = new List<int>();
int[,,] map = Chunk.GetIntFromBlockType(blocks, chunkSize);
Vector3 worldPos = new Vector3(chunkPos.x * chunkSize, chunkPos.y * chunkSize, chunkPos.z * chunkSize);
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
if (map[x, y, z] == 0) continue;
int block = map[x, y, z];
// Left wall
if (IsTransparent(x - 1, y, z, worldPos, chunk))
{
BuildFace(block, new Vector3(x, y, z), Vector3.up, Vector3.forward, false, verts, uvs, tris);
}
// Right wall
if (IsTransparent(x + 1, y, z, worldPos, chunk))
BuildFace(block, new Vector3(x + 1, y, z), Vector3.up, Vector3.forward, true, verts, uvs, tris);
// Bottom wall
if (IsTransparent(x, y - 1, z, worldPos, chunk))
BuildFace(block, new Vector3(x, y, z), Vector3.forward, Vector3.right, false, verts, uvs, tris);
// Top wall
if (IsTransparent(x, y + 1, z, worldPos, chunk))
BuildFace(block, new Vector3(x, y + 1, z), Vector3.forward, Vector3.right, true, verts, uvs, tris);
// Back
if (IsTransparent(x, y, z - 1, worldPos, chunk))
BuildFace(block, new Vector3(x, y, z), Vector3.up, Vector3.right, true, verts, uvs, tris);
// Front
if (IsTransparent(x, y, z + 1, worldPos, chunk))
BuildFace(block, new Vector3(x, y, z + 1), Vector3.up, Vector3.right, false, verts, uvs, tris);
}
}
}
visualMesh.vertices = verts.ToArray();
visualMesh.uv = uvs.ToArray();
visualMesh.triangles = tris.ToArray();
visualMesh.RecalculateBounds();
visualMesh.RecalculateNormals();
meshFilter.mesh = visualMesh;
meshCollider.sharedMesh = visualMesh;
return new MeshObject(visualMesh, meshCollider, meshFilter);
}
public static void BuildFace(int block, Vector3 corner, Vector3 up, Vector3 right, bool reversed, List<Vector3> verts, List<Vector2> uvs, List<int> tris)
{
int index = verts.Count;
Vector2 uvCorner = GetTexturePos(block);
verts.Add(corner);
verts.Add(corner + up);
verts.Add(corner + up + right);
verts.Add(corner + right);
Vector2 uvWidth = new Vector2(0.22f, 0.22f);
uvs.Add(uvCorner);
uvs.Add(new Vector2(uvCorner.x, uvCorner.y + uvWidth.y));
uvs.Add(new Vector2(uvCorner.x + uvWidth.x, uvCorner.y + uvWidth.y));
uvs.Add(new Vector2(uvCorner.x + uvWidth.x, uvCorner.y));
if (reversed)
{
tris.Add(index + 0);
tris.Add(index + 1);
tris.Add(index + 2);
tris.Add(index + 2);
tris.Add(index + 3);
tris.Add(index + 0);
}
else
{
tris.Add(index + 1);
tris.Add(index + 0);
tris.Add(index + 2);
tris.Add(index + 3);
tris.Add(index + 2);
tris.Add(index + 0);
}
}
public static int[,,] GetMap(int chunkSize, List<Block> blocks)
{
int[,,] map = new int[chunkSize, chunkSize, chunkSize];
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
try
{
map[x, y, z] = (int)blocks[x + y + z].Id;
}
catch
{
Debug.Log(x + y + z);
}
}
}
}
return map;
}
public static bool IsTransparent(int x, int y, int z, Vector3 worldPos, Chunk chunk)
{
BlockType block = GetByte(x, y, z, worldPos, chunk);
switch (block)
{
case BlockType.air:
return true;
default:
return false;
}
}
public static BlockType GetByte(int x, int y, int z, Vector3 worldPos, Chunk chunk)
{
if ((x < 0) || (y < 0) || (z < 0) || (y >= chunk.chunkSize) || x >= chunk.chunkSize || z >= chunk.chunkSize)
{
Vector3 pos = new Vector3(x, y, z) + chunk.gameObject.transform.position;
Chunk chunk1 = chunkManager.GetChunkByWorldPos(pos);
if (chunk1 == chunk) { return 0; }
if (chunk1 == null) { return Generate.GetTheoreticalByte(worldPos, Biomes.plains); }
return GetByte(Mathf.FloorToInt(pos.x), Mathf.FloorToInt(pos.y), Mathf.FloorToInt(pos.z), chunk1.transform.position, chunk1);
}
return chunk.GetBlockFromPosition(x, y, z);
}
public static Vector2 GetTexturePos(int block)
{
int b1 = block;
float lenght = 0.00f;
float height = 0.75f;
while (b1 >= 1)
{
if (b1 < 17)
{
lenght += 0.25f;
b1 -= 1;
}
else
{
height -= 0.25f;
b1 -= 16;
}
}
return new Vector2(lenght + 0.02f, height + 0.02f);
}
private static List<Block> GetBlockListFromMap(int[,,] map, int chunkSize)
{
List<Block> blockList = new List<Block>();
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
blockList.Add(Block.getBlock((BlockType)map[x, y, z]));
}
}
}
return blockList;
}
private static int GetXUnderBlock(Vector3 chunkPos, int y, int z)
{
Chunk chunk = World.currentWorld.chunkManager.GetChunkByPos(new Vector3(chunkPos.x - 1, chunkPos.y, chunkPos.z));
try
{
return (int)chunk.GetBlockFromPosition(15, y, z);
}
catch (System.NullReferenceException)
{
return (int)BlockType.air;
}
}
private static int GetXAboveBlock(Vector3 chunkPos, int y, int z)
{
Chunk chunk = World.currentWorld.chunkManager.GetChunkByPos(new Vector3(chunkPos.x - 1, chunkPos.y, chunkPos.z));
try
{
return (int)chunk.GetBlockFromPosition(0, y, z);
}
catch (System.NullReferenceException)
{
return (int)BlockType.air;
}
}
private static int GetYUnderBlock(Vector3 chunkPos, int x, int z)
{
Chunk chunk = World.currentWorld.chunkManager.GetChunkByPos(new Vector3(chunkPos.x - 1, chunkPos.y, chunkPos.z));
try
{
return (int)chunk.GetBlockFromPosition(x, 15, z);
}
catch (System.NullReferenceException)
{
return (int)BlockType.air;
}
}
private static int GetYAboveBlock(Vector3 chunkPos, int x, int z)
{
Chunk chunk = World.currentWorld.chunkManager.GetChunkByPos(new Vector3(chunkPos.x - 1, chunkPos.y, chunkPos.z));
try
{
return (int)chunk.GetBlockFromPosition(x, 0, z);
}
catch (System.NullReferenceException)
{
return (int)BlockType.air;
}
}
private static int GetZUnderBlock(Vector3 chunkPos, int x, int y)
{
Chunk chunk = World.currentWorld.chunkManager.GetChunkByPos(new Vector3(chunkPos.x - 1, chunkPos.y, chunkPos.z));
try
{
return (int)chunk.GetBlockFromPosition(x, y, 15);
}
catch (System.NullReferenceException)
{
return (int)BlockType.air;
}
}
private static int GetZAboveBlock(Vector3 chunkPos, int x, int y)
{
Chunk chunk = World.currentWorld.chunkManager.GetChunkByPos(new Vector3(chunkPos.x - 1, chunkPos.y, chunkPos.z));
try
{
return (int)chunk.GetBlockFromPosition(x, y, 0);
}
catch (System.NullReferenceException)
{
return (int)BlockType.air;
}
}
}
Chunk Manager :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChunkManager {
private List<GameObject> chunkList;
private List<GameObject> visibleChunksList;
public List<GameObject> VisibleChunksList
{
get
{
return visibleChunksList;
}
set
{
visibleChunksList = value;
}
}
public ChunkManager()
{
chunkList = new List<GameObject>();
}
public BlockType GetBlockByWorldPos(Vector3 p)
{
Chunk chunk = GetChunkByWorldPos(p);
Vector3 blockPos = getBlockFromPos(p);
return chunk.GetBlockFromPosition(blockPos);
}
public BlockType GetMeshBuildBlock(Vector3 pos)
{
if(GetChunkByWorldPos(pos) == null)
{
return Generate.GetTheoreticalByte(pos, Biomes.plains);
}
else
{
Vector3 pos1 = getBlockFromPos(pos);
return GetChunkByWorldPos(pos).GetBlockFromPosition(pos1);
}
}
public void SetBlockByWorldPos(Vector3 p, BlockType block)
{
//Debug.Log(p.x + ":" + p.y + ":" + p.z + ":");
Chunk chunk = GetChunkByWorldPos(p);
Vector3 blockPos = getBlockFromPos(p);
chunk.SetBlock(block, blockPos);
}
public Vector3 getBlockFromPos(Vector3 pos)
{
Vector3 vector3 = new Vector3();
vector3.x = Mathf.Abs(pos.x) % World.staticChunkSize;
vector3.y = Mathf.Abs(pos.y) % World.staticChunkSize;
vector3.z = Mathf.Abs(pos.z) % World.staticChunkSize;
return vector3;
}
public void AddChunkToList(GameObject chunk)
{
chunkList.Add(chunk);
}
public Chunk GetChunkByPos(Vector3 pos)
{
for(int i = 0; i < chunkList.Count; i++)
{
if(chunkList[i].GetComponent<Chunk>().chunkPos == pos)
{
return chunkList[i].GetComponent<Chunk>();
}
}
return null;
}
public Chunk GetChunkByWorldPos(Vector3 pos)
{
Vector3 chunkPos = new Vector3(Mathf.FloorToInt((pos.x / World.staticChunkSize)),
Mathf.FloorToInt((pos.y / World.staticChunkSize)),
Mathf.FloorToInt((pos.z / World.staticChunkSize)));
return GetChunkByPos(chunkPos);
}
public Chunk[] GetVisibleChunks()
{
Chunk[] visibleChunkArray = new Chunk[VisibleChunksList.Count];
for(int i = 0; i < visibleChunkArray.Length; i++)
{
visibleChunkArray[i] = VisibleChunksList[i].GetComponent<Chunk>();
}
return visibleChunkArray;
}
public void LoadVisibleChunks(Vector3 chunkPos)
{
VisibleChunksList.Clear();
for (int i = 0; i < chunkList.Count; i++)
{
Vector3 pos = chunkList[i].GetComponent<Chunk>().chunkPos;
if (pos.x < chunkPos.x + World.viewDistance || pos.x >= chunkPos.x - World.viewDistance ||
pos.z < chunkPos.z + World.viewDistance || pos.z >= chunkPos.z - World.viewDistance)
{
VisibleChunksList.Add(chunkList[i]);
chunkList[i].GetComponent<Chunk>().isVisble = true;
}else
{
chunkList[i].GetComponent<Chunk>().isVisble = false;
}
}
}
public Chunk GetNextChunkX(int pos, Chunk chunk)
{
if(pos == 1)
{
return GetChunkByPos(new Vector3(chunk.ChunkPos.x + 1, chunk.ChunkPos.y, chunk.ChunkPos.z));
} else if(pos == -1)
{
return GetChunkByPos(new Vector3(chunk.ChunkPos.x - 1, chunk.ChunkPos.y, chunk.ChunkPos.z));
}
else
{
return null;
}
}
public Chunk GetNextChunkY(int pos, Chunk chunk)
{
if (pos == 1)
{
return GetChunkByPos(new Vector3(chunk.ChunkPos.x, chunk.ChunkPos.y + 1, chunk.ChunkPos.z));
}
else if (pos == -1)
{
return GetChunkByPos(new Vector3(chunk.ChunkPos.x, chunk.ChunkPos.y - 1, chunk.ChunkPos.z));
}
else
{
return null;
}
}
public Chunk GetNextChunkZ(int pos, Chunk chunk)
{
if (pos == 1)
{
return GetChunkByPos(new Vector3(chunk.ChunkPos.x, chunk.ChunkPos.y, chunk.ChunkPos.z + 1));
}
else if (pos == -1)
{
return GetChunkByPos(new Vector3(chunk.ChunkPos.x, chunk.ChunkPos.y, chunk.ChunkPos.z - 1));
}
else
{
return null;
}
}
public Vector3 GetChunkPos(Vector3 worldPos)
{
World world = GameObject.FindGameObjectWithTag("World").GetComponent<World>();
return new Vector3((int) worldPos.x / world.chunkSize, (int)worldPos.y / world.chunkSize, (int)worldPos.z / world.chunkSize);
}
}
Chunk :
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshCollider))]
[RequireComponent(typeof(MeshFilter))]
public class Chunk : MonoBehaviour
{
public BlockType[,,] blockList;
public int chunkSize;
public Vector3 chunkPos;
public Biomes biome;
public Vector3 worldPos;
private Mesh mesh;
private MeshFilter meshFilter;
private MeshCollider meshCollider;
public bool isVisble = false;
private bool previousVisible = false;
public bool created = false;
public void Update()
{
if (isVisble == true && previousVisible == false)
{
gameObject.GetComponent<MeshRenderer>().enabled = true;
meshCollider.enabled = true;
previousVisible = true;
}
if (isVisble == false && previousVisible == true)
{
gameObject.GetComponent<MeshRenderer>().enabled = false;
meshCollider.enabled = false;
previousVisible = false;
}
}
public BlockType GetBlockFromPosition(Vector3 blockPos)
{
return GetBlockFromPosition(Mathf.FloorToInt(blockPos.x), Mathf.FloorToInt(blockPos.y), Mathf.FloorToInt(blockPos.z));
}
public MeshCollider MeshCollider
{
get
{
return meshCollider;
}
set
{
meshCollider = value;
}
}
public MeshFilter MeshFilter
{
get
{
return meshFilter;
}
set
{
meshFilter = value;
}
}
public Mesh Mesh
{
get
{
return mesh;
}
set
{
mesh = value;
}
}
public Vector3 ChunkPos
{
get
{
return chunkPos;
}
set
{
chunkPos = value;
}
}
public void SetChunk(Chunk chunk)
{
this.blockList = chunk.blockList;
this.chunkSize = chunk.chunkSize;
this.MeshCollider = chunk.MeshCollider;
this.MeshFilter = chunk.MeshFilter;
this.Mesh = chunk.Mesh;
this.ChunkPos = chunk.ChunkPos;
}
public void CreateChunk(Biomes biome)
{
this.worldPos = transform.position;
blockList = new BlockType[chunkSize, chunkSize, chunkSize];
this.biome = biome;
this.blockList = Generate.GenerateChunk(chunkSize, chunkPos, 20, biome);
BuildFullMesh();
created = true;
}
private void BuildFullMesh()
{
BlockType[,,] list = blockList;
MeshObject meshObject = new MeshObject(new Mesh(),
gameObject.GetComponent<MeshCollider>(), gameObject.GetComponent<MeshFilter>());
meshObject = MeshManager.CreateVisualMesh(chunkSize, list, meshObject, chunkPos, this);
mesh = meshObject.Mesh;
meshFilter = meshObject.MeshFilter;
meshCollider = meshObject.MeshCollider;
}
public void SetBlock(BlockType block, Vector3 worldPos)
{
worldPos -= transform.position;
SetBlock(block, Mathf.FloorToInt(worldPos.x), Mathf.FloorToInt(worldPos.y), Mathf.FloorToInt(worldPos.z));
}
public void SetBlock(BlockType block, int x, int y, int z)
{
blockList[x, y, z] = block;
BuildFullMesh();
if(x == 0 || x == chunkSize - 1)
{
if(x == 0)
{
World.currentWorld.chunkManager.GetNextChunkX(-1, this).BuildFullMesh();
}else
{
World.currentWorld.chunkManager.GetNextChunkX(1, this).BuildFullMesh();
}
}
if (y == 0 || y == chunkSize -1)
{
if (y == 0)
{
World.currentWorld.chunkManager.GetNextChunkY(-1, this).BuildFullMesh();
}
else
{
World.currentWorld.chunkManager.GetNextChunkY(1, this).BuildFullMesh();
}
}
if(z == 0 || z == chunkSize - 1)
{
if (z == 0)
{
World.currentWorld.chunkManager.GetNextChunkZ(-1, this).BuildFullMesh();
}
else
{
World.currentWorld.chunkManager.GetNextChunkZ(1, this).BuildFullMesh();
}
}
}
public string BlockListToString()
{
string list = "";
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
list += ", " + x + " : " + y + " : " + z + " = " + this.blockList[x, y, z];
}
}
}
return list;
}
public BlockType GetBlockFromPosition(int x, int y, int z)
{
//Debug.Log(x + ":" + y + ":" + z);
return blockList[x, y, z];
}
public static int[,,] GetIntFromBlockType(BlockType[,,] blocks, int chunkSize)
{
int[,,] map = new int[chunkSize, chunkSize, chunkSize];
for (int x = 0; x < chunkSize; x++)
{
for (int y = 0; y < chunkSize; y++)
{
for (int z = 0; z < chunkSize; z++)
{
map[x, y, z] = (int)blocks[x, y, z];
}
}
}
return map;
}
}