【Unity3D】Unity3D开发《我的世界》之四、创建一个Block
转载请注明出处: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像素,形成锯齿
我们添加了这个偏差值以后就好了
我们加了偏差值以后,是少取了这个瓦片外面的一圈,这样即使某些情况下多取一像素也没事。