欢迎光临散文网 会员登陆 & 注册

用Unity盖房子(三)——《勇者斗恶龙:建造者2》自动建造

2019-07-11 17:06 作者:皮皮关做游戏  | 我要投稿

作者:沈琰


前言

最后一篇文章,来把这个坑填完。

接上期:用Unity盖房子(二)——《勇者斗恶龙:建造者2》地形生成和人物控制

上期内容中搞定了无限地形的生成和建造功能的实现,现在总算站在了实现最初设想功能的起点上,即让AI按设计图自动建造建筑。

先大致思考一下实现的步骤吧。

首先是设计图的部分。不管是像原版一样是放到背包里的道具还是说在场景中把待处理的方块都显示出来,设计图从本质上来说,都是一个数据的集合。其中存储的是限定范围内的方块数据,而我们要做的事就是以鼠标击中的位置为基点,让这些数据以我们预想的排列方式传递到chunk上。

其次是自动建造的AI。这里的主要难点就是如何在一个不断变化的地形中正确寻路,以及多个AI协同工作的问题。

下面就以这个顺序分别实现以上功能。

另外这期就尽量不贴代码,只说思路。由于代码会在实现过程中反复修改,直接贴的效果也不好,如果大家对实现方式有疑问,可以下载文章末尾的工程。

建造模式预览

上期还留了一个坑忘了写,这期就合并在一起说了,即建造时方块的朝向控制。

之前我们用一个镂空的框来表示当前选中的位置,在删除模式时看起来似乎还行,但在建造模式时是无法显示当前方块的朝向的,因此在建造模式时直接把选中的方块按类型以mesh的方式显示出来,新建一个脚本来处理这个问题。

这个脚本的作用与chunk类似,都是根据数据生成mesh信息,所以这里面也需要对mesh的引用。不同的地方则是它可以旋转朝向,旋转的方式就是用的上一期说的自己写的一个简易四方向矩阵变换。因此方块不是真的自己转向了,而是方块的每个顶点的相对位置变了。

然后材质球就用和chunk一样的,只不过稍微改一下shader,使其显示为半透明。之前找材质贴图的时候特意找了一个能表示方向的,刚好能派上用场。

这还不算完,现在有个问题。朝向不同的方块处于相邻位置时对相邻边的隐藏判断会出错。

这是因为旋转方块的朝向是相对于方块自己的朝向,而chunk中的相邻边检测用的是相对于chunk的方向,因此这里在检测是修正一下检测方向就能解决问题。

设计图模式预览

现在把这个功能扩展到显示一个指定区域内。设计图模式的功能大致能分成两部分,一个是指定一个区域copy地形数据的复制功能,一个是根据指定区域位置去修改chunk数据的建造功能,即getData与setData的区别。

我的想法是,当处于getData的模式时,用代码生成一个预览框显示获取数据的范围,而在SetData的模式下除了这个预览框,还要用半透明材质生成mesh 显示当前的方块数据。此外,不管是哪种模式,这个区域都是基于当前鼠标位置可旋转的。

建造模式的旋转方式

这个功能还是属于PreviewBox这个脚本的功能范畴。为方便后面功能的实现,我们先假设这个区域是一个正方体,这样只需一个int值就能表示区域大小。后面的思路就是根据朝向来把这块区域分成与cube大小相同的空间,找到它的起始点。然后以此为基点获取所有的方块中心点并存入数组中。

那么剩下的思路就清晰了,再新建一个byte数组来存储方块的数据,接下来我们就需要保证坐标数组和数据数组中的索引总是能正确地一一对应,并使用这些数据完成getData和setData功能。

复制功能

复制功能的预览mesh用框不太好去设计纹理的样式,原本想的是用镂空的正方体框形来表示,由于在建造时方块的数据预览会与之叠加,为了不会遮挡方块,这里选择生成一个贴地的矩形面来表示区域的大小。还是根据坐标计算出顶点,并传递数据到mesh中。

