Yoana Wong Yoana Wong

由其8大著數2023!(持續更新).

Article hero image
由其

Meta Attribute对于在"我们应该造成多少伤害?"和"我们该如何处理伤害值?"这种问题之中的伤害值和治疗值做了很好的解构, 这种解构意味着GameplayEffect和ExecutionCalculation无需了解目标是如何处理伤害值的. 如果你想Reticle保留在最后一个有效Target上, 就需要自定义TargetActor来记住最后一个有效Target并使Reticle保留在其上. 我称之为持久化Target, 因为其会持续存在直到TargetActor接收到确认或取消消息, TargetActor会在它的射线/Overlap中找到一个新的有效Target, 或者Target不再有效(已销毁). GASShooter对火箭筒二技能的制导火箭定位使用了持久化Target.

GameplayAbility在一帧中的的激活、选择性地向服务器发送TargetData、结束可以被批处理而将2-3个RPC压缩成一个RPC. GAS对此没有提供开箱即用的功能(SpawnActor AbilityTask只在服务端生成Actor). 每次GameplayCue触发都是一次不可靠的多播(NetMulticast)RPC. 在同一时刻触发多个GameplayCue的情况下, 有一些优化方法来将它们压缩成一个RPC或者通过发送更少的数据来节省带宽. 对于只能用一次输入激活的GameplayAbility(它们总是像MOBA游戏一样存在于相同的"槽"中), 我倾向在UGameplayAbility子类中添加一个变量, 这样我就可以定义他们的输入, 之后在授予Ability的时候可以从ClassDefaultObject中读取这个值.

由其: 模型简介

