【Unity3D】Unity3D开发《我的世界》之一、创建一个面
转载请注明出处:http://www.cnblogs.com/shamoyuu/p/unity_minecraft_01.html
最近总有人问及我的游戏里跟《我的世界》一样的地形是如何实现的,所以在这里开一个系列教程总结回答一下,也算是对自己的一个梳理和记录。
一、简单分析
对《我的世界》感兴趣,且有一定Unity3D基础的人,应该都或多或少的尝试过自己去做一个吧。
有些人是用Unity3D自带的cube,然后用三层for循环,把cube堆叠起来,但是会发现游戏几乎卡爆了,根本没办法运行。
如果看过我以前换装系统教程人应该都知道,这是因为每一个cube都是独立的物体,一个chunk就是65536个方块,不卡爆就怪了。
既然这样,那我们把所有的cube合并成一个物体行不行?
也不行。
因为在地底90%的面都是看不到的,这些面实际上不应该存在,也都应该删掉,否则对CPU和GPU都是极大的负担。
基于以上的原因,直接用cube是肯定不行的,那就只能我们自己通过代码来生成面,而且只生成我们需要的面,这样就没有上面的问题了。《我的世界》也是这么做的。
二、理论基础(重点)
1、法线
法线就是垂直于面的一条线,它有方向,没有大小。
法线的方向就是面朝外的方向。比如我们现在盯着显示器看,从显示器的正中心会有一条法线垂直于屏幕指向我们。
法线向外的面就是正面,相反的就是背面,一般来讲,从正面看才能看到面,背面看面是看不到的。
2、三边面和四边面
三边面就是三条边组成的面,四边面就是四条边组成的面。
三边面在三维空间中是不可扭曲的,而四边面在三维空间中可以扭曲。所以Unity里只支持三边面。其他支持四边面的软件例如3dmax在导出fbx的时候,会把四边面转换成三边面。
3、左手坐标系和右手坐标系
我们的三维坐标系,在3dmax里是右手坐标系,而在Unity里是左手坐标系。
左手坐标系和右手坐标系的区别可以看一下这个博客 http://www.cnblogs.com/mythou/p/3327046.html
4、三边面如何组成四边面
先看看上面这张非常传神的图,左边是Unity里的左手坐标系,右边是我们想在这个坐标系里生成的一个面以及它的各个点坐标。
012和230这两个三边面就组成了一个四边面。
如果我问,这个四边面有几个顶点,想必大家都会回答4个,实际上是6个,012和230这是6个顶点,不同面的顶点不公用。
要组成2个三边面可以有很多种顺序,例如012和320、012和032、023和012等等等
但是我们一般都是按照4个点的顺序来画2个三边面组成四边面,所以可选的只有【012和230、230和012】,以及【032和210、210和032】这两大类
这两类画法有什么区别呢?细心的童鞋应该已经发现,这两种方式前者是逆时针,后者是顺时针。
这种循环的方向会导致面的法线方向不同,而这个法线方向会决定这个面的朝向。
我们要确定这个法线方向其实很简单,上面说了,Unity里是左手坐标系,我们拿出左手,伸直,拇指与其他四个指头垂直,然后四指弯曲,指尖朝向循环的方向,拇指就指向法线的方向。
有没有觉得很熟悉?这和初中学过的左手螺旋定则一模一样。
由此我们得出结论,要想生成正确的面(法线指向我们),我们只能用【032和210、210和032】
这里需要注意的一点是,我们确定4个点的循环方向,和生成三边面时的循环方向无关,只要生成三边面时,用到的前4个点的index顺序没错就行了。
三、代码实现
该有的理论知识都有了,下面我们就手动生成一个面试试看
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Chunk : MonoBehaviour { private Mesh mesh; //面需要的点 private List<Vector3> vertices = new List<Vector3>(); //生成三边面时用到的vertices的index private List<int> triangles = new List<int>(); void Start() { mesh = new Mesh(); AddFrontFace(); //为点和index赋值 mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); //重新计算顶点和法线 mesh.RecalculateBounds(); mesh.RecalculateNormals(); //将生成好的面赋值给组件 GetComponent<MeshFilter>().mesh = mesh; } void AddFrontFace() { //添加4个点 vertices.Add(new Vector3(0, 0, 0)); vertices.Add(new Vector3(0, 0, 1)); vertices.Add(new Vector3(0, 1, 1)); vertices.Add(new Vector3(0, 1, 0)); //第一个三角面 triangles.Add(0); triangles.Add(3); triangles.Add(2); //第二个三角面 triangles.Add(2); triangles.Add(1); triangles.Add(0); } }
然后建一个空物体,把这个脚本拖上去,再添加MeshFilter和MeshRenderer组件,然后随便给一个Material,运行