最后在unity商店里找了个带边缘轮廓的材质作为其材质球。

建造功能

有前面的基础,建造功能此时就只需加上其包含的方块数据的预览mesh,这个部分与chunk干的活类似,只有一点要注意,即刚才说到的保证坐标数组的下标与数据数组的下标的对应关系正确。

目前为止看起来都没什么问题,接着就是实现数据的get和set功能。

然后试试效果,这个时候问题就来了。

朝向问题的修正

首先是setData模式时,方块mesh预览的隐藏面问题。有了之前的经验大概就能猜出问题出在哪了。显然就是在旋转预览框时的相对朝向和方块自己的朝向之间的问题。咱们先不慌修改,再看看其他问题。

可以看到除了预览为初始朝向时,setData时的方块朝向基本全错了。这其实也好理解,当我们在旋转预览区域时,可以看做是直接修改了方块的绝对朝向。那么同理,getData时也要修正获得的方块数据的朝向。

都像chunk处理方块相邻方向那么去写,代码就显得太难看了。因此干脆在朝向枚举那里加两个扩展方法去处理朝向修正问题。

然后在getData、setData和预览mesh相邻边检测时修正相对朝向。

修正之后一切都符合预期了。

chunk的第二个mesh组件

现在相当于是直接把数据更新到chunk里,但我们要的只是起一个指示和预览作用的mesh,最大的区别是它不该有碰撞。

所以在chunk的预制体下多加一个mesh,当发现当前的cube数据为透明时,就把数据传递到第二个mesh里。

AI的自动建造

接下来就是重点功能的实现了,这部分的核心就是寻路。

2D平面的寻路功能相信大家已经不陌生了,如果有这部分不太熟悉的同学,推荐去看专栏里的另一篇文章。传送门:给猫看的游戏AI实战(四)眼见为实——让AI的思考过程可视化

但我们这里的寻路稍微有点不一样,它不是平面的而是立体的。这里我们简化一下思路,把方块与方块之间的连线作为路径,那么地图仍然可以看做是无向图。估算一下规模,目前的默认地图为4x4个chunk,算上立体结构节点也应该不会超过1万个,所以就用最简单的广度优先搜索(BFS)就行。

新建一个脚本来编写AI,定义一下寻路的规则。一图流表示路径规则:

把这个逻辑转换为代码:

然后根据判断条件写一个简易的BFS,由于路径不一定都有,所以还要处理当寻不到路径时的情况,这里的逻辑就是维护一个最近的节点,当寻不到路时就返回这个最近点。

现在把这个脚本挂在主角身上,把路径画出来测试一下效果:

可以看到路径没问题,那接下来就是根据这个路径走过去,把方块摆到预览的位置,然后切换到下一个,中间还要加上角色的动画和移动过程。这部分没什么难度,就跳过了,直接看效果。

多目标协作

现在单个AI的功能没问题了,来考虑多目标协作的问题。最显著的问题是NPC的建造会影响其它NPC的路径。比如A在B的路径上放了一个方块,那么B的路径就需要更新。

最简单的办法就是在每个AI放置方块时,通知其它所有的AI路径需要更新。

然后其它AI在运行时根据这个bool值去刷新自己的路径:

最终得到的效果:

现在基本达到预期效果了,但还留下了两个问题:

1.现在用了一个自定义的类型来管理路径节点,当AI较多时,同时更新路径是生成节点过多,会产生较大的GC压力。

2.当AI处于移动中时无法更新路径,导致有些时候AI看起来像是从方块里走出来一样。

这两个问题就先留给大家思考一下,后续更新的我再来修改。

那么这个坑到这里就算是填完了,感谢大家观看至此。

本期工程地址:https://github.com/tank1018702/CubeBuilder


有意向参与线下游戏开发学习的童鞋,欢迎访问levelpp.com/

用Unity盖房子(三)——《勇者斗恶龙:建造者2》自动建造的评论 (共 条)

分享到微博请遵守国家法律