现代游戏引擎 - 游戏引擎的Gameplay玩法系统基础(十五)

游戏性课程框架(Overview)

玩法系统是游戏引擎最重要的部分,它是区分游戏引擎和渲染器以及物理引擎的核心。
实际上玩法系统往往会贯穿整个游戏引擎,与引擎的其它系统进行交互,这样才能满足游戏设计师的需求。
游戏玩法中的挑战

另一方面现代游戏的玩法是极其丰富的,即使是同一类型的游戏也具有多种多样的表现形式。
这些丰富的游戏内容都需要通过玩法系统来进行实现。
游戏玩法中的挑战2

而在游戏行业中玩法系统还面临着快速迭代的问题,同一个游戏的核心玩法在开发运营初期和后期可能会有着巨大的差别。
因此玩法系统在设计时也需要考虑需求变更和快速迭代。
游戏玩法中的挑战3

事件机制(Event Mechanism)

玩法系统的核心是事件机制(event mechanism),在游戏世界中不同类型的GO会通过事件/消息的方式进行通信从而驱动游戏世界的运行。
事件机制

发布-订阅模式(Publish-Subscribe Pattern)

在游戏引擎中一般会使用publish-subscribe这样的模式来实现具体的通信过程。在publish-subscribe模式中每个GO有着自己的publish功能来向其它GO发送事件,
同时自身的subscribe功能来实现接收事件以及相应的反馈。当然在publish-subscribe模式中还需要一个event dispatcher来执行高效的事件派送。
因此在publish-subscribe模式中的三要素为事件定义(event definition)注册回调(callback registration)以及事件派送(event dispatching)
发布-订阅
发布-订阅的三个关键组件

事件定义(Event Definition)

事件定义是实现事件机制的第一步,最简单的定义方式是为事件定义一个类型以及相应的参数。
事件定义

因此我们可以使用继承的方式来实现不同类型事件的定义。但这种方式的缺陷在于玩法系统往往是由设计师来实现的,而对于游戏引擎来说一般无法由程序员事先确定。
事件定义2

在现代游戏引擎中会使用反射和代码渲染的方式来允许设计师自定义事件类型和相关的数据。
事件定义3

回调注册(Callback Registration)

当GO接收到事件后就会激活调用相应的回调函数来改变自身的状态,而为了正确地使用回调函数则首先需要对不同的回调函数进行注册。
回调注册

回调函数的一大特点在于它的注册和执行往往是分开的。这一特点可能会导致回调函数调用时相关的一些GO可能已经结束了生命周期,因此回调函数的安全性是整个事件系统的一大难题。
对象生命周期和回调注册
对象生命周期和回调注册2

为了处理这样的问题我们可以使用强引用(strong reference)这样的机制来锁定相关GO的生命周期。强引用会保证所有和回调函数相关的资源在回调函数调用前不会被回收,从而确保系统的安全。
对象强引用

当然强引用的方式在一些场景下可能是不合适的,很多时候我们希望某些资源可以正确的卸载掉。因此我们还可以使用弱引用(weak reference)的机制在调用回调函数时判断资源是否已经被卸载掉。当然弱引用机制的滥用可能会影响整个系统的性能。
对象弱引用

事件调度(Event Dispatching)

事件会通过分发系统来实现消息的传递。由于游戏中每一时刻往往存在着成千上万个GO和相应的回调函数,我们需要一个非常高效的分发系统才能保证游戏的实时性。
事件调度

最简单的分发机制是把消息瞬时发出去。这种方式的缺陷在于它会阻塞前一个函数的执行,从而形成一个巨大的调用栈使得系统难以调试;此外很多回调函数在执行时会申请一些额外的资源,这就容易导致游戏的帧率很难稳定。
事件调度:立即
事件调度:立即2
事件调度:立即3

现代游戏引擎中更常用的分发方式是使用事件队列(event queue)来收集当前帧上所有的事件,然后在下一帧再进行分发和处理。
事件队列

由于event queue中有不同类型的事件,因此我们还需要结合序列化和反序列化的操作来进行存储。
事件序列化和反序列化

event queue一般会使用ring buffer这样的数据结构来实现,这样可以重用一块统一的内存空间来提升效率。
事件队列-环形缓冲器

现代游戏引擎中往往会同时有多个不同的event queue来处理不同类型的事件,每个queue用来保存一些相对独立的事件。这种方式可以便于我们进行调试,也可以提升系统的性能。
事件队列-定量

当然event queue也有一些自身的问题。首先event queue无法保证event执行的顺序,同时对于一些实时性的事件event queue可能会导致执行的延误。
事件队列问题

游戏逻辑与脚本系统(Game Logic)

在事件机制的基础上就可以开始设计游戏的逻辑了。在早期的游戏开发中会直接使用C++来编写游戏的逻辑来获得更高的运行效率,而随着游戏系统变得越来越复杂这种直接基于高级语言的开发方式就不能满足开发者的需求了。
早期阶段的游戏逻辑编程
编译语言问题

