I am working on a survival game where the world is made out of cubes (much like Minecraft and many others), but I am having trouble with generating the chunks in a timely manner. Currently, generating a chunk takes at most 800ms on my computer.
This is what the profiler says:
Also, here is the code being used to generate cubes:
MeshData.cs
public MeshData () {
vertices = new List<Vector3> (65000);
uvs = new List<Vector2> (65000);
triangles = new List<int> (65000);
}
public Mesh GetMesh (Block [,,] blocks, int offsetX, int offsetY) {
for (int i = 0; i < blocks.GetLength (0); i++) {
for (int j = 0; j < blocks.GetLength (1); j++) {
for (int k = 0; k < blocks.GetLength (2); k++) {
CreateVoxel (blocks [i, j, k]);
}
}
}
Mesh mesh = new Mesh ();
mesh.SetVertices (vertices);
mesh.SetUVs (0, uvs);
mesh.SetTriangles (triangles, 0);
mesh.RecalculateNormals ();
mesh.RecalculateTangents ();
mesh.RecalculateBounds ();
return mesh;
}
void CreateVoxel (Block data) {
if (data.shape == Block.Shape.Cube)
CreateCube (data);
}
void CreateCube (Block data) {
int numVertices = vertices.Count;
if (data.CanSeeFace (Vector3.forward)) {
CreateSquare (data);
ApplyRotation (vertices.Count - 4, data.position, new Vector3 (0f, 0f, 0f));
}
if (data.CanSeeFace (Vector3.right)) {
CreateSquare (data);
ApplyRotation (vertices.Count - 4, data.position, new Vector3 (0f, 90f, 0f));
}
if (data.CanSeeFace (Vector3.back)) {
CreateSquare (data);
ApplyRotation (vertices.Count - 4, data.position, new Vector3 (0f, 180f, 0f));
}
if (data.CanSeeFace (Vector3.left)) {
CreateSquare (data);
ApplyRotation (vertices.Count - 4, data.position, new Vector3 (0f, 270f, 0f));
}
if (data.CanSeeFace (Vector3.up)) {
CreateSquare (data);
ApplyRotation (vertices.Count - 4, data.position, new Vector3 (270f, 0f, 0f));
}
if (data.CanSeeFace (Vector3.down)) {
CreateSquare (data);
ApplyRotation (vertices.Count - 4, data.position, new Vector3 (90f, 0f, 0f));
}
ApplyRotation (numVertices, data.position, data.quaternion);
}
void CreateSquare (Block data, bool slant = false) {
vertices.Add (new Vector3 (data.position.x - 0.5f, data.position.y - 0.5f, data.position.z + 0.5f));
vertices.Add (new Vector3 (data.position.x + 0.5f, data.position.y - 0.5f, data.position.z + 0.5f));
vertices.Add (new Vector3 (data.position.x - 0.5f, data.position.y + 0.5f, data.position.z + 0.5f));
vertices.Add (new Vector3 (data.position.x + 0.5f, data.position.y + 0.5f, data.position.z + 0.5f));
triangles.Add (vertices.Count - 4);
triangles.Add (vertices.Count - 3);
triangles.Add (vertices.Count - 1);
triangles.Add (vertices.Count - 4);
triangles.Add (vertices.Count - 1);
triangles.Add (vertices.Count - 2);
}
void ApplyRotation (int numVertices, Vector3 center, Quaternion rotation) {
if (numVertices == vertices.Count)
return;
for (int i = numVertices; i < vertices.Count; i++) {
vertices [i] = rotation * (vertices [i] - center) + center;
//normals [i] = q * (normals [i] - center) + center;
}
}
void ApplyRotation (int numVertices, Vector3 center, Vector3 rotation) {
if (numVertices == vertices.Count)
return;
Quaternion q = Quaternion.Euler (rotation);
for (int i = numVertices; i < vertices.Count; i++) {
vertices [i] = q * (vertices [i] - center) + center;
//normals [i] = q * (normals [i] - center) + center;
}
}
Is there anything I can do to improve the chunk generation? I think Minecraft creates new chunks at runtime (because there are way too many chunks in the world to do it all when you start the game), so I know what I am trying to do is possible. Also, if I do generate them in a loading screen before you start the game, the loading time will vary with the size of the world. If it’s 62x62 chunks (making the world about 1000x1000 blocks in size) it would take an absurd amount of time to load.
Also, in just in case it’s important, here is the code for the blocks and the chunk.
Block.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Block {
public Vector3 position;
public Vector3 rotation;
public Quaternion quaternion;
public enum Face { Unknown, Square, Triangle, StairSide, Slant, StairFront };
public enum Shape { Cube, Slant, Stair, Half };
public enum Material { Air, Filled };
public Shape shape;
public Material material;
public Block (Vector3 position, Vector3 rotation, Shape shape, Material material) {
this.position = position;
this.rotation = rotation;
this.shape = shape;
this.material = material;
quaternion = Quaternion.Euler (rotation);
}
public static Block Air = new Block (Vector3.zero, Vector3.zero, Block.Shape.Cube, Block.Material.Air);
public Face GetFace (Vector3 direction) {
if (shape == Shape.Cube)
return Face.Square;
if (shape == Shape.Slant) {
if (direction == quaternion * Vector3.back || direction == quaternion * Vector3.down)
return Face.Square;
if (direction == quaternion * Vector3.left || direction == quaternion * Vector3.right)
return Face.Triangle;
return Face.Slant;
}
if (shape == Shape.Stair) {
if (direction == quaternion * Vector3.back || direction == quaternion * Vector3.down)
return Face.Square;
if (direction == quaternion * Vector3.left || direction == quaternion * Vector3.right)
return Face.StairSide;
return Face.StairFront;
}
return Face.Unknown;
}
public bool CanSeeFace (Vector3 direction) {
//Rotate the vector to the direction
direction = quaternion * direction;
//Are we next to a block we can see through anyway?
Block neighbor = Chunk.instance.GetBlockAt (position.x + direction.x, position.y + direction.y, position.z + direction.z);
if (neighbor == null || neighbor.material == Material.Air)
return true;
//No?! Well, fudge no we have to do something more complicated.
Face myFace = GetFace (direction);
Face neighborFace = neighbor.GetFace ((direction * -1));
if (neighborFace == Face.Square) {
if (myFace == Face.Square || myFace == Face.Triangle || myFace == Face.StairSide)
return false;
}
if (neighborFace == Face.StairSide && myFace == Face.Triangle && rotation == neighbor.rotation)
return false;
if (neighborFace == Face.StairSide && myFace == Face.StairSide && rotation == neighbor.rotation)
return false;
if (neighborFace == Face.Triangle && myFace == Face.Triangle && rotation == neighbor.rotation)
return false;
return true;
}
}
Chunk.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Chunk : MonoBehaviour {
public static Chunk instance;
public static int size = 16;
public static int height = 16;
Block [,,] blocks;
Block [] blocks1D;
void Awake () {
instance = this;
}
// Use this for initialization
void Start () {
UnityEngine.Profiling.Profiler.BeginSample ("Initializing Voxels");
blocks = new Block[size, height, size];
for (int i = 0; i < size; i++) {
for (int j = 0; j < height; j++) {
for (int k = 0; k < size; k++) {
blocks [i, j, k] = new Block (new Vector3 (i, j, k), Vector3.zero, Block.Shape.Cube, Block.Material.Filled);
}
}
}
UnityEngine.Profiling.Profiler.EndSample ();
UnityEngine.Profiling.Profiler.BeginSample ("Constructing Mesh");
MeshData mesh = new MeshData ();
mesh.GetMesh (blocks, 0, 0);
UnityEngine.Profiling.Profiler.EndSample ();
}
public Block GetBlockAt (float X, float Y, float Z) {
int x, y, z;
x = Mathf.FloorToInt (X + 0.5f);
y = Mathf.FloorToInt (Y + 0.5f);
z = Mathf.FloorToInt (Z + 0.5f);
if (IsPointWithinBounds (x, y, z))
return blocks [x, y, z];
return null;
}
bool IsPointWithinBounds (float x, float y, float z) {
return (x >= 0 && x < size) && (y >= 0 && y < height) && (z >= 0 && z < size);
}
}
I appreciate any help.




