【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,方块高度最大是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开发《我的世界》之六、创建地形(视频 + 源码)相关推荐
- 【Android 安全】DEX 加密 ( 代理 Application 开发 | 加载 dex 文件 | 使用反射获取方法创建本应用的 dexElements | 各版本创建 dex 数组源码对比 )
文章目录 一.不同 Android 系统创建 dex 数组源码对比 二.不同 Android 系统创建 dex 数组源码对比 三. Android 5.1 及以下系统反射方法并创建 Element[] ...
- Unity3D休闲射击类游戏《Survival Shooter》完整源码
Unity3D休闲射击类游戏<Survival Shooter>完整源码分享给大家学习,这个对于那些想要制作u3d射击类游戏有很大帮助. 运行环境是:Unity5.3.1 下载地址: ht ...
- unity3d游戏3d局域网联机吃球游戏完整项目源码分享
unity3d游戏3d局域网联机吃球游戏完整项目源码分享 免费下载地址: 链接:https://pan.baidu.com/s/1APlOCmoK9aUfiVJD48dBQA 提取码:p5nl 复制这 ...
- java计算机毕业设计vue开发一个简单音乐播放器(附源码、数据库)
java计算机毕业设计vue开发一个简单音乐播放器(附源码.数据库) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Ec ...
- 基于小程序开发的宝可梦图鉴小程序源码课程设计毕业设计
源码地址:基于小程序开发的宝可梦图鉴小程序源码课程设计毕业设计 宝可梦是一款备受喜爱的游戏,其丰富的剧情和可爱的角色深受玩家们的喜欢.而对于宝可梦爱好者来说,一款好的宝可梦图鉴是必不可少的.今天,我来 ...
- PHP开发B2C商城 微信小程序商城系统源码+数据库,轻量级前后端分离的电商系统,支持微信小程序 + H5+ 公众号 + APP
项目介绍 一款轻量级.高性能.前后端分离的电商系统,支持微信小程序 + H5+ 公众号 + APP,前后端源码完全开源,看见及所得,完美支持二次开发,可学习可商用,让您快速搭建个性化独立商城. 完整代 ...
- 基于微信小程序云开发的职业学校招生报名小程序源码,职业学校招生报名微信小程序源码 ,职业学校招生报名小程序源码
功能介绍 这是一个以报名为核心的职业学校招生小程序,目的是方便想要系统学习技能,入门某项技能或者领域的初高中毕业生,了解该学校的基本情况及各个专业,并提供报名路径,致力于技能型人才培养. 本程序前后端 ...
- 计算机毕业设计Javaweb开发数码产品推荐平台系统设计与实现(源码+系统+mysql数据库+lw文档)
计算机毕业设计Javaweb开发数码产品推荐平台系统设计与实现(源码+系统+mysql数据库+lw文档) 计算机毕业设计Javaweb开发数码产品推荐平台系统设计与实现(源码+系统+mysql数据库+ ...
- java计算机毕业设计vue.js开发红酒网站MyBatis+系统+LW文档+源码+调试部署
java计算机毕业设计vue.js开发红酒网站MyBatis+系统+LW文档+源码+调试部署 java计算机毕业设计vue.js开发红酒网站MyBatis+系统+LW文档+源码+调试部署 本源码技术栈 ...
- ios开发学习-手势交互(Gesture)效果源码分享
qianqianlianmeng ios开发学习-手势交互(Gesture)效果源码分享 All Around Pull View 介绍:实现视图四个方向(上下左右)都能够拖动更新(pull to r ...
最新文章
- Java Day01-2
- FPGA之道(30)编写自己的vhdl库文件
- BZOJ 1833 ZJOI2010 count 数字计数 数位DP
- SpringMVC处理自定义异常,通过读取配置文件把错误信息显示在前台页面
- [原创]微软拼音输入法2007(含64位版)
- 像素位移_1亿像素放大也清晰 OPPO Ace2超清四摄解析
- Mangos源码分析(2):服务器结构探讨之登录服的负载均衡
- C语言之strstr函数类似Java字符串的contain函数
- Apache Shiro第1部分–基础
- gnu linux中 使用,在Linux上使用GNU sed的方法
- Mysql varchar 字节长度
- linux查看主机硬件命令
- INSERT INTO SELECT语句概述和示例
- 男人30岁以前要作的事
- Backup Exec 在Windows平台下安装、设置及对Oracle数据库备份详细说明
- Linux ab压力测试工具安装教程
- Gradle教程——(二)Gradle介绍
- 好用的BUG、内存泄露捕捉工具 EurekaLog v6.0.3 Enterprise For D5-D2007
- The OpenGL® Shading Language, Version 4.60.7 翻译第一章
- Egret 学习笔记
热门文章
- 【智能柜领域】智能柜快递员端APP原型作品(另有整个项目原型)
- 成功人士必备“十商”,一张思维导图让你清晰认识自己
- MT6765 + Android9.0修改开机动画
- SQL显示当前数据库的名称和标识号
- 斐波纳契回调线_原来外汇高手是这样抓反转的!3大方法7种K线,带你抄底摸顶...
- 2018年上安徽c语言试卷答案,安徽省2018年中考物理试题(含答案).doc
- 实现类似微博视频滚动自动播放与暂停
- 用Mysql设计一张学生表
- k8s client-go 之 依赖问题解决
- C# Winform 自定义 日程日历控件