《我的世界》基础篇:生成方块

『技巧 | 经验』 版权:禁止匿名转载;禁止商业使用。

2043 0 0 2019-06-12 举报

CG素材

未设置职业

《我的世界》基础篇:生成数据块



系列概述

这套教程涵盖了Unity Mesh编程、模拟水算法(water simulations)、方块移动算法(marching-cubes)等等。这是一套比较有深度的教程,可能需要你了解一些Unity和C#相关的知识。


预备开始

首先,我们先来创建一个空的项目,命名随意即可。然后创建在Assets下创建一个文件夹,命名为“Scripts”,并创建三个C#脚本,如下图所示:




Chunk用于存储方块数据和创建网格,并且对网格进行渲染和碰撞;Block用于存放方块需要的信息;MeshData用于存储网格数据。


Chunk.cs脚本:


首先,我们要求该脚本必须包含上个组件:MeshFilter、MeshRenderer、MeshCollider。我们的数据块(就是由一堆方块组成的大方块~)需要这三个组件完成网格的创建和碰撞。


然后,我们有三个变量。我们有一个Block类型的三维数组变量 blocks,Block类用于存放方块需要的信息,因此我们这个blocks变量就是用于存放一个数据块的方块的信息。


chunkSize是一个静态变量,它用于表示我们的数据块各个方向的大小(就是长宽高的大小)。


最后我们有一个bool类型的变量,用于标志该数据块是否在每帧结束后更新。


其次有三个函数,分别为GetBlock、UpdateChunk、RenderMesh。GetBlock用于获取对应位置的方块;UpdateChunk用于更新数据块的网格数据,然后将更新的数据提供给RenderMesh去渲染。


MeshData.cs脚本:


由于这个脚本只是为了存储数据,因此不必继承自MonoBehaviour。




前三个变量(vertices、triangles、uv)是用于渲染网格用的,后两个用于网格碰撞(colVetices、colTriangles)。



Block.cs脚本:



同样的,Block脚本也不需要继承自MonoBehaviour,并且它将会是所有方块的基类。主要用于存储方块所需信息。


BlockData函数用于生成该方块的网格信息。


我们来设想一下,假如我们的数据块是有25个方块组成的,那么在相邻的方块之间,有一些面就不必渲染出来,浪费系统资源。因此,接下来让我们进行剔除多余面数的处理。


首先,我们需要一个函数去判断两个方块是否相邻,如果相邻则对各个方向的面进行剔除处理。


在那之前,让我们先定一个表示方向的枚举(Block脚本中):



然后让我们为Block脚本添加判断的函数:



因为Block脚本是所有方块类的基类,所有我们在Block脚本中对IsSolid函数没有进行判断处理,全部返回true。


现在让我们开始写一些我们的BlockData函数,在这个函数中,我们会根据当前方块对应方向上相邻方块的面进行剔除处理。




上图中的注释也说得很清楚了。举个栗子:判断当前方块顶上相邻的方块是否有底面,如果有则当前方块就不制作顶面,如下图分析所示:


PS:这个,,,图画得有点Low,希望大家能看懂它的意思。


接下来就是添加需要绘制对应的面的函数了。


上:




上面的注释也说得很清楚了,但是克森还是给你们秀一秀我的美术功底。



其它的面就不细讲,代码如下:


下:




东:



北:



南:



西:



添加点之后,我们还要把这些点组合成三角形,因此在函数的最后调用了MeshData里的AddQuadTriangles(),由名字可知道,该函数用于添加面片,因此我们要用这四个顶点组合成一个面片,让我们回到MeshData中添加该函数:




再给大家上一次克森的美术作品,相信大家都能理解了吧。




接下来,让我们创建一个新的脚本,命名为“BlockAir”,让它继承值Block类,如下所示:




Okey,现在让我们回到Chunk脚本中,开始添加方块进行测试咯。添加如下代码:




首先声明两个变量,一个为MeshFilter(网格过滤器)类型,另一个为MeshCollider(网格碰撞器)类型。分别用于存储和设置我们对应数据库上的组件属性。


首先通过GetComponent方法获取对应物体上的对应的组件,然后初始化了我们的数据块(blocks),我们的数据块是一个16*16*16大小的正方体,里面由一堆小方块组成。当前数据库的小方块类型为 BlockAir。


然后修改了该数据库blocks[3, 5, 2]的方块数据,修改为Block类型方块。


最后调用UpdateChunk函数进行数据的更新。


好的,接下来让我们完善我们的UpdateChunk函数:



首先声明一个类型为MeshData的变量。然后循环遍历blocks进行数据的更新(就是调用每一个方块的BlockData函数,而BlockData函数则是用来处理方块的网格数据,例如剔除面等等)。


最后就是调用RenderMesh函数将更新好的网格数据传入,然后进行网格的渲染。


那么,接下来让我们完成我们的RenderMesh函数:



这个函数很简单,就是先调用Clear函数清除上一次网格的数据,然后重新设置即可。


这一篇只是简单的介绍怎么生成数据块,还没涉及到贴图和碰撞,所以在RenderMesh函数里只是更新了网格数据。下一篇则教大家如何添加贴图到数据块上。


说那么多,先看看效果。


首先创建一个空物体,然后为该物体添加Chunk脚本。你将会发现如下效果:



该物体便会自动添加脚本中需要的那三个组件。对了,记得将positio属性置为0,不然你很可能看不到我们的方块,之后启动游戏,你将会看到如下效果:



为什么方块会跑那去了,为什么会是紫色的呢?因为该方块没有材质,所以是紫色的。因为我们设置了该方块的位置为(3,5,2),那我们是在哪里设置的呢,其实是在这个地方设置了,如下图所示:



这个时候你可以创建一个Cube物体去比对一下就知道了,下图演示:



看来是这样的没错。好,接下来克森带大家来走一走这个运行时候的步骤:


1.首先我们先进行实例化数据块,也就是Chunk类里面的blocks变量。



2.调用UpdateChunk更新网格数据,在UpdateChunk中又调用了各个方块的BlockData函数生成网格数据。



3.在BlockData中我们对当前方块根据检测相邻方块进行剔除面操作。


(由于函数太大,所以只截一小部分)


4.最后调用RenderMesh对网格数据进行更新。



在这里,为什么我们只生成了一个方块呢。因为要想生成方块,就必须调用BlockData函数,而BlockAir的BlockData函数里我们只做了一个返回,并没有生成网格数据,



因此只生成了一个方块,也就是我们在后面修改的那个位置为(3,5,2)的方块,因为在Block类里BlockData函数已经生成了网格数据。



为什么克森不直接将所有的方块实例化为Block类呢,原因是这样做会造成数组下标越界。大家还记得下面这个函数吗?



假设当前方块的y为16,这y+1便会越界。对于这个处理,后续的文章中会有介绍。敬请关注吧。


忘记说最后一点了,对于为什么会生成一个方块呢。原因就是在下图判断中,如果返回为false则制作该方块对应的面,然后我们的BlockAir的IsSolid函数返回的就是false,因此我们的方块就出来了。



好吧,就先到这儿吧。


原文作者: Unity墙外的世界

原文链接:https://mp.weixin.qq.com/s/daAhjLGfPlrkeXr45FE5ug

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

让资源更有价值

  • Archiver|
  • 手机版|
  • 小黑屋|
  • CG素材网
  • 蜀ICP备18003526号-3
  • Powered by Discuz! X3.4
  • © 2001-2017 Comsenz Inc.
  • GMT+8, 2024-3-29 16:18 , Processed in 0.295569 second(s), 34 queries .

 关注CG资源素材

快速回复 返回顶部 返回列表