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

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

    一、引入LibNoise

    虽然Unity3D里也有一个Mathf.PerlinNoise,但是只能是2D的,这个可以生成3D的柏林噪音

    https://github.com/ricardojmendez/LibNoise.Unity

    二、创建GameManager对象

    这个对象很简单,就是用来管理随机数种子

    using System;
    using UnityEngine;
    
    public class GameManager : MonoBehaviour
    {
        public static int randomSeed;
    
        void Awake()
        {
            //让默认的随机数种子为当前的时间戳
            TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            randomSeed = (int)timeSpan.TotalSeconds;
        }
    }

    三、创建Terrain对象

    这个对象就是负责生成地形的,我们这里只是简单地通过世界坐标来获取一个噪音的值,然后通过这个值判断这个坐标上是什么方块。

    using LibNoise;
    using LibNoise.Generator;
    using Soultia.Util;
    using UnityEngine;
    
    public class Terrain : MonoBehaviour
    {
        //通过方块的世界坐标获取它的方块类型
        public static byte GetTerrainBlock(Vector3i worldPosition)
        {
            //LibNoise噪音对象
            Perlin noise = new LibNoise.Generator.Perlin(1f, 1f, 1f, 8, GameManager.randomSeed, QualityMode.High);
    
            //为随机数指定种子,这样每次随机的都是同样的值
            Random.InitState(GameManager.randomSeed);
            //因为柏林噪音在(0,0)点是上下左右对称的,所以我们设置一个很远很远的地方作为新的(0,0)点
            Vector3 offset = new Vector3(Random.value * 100000, Random.value * 100000, Random.value * 100000);
    
            float noiseX = Mathf.Abs((worldPosition.x + offset.x) / 20);
            float noiseY = Mathf.Abs((worldPosition.y + offset.y) / 20);
            float noiseZ = Mathf.Abs((worldPosition.z + offset.z) / 20);
            double noiseValue = noise.GetValue(noiseX, noiseY, noiseZ);
    
            noiseValue += (20 - worldPosition.y) / 15f;
            noiseValue /= worldPosition.y / 5f;
    
            if (noiseValue > 0.5f)
            {
                return 1;
            }
    
            return 0;
        }
    }

    四、修改PlayerController,添加Y轴的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 y = transform.position.y - Chunk.height * 3; y < transform.position.y + Chunk.height * 3; y += Chunk.height)
                {
                    //Y轴上是允许最大16个Chunk,方块高度最大是256
                    if (y <= Chunk.height * 16 && y > 0)
                    {
                        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 yy = Chunk.height * Mathf.FloorToInt(y / Chunk.height);
                            int zz = Chunk.width * Mathf.FloorToInt(z / Chunk.width);
                            if (!Map.instance.ChunkExists(xx, yy, zz))
                            {
                                Map.instance.CreateChunk(new Vector3i(xx, yy, zz));
                            }
                        }
                    }
                }
            }
        }
    }

    五、修改Chunk对象

    1.修改CreateMap方法来通过噪音生成地形

    2.修改Chunk生成地形的方法

    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是否正在生成中
            public static bool isWorking = false;
            private bool isFinished = 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 Update()
            {
                if (isWorking == false && isFinished == false)
                {
                    isFinished = true;
                    StartFunction();
                }
            }
    
    
            void StartFunction()
            {
                isWorking = true;
                mesh = new Mesh();
                mesh.name = "Chunk";
    
                StartCoroutine(CreateMap());
            }
    
            IEnumerator CreateMap()
            {
                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++)
                        {
                            byte blockid = Terrain.GetTerrainBlock(new Vector3i(x, y, z) + position);
                            if (blockid == 1 && Terrain.GetTerrainBlock(new Vector3i(x, y + 1, z) + position) == 0)
                            {
                                blocks[x, y, z] = 2;
                            }
                            else
                            {
                                blocks[x, y, z] = Terrain.GetTerrainBlock(new Vector3i(x, y, z) + position);
                            }
                        }
                    }
                }
    
                yield return null;
                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));
            }
        }
    }

    源码下载地址

    链接: https://pan.baidu.com/s/1o8uqNY6
    密码: hgar

    放置和破坏方块是比较简单的,算是留给读者的一个小作业吧,如果你理解了我这整个系列教程中每一个知识点的话。提示:改变Chunk中blocks的值,然后重新绘制。

     

    至此,我们Unity3D开发《我的世界》的教程就到此结束了。

    但是我们离《我的世界》还差得很远很远很远很远。。。。。

    《我的世界》中的五大难点

    1.方块构成地形

    2.通过生态构成自然的地形

    3.液体

    4.光照

    5.红石电路

    我们只勉勉强强完成了1,其他4个每一个都是极大的挑战,如果读者感兴趣,可以自己试一下。

    《我的世界》这么火,真的不无道理。

     

    完结,散花

  • 相关阅读:
    不断学习和思考让自己成长、过得充实快乐!
    先制订个能力提升计划
    如何删除SQL Server下注册的服务器
    [转摘] 我的同学聚会--性格决定命运
    如何提高阅读速度2
    Oracle IW,WW的区别
    想象5年后的你(很多人看了很受启迪!)
    女要富养
    5年内买车买房(理财篇)
    [转摘] 从月薪3500到身价700万 我在上海的奋斗岁月
  • 原文地址:https://www.cnblogs.com/shamoyuu/p/unity_minecraft_06.html
Copyright © 2011-2022 走看看