【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,运行

 

 

posted @ 2017-10-23 13:38  静茹♂鱼  阅读(10135)  评论(0编辑  收藏  举报