转载请注明出处: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,方块高度最大是256if (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的indexprivate List<int> triangles = new List<int>();//所有的uv信息private List<Vector2> uv = new List<Vector2>();//uv贴图每行每列的宽度(0~1),这里我的贴图是32×32的,所以是1/32public 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个每一个都是极大的挑战,如果读者感兴趣,可以自己试一下。

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

完结,散花

转载于:https://www.cnblogs.com/shamoyuu/p/unity_minecraft_06.html

【Unity3D】Unity3D开发《我的世界》之六、创建地形(视频 + 源码)相关推荐

  1. 【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 使用反射获取方法创建本应用的 dexElements | 各版本创建 dex 数组源码对比 )

    文章目录 一.不同 Android 系统创建 dex 数组源码对比 二.不同 Android 系统创建 dex 数组源码对比 三. Android 5.1 及以下系统反射方法并创建 Element[] ...

  2. Unity3D休闲射击类游戏《Survival Shooter》完整源码

    Unity3D休闲射击类游戏<Survival Shooter>完整源码分享给大家学习,这个对于那些想要制作u3d射击类游戏有很大帮助. 运行环境是:Unity5.3.1 下载地址: ht ...

  3. unity3d游戏3d局域网联机吃球游戏完整项目源码分享

    unity3d游戏3d局域网联机吃球游戏完整项目源码分享 免费下载地址: 链接:https://pan.baidu.com/s/1APlOCmoK9aUfiVJD48dBQA 提取码:p5nl 复制这 ...

  4. java计算机毕业设计vue开发一个简单音乐播放器(附源码、数据库)

    java计算机毕业设计vue开发一个简单音乐播放器(附源码.数据库) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Ec ...

  5. 基于小程序开发的宝可梦图鉴小程序源码课程设计毕业设计

    源码地址:基于小程序开发的宝可梦图鉴小程序源码课程设计毕业设计 宝可梦是一款备受喜爱的游戏,其丰富的剧情和可爱的角色深受玩家们的喜欢.而对于宝可梦爱好者来说,一款好的宝可梦图鉴是必不可少的.今天,我来 ...

  6. PHP开发B2C商城 微信小程序商城系统源码+数据库,轻量级前后端分离的电商系统,支持微信小程序 + H5+ 公众号 + APP

    项目介绍 一款轻量级.高性能.前后端分离的电商系统,支持微信小程序 + H5+ 公众号 + APP,前后端源码完全开源,看见及所得,完美支持二次开发,可学习可商用,让您快速搭建个性化独立商城. 完整代 ...

  7. 基于微信小程序云开发的职业学校招生报名小程序源码,职业学校招生报名微信小程序源码 ,职业学校招生报名小程序源码

    功能介绍 这是一个以报名为核心的职业学校招生小程序,目的是方便想要系统学习技能,入门某项技能或者领域的初高中毕业生,了解该学校的基本情况及各个专业,并提供报名路径,致力于技能型人才培养. 本程序前后端 ...

  8. 计算机毕业设计Javaweb开发数码产品推荐平台系统设计与实现(源码+系统+mysql数据库+lw文档)

    计算机毕业设计Javaweb开发数码产品推荐平台系统设计与实现(源码+系统+mysql数据库+lw文档) 计算机毕业设计Javaweb开发数码产品推荐平台系统设计与实现(源码+系统+mysql数据库+ ...

  9. java计算机毕业设计vue.js开发红酒网站MyBatis+系统+LW文档+源码+调试部署

    java计算机毕业设计vue.js开发红酒网站MyBatis+系统+LW文档+源码+调试部署 java计算机毕业设计vue.js开发红酒网站MyBatis+系统+LW文档+源码+调试部署 本源码技术栈 ...

  10. ios开发学习-手势交互(Gesture)效果源码分享

    qianqianlianmeng ios开发学习-手势交互(Gesture)效果源码分享 All Around Pull View 介绍:实现视图四个方向(上下左右)都能够拖动更新(pull to r ...

最新文章

  1. Java Day01-2
  2. FPGA之道(30)编写自己的vhdl库文件
  3. BZOJ 1833 ZJOI2010 count 数字计数 数位DP
  4. SpringMVC处理自定义异常,通过读取配置文件把错误信息显示在前台页面
  5. [原创]微软拼音输入法2007(含64位版)
  6. 像素位移_1亿像素放大也清晰 OPPO Ace2超清四摄解析
  7. Mangos源码分析(2):服务器结构探讨之登录服的负载均衡
  8. C语言之strstr函数类似Java字符串的contain函数
  9. Apache Shiro第1部分–基础
  10. gnu linux中 使用,在Linux上使用GNU sed的方法
  11. Mysql varchar 字节长度
  12. linux查看主机硬件命令
  13. INSERT INTO SELECT语句概述和示例
  14. 男人30岁以前要作的事
  15. Backup Exec 在Windows平台下安装、设置及对Oracle数据库备份详细说明
  16. Linux ab压力测试工具安装教程
  17. Gradle教程——(二)Gradle介绍
  18. 好用的BUG、内存泄露捕捉工具 EurekaLog v6.0.3 Enterprise For D5-D2007
  19. The OpenGL® Shading Language, Version 4.60.7 翻译第一章
  20. Egret 学习笔记

热门文章

  1. 【智能柜领域】智能柜快递员端APP原型作品(另有整个项目原型)
  2. 成功人士必备“十商”,一张思维导图让你清晰认识自己
  3. MT6765 + Android9.0修改开机动画
  4. SQL显示当前数据库的名称和标识号
  5. 斐波纳契回调线_原来外汇高手是这样抓反转的!3大方法7种K线,带你抄底摸顶...
  6. 2018年上安徽c语言试卷答案,安徽省2018年中考物理试题(含答案).doc
  7. 实现类似微博视频滚动自动播放与暂停
  8. 用Mysql设计一张学生表
  9. k8s client-go 之 依赖问题解决
  10. C# Winform 自定义 日程日历控件