[Tutorial] Minecraft Chunks

After noticing the vast amount of help for Minecraft clones I’ve decided to post up a few tutorials explaining different aspects of building one. In this section I’ll try my best to show you how to create “Chunks” although I’ll apologise for my explanations now as I’m awful at explaining.

I would also like to credit the MinePackage located HERE as most of the code has been taken and rewritten out from this. If you’d like to learn more there is another thread which you can check out HERE

If people like this then I’ll make a few more, if any of my information is incorrect then I apologise, let me know and I’ll update it, or if you have a better explanation or extra info just add it as an answer.

So what is a “Chunk” then?

In Minecraft think of the thousands of blocks there are to your disposal, now what would happen if we were to render them all every frame? Well our FPS would be non existant. So one way we can help speed things up is to slice our level in to manageable “Chunks”.

How would this speed up the game?

Instead of rendering the entire level over and over, we could just take the chunks within a certain radius of our player and only render those, not only does this speed things up but it also means we aren’t working with the entire level only a small portion of it. This is bordering another aspect but what if we wanted to find out what blocks the player is able to see? Rather than doing this for the entire level every frame we can just check the surrounding chunks.

What will I learn for this then?

Fingers crossed you will be able to create empty chunks in your world to a size you choose. This is merely showing you how chunks work and how to create them, they have no functionality. I’m posting this because it took me a while to scan examples to learn how they work.

Now I know chunks, what next?

Well I’m still learning myself but I would recommend looking up Octrees, Procedural Meshes, Texture atlas’ and Perlin Noise (Hopefully if I get time I’ll post up some stuff about these)

WorldGameObject.cs attaches to an empty GameObject

WorldGameObject PasteBin

WorldGameObject.cs

using UnityEngine;
using System.Collections;

public class WorldGameObject : MonoBehaviour {
	
	private WorldData world = new WorldData();					// Holds all the data about our world
	
	public Transform chunkPrefab;								// A template of a chunk
	public Transform[,,] chunkGO;								// A list of our chunks coords

	void Start () {
		// So we start by initializing a grid for our chunks
		// In our world data script we run a function that
		// Creates a 3 dimensional array and sets the coords
		// For each Chunk
		world.InitChunkGrid();
		
		// After initializing our grid for our chunks
		// We can now go ahead and instantiat our chunks
		// Referencing their Transform
		CreateChunks();
	}
	
	private void CreateChunks()
    {
		// This will only contain the locations of our chunks
		// The actually chunk data gets stored in our ChunkData script
        chunkGO = new Transform[world.ChunksX,world.ChunksY,world.ChunksZ];
		
		// 3 for loops, x,y,z each increment will represent a chunk
		// We only want to go as high as the amount of chunks in our
		// World so we set the loop amount as ChunksX,ChunksY,ChunksZ
        for (int x = 0; x < world.ChunksX; x++)
        {
            for (int y = 0; y < world.ChunksY; y++)
            {
                for (int z = 0; z < world.ChunksZ; z++)
                {
					// We get the chunk from the array located at x,y,z
					// And store it as the current chunk we're working on
                    ChunkData curChunk = world.chunks[x, y, z];
					
					// Here we create a Vector3 of where the chunk will be placed
					// the x position on our vector3 is the current chunk's x coord multiplied
					// By the chunk width for example if curChunk is 2 we multiply it by our ChunkWidth,
					// Which in this case would be 32 because our chunks are 32x32x128 meaning
					// the chunks x position would be 64, we don't need to worry about Y because
					// Our chunk height is 1, then we do the same on the Z axis
                    Vector3 chunkInstPos = new Vector3(curChunk.X * world.ChunkWidth, 0, curChunk.Y * world.ChunkHeight);
					
					// Now that we have where to we want put our chunk lets create a chunk instance
                    Transform chunkInst = Instantiate(chunkPrefab, chunkInstPos, Quaternion.identity) as Transform;
					
					// After creating an instance of a chunk lets set the parent to our World GameObject
                    chunkInst.parent = transform;
					
					// Lets rename the chunk to something more managable
					// In our ChunkData script is a function called ToString
					// This basically returns a string of the chunks position
					// It will return something like Chunk ( 1,0,1 )
                    chunkInst.name = curChunk.ToString();
					
					// Now our chunk exists lets make a reference of it's
					// Position by adding it to our array
                    chunkGO[x, y, z] = chunkInst;
                }
            }
        }
    }
}

WorldData.cs doesn’t need to be attached to anything

WorldData PasteBin

WorldData.cs

using System.Collections;

public class WorldData  {
	
	private int chunkWidth = 32, chunkHeight = 128, chunkDepth = 32;		// The dimensions of each chunk in blocks, so each chunk is 32 blocks in width and depth but 128 blocks in height
	private int chunksX = 8, chunksY = 1, chunksZ = 8;						// How many chunks we want to generate in the world along each axis
	
	public ChunkData[,,] chunks;											// And array of our chunks so we can keep track of them
	
	public const int BottomChunkBorderRow = 0, LeftChunkBorderColumn = 0;
	
	public void InitChunkGrid()
    {
		// Lets initialize our array where our chunk data will be stored
        chunks = new ChunkData[chunksX,chunksY,chunksZ];
		
		// Our for loops so we can iterate through and set our chunk
		// Position in the array
        for (int x = LeftChunkBorderColumn; x <= RightChunkBorderColumn; x++)
        {
            for (int y = BottomChunkBorderRow; y <= TopChunkBorderRow; y++)
            {
                for (int z = 0; z < chunksZ; z++)
                {
					// Create a new set of ChunkData at x,y,z
                    chunks[x, y, z] = new ChunkData(x, y, z);
                }
            }
        }
    }
	
