轉載請注明出處:http://www.cnblogs.com/shamoyuu/p/unity_minecraft_04.html
一、新建Block類
我們的Block類用來存儲跟Block相關的信息,例如name,id,貼圖坐標等等
using UnityEngine; /// <summary> /// 方塊的方向 /// </summary> public enum BlockDirection : byte { Front = 0, Back = 1, Left = 2, Right = 3, Top = 4, Bottom = 5 } /// <summary> /// 方塊對象,存儲方塊的所有信息 /// </summary> public class Block { //方塊的ID public byte id; //方塊的名字 public string name; //方塊的圖標,並不會采用在游戲中動態生成的做法 public Texture icon; //方向(指的是前面所面朝的方向) public BlockDirection direction = BlockDirection.Front; //前面貼圖的坐標 public byte textureFrontX; public byte textureFrontY; //后面貼圖的坐標 public byte textureBackX; public byte textureBackY; //右面貼圖的坐標 public byte textureRightX; public byte textureRightY; //左面貼圖的坐標 public byte textureLeftX; public byte textureLeftY; //上面貼圖的坐標 public byte textureTopX; public byte textureTopY; //下面貼圖的坐標 public byte textureBottomX; public byte textureBottomY; //都是A面的方塊 public Block(byte id, string name, byte textureX, byte textureY) : this(id, name, textureX, textureY, textureX, textureY, textureX, textureY, textureX, textureY) { } //上面是A,其他面是B的方塊 public Block(byte id, string name, byte textureX, byte textureY, byte textureTopX, byte textureTopY) : this(id, name, textureX, textureY, textureX, textureY, textureX, textureY, textureX, textureY, textureTopX, textureTopY, textureX, textureY) { } //上面是A,下面是B,其他面是C的方塊 public Block(byte id, string name, byte textureX, byte textureY, byte textureTopX, byte textureTopY, byte textureBottomX, byte textureBottomY) : this(id, name, textureX, textureY, textureX, textureY, textureX, textureY, textureX, textureY, textureTopX, textureTopY, textureBottomX, textureBottomY) { } //上面是A,下面是B,前面是C,其他面是D的方塊 public Block(byte id, string name, byte textureFrontX, byte textureFrontY, byte textureX, byte textureY, byte textureTopX, byte textureTopY, byte textureBottomX, byte textureBottomY) : this(id, name, textureFrontX, textureFrontY, textureX, textureY, textureX, textureY, textureX, textureY, textureTopX, textureTopY, textureBottomX, textureBottomY) { } //上下左右前后面都不一樣的方塊 public Block(byte id, string name, byte textureFrontX, byte textureFrontY, byte textureBackX, byte textureBackY, byte textureRightX, byte textureRightY, byte textureLeftX, byte textureLeftY, byte textureTopX, byte textureTopY, byte textureBottomX, byte textureBottomY) { this.id = id; this.name = name; this.textureFrontX = textureFrontX; this.textureFrontY = textureFrontY; this.textureBackX = textureBackX; this.textureBackY = textureBackY; this.textureRightX = textureRightX; this.textureRightY = textureRightY; this.textureLeftX = textureLeftX; this.textureLeftY = textureLeftY; this.textureTopX = textureTopX; this.textureTopY = textureTopY; this.textureBottomX = textureBottomX; this.textureBottomY = textureBottomY; } }
最上面我們定義了方塊方向,不過我們暫時不會用到,先放着。
然后我們定義了方塊的基礎信息,還有它各個面上的貼圖。
重載了多個構造函數來生成方塊,都是A面的那個應該比較常用。
貼圖的坐標是0~1的float,我們把一張貼圖划分成了32×32的(如下圖),每一個瓦片寬高都是1/32,Block的貼圖坐標就是這么算出來的。

