【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(, , , , , , );
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, , GameManager.randomSeed, QualityMode.High); //为随机数指定种子,这样每次随机的都是同样的值
Random.InitState(GameManager.randomSeed);
//因为柏林噪音在(0,0)点是上下左右对称的,所以我们设置一个很远很远的地方作为新的(0,0)点
Vector3 offset = new Vector3(Random.value * , Random.value * , Random.value * ); float noiseX = Mathf.Abs((worldPosition.x + offset.x) / );
float noiseY = Mathf.Abs((worldPosition.y + offset.y) / );
float noiseZ = Mathf.Abs((worldPosition.z + offset.z) / );
double noiseValue = noise.GetValue(noiseX, noiseY, noiseZ); noiseValue += ( - worldPosition.y) / 15f;
noiseValue /= worldPosition.y / 5f; if (noiseValue > 0.5f)
{
return ;
} return ;
}
}
四、修改PlayerController,添加Y轴的Chunk生成
using Soultia.Util;
using Soultia.Voxel;
using UnityEngine; public class PlayerController : MonoBehaviour
{
//视线范围
public int viewRange = ; void Update()
{
for (float x = transform.position.x - Chunk.width * ; x < transform.position.x + Chunk.width * ; x += Chunk.width)
{
for (float y = transform.position.y - Chunk.height * ; y < transform.position.y + Chunk.height * ; y += Chunk.height)
{
//Y轴上是允许最大16个Chunk,方块高度最大是256
if (y <= Chunk.height * && y > )
{
for (float z = transform.position.z - Chunk.width * ; z < transform.position.z + Chunk.width * ; 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 = ;
public static int height = ; 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 = / 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 = ; x < Chunk.width; x++)
{
for (int y = ; y < Chunk.height; y++)
{
for (int z = ; z < Chunk.width; z++)
{
byte blockid = Terrain.GetTerrainBlock(new Vector3i(x, y, z) + position);
if (blockid == && Terrain.GetTerrainBlock(new Vector3i(x, y + , z) + position) == )
{
blocks[x, y, z] = ;
}
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 = ; x < Chunk.width; x++)
{
for (int y = ; y < Chunk.height; y++)
{
for (int z = ; z < Chunk.width; z++)
{
//获取当前坐标的Block对象
Block block = BlockList.GetBlock(this.blocks[x, y, z]);
if (block == null) continue; if (IsBlockTransparent(x + , y, z))
{
AddFrontFace(x, y, z, block);
}
if (IsBlockTransparent(x - , y, z))
{
AddBackFace(x, y, z, block);
}
if (IsBlockTransparent(x, y, z + ))
{
AddRightFace(x, y, z, block);
}
if (IsBlockTransparent(x, y, z - ))
{
AddLeftFace(x, y, z, block);
}
if (IsBlockTransparent(x, y + , z))
{
AddTopFace(x, y, z, block);
}
if (IsBlockTransparent(x, y - , 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 < || y < || z < )
{
return true;
}
else
{
//如果当前方块的id是0,那的确是透明的
return this.blocks[x, y, z] == ;
}
} //前面
void AddFrontFace(int x, int y, int z, Block block)
{
//第一个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + 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( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + 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( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + 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( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + 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( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + 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( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + 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个每一个都是极大的挑战,如果读者感兴趣,可以自己试一下。
《我的世界》这么火,真的不无道理。
完结,散花
【Unity3D】Unity3D开发《我的世界》之六、创建地形(视频 + 源码)的更多相关文章
- Python开发技术详解(视频+源码+文档)
Python, 是一种面向对象.直译式计算机程序设计语言.Python语法简捷而清晰,具有丰富和强大的类库.它常被昵称为胶水语言,它能够很轻松的把用其他语言制作的各种模块(尤其是C/C++)轻松地联结 ...
- Maven自定义绑定插件目标:创建项目的源码jar
<build> <plugins> <!-- 自定义绑定,创建项目的源码jar --> <plugin> <groupId>org.apac ...
- C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)
C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...
- Firefly卡牌手游《暗黑世界V1.5》服务器端源码+GM管理后台源码
http://www.9miao.com/content-6-304.html Firefly卡牌手游<暗黑世界V1.5>服务器端源码+GM管理后台源码 关于<暗黑世界V1.5> ...
- c++实现游戏开发中常用的对象池(含源码)
c++实现游戏开发中常用的对象池(含源码) little_stupid_child2017-01-06上传 对象池的五要素: 1.对象集合 2.未使用对象索引集合 3.已使用对象索引集合 4.当前 ...
- 第四次作业;创建raid5,源码编译安装;磁盘配额
创建raid5 格式化 ext4 创建物理卷: 创建卷组: 创建逻辑卷: 格式化 ext4 挂载 开机自启动 创建raid配置文件 源码编译安装: 创建本地yum仓库 umount /dev/sr0 ...
- BAT推荐免费下载JAVA转型大数据开发全链路教程(视频+源码)价值19880元
如今随着环境的改变,物联网.AI.大数据.人工智能等,是未来的大趋势,而大数据是这些基石,万物互联,机器学习都是大数据应用场景! 为什么要学习大数据?我们JAVA到底要不要转型大数据? 好比问一个程序 ...
- Netty 学习(七):NioEventLoop 对应线程的创建和启动源码说明
Netty 学习(七):NioEventLoop 对应线程的创建和启动源码说明 作者: Grey 原文地址: 博客园:Netty 学习(七):NioEventLoop 对应线程的创建和启动源码说明 C ...
- iOS项目开发实战——iOS网络编程获取网页Html源码
现在我们身处互联网的时代.不论什么一个软件或是App,都会或多或少与网络打交道,并不断发生数据交互.一个没有涉及网络编程的应用会显得比較low,这里我们将会開始使用Swift开发iOS应用,而且主要来 ...
随机推荐
- vim批量注释
vim批量注释 法一.在vim中 :20,30 s/^/#/g 20-30行 用 # 注释掉.(python是用#注释地---) :20,30 s/^#//g 20-30行 取消注释 法二. 1.多行 ...
- C#面向对象方式设置、读取应用配置
关注点: 1.用面向对象方式的方式(get,set)访问和设置配置项 2.“CallerMemberName”在.net 4以下的变通方式 最后一周了,大伙都进入过年模式了.身还在,心已远.最近事情不 ...
- 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](九)
前言 童鞋们,大家好 我是专注.NET开发者社区建设的实践者Rector. 首先,为自己间隔了两个星期五再更新本系列文章找个不充分的理由:Rector最近工作,家庭的各种事务所致,希望大家谅解. 本文 ...
- FTP配置之 chroot_list 用户切换文件夹
FTP配置文件vsftpd.conf关于限制用户在默认目录的配置,涉及到三个字段:chroot_local_user,chroot_list_enable,chroot_list_file. 我们按顺 ...
- 基本c功能使用不当导致崩溃
一些基本的c语言操作,使用不当也会有出其不意的问题.比如我最近的一个项目中,用到几句代码: uint8_t * out_pcm = NULL; ....... if (NULL == out_pcm) ...
- 为Android添加JNI支持
起因 今天在进行Android原生开发时,需要通过JNI调用C++代码实现一些处理.以前没有做过类似的东西,在网上找了很久才解决问题,特记录下来以便以后翻阅. Eclipse无cygwin编译so的方 ...
- 修改MacBook Pro主机名,共享电脑名
https://support.apple.com/kb/PH25384?viewlocale=zh_CN&locale=zh_CN http://www.ituring.com.cn/art ...
- 备忘录之 —— .bashrc(IC工具篇)
好久没有使用这些IC工具了,装在自己的虚拟机中的Linux系统里面,现在想要卸载掉,想起之前自己辛辛苦苦的折腾这些工具配置,如果直接删除,感觉未免有点对不起自己的劳动成果,或许以后再也用不到了,就当是 ...
- linux scp远程拷贝文件及文件夹
[http://www.jb51.net/LINUXjishu/73131.html] 1.拷贝本机/home/administrator/test整个目录至远程主机192.168.1.100的/ro ...
- [poj3565]Ants
[poj3565]Ants 标签(空格分隔):二分图 描述 Young naturalist Bill studies ants in school. His ants feed on plant-l ...