	// Chunk width,height,depth Functions just allow us
	// To get and set the value of our vars because they're private
	public int ChunkWidth
    {
        get { return chunkWidth; }
        set { chunkWidth = value; }
    }
	
	public int ChunkHeight
    {
        get { return chunkHeight; }
        set { chunkHeight = value; }
    }
	
	public int ChunkDepth
    {
        get { return chunkDepth; }
        set { chunkDepth = value; }
    }
	
	// Chunks X,Y,Z Functions just allow us
	// To get and set the value of our vars because they're private
	public int ChunksX
    {
        get { return chunksX; }
        set { chunksX = value; }
    }
	
	public int ChunksY
    {
        get { return chunksY; }
        set { chunksY = value; }
    }
	
	public int ChunksZ
    {
        get { return chunksZ; }
        set { chunksZ = value; }
    }
	
	public int TopChunkBorderRow
    {
        get { return chunksY - 1; }
    }

    public int RightChunkBorderColumn
    {
        get { return chunksX - 1; }
    }
}

ChunkData.cs doesn’t need to be attached to anything

ChunkData PasteBin

ChunkData.cs

using System.Collections;

public class ChunkData {
	
	private int x,y,z;							// Create 3 ints called x,y,z to store our position in our WorldData array
	
	// When ever our chunkData, we will send in
	// The position it will be stored in the WorldData array
	public ChunkData(int _x, int _y, int _z)
    {
        x = _x;
        y = _y;
        z = _z;
    }
	
	// X,Y,Z Functions just allow us to get and set the value
	// Of our vars x,y,z because they're private
	public int X
    {
        get { return x; }
        set { x = value; }
    }

    public int Y
    {
        get { return y; }
        set { y = value; }
    }

    public int Z
    {
        get { return z; }
    }
	
	// We will use this when renaming our chunk
	// So it is something more readable and managable
	// Instead of having them all name Chunk(clone)
	public override string ToString()
    {
        return string.Format("Chunk ( {0},{1},{2} )", x,y,z);
    }
}

This is not a question. This site is only for questions, not tutorials.

Copy This Code Here:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Block
{
    public int type;
    public bool vis;

    public Block(int t, bool v)
    {
        type = t;
        vis = v;
    }
}

public class Woldcreator : MonoBehaviour
{

    public static int width = 128;
    public static int depth = 128;
    public static int height = 128;
    public int heightSacale = 20;
    public float detailScale = 25.0f;


    public GameObject BlockDirtStone01;
    public GameObject BlockGrass01;
    public GameObject Blocksnow01;

    Block[,,] worldBlock = new Block[width, height, depth];

    void Start()
    {
        int seed = (int) Network.time * 10;
        for(int z = 0; z < depth; z++)
        {
            for (int x = 0; x < width; x++)
            {
                int y = (int) (Mathf.PerlinNoise((x+seed)/detailScale, (z+seed)/detailScale) * heightSacale);
                Vector3 blockPos = new Vector3(x,y,z);

                CreateBlock(y, blockPos, true);
                while(y > 0)
                {
                    y--;
                    blockPos = new Vector3(x, y, z);
                    CreateBlock(y, blockPos, false);
                }
            }
        }
    }

    void CreateBlock(int y, Vector3 blockPos, bool create)
    {
        if (y > 15)

        {
            if(create)
                 Instantiate(Blocksnow01, blockPos, Quaternion.identity);
            worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z] = new Block(1, create);
        }
        else if (y > 5)
        {
            if(create)
                 Instantiate(BlockGrass01, blockPos, Quaternion.identity);
            worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z] = new Block(2, create);
        }
        else
        {
            if(create)
                Instantiate(BlockDirtStone01, blockPos, Quaternion.identity);
            worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z] = new Block(3, create);
        }
    }

    void DrawBlock(Vector3 blockPos)
    {
        if (blockPos.x < 0 || blockPos.x >= width || blockPos.y < 0 || blockPos.y >= height || blockPos.z < 0 || blockPos.z >= depth)return;


        if (!worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z].vis)
        {

                worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z].vis = true;
            if (worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z].type == 1)
                Instantiate(Blocksnow01, blockPos, Quaternion.identity);

            else if (worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z].type == 2)
                Instantiate(Blocksnow01, blockPos, Quaternion.identity);

            else if (worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z].type == 3)
                Instantiate(Blocksnow01, blockPos, Quaternion.identity);

            else
                worldBlock[(int)blockPos.x, (int)blockPos.y, (int)blockPos.z].vis = false;
            }
        }
    //ok
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            RaycastHit hit;
            Ray ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2.0f, Screen.height / 2.0f, 0));
            // hinter hit gibt der wert die reich weite an z.b 4.0f wie weit der char Blöcke abbauen kann von seinen stand punkt aus
            if (Physics.Raycast(ray, out hit, 4.0f))
            {
                Destroy(hit.transform.gameObject);
	}
	else if (Input.GetMouseButtonDown(1)) {
 RaycastHit hit;
 Ray ray = Camera.main.ScreenPointToRay (new Vector3 (Screen.width / 2f, Screen.height / 2f, 0));
 if (Physics.Raycast (ray, out hit, 50f)) {
  Vector3 newBlockPos = hit.transform.position;
  newBlockPos += hit.normal;
   CreateBlock ((int)newBlockPos.y, newBlockPos, true);
		}
	}
}
}

It Will Create Small Minecraft Chuncks

Block Adds
-Snow
-Grass
-Sand

This site is for people to post questions and then other people to answer them not to post an answer to a question that wasn’t asked.