zoukankan      html  css  js  c++  java
  • 【Unity3D】Unity3D开发《我的世界》之五、创建无限地形(视频)

    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/unity_minecraft_05.html

    一、导入Unity3D自带的第一人称角色控制器

    直接导入就行,我们用FPSController。

    二、为Map添加创建Chunk和判断Chunk是否存在的方法

    using Soultia.Util;
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    namespace Soultia.Voxel
    {
        public class Map : MonoBehaviour
        {
            public static Map instance;
    
            public static GameObject chunkPrefab;
            
            public Dictionary<Vector3i, GameObject> chunks = new Dictionary<Vector3i, GameObject>();
    
    
            //当前是否正在生成Chunk
            private bool spawningChunk = false;
    
            void Awake()
            {
                instance = this;
                chunkPrefab = Resources.Load("Prefab/Chunk") as GameObject;
            }
    
            //生成Chunk
            public void CreateChunk(Vector3i pos)
            {
                if (spawningChunk) return;
    
                StartCoroutine(SpawnChunk(pos));
            }
    
            private IEnumerator SpawnChunk(Vector3i pos)
            {
                spawningChunk = true;
                Instantiate(chunkPrefab, pos, Quaternion.identity);
                yield return null;
                spawningChunk = false;
            }
    
            //通过Chunk的坐标来判断它是否存在
            public bool ChunkExists(Vector3i worldPosition)
            {
                return this.ChunkExists(worldPosition.x, worldPosition.y, worldPosition.z);
            }
            //通过Chunk的坐标来判断它是否存在
            public bool ChunkExists(int x, int y, int z)
            {
                return chunks.ContainsKey(new Vector3i(x, y, z));
            }
        }
    }

    上上一章用来测试的Start方法也删掉了,我们下面会通过玩家的位置来生成

    三、添加草方块

    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);
    
            Block grass = new Block(2, "Grass", 3, 31, 0, 31, 2, 31);
            blocks.Add(grass.id, grass);
        }
    
        public static Block GetBlock(byte id)
        {
            return blocks.ContainsKey(id) ? blocks[id] : null;
        }
    }

    四、修改Chunk

    using Soultia.Util;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    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.ChunkExists(position))
                {
                    Debug.Log("此方块已存在" + position);
                    Destroy(this);
                }
                else
                {
                    Map.instance.chunks.Add(position, this.gameObject);
                    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++)
                        {
                            if (y == Chunk.height - 1)
                            {
                                if (Random.Range(1, 5) == 1)
                                {
                                    blocks[x, y, z] = 2;
                                }
                            }
                            else
                            {
                                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;
            }
    
            //此坐标方块是否透明,Chunk中的局部坐标
            public bool IsBlockTransparent(int x, int y, int z)
            {
                if (x >= width || y >= height || z >= width || x < 0 || y < 0 || z < 0)
                {
                    return true;
                }
                else
                {
                    //如果当前方块的id是0,那的确是透明的
                    return this.blocks[x, y, z] == 0;
                }
            }
    
    
            
            //前面
            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));
            }
        }
    }

    我们修改了Chunk的CreateMap方法,让它在最顶部有一定几率生成草方块,便于我们直观地看到无限地形的生成。

    然后修改了Start方法,判断了它是否存在,如果不存在,就把它添加到Map的chunks里,如果已经存在了,就销毁它。

    五、添加PlayerController

    这个对象就是用来检测玩家周围一定范围内的Chunk是否已经生成,如果没有生成就会生成它。

    using Soultia.Util;
    using Soultia.Voxel;
    using UnityEngine;
    
    public class PlayerController : MonoBehaviour
    {
        //视线范围
        public int viewRange = 30;
    
    
        void Update()
        {
            for (float x = transform.position.x - Chunk.width * 3; x < transform.position.x + Chunk.width * 3; x += Chunk.width)
            {
                for (float z = transform.position.z - Chunk.width * 3; z < transform.position.z + Chunk.width * 3; z += Chunk.width)
                {
                    int xx = Chunk.width * Mathf.FloorToInt(x / Chunk.width);
                    int zz = Chunk.width * Mathf.FloorToInt(z / Chunk.width);
                    if (!Map.instance.ChunkExists(xx, 0, zz))
                    {
                        Map.instance.CreateChunk(new Vector3i(xx, 0, zz));
                    }
                }
            }
        }
    }

    然后把它拖给玩家

    到这里就已经可以生成无限地形了,按住Shift一直跑,已经可以当成小小的跑酷游戏玩了~

  • 相关阅读:
    WPF Layout & Image异步加载
    WPF Binding Validation 数据验证
    推荐一个.NET 命令行参数Parser 库
    Windows 下 命令行增强工具
    Windbg 离线调试.Net 程序入门
    拼写检查算法 Golang 版
    新Blog
    WPF 实现Loading效果
    struct结构体的变长特性
    第2章 构造函数语意学
  • 原文地址:https://www.cnblogs.com/shamoyuu/p/unity_minecraft_05.html
Copyright © 2011-2022 走看看