除此之外游戏引擎面对的用户往往是设计师而不是程序员,对于设计师来说直接使用编程语言来设计游戏可能是过于复杂的。
设计师和程序员

因此在现代游戏引擎中往往会使用脚本语言来实现游戏的开发工作,它的优势在于它可以直接在虚拟机上运行。
脚本语言
脚本语言的工作原理

因此我们可以把游戏中的很多逻辑使用脚本语言来实现。
脚本和引擎之间的对象管理
脚本和引擎之间的对象管理2
脚本系统的架构
脚本系统的架构2

脚本语言的另一大特点是可以进行热更新。
高级脚本功能-热更新

当然脚本语言也有许多缺点,比如说它的效率一般会比较低,因此选择合适的脚本语言是十分重要的。
脚本语言的问题
正确选择脚本语言

目前游戏行业中常用的脚本语言包括Lua、Python、C#等。
流行的脚本语言
流行的脚本语言2

可视化脚本(Visual Scripting)

可视化脚本(visual scripting)是现代游戏引擎几乎必备的功能。和传统的脚本语言相比可视化脚本无需开发者直接进行编程,因此它对于设计师和艺术家而言更容易进行掌握。像虚幻和unity这样的商业级游戏引擎都实现了自身的可视化脚本功能。
为什么我们需要可视化的脚本

当然可视化脚本本身也是一种编程语言,它需要实现相应的变量、语句、表达式、控制流程、函数甚至面向对象编程等功能。
视觉脚本是一种程序语言

以虚幻引擎的blueprint功能为例,每个变量需要封装数据类型以及作用域两种属性。而在可视化脚本中,不同类型的数据会使用不同的颜色来方便用户进行区分。
变量
变量可视化-数据插针和导线

在变量的基础上可以构造表达式,在可视化脚本中会使用节点来进行表示。
语句和表达式
语句和表达式可视化-节点

而控制语句则会使用三角形的符号来表示。
控制流
控制流程可视化-执行销钉和电线

类似于高级语言中函数的概念,我们可以在可视化脚本中把节点按照一定的顺序连接起来形成一张图。
函数
函数可视化-函数图

我们还可以把数据和函数打包在一起,这样就形成了类的概念。

类可视化-蓝图

除此之外在可视化脚本中往往还需要提供一些方便用户操作的功能,如查找节点、调试等功能。
使图形用户友好
视觉脚本调试器

可视化脚本也有一些自身的问题。首先可视化脚本很难进行协作,在大型开发团队中很难把每个开发者定义的脚本直接组装到一起,往往需要通过大量人工的重新排序才能得到正确的结果;同时当脚本中的节点过多时整个脚本在视觉上可能会非常繁琐,让人难以阅读。
可视化脚本的问题
可视化脚本的问题2
脚本和图表都是双胞胎

游戏性开发中的3C

角色(character)控制(control)以及镜头(camera)是构成玩家体验最重要的元素。
什么是3C

角色(Character)

角色是指游戏中如何设置玩家控制角色以及NPC的运动和各种状态。
角色

在现代游戏中角色的运动往往涉及大量不同的动画和状态改变,我们需要非常多的细节才能设计出符合人认知的角色运动。同时在游戏世界中角色还需要和环境中的各种元素进行互动,往往需要结合游戏引擎其它系统进行设计。
特点:精心设计的运动
扩展特征:更复杂和多样的状态
扩展性能:与其他系统合作
扩展特性:更真实的物理运动

因此角色系统一般需要一个非常复杂的状态机模型来描述角色状态的切换和转移。
运动状态机

控制(Control)

控制系统的核心问题是要兼容玩家不同的输入设备。
控制
来自控制器的信号通过控制系统会转换成游戏可以识别的输入,然后通过事件机制来控制角色。
从输入到游戏逻辑
控制:放大和缩小
控制:目标协助

同时我们还可以利用控制器的反馈功能来进一步提升玩家的游戏体验。
控制:反馈
控件:上下文识别
控制:组合键序列

镜头(Camera)

镜头会直接描述玩家视角中看到的场景和事物。
镜头:主观感受

从实现角度来说,我们只要设置好相机的位置和视角就可以直接利用渲染系统来实现镜头的功能。
相机基本: POV & FOV

在游戏场景中最常见的情况是相机要绑定在玩家控制的角色身上,随着角色的运动一起运动。同时我们还需要保证相机在运动的过程中不要出现穿墙等各种问题。
相机绑定
相机控制

镜头系统的一大难点在于如何根据角色的状态来调整相机的相关参数,使游戏画面更接近于人眼的真实反映。
相机轨道

同时镜头系统也需要考虑各种相机特效的实现,包括镜头抖动、动态模糊等。
相机特效

在复杂场景中往往还会有多个相机同时存在,因此我们也需要管理这些相机和相关参数。
多个照相机:相机管理器

整个镜头系统对于提升和改善玩家的游戏体验起着至关重要的作用。
相机:主观感受
相机:主观感受2

因此游戏引擎中的镜头系统需要允许用户使用脚本或是可视化脚本来编辑相机的各种属性和参数。
相机

引用

参考文章

课程视频

课件PPT

查看评论