Unity3D开发《我的世界》之创建一个Block

Posted 静茹♂鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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像素,形成锯齿

 

我们添加了这个偏差值以后就好了

 

我们加了偏差值以后,是少取了这个瓦片外面的一圈,这样即使某些情况下多取一像素也没事。

 

以上是关于Unity3D开发《我的世界》之创建一个Block的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D开发《我的世界》之创建无限地形

Unity3D开发《我的世界》之七番外篇

[Unity3D]Unity3D游戏开发之伤害数值显示

Unity3D游戏开发 地形系统

Unity3D之笛卡尔坐标系转换——屏幕坐标转换世界坐标,世界坐标转换相机坐标工具

Unity3D之笛卡尔坐标系转换——屏幕坐标转换世界坐标,世界坐标转换相机坐标工具