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一直跑,已经可以当成小小的跑酷游戏玩了~

  • 相关阅读:
    HDU 5313 bitset优化背包
    bzoj 2595 斯坦纳树
    COJ 1287 求匹配串在模式串中出现的次数
    HDU 5381 The sum of gcd
    POJ 1739
    HDU 3377 插头dp
    HDU 1693 二进制表示的简单插头dp
    HDU 5353
    URAL 1519 基础插头DP
    UVA 10294 等价类计数
  • 原文地址:https://www.cnblogs.com/shamoyuu/p/unity_minecraft_05.html
Copyright © 2011-2022 走看看