概述
在战斗中,对敌人造成的每一击,称之为一次“攻击”。角色使用一次近战攻击技能(含攻击动作),可能会对触发1~多次攻击,每一击能够对目标造成不同类型,程度的伤害。
为实现这样的灵活性,GCS引出“攻击系统”。
攻击定义
攻击定义,是一个结构体,游戏中有多少种攻击,就应该有多少个攻击定义,它包含战斗游戏中"每一击"的静态信息。一个近战技能会播放一个攻击动画,但可以产生多个不同种类的攻击。
每种攻击/武器挥舞/Projectile都有与之相关的非常具体的属性,诸如伤害类型,数值加成,挥舞方向等内容。
像艾尔登法环这样的游戏,它有成千上万个不同的攻击定义,因为在GCS中使用DataTable管理攻击定义,并全部使用SoftClass/Object引用,意味着它不会拖慢你的项目性能。
创建攻击定义表
前面提到发起攻击请求时,需要指定攻击定义,而你可以通过新建类型为FGCS_AttackDefinition的数据表,然后在表里定义你会用到的所有攻击定义。
通常攻击定义的数量,取决于你的游戏机制的复杂度,以及有多少攻击变体。
像EldenRing中的技能“水鸟乱舞Waterfowl Dance”,该技能的攻击定义高达20个!!!每一击都有着不同的伤害类型,韧性伤害,以及附带的属性。

这里有大量字段可以配置,其中,最重要的属性如下:
AttackTags:你通过为Attack添加游戏标签来描述它的特性,这些Tags会添加到攻击信息中,所以在处理攻击信息时,你可以利用此信息来做更多的逻辑判断。
案例:在处理攻击结果时,你可以检查Attacker的攻击是否带火属性,同时再检测自身是否弱火,若满足条件,你可以再给自身添加额外的表现。
SetByCallerMagnitudes:本质上是一个GameplayTag->float的键值对,你在这里可以填写任意信息,它会被传入GameplayEffectSpec,所以你可以在Calculation中做更多的操作。
案例:如果你有一系列连招,那么可能最后一段是有极大的伤害加成,而具体加成多少,你可以在这里填写,只要你的Calculation中考虑到了这一点。
管理攻击定义表
经过我的了解,艾尔登法环根据武器的类型来划分不同的攻击定义表。
所以你可以按照:DT_Atk_Sword_A,DT_Atk_Sword_B, DT_Atk_GreatSword_A, DT_Atk_GreatWord_B这样的方式来规划和组织你的攻击定义表。
攻击请求
如果你想发起任何形式的攻击,你需要通过攻击请求(AttackRequest)。GCS有不同类型的攻击请求,如Melee或者Bullet。且攻击请求一般通过动画通知状态触发。
这一部分解释你如何发起不同的攻击请求。同时所有继承自GA_GCS_Attack的GameplayAbility会自动处理攻击请求。
近战攻击
当一个近战攻击技能播放一个Montage时,你可以在合适的攻击帧范围内为其添加ANS_GCS_AttackTrace(动画通知状态)来配置一个攻击请求。

- TraceToControls:定义了在动画通知状态激活过程中,需要激活的
碰撞检测实例。 - AttackDefinitionHandle:指向攻击定义表中的某一个攻击定义,它包含“这一击”相关的所有静态数据(如攻击力,Cost等信息)。
上图动作是用右手武器攻击,所以应该开启名为"RightHandWeapon"的检测实例(它绑定了武器的Mesh)
如果动作是用拳头攻击或者用脚踩踏,你也应该开启对应的检测实例(如RightHand,LeftFoot。它们绑定了角色Mesh)
远程攻击
在需要发射子弹的Montage中(比如射箭),你通过添加ANS_GCS_BulleTrace在攻击帧定义攻击请求。

- BulletDefinitionHandle:指向子弹定义表中的某一个子弹定义,它包含“该子弹”相关的所有静态数据。(比如实际生成多少个弹丸,它的攻击力等等。)
- TargetingSourceType:决定子弹的生成位置是如何计算的。

- SourceSocketName:如果TargetingSourceType是Pawn开头的,那么SourceSocketName用于决定在角色Mesh的哪个Socket位置上生成。
- SourceWeaponSocketName:如果TargetingSource是Weapon开头的,那么SourceWeaponSocketName用于决定在武器的哪个位置上生成的。
自定义攻击请求类型
你可以通过蓝图/C++创建一个GCS_AttackRequest的子类,以添加更多的信息,并在后续流程中使用。
拿GCS_AttackRequest_Bullet举例,当你将TargetingSourceType选择为Custom时,你应该继承GCS_AttackRequest_Bullet来创建一个新的版本,并实现GetTargetingTransform。