圖是高清圖,右鍵保存下來,拖給Chunk的預制體
二、新建BlockList類
BlockList類,用來管理我們所有的Block,我們先簡單地用它生成一個土塊。
using System.Collections.Generic; using UnityEngine; /// <summary> /// 存儲所有的Block對象的信息 /// </summary> public class BlockList : MonoBehaviour { public static Dictionary<byte, Block> blocks = new Dictionary<byte, Block>(); void Awake() { Block dirt = new Block(1, "Dirt", 2, 31); blocks.Add(dirt.id, dirt); } public static Block GetBlock(byte id) { return blocks[id]; } }
這里Block dirt = new Block(1, "Dirt", 2, 31)中的2,31是以左下角為起點開始數的,左下角的貼圖是0,0,所以2,31是我們最頂部第3個瓦片
三、修改Chunk,為Block添加UV坐標
我們新建一個private List<Vector2> uv = new List<Vector2>(),用來存儲uv點的信息,它和vertices的用法是一樣的,我們也是從左下角開始,逆時針循環。

我們按照這個順序添加點就可以了
using Soultia.Util; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Soultia.Voxel { [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] [RequireComponent(typeof(MeshCollider))] public class Chunk : MonoBehaviour { public static int width = 16; public static int height = 16; public byte[,,] blocks; public Vector3i position; private Mesh mesh; //面需要的點 private List<Vector3> vertices = new List<Vector3>(); //生成三邊面時用到的vertices的index private List<int> triangles = new List<int>(); //所有的uv信息 private List<Vector2> uv = new List<Vector2>(); //uv貼圖每行每列的寬度(0~1),這里我的貼圖是32×32的,所以是1/32 public static float textureOffset = 1 / 32f; //讓UV稍微縮小一點,避免出現它旁邊的貼圖 public static float shrinkSize = 0.001f; //當前Chunk是否正在生成中 private bool isWorking = false; void Start() { position = new Vector3i(this.transform.position); if (Map.instance.chunks.ContainsKey(position)) { Destroy(this); } else { this.name = "(" + position.x + "," + position.y + "," + position.z + ")"; StartFunction(); } } void StartFunction() { mesh = new Mesh(); mesh.name = "Chunk"; StartCoroutine(CreateMap()); } IEnumerator CreateMap() { while (isWorking) { yield return null; } isWorking = true; blocks = new byte[width, height, width]; for (int x = 0; x < Chunk.width; x++) { for (int y = 0; y < Chunk.height; y++) { for (int z = 0; z < Chunk.width; z++) { blocks[x, y, z] = 1; } } } StartCoroutine(CreateMesh()); } IEnumerator CreateMesh() { vertices.Clear(); triangles.Clear(); //把所有面的點和面的索引添加進去 for (int x = 0; x < Chunk.width; x++) { for (int y = 0; y < Chunk.height; y++) { for (int z = 0; z < Chunk.width; z++) { //獲取當前坐標的Block對象 Block block = BlockList.GetBlock(this.blocks[x, y, z]); if (block == null) continue; if (IsBlockTransparent(x + 1, y, z)) { AddFrontFace(x, y, z, block); } if (IsBlockTransparent(x - 1, y, z)) { AddBackFace(x, y, z, block); } if (IsBlockTransparent(x, y, z + 1)) { AddRightFace(x, y, z, block); } if (IsBlockTransparent(x, y, z - 1)) { AddLeftFace(x, y, z, block); } if (IsBlockTransparent(x, y + 1, z)) { AddTopFace(x, y, z, block); } if (IsBlockTransparent(x, y - 1, z)) { AddBottomFace(x, y, z, block); } } } } //為點和index賦值 mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.uv = uv.ToArray(); //重新計算頂點和法線 mesh.RecalculateBounds(); mesh.RecalculateNormals(); //將生成好的面賦值給組件 this.GetComponent<MeshFilter>().mesh = mesh; this.GetComponent<MeshCollider>().sharedMesh = mesh; yield return null; isWorking = false; } public static bool IsBlockTransparent(int x, int y, int z) { if (x >= width || y >= height || z >= width || x < 0 || y < 0 || z < 0) { return true; } return false; } //前面 void AddFrontFace(int x, int y, int z, Block block) { //第一個三角面 triangles.Add(0 + vertices.Count); triangles.Add(3 + vertices.Count); triangles.Add(2 + vertices.Count); //第二個三角面 triangles.Add(2 + vertices.Count); triangles.Add(1 + vertices.Count); triangles.Add(0 + vertices.Count); //添加4個點 vertices.Add(new Vector3(0 + x, 0 + y, 0 + z)); vertices.Add(new Vector3(0 + x, 0 + y, 1 + z)); vertices.Add(new Vector3(0 + x, 1 + y, 1 + z)); vertices.Add(new Vector3(0 + x, 1 + y, 0 + z)); //添加UV坐標點,跟上面4個點循環的順序一致 uv.Add(new Vector2(block.textureFrontX * textureOffset, block.textureFrontY * textureOffset) + new Vector2(shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureFrontX * textureOffset + textureOffset, block.textureFrontY * textureOffset) + new Vector2(-shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureFrontX * textureOffset + textureOffset, block.textureFrontY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize)); uv.Add(new Vector2(block.textureFrontX * textureOffset, block.textureFrontY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize)); } //背面 void AddBackFace(int x, int y, int z, Block block) { //第一個三角面 triangles.Add(0 + vertices.Count); triangles.Add(3 + vertices.Count); triangles.Add(2 + vertices.Count); //第二個三角面 triangles.Add(2 + vertices.Count); triangles.Add(1 + vertices.Count); triangles.Add(0 + vertices.Count); //添加4個點 vertices.Add(new Vector3(-1 + x, 0 + y, 1 + z)); vertices.Add(new Vector3(-1 + x, 0 + y, 0 + z)); vertices.Add(new Vector3(-1 + x, 1 + y, 0 + z)); vertices.Add(new Vector3(-1 + x, 1 + y, 1 + z)); //添加UV坐標點,跟上面4個點循環的順序一致 uv.Add(new Vector2(block.textureBackX * textureOffset, block.textureBackY * textureOffset) + new Vector2(shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureBackX * textureOffset + textureOffset, block.textureBackY * textureOffset) + new Vector2(-shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureBackX * textureOffset + textureOffset, block.textureBackY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize)); uv.Add(new Vector2(block.textureBackX * textureOffset, block.textureBackY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize)); } //右面 void AddRightFace(int x, int y, int z, Block block) { //第一個三角面 triangles.Add(0 + vertices.Count); triangles.Add(3 + vertices.Count); triangles.Add(2 + vertices.Count); //第二個三角面 triangles.Add(2 + vertices.Count); triangles.Add(1 + vertices.Count); triangles.Add(0 + vertices.Count); //添加4個點 vertices.Add(new Vector3(0 + x, 0 + y, 1 + z)); vertices.Add(new Vector3(-1 + x, 0 + y, 1 + z)); vertices.Add(new Vector3(-1 + x, 1 + y, 1 + z)); vertices.Add(new Vector3(0 + x, 1 + y, 1 + z)); //添加UV坐標點,跟上面4個點循環的順序一致 uv.Add(new Vector2(block.textureRightX * textureOffset, block.textureRightY * textureOffset) + new Vector2(shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureRightX * textureOffset + textureOffset, block.textureRightY * textureOffset) + new Vector2(-shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureRightX * textureOffset + textureOffset, block.textureRightY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize)); uv.Add(new Vector2(block.textureRightX * textureOffset, block.textureRightY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize)); } //左面 void AddLeftFace(int x, int y, int z, Block block) { //第一個三角面 triangles.Add(0 + vertices.Count); triangles.Add(3 + vertices.Count); triangles.Add(2 + vertices.Count); //第二個三角面 triangles.Add(2 + vertices.Count); triangles.Add(1 + vertices.Count); triangles.Add(0 + vertices.Count); //添加4個點 vertices.Add(new Vector3(-1 + x, 0 + y, 0 + z)); vertices.Add(new Vector3(0 + x, 0 + y, 0 + z)); vertices.Add(new Vector3(0 + x, 1 + y, 0 + z)); vertices.Add(new Vector3(-1 + x, 1 + y, 0 + z)); //添加UV坐標點,跟上面4個點循環的順序一致 uv.Add(new Vector2(block.textureLeftX * textureOffset, block.textureLeftY * textureOffset) + new Vector2(shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureLeftX * textureOffset + textureOffset, block.textureLeftY * textureOffset) + new Vector2(-shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureLeftX * textureOffset + textureOffset, block.textureLeftY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize)); uv.Add(new Vector2(block.textureLeftX * textureOffset, block.textureLeftY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize)); } //上面 void AddTopFace(int x, int y, int z, Block block) { //第一個三角面 triangles.Add(1 + vertices.Count); triangles.Add(0 + vertices.Count); triangles.Add(3 + vertices.Count); //第二個三角面 triangles.Add(3 + vertices.Count); triangles.Add(2 + vertices.Count); triangles.Add(1 + vertices.Count); //添加4個點 vertices.Add(new Vector3(0 + x, 1 + y, 0 + z)); vertices.Add(new Vector3(0 + x, 1 + y, 1 + z)); vertices.Add(new Vector3(-1 + x, 1 + y, 1 + z)); vertices.Add(new Vector3(-1 + x, 1 + y, 0 + z)); //添加UV坐標點,跟上面4個點循環的順序一致 uv.Add(new Vector2(block.textureTopX * textureOffset, block.textureTopY * textureOffset) + new Vector2(shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureTopX * textureOffset + textureOffset, block.textureTopY * textureOffset) + new Vector2(-shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureTopX * textureOffset + textureOffset, block.textureTopY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize)); uv.Add(new Vector2(block.textureTopX * textureOffset, block.textureTopY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize)); } //下面 void AddBottomFace(int x, int y, int z, Block block) { //第一個三角面 triangles.Add(1 + vertices.Count); triangles.Add(0 + vertices.Count); triangles.Add(3 + vertices.Count); //第二個三角面 triangles.Add(3 + vertices.Count); triangles.Add(2 + vertices.Count); triangles.Add(1 + vertices.Count); //添加4個點 vertices.Add(new Vector3(-1 + x, 0 + y, 0 + z)); vertices.Add(new Vector3(-1 + x, 0 + y, 1 + z)); vertices.Add(new Vector3(0 + x, 0 + y, 1 + z)); vertices.Add(new Vector3(0 + x, 0 + y, 0 + z)); //添加UV坐標點,跟上面4個點循環的順序一致 uv.Add(new Vector2(block.textureBottomX * textureOffset, block.textureBottomY * textureOffset) + new Vector2(shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureBottomX * textureOffset + textureOffset, block.textureBottomY * textureOffset) + new Vector2(-shrinkSize, shrinkSize)); uv.Add(new Vector2(block.textureBottomX * textureOffset + textureOffset, block.textureBottomY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize)); uv.Add(new Vector2(block.textureBottomX * textureOffset, block.textureBottomY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize)); } } }
在CreateMesh方法中我們獲取到了當前的Block對象,然后在繪制面的時候,根據Block的信息繪制了它對應的貼圖。
需要注意的是,每一個uv點,我們都加了一個shrinkSize的偏差值,這是因為如果緊貼着邊緣獲取瓦片的話,當前的瓦片可能多取到旁邊瓦片的1像素,形成鋸齒

我們添加了這個偏差值以后就好了


我們加了偏差值以后,是少取了這個瓦片外面的一圈,這樣即使某些情況下多取一像素也沒事。
