撰写了文章 发布于 2021-01-10 16:38:30
【自学笔记】制作动森岛建规划器(三)
前言
前面完成了可自适应形状的方块的建造功能,接下来要开始做的功能有两个。首先是方块的拆除,其次是河流的建造。
方块拆除很简单,基本上与建造是成对的功能,实现方式上也有很多接近的地方。
河流的建造则和前面造方块不一样,建造方块是生成一个新的GameObject,而建造河流则是要替换已有的GameObject。
调整UI
考虑到接下来要增加多个通过按钮开关的功能,这里直接先把UI进行一下调整,变成适应后续情况的UI布局。
将悬崖与河流的建造与拆除划分为主选项与次级选单,先选择目标,再选择执行动作。

点击主选项按钮后,判定次级选单的开关状态并切换,如果是打开次级选单,还需要关闭其他主选项下的次级选单。

修改后的UI效果如下:

拆除方块
依照建造方块时同样的方式,生成一个命名为「BoxRemove」的对象,并且以相同的方式对其进行销毁。
利用相机的射线对击中的目标进行判断,当击中目标且目标的Layer是Cliff时,点击左键对目标发送「Destroy」的事件指令。

既然对方块发送指令进行销毁,那么在方块内就需要有相应的销毁自身的代码。
为了方便,这里将之前的首次方块检测做成了一个Custom Event,然后在Destroy事件中进行调用,用来更新周围方块的形状。

这里在制作时有一个矛盾,就是周围的方块需要在自身销毁后再检测更新,但是自身已经销毁就无法再给周围方块发送指令。
我想到的解决方案是,将周围方块的检测更新放在销毁之前,同时为了让周围的方块无视尚未销毁的自身,提前将自己的Layer值改为0,因为方块对周围的检测是根据Layer进行的,所以当Layer改变之后就会被忽视,周围的方块就能正确的更新形状。
拆除目标的反馈
现在已经可以将建造出来的方块进行拆除,但是还缺少视觉上的反馈,无法知道现在指着的拆除目标是哪个方块,所以还需要在拆除状态下让被指着的目标方块改变颜色。
用射线判定我想不到有什么办法可以让方块各自在「被指着」和「未被指着」这两种状态下切换,思考了一会后我想到,我要的这个效果其实就是「鼠标悬停高亮」,于是我搜索了一下相应的关键词,找到了「OnMouse」事件,可以判定鼠标进入、离开、按下、抬起、拖拽等动作。
找到方法之后就好办了,判断处于拆除方块的状态下时,在鼠标进入和离开的时候分别改变方块的材质,实现悬停高亮的效果。同时还做了一个事件判定,避免鼠标悬停在某个方块上时退出拆除状态导致方块颜色不恢复正常的问题。

最终得到的效果如下所示:

河流生成
建造河流的原理和建造方块时基本一致,唯一的区别在于,方块是凭空生成或销毁的,而河流是将地面替换成河流或是替换回地面。
首先直接把「BoxSpawner」复制一份,重命名为「RiverSpawner」,将里面的节点做一点修改,在地面的位置生成河流的同时,将该处的地面销毁。同时无法建造时的视觉反馈部分也做相应的调整,这里不再需要判定是否有碰撞体重叠,而只需要判定需要替换的对象的Layer是否正确即可。

接着在UI上添加上「RiverSpawner」的生成以及相应的建造状态,让河流的建造按钮正确剩下就行了,这部分跟建造方块基本一致。

河流的形状也需要像方块一样根据周围河流的分布自适应变化,但这部分与方块也是完全一致的,所以等必要功能完成后直接复制一份出来就行。
拆除河流
可以建造河流之后,相应的就是河流的拆除了。
这部分依旧跟方块的拆除一致,具体实现则与建造河流相反,是把河流替换回地面。

同样的在UI上实装拆除河流的功能,就算完事了。
现在拆除河流的时候,被拆河流还不会变色提示具体被拆的那格河流,这个效果留在做河流的自动Tile的时候一起做。
以下就是现阶段所实现的效果:

另外,因为增加了河流,建造方块时需要判定是否造在河流上,因此对「BoxSpawner」做了一些相应的调整,增加了这部分判定。
河流自动3DTile
这部分和方块的自动Tile也基本一致。
为了替换方便,也为了眼不见为净,我先把自动Tile的判定树封装成了一个Super Unit。