GAS向Gameplay Debugger中添加了功能, 使用反引号(`)键以访问Gameplay Debugger. 按下小键盘的3键以启用Ability分类, 取决于你所拥有的插件, 分类可能是不同的. 如果你的键盘没有小键盘, 比如笔记本, 那么你可以在项目设置(Project Settings)里修改键盘绑定.

AttributeSet可以在运行时从ASC上添加和移除, 然而移除AttributeSet是很危险的, 例如, 如果某个AttributeSet在客户端上移除早于服务端, 而某个Attribute的变化又同步到了客户端, 那么Attribute就会因为找不到AttributeSet而使游戏崩溃. 有种方案是设置一个单一且巨大的AttributeSet, 共享于游戏中的所有Actor, 并且只使用需要的Attribute, 忽略不用的Attribute. 我使用一个简单的多人游戏模板项目来阐述对Unreal Engine 4中GameplayAbilitySystem(GAS)插件的理解.

由其: 4 生命偷取(Lifesteal)

wenet/examples/aishell/s0/ conf/dyq_conformer.yaml 因为算力有限,这里我只采用了前10%的aishell的数据做训练和测试,注意数据集大小会影响vocab_size参数的大小。 查看GASShooter是如何处理爆头问题的, 其概念是一样的, 除了不再依赖随机数作为概率而是检测FHitResult骨骼名. 在这个截图例子中, 我们给捕获的伤害值Attribute增加了50, 你也可以将其设为Override来直接传入硬编码值.

由其

取决于玩家的延迟情况, 可能客户端预测的冷却已经结束, 但是服务端上的GameplayAbility仍处于冷却过程, 这会阻止GameplayAbility的立刻再激活直到服务端冷却结束. 如果你的随机偏差很小, 大多数玩家是不会注意到每次游戏的随机序列都是相同的, 那么使用Activation Prediction Key作为随机种子就应该适用于你. 如果你正在做一些更复杂的事, 需要防范破解者, 那么使用Server Initiated GameplayAbility会更好, 服务端可以创建Prediction Key或者生成随机数种子来通过事件负载(Event Payload)发送. 如果Actor只是用于场景装饰或者不服务于任何游戏逻辑, 简单的解决方案就是重写Actor的IsNetRelevantFor()函数以限制服务端同步到所属(Owning)客户端, 所属(Owning)客户端会拥有其本地生成的版本, 而服务端和其他客户端会拥有服务端同步的版本. 尽管我们可以预测GameplayEffect的应用, 但是不能预测GameplayEffect的移除. 绕过这条限制的一个方法是当想要移除GameplayEffect时, 可以预测性地执行相反的效果, 假设我们要降低40%移动速度, 可以通过应用增加40%移动速度的buff来预测性地将其移除, 之后同时移除这两个GameplayEffect.

由其: 10 预测(Prediction)

样例项目会在角色AttributeSet中的值受到致命一击时创建该GameplayEffect来将金币和经验点数返还给击杀者. 运行时创建的即刻(Instant)GameplayEffect也可以在客户端预测的GameplayAbility中调用. 一个普遍用法是当想要强制另一个玩家做某些事的时候, 像击退或拉取时移动他们, 就会对它们应用一个GameplayEffect来授予其一个自动激活的Ability(查看被动Ability来了解如何在Ability被授予时自动激活它), 从而使其做出相应的动作.

这些问题导致该模型要达到极致的性能需要做很多深度的工程优化,存在较大的开发工作量和较大的难度,有一定的挑战性。 本项目为 WeNet 在 TensorRT 上的推理加速优化方案,项目对 WeNet 中的 Encoder 模型和 Decoder 模型做了深度的性能优化,并且支持了FP32, FP16精度的混合计算,最终相比于baseline达到了一个非常高的加速比。 GAS包含了大量不同详细级别的日志生成语句, 你很可能见到的是ABILITY_LOG()这样的语句.

由其: 「尤其」或「由其」

关于预测伤害值, 我个人不建议使用, 尽管它是大多数刚接触GAS的人最先做的事情之一, 我特别不建议尝试预测死亡, 虽然你可以预测伤害值, 但是这样做很棘手. 如果你错误预测地应用了伤害, 那么玩家将会看到敌人的生命值会跳动, 如果你尝试预测死亡, 那么这将会是特别尴尬和令人沮丧的, 假设你错误预测了某个Character的死亡, 那么它就会开启布娃娃模拟, 只有当服务端纠正后才会停止布娃娃模拟并继续向你射击. GameplayCue(GC)执行非游戏逻辑相关的功能, 像音效, 粒子效果, 镜头抖动等等.

GAS带有开箱即用的客户端预测功能, 然而, 它不能预测所有. GAS中客户端预测的意思是客户端无需等待服务端的许可而激活GameplayAbility和应用GameplayEffect. 它可以"预测"许可其可以这样做的服务端和其应用GameplayEffect的目标. 服务端在客户端激活之后运行GameplayAbility(网络延迟)并告知客户端它的预测是否正确, 如果客户端的预测出错, 那么它就会"回滚"其"错误预测"的修改以匹配服务端. 假设你有一个可以发射8枚弹丸的霰弹枪, 就会有8个轨迹和碰撞GameplayCue. GASShooter采用将它们联合成一个RPC的延迟(Lazy)方法, 其将所有的轨迹信息保存到EffectContext作为TargetData.

由其: 样例项目

这不是官方文档并且这个项目和我自己都不来自Epic Games. Epic最近发起了一项倡议, 将使用新的网络预测插件替换CharacterMovementComponent, 该插件仍处于起步阶段, 但是在Unreal Engine Github上已经可以访问了, 现在说未来哪个引擎版本将首次搭载其试验版还为时尚早. 如果某个GameplayCue是客户端添加的, 那么它也应该自客户端移除. GameplayAbility的网络安全策略决定了Ability应该在网络的何处执行. CAR还有很多高阶功能, 像检查GameplayEffect实例是否已经位于目标上, 修改当前实例的持续时间而不是应用一个新实例(对于CanApplyGameplayEffect()返回false). 样例项目将其绑定到了GDPlayerState用于更新HUD, 当生命值下降为0时, 也可以响应玩家死亡.

由其

堆栈对过期, 持续刷新和周期性刷新也有一些处理策略, 这些在GameplayEffect蓝图中都有很友好的悬浮提示帮助. 公式中的Bias基本上都会减去[1, 2)区间中的整数位, 第一个Modifier的Bias会从最开始的Sum值减值(在循环体前设置Bias), 这就是为什么某个值它本身能起作用的原因以及某个小于1的值与[1, 2)区间中的值起作用的原因. 其次, 该公式还有一些对于可以使用哪些值而未说明的的规则, 因为这是考虑Paragon的情况而设计的. 重命名GameplayTag会创建重定向, 因此仍引用原来GameplayTag的资源会重定向到新的GameplayTag.

由其: 优化过程

如果可以的话, 我更倾向于创建新的GameplayTag, 手动更新所有引用到新的GameplayTag, 之后删除旧的GameplayTag以避免创建新的重定向. Baseline采用pytorch1.11运行, 模型采用自己训练的模型(见复现步骤),测试数据采用的是初赛的测试数据。 朦(音“蒙“)- 月色模糊/不明顯(朦朧) 矇(音同上)- 有眼不能視(矇矓/矇昧)、欺瞞(矇騙/矇混) 由其 「矇混」才是正確的用法。

Attribute一般应该只能由GameplayEffect修改, 这样ASC才能预测(Predict)其改变. GameplayAbility无论是由蓝图还是C++创建都没关系. 这里我们使用蓝图和C++混合创建, 意在展示每种方式的使用方法. AI控制的小兵没有预先定义的GameplayAbility. 对于GameplayAbility的命名, 我使用_BP后缀表示由蓝图创建的GameplayAbility逻辑, 没有后缀则表示由C++创建.

由其: 8 游戏暂停时生成TargetData

我建议在UGameplayAbility子类中增加一个布尔值以明确使用哪种方法. 有几种方法可以实现带有Attribute(武器弹药, 盔甲耐久等等)的可装备物品, 所有这些方法都直接在物品中存储数据, 这对于在生命周期中可以被多个玩家装备的物品来说是必须的. 由其2023 作为选择, 你可以使用多个AttributeSet来表示按需添加到Actor的Attribute分组, 例如, 你可以有一个生命相关的AttributeSet, 一个魔法相关的AttributeSet等等. 在MOBA游戏中, 英雄可能需要魔法, 但是小兵并不需要, 因此英雄就需要魔法AttributeSet而小兵则不需要. Attribute是由FGameplayAttributeData结构体定义的浮点值, 其可以表示从角色生命值到角色等级再到一瓶药水的剂量的任何事物, 如果某项数值是属于某个Actor且游戏相关的, 你就应该考虑使用Attribute.

  • 默认的详细级别是Display, 更高的默认不会显示在控制台里.
  • Baseline采用pytorch1.11运行, 模型采用自己训练的模型(见复现步骤),测试数据采用的是初赛的测试数据。
  • 有几种方法可以实现带有Attribute(武器弹药, 盔甲耐久等等)的可装备物品, 所有这些方法都直接在物品中存储数据, 这对于在生命周期中可以被多个玩家装备的物品来说是必须的.
  • Epic希望在未来的GAS迭代版本中实现真正的冷却预测(玩家可以激活一个在客户端冷却完成但服务端仍处于冷却过程的GameplayAbility).
  • 这意味着高延迟的客户端会花费较长事件来告知服务端开始冷却和接收到服务端Cooldown GE的移除.
  • 使用默认的TargetActor类时, Actor只有直接在射线/Overlap中时才是有效的, 如果它离开射线/Overlap(它移动开或你的视线转向别处), 就不再有效了.

GameplayCue一般是可同步(除非在客户端明确执行(Executed), 添加(Added)和移除(Removed))和可预测的. 如果GameplayAbility在一帧同一原子(Atomic)组中执行这些操作, 我们就可以优化该工作流, 由其 将所有2个或3个RPC批处理(整合)为1个RPC. 枪支命中判断时, 会在一帧同一原子(Atomic)组中做射线检测, 发送TargetData到服务端并结束Ability. 我建议添加一个布尔值到你的自定义UGameplayAbility类来表明其在授予时是否应该被激活.

由其: 优化

尽管Meta Attribute是一个很好的设计模式, 但其并不是强制使用的. 样例项目和文档目前基于Unreal Engine 4.26. 该文档拥有可用于旧版本Unreal Engine的分支, 但是它们不再受支持, 并且可能存在bug和过时信息. GASShooter是该样例项目的姐妹项目, 其演示了基于多人FPS/TPS的高阶GAS技术. 如果你需要同时发送很多GameplayCue, 可以考虑将其批处理成一个RPC, 目标就是减少RPC数量(GameplayCue是不可靠的网络多播(NetMulticast))并以尽可能少的数据量发送.

设计师可以决定一个GameplayEffect能够授予哪些Ability, 授予的Ability等级, 将其绑定在什么输入键上以及该Ability的移除策略. 你会发现自己使用GameplayTag替换了过去使用布尔值或枚举值来编程, 由其 并且需要对对象有无特定的GameplayTag做布尔逻辑判断. 再次感谢NVIDIA提供这样的活动,也非常感谢DevTech团队的老师们的辛苦付出与活动过程中的各种帮助,期待将来会有更多相关的活动举办,让大家有更多互相交流互相学习的机会。 目前为止其在github上收获了2k+Star,说明其被众多的相关从业者所青睐。 由其PR文章可知,目前 WeNet 应用到了喜马拉雅、作业帮、京东、腾讯等数百家公司,他们采用 WeNet 构建自己语音服务,覆盖智能车载、智能家居、智能客服、音频内容生产、直播、会议等语音识别应用场景。 由其 训练,模型采用了和初赛一样的结构,详细配置见

由其: 特别推荐

多个GameplayAbility可以在同一时刻激活, 例如奔跑和射击. 例如, 在样例项目中, 我们在这里从生命值Attribute中减去了最终的伤害值Meta Attribute, 如果有护盾值Attribute的话, 我们也会在减除生命值之前从护盾值中减除伤害值. 样例项目也在这里应用了被击打反应动画, 显示浮动的伤害数值和为击杀者分配经验值和赏金. 通过设计, 伤害值Meta Attribute总是会传递给即刻(Instant)GameplayEffect而不是Attribute Setter. 在每个物品上都创建一个AbilitySystemComponent是种很极端的方案.

由其

例如, Paragon和Fortnite不能预测伤害值, 它们很可能对伤害值使用了ExecutionCalculations, 而这无论如何是不能预测的. 这并不是说你不能试着预测像伤害值这样的事情, 无论如何, 如果你这样做了并且效果很好, 那就是极好的. GameplayAbility(GA)是Actor在游戏中可以触发的一切行为和技能.

由其: 优化

初学者经常会问"我怎样才能获取激活的Ability?", 也许是用来设置变量或取消它. 多个GameplayAbility可以在同一时刻激活, 因此没有"一个激活的Ability", 相反, 你必须搜索ASC的ActivatableAbility列表(ASC拥有的已授予GameplayAbility)并找到一个与你正在寻找的资源或授予的GameplayTag相匹配的Ability. Epic希望在未来的GAS迭代版本中实现真正的冷却预测(玩家可以激活一个在客户端冷却完成但服务端仍处于冷却过程的GameplayAbility). 如果每个子组件都需要很多Attribute且子组件的数量可以是无限的, 或者子组件可以分离被其他玩家使用(比如武器), 或者出于其他原因上述方法不适用于你, 那么我建议就不要使用Attribute, 而是在组件中保存普通的浮点数.

由其

为了增加设计师友好的迭代次数, 特别是在为UI设计UMG Widget时, 可以创建蓝图AsyncTask(在C++中)以直接在UMG蓝图图表中绑定到ASC中常见的修改委托, 唯一的告诫就是其必须手动销毁(比如在widget销毁时), 否则就会永远存于内存中.样例项目包含三个蓝图AsyncTask. 一般在眩晕状态时, 我们就要取消Character所有已激活的GameplayAbility, 阻止新的GameplayAbility激活, 并在整个眩晕期间阻止移动. 样例项目的陨石GameplayAbility会在击中的目标上应用眩晕效果. 该方法也可以在你的GameplayCue需要特别指定的参数时使用, 这些需要特别指定的参数不能由GameplayCueParameter提供, 并且你不想将它们添加到EffectContext, 像伤害数值, 暴击标识, 破盾标识, 处决标识等等. GameplayAbility只能在一帧中执行, 这本身并不能提供太多灵活性, 为了实现随时间推移而触发或响应一段时间后触发的委托操作, 我们需要使用AbilityTask.

由其: 8 游戏暂停时生成TargetData

GA处理响应左Shift输入, 告知CMC开始和停止奔跑, 并且在左Shift按下时预测性地消耗耐力. GASShooter对火箭筒二技能制导火箭锁定的目标使用了Reticle. 敌人身上的红色标识就是Reticle, 相似的白色图像是火箭筒的准星. 我们确实想要GameplayCueManager扫描并找到所有的GameplayCueNotify, 然而, 我们不想要它异步加载每一个, 因为这会将每个GameplayCueNotify和它们所引用的音效和粒子特效放入内存而不管它们是否在关卡中使用. 在像Paragon这样的大型游戏中, 内存中会放入数百兆的无用资源并造成卡顿和启动时无响应. 查看UAbilitySystemGlobals中用于填充GameplayCueParameters结构体的三个函数以获得更多信息.

默认的详细级别是Display, 更高的默认不会显示在控制台里. 第三页显示了所有授予到你的GameplayAbility, 无论其是否正在运行, 无论其是否被阻止激活, 和当前正在运行的AbilityTask的状态. 有两个GameplayCueNotify类, 由其2023 Static和Actor. 它们各自响应不同的事件, 并且不同的GameplayEffect类型可以触发它们.

其他文章推薦: