图形引擎实战:城建功能技术分享

概述

随着城建功能在游戏中的应用越来越广泛,玩家对于城建功能的需求越来越高;城建功能的需求主要集中在以下几方面:

  • 积木建造、移动
  • 积木破碎
  • 积木的旋转(自由旋转)
  • 积木自动吸附功能
  • PC端和移动端操作一致,并且操作顺滑
  • 多玩家同步建造
  • 建造后实时寻路功能

基于上述需求,经过反复斟酌,最终决定基于体素研发这一套包括客户端-服务器的城建功能。基于体素研发的城建功能既可以满足多玩家同步建造功能,也能满足实时寻路功能。有关城建的操作模式,则参考了包括剑网3、原神、英灵神殿以及吸血鬼崛起等多款游戏在内的城建操作,最终形成了一套完备的同时适配PC端和移动端,操作顺滑的操作模式。

首先,城建功能基于体素,所以简单介绍下体素的概念。

所谓体素,是像素(pixel)、体积(volume)和元素(element)的组合词,相当于3D空间中的像素。通过体素,可以对3D空间进行网格划分,并赋予每个网格特征,如下图,可以想象体素就是二维像素在y轴方向上,再增加一个单位的切割划分。

对于需要使用体素的场景来讲,需要预先生成场景体素、导航寻路数据、以及积木的体素,并且同步存储在服务器和客户端。

需要注意的是,生成体素世界,需要预先指定体素大小,即一个体素格子要定位为多大。城建Demo中CellSize设定xz轴方向0.25为一单位,y轴方向0.1为一单位。

场景体素以及积木体素都可以预览,并进行二次编辑。下图为网络上找的一张体素地图:

一个完备的城建功能需要同时兼顾PC端和移动端的操作,并且需要考虑如何获得积木放置位置、如何做对齐操作,是否提供吸附功能等等。本文将从以下几个角度来讲解基于体素的城建功能:

操作模式

如何得到积木放置的正确位置

积木旋转操作

积木破碎方案的选择

积木吸附操作

操作模式

目前支持城建系统的游戏,比较火爆的有剑网3、原神、英灵神殿、吸血鬼崛起以及明日之后等等。通过在建造模式、PC端移动端适配、自动吸附等多方面对上述几个游戏进行对比,截止到2022年11月,得到结论如下:

通过对比以上游戏建造功能,并且通过游戏体验,得到的结论是

  1. 边走边搭建,即积木跟随角色建造可以增强玩家的交互体验,并且可以直观的显示多玩家建造过程,所以角色跟随在这一方面优于上帝视角。
  2. 部分积木(比如地板、墙壁)的自动吸附功能可以简化玩家的操作,便于玩家快速便捷的搭建建筑框架,所以对于一个优秀城建功能,自动吸附功能必不可少。
  3. 对于可以自由摆放的积木,比如书,椅子等等。这类积木如果增加自由旋转功能,可以增加玩家城建的自由度,自由旋转是一个锦上添花的功能。
  4. 现在无论是PC端游戏还是移动端游戏,社交功能越来越受到重视,多人协同城建的功能,实时寻路的功能必定能给玩家带来交互性更强,体验感更深入的游戏体验。
  5. 对于操作模式,有两种选择:一种是角色所在区域,选中积木,在点击所要添加积木的位置,点确定进行添加,如果需要旋转视角,需要单独操作;另一种是,屏幕中心显示准心,选中积木,旋转相机视角定位到所要添加的位置,点确定进行添加,无需单独调整视角。第二种操作,在需要集中添加大量积木的情况下,可以节省玩家操作的复杂度,从而节省搭建时间。第一种操作的优点在于,如果视口比较大,玩家可以比较宏观的、完整的看到所搭建的建筑,对于社交性、互动性不那么强的游戏来讲,是比较好的选择。
  6. 城建功能下的实时寻路功能,对游戏开发来讲是个不小的挑战,既要保证性能不受影响,又要保证功能。经过调研举证,基于体素开发的城建功能可以较好的支持实时寻路功能。

因此,引擎部自主研发的城建系统同时支持多人模式与单人模式:多人模式下,参照英灵神殿模式,使用准心定位积木位置、选中积木,支持多人同时建造;而单人模式下,参考剑网3模式,使用上帝视角进行建造操作,使用鼠标和手指进行积木的添加和选中操作。此外,该建造系统的操作同时适配PC端和移动端,且PC端和移动端的操作保持一致。

下图所示为多人模式下建造的效果图:

基于体素的射线查询

不同于现有的游戏建造功能,该城建系统基于体素开发,同时支持多人同时建造以及实时寻路。所以如何确定待添加积木的位置,进而高效的更新场景体素是比较核心的问题。

添加积木时位置的计算步骤:

  1. 首先计算相机到屏幕中心的射线,向下做射线查询,得到添加位置POS1
  2. 根据第一步计算得到的添加位置,计算调整得到对应的体素位置—积木的左下角位置须和体素格子的左下角对齐,调整后的位置为POS2;
  3. 根据POS1来更新待添加积木的位置;根据POS2来更新当前待添加积木与现有积木、角色等的碰撞检测状态,根据是否碰撞来决策是否可以添加。
  4. 得到添加确认的操作后,客户端将POS2作为参数发送给服务器,服务器进一步根据当前状态调整积木的体素位置,计算完成后,反馈给客户端
  5. 客户端收到服务器添加积木成功的消息,根据接收得到的积木ID,位置等信息,创建积木。

首先,第一步的射线查询是体素系统的射线查询,可以得到相对精准的体素世界添加位置;

其次,需要区分POS1和POS2,之所以不用POS2更新待添加积木,是为了使得创建过程中积木移动更平滑,如果使用POS2,则会出现积木位置频繁调整抖动的情况;

最后,在客户端计算POS2,可以检测待添加积木的碰撞情况,与服务器同步,这个是必要的。

下图直观的展示了积木的添加过程。

强制添加

在体素积木搭建过程中,开始设定的是体素不能碰撞。

积木的体素是根据模型的mesh生成,如果积木的长宽高和体素的单位长度不匹配,可能会导致体素大于模型本身,或者小于模型本身。如果体素大于模型本身,则体素不能碰撞会导致积木和积木不可以紧挨着摆放;如果体素本身小于模型mesh,则会导致奇怪的积木穿插现象。此外,还有一个问题,比如使用积木铺设了草坪,但是由于体素无法碰撞,则无法在草坪中搭建房子,只能悬浮在草坪至上。

有两种解决方法,第一种是根据模型本身,生成特定的体素,比如一棵树,只生成树干,草生成一个片之类的,但是这种需要根据每种模型进行体素定制,不仅工作量增加了,而且最终的搭建效果也不敢保证完全没问题;

第二种是强制添加,所谓强制添加,就是体素可以碰撞;每个体素格子存储所属积木对象的id,如果体素发生碰撞,则体素格子同时存储多个所属对象的id,也就是说,当前格子体素可能属于多个积木。这种方式灵活,通用,并且无需增加额外的工作量。

所以我们增加强制添加功能,使得积木可以叠加摆放,由使用者来确定积木位置。如下图所示:

积木旋转

众所周知,体素是由小的长方体格子组成,而积木则会有各种各样的形状。积木旋转主要有两种情况,90度旋转和任意角度旋转。

对于90度旋转的情况,如果体素格子xz方向长度相同并且积木的长宽是体素单位的偶数倍,则积木旋转90度时则积木体素不变,无需重新生成,并且看起来是绕积木的中心旋转的;如果体素格子xz方向的长度为体素单位的奇数倍,否则需要根据旋转后的积木重新调整。如下图所示,如果积木的xz方向的体素长度为体素的偶数倍,则体素的中心位于红色的点;如果x方向为3倍,如右图,则体素的中心点可能位于两个蓝色点的其中一个,积木旋转的时候会绕着中心点旋转,所以基数倍长度积木的旋转,具有不确定性。所以建议在积木制作过程中,遵守制作规范。

此外,90度旋转的积木,无论中心点在何位置,旋转后都无需重新生成体素。

对于自由旋转的情况,则需要根据旋转角度,重新生成积木的体素,这个计算量相对较大,比较消耗性能。如下图所示:左侧是原积木,左侧是积木旋转222度之后重新得到的积木,蓝色框显示积木,白色区域代表积木体素。

城建功能Demo提供两种积木旋转,90度旋转和自由旋转。自由旋转参考原神的方法,加入了一个圆环,方便用户操作。

积木破碎

为了增加游戏世界的真实感,积木破碎功能是必不可少的。如何在体素世界完成破碎功能?如何实时生成破碎积木的体素?

首先,如果使用实时的物理生成破碎的积木,性能消耗较大。其次,如果破碎积木实时生成,每次破碎的积木数量和形状不一致,对于重新生成体素操作来讲压力还是不小的。所以斟酌再三,还是需要离线生成破碎积木,以及破碎动画。

城建功能Demo破碎过程使用如下流程:

  1. 使用第三方插件预先生成积木的破碎模型以及破碎动画。
  2. 将破碎模型作为普通积木一般,预生成体素数据。体素数据是破碎后积木的体素。
  3. 将积木的破碎模型在配表中进行设置,与积木进行关联
  4. Demo运行中,选中积木进行破碎后,先删除积木,再读取并创建破碎模型,同时如果存在破碎动画,则播放破碎模型的动画

经验证,如此实现的积木破碎过程自然流畅。

下图给出积木的两种破碎效果:上面一个是不带破碎动画的,下面是带破碎动画的。上面的积木破碎后,玩家可以自由穿行,增加了游戏的趣味性。下面的带破碎动画的方案需要考虑动画所占存储的情况。Demo中城墙积木,切分成了260个碎片,动画录制帧率为15,时长3s钟,动画大小为32M。可以根据实际情况,将碎片设置到合适的数目,并且适当压缩,控制动画数据的存储占比,减少内存占用。

积木吸附

毋庸置疑,积木吸附操作可以使积木摆放更加便捷。城建功能Demo参考现有的吸附技术,并且再次基础上扩充改进,完善了积木吸附功能。

积木吸附功能建立在socket基础上。

所谓socket,是挂接在积木吸附边界上的节点,如下图所示,当前积木的四个边界都可吸附积木,所以构建四个socket。

每个sockets上挂载一个BuidlSocketBehaviour,设置该socket可以吸附哪些积木,并且设置如果吸附,被吸附积木的位置相对该socket的偏移:

吸附积木在添加过程中,通过检测,判断待添加积木离哪个socket最近,定位最近的socket之后,便可重新计算待添加积木的新位置,完成吸附。

关于如何定位最近的socket,使用如下策略:

  1. 获得相机到屏幕中心射线Ray
  2. 筛选所有与Ray垂直距离小于指定阈值的socket
  3. 在所有第二步筛选的socket中,依次算相机到socket射线与Ray的夹角,最小夹角的socket为吸附的socket

积木叠加

由于城建功能Demo基于体素创建,并且增加了自动吸附功能,所以可以轻松的完成积木的叠加摆放,如下图所示:

案例分享:

如下图所示:在如下视角添加积木:

首先沿相机往Z轴Forward方向打射线,筛选所有与射线距离小于0.2f的socket,筛选出了如下图标识红点的五个socket

然后从相机到五个socket依次打射线,计算出其与相机射线的夹角,夹角最小的为最终吸附的点。

再看下面两幅图,通过上述算法,如果选择合适的视角,就能将积木放在想要放置的地方:

如上案例说明,只要角色攀爬或者行走至某些位置,同时创建积木时适当调整相机视角,便可选定预先想要添加积木的位置:无论是搭建高空积木,还是悬空的屋顶,均可实现。

欢迎加入我们!

感兴趣的同学可以投递简历至:CYouEngine@cyou-inc.com

#游戏引擎##技术美术##搜狐畅游#
全部评论
能不能快点把我挂了 在池子里都泡发了
点赞 回复 分享
发布于 2023-09-05 10:49 陕西

相关推荐

死在JAVA的王小美:哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,我也是,让我免了一轮,但是硬气拒绝了
点赞 评论 收藏
分享
3 4 评论
分享
牛客网
牛客企业服务