zoukankan      html  css  js  c++  java
  • 【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像素,形成锯齿

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

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

  • 相关阅读:
    BUUCTF | [极客大挑战 2019]PHP
    BUUCTF | [极客大挑战 2019]Secret File
    [网鼎杯 2018] Fakebook 复现
    [强网杯2019 随便注]总结
    [Flask(Jinja2)服务端模板注入漏洞(SSTI)]学习简记
    [Flask框架]学习简记
    ADO.NET基础
    SQL Server基础
    AdventureWorks2012下载链接
    SQL Server2012从入门到精通
  • 原文地址:https://www.cnblogs.com/shamoyuu/p/unity_minecraft_04.html
Copyright © 2011-2022 走看看