把方块的Tile检测与判定的节点原样复制到河流上,把生成的Mesh以及各种判断条件替换一遍,就算完成了。

高处河流
现在已经可以在地面上建造河流,但是方块上也应该要能建造河流,现在还没有做相应的判定,所以接下来就要让方块上也能建造出河流。
因为动森在所有的边缘方块上都无法建造河流(除了瀑布),那么这里除了将方块也纳入判定范围以外,还需要判定建造的格子是否非边缘。
首先将「River」的Prefab复制一份出来,重命名为「CliffRiver」,作为在方块上创建的河流。
之所以要区分「River」和「CliffRiver」,一来是因为二者相对于坐标原点的高度是不同的,二来是因为拆除的时候要分别还原为「Floor」和「Box」。
同时因为都是这两个Prefab的本质都是河流,Tile检测上应该是一体的,所以可以直接用相同的代码,只需要把两种不同的河流都作为判定对象即可。

优化Tile算法
因为实在是纠结目前Tile算法的麻烦程度和稀烂的可读性,我问了些朋友的意见开拓一下思路,受到他们的启发,我想起了RPG Maker XP的自动元件。
按理来说,自动Tile算法应该是一个比较成熟的算法,不知为何这么难找到现成可学习的实例。

这是RPG Maker XP自带的一个河流的自动元件,它可以产生的结果和我一样也是47种,但是可以看到它并没有把所有情况都画出来,这里面只表示出了11种情况。

在地图素材栏双击自动元件,可以打开这个窗口,能看到它实际上可以产生的47种结果(右下角有两个重复)全部列在其中了。
那么RMXP的自动元件是如何通过这么简单的一张图实现47种不同的结果的呢?
这种时候,最好的老师那必然就是——「搜索引擎」。
在百度搜索「rpgmakerxp 自动元件」后,我找到了一篇文章,文章的作者也是想做个类似的东西却找不到相关资料,于是他自己分析整理了一套自己的做法,我认为他的方案是相当有参考价值的。
(文章地址:https://blog.csdn.net/gouki04/article/details/7107088)
参考这些思路,我决定对我目前的Tile算法进行一次大改。
首先我将原本是一个整体的模型拆分成了四个角,然后给四个角的模型分别做一次判定。


这个是一个角的判断,只需要先判断该角的两个正方向的状态,如果两个正方向都为「True」则再额外判定一次斜方向。将这部分程序进行四次,分别判断四个角,就实现了与之前基本一致的效果,而程序可读性和复杂度都得到了很大的改善。

接着只需要把河流也改成相同的算法,同时修正一下其他因为算法改变而出现小问题的地方,这部分算法优化就算完成了。
后续计划
地形建造部分现在还差最后的「瀑布」,这部分我想了一下,感觉影响比较大,所以这部分整个作为下一个阶段的目标。
这里就先分析一下制作「瀑布」的影响,以及这部分的制作计划。
瀑布属于河流的一种状态,它的形状判定应该是河流判定的一部分。另一方面,瀑布所处的位置是特定的,判定情况也和平地上的河流有些区别。
瀑布只能存在于方块的直边上,周围八个必定有一侧的格子是空的,另外三个方向的六格必定有其他方块。可以说,瀑布周围的方块存在情况只有四种。
瀑布本身与周围河流/瀑布的连接在河流的判定基础上,还连接了两层不同高度的河流。
这样看来似乎瀑布的判定情况相当复杂,既要判断所处的方块和方向,又要判定两层的河流情况。
但是如果换一种思路,河流不可能存在「地下河」,从上方俯瞰而不考虑高度落差,河流和前面已经做出来的情况是没什么区别的。
基于这个思路,考虑到将来必然需要生成不同初始地图、必然需要存读档以免规划丢失,是需要一种方式来记录每个格子的地形情况的。
在这个基础上,用格子内的河流记号来确定哪里是河流,再用格子内的地形记号来确定河流有多高、哪里是瀑布,应该会是将来的实现方式。
以此判断,下一阶段我应该先着手重构目前的整个建造系统,将其改成上述的那种能够适应自动生成以及存读档的方式,并且分析这种实现方式下应当如何生成瀑布。