伤害(Damage)是一个游戏中常见的概念,这里快速预览一下UE4里面的伤害系统。
伤害支持是Actor基础类的特性。该系统既提供简洁的接口访问通用功能,又具备按需定制伤害模型的扩展能力。它没有去假设伤害如何响应,你也找不到什么HP
、death
这类游戏相关的概念。在引擎层尝试归纳这些概念终究是弊大于利。
基本的伤害相关概念
下面快速预览一部分讨论伤害时使用的常用概念。
伤害类型
DamageType
是用于描述伤害类型的对象
。如果你有很多种伤害还想抽象出通用功能的话,这个概念是非常有用的。
举个简单的火属性伤害
例子。你想让受到该伤害的人大声喊“烫死老子了”,并奔向最近的水源。实现方案肯定不能是给每个可能受到烧伤的actor都复制一遍代码,更好的做法自然是为火伤定义一个伤害类型(UDamageTypeFire),然后给HandleDamageCharacter()
传递类型参数,最终作用于TakeDamage()
调用链。
伤害发起者
谁造成了该伤害,一般都是玩家或者AI。比如上述火伤中,就可能是某个玩家或者AI燃了一把火。
伤害源
什么东西造成了该伤害,比如是你刚踩过的一堆篝火造成的。
C++中使用
先看原生代码对伤害的支持。
要对一个actor造成伤害非常简单——只需调用TakeDamage
即可。
virtual float TakeDamage(float DamageAmount,
struct FDamageEvent const& DamageEvent,
class AController* EventInstigator,
class AActor* DamageCauser);
同理,受到伤害就是实现受伤害actor里的TakeDamage()
接口,写上你自己的处理逻辑即可。
TakeDamage
接口调用可以接受一个DamageEvent参数。这个FDamageEvent
数据结构包含伤害事件相关的特定数据,你可以合理利用起来。UE4自带三种内建伤害事件。
FPointDamageEvent
点伤害事件模型指伤害作用于受害者指定点(point),比如来自一颗子弹或者一拳头。该事件包含打击方向和用于描述表面撞击的FHitResult
。
FRadialDamageEvent
辐射伤害事件模型会把伤害从源点辐射出去,明显的例子就是爆炸伤害。该事件包含爆炸中心,空间内伤害衰减数据,和一系列受影响的成分。
FDamageEvent
这是最通用的伤害模型,只包含一个可选DamageTypeClass
。
如果这些内置事件类型不满足需求,你也可以继承FDamageEvent
存储任何想要的数据。
BluePrint中使用
在蓝图中处理伤害方式差不多,区别在于伤害的产生与反馈已经被各种事件类型分开了。然后提供了一些可全局访问的节点来造成伤害,比如ApplyDamage
、ApplyPointDamage
、ApplyRadialDamage
。要响应伤害事件的话,在关卡里为actor类和actor实例都提供了一套相似的“受伤害”事件。
如果是自定义伤害事件,则需要同时实现并暴露一套与引擎提供的相似的函数接口与代理方法供蓝图使用。
总结
- UE4抽象出一套伤害系统,概念上分为伤害类型,伤害发起者,伤害源。实现同时支持C++和BluePrint。
- C++对该系统的利用核心就在
TakeDamage()
函数上,造成伤害就调用,受到的伤害就看函数的实现。 - 伤害相关的数据由
FDamageEvent
结构控制,内置三种,可以自由扩展。
个人有点意外引擎层会提供伤害系统。粗看时觉得,这是引擎越界干了游戏业务逻辑的事,但再往深处思考,发现引擎就是干这个的。因为本质上引擎就是大量游戏开发 中积攒的通用库集合,如果大量的游戏都有伤害效果这个需求,那它和音效需求、AI需求、物理需求一样,都可以慢慢融入引擎中。
UE4内置的伤害系统可以给一些游戏开发带来便利,毕竟从零架构一套伤害系统也不是简单的事。而无法直接利用的开发者也可以学习之,毕竟是顶级引擎抽象出来的。理清它的 理念与抽象架构然后灵活运用。不说其他,以这套伤害系统为基础,你可以构建一套很清晰的战斗系统,技能系统。毕竟,战斗放技能的终点就是造成伤害啊。是吧。