当前位置: 首页 > news >正文

UE GAS 实战(六)完美格挡与动画分层融合

WarriorRPG 防御系统深度分析

格挡完整流程图

按住格挡键→ ASC 识别MustBeHeld类型输入 → 激活 Block GA(蓝图) → 给角色添加Player_Status_Blocking标签
敌人攻击命中时EnemyCombatComponent::OnHitTargetActor)做三重判定:

  1. 玩家有Blocking标签?
  2. 敌人没有Unblockable标签?
  3. IsValidBlock()两者朝向点积 < -0.1(面对面)?

三条全过 → 发送SuccessfulBlock事件(不扣血,只播特效音效) 任一不过 → 发送MeleeHit事件(正常伤害流程)
松开格挡键→ ASC 检测到MustBeHeld释放 → 取消 Block GA → 移除Blocking标签
投射物格挡同理,但没有Unblockable检查(远程攻击总能挡)。目标锁定时格挡会暂停自动旋转,让玩家自由调整朝向。没有弹反和反击机制。


二、格挡系统各环节详细分析

2.1 GameplayTag 定义(基础设施)

文件WarriorGameplayTags.h/cpp

防御系统依赖的 6 个核心 Tag:

// 输入标签InputTag_MustBeHeld// "InputTag.MustBeHeld" - 持续按住类父标签InputTag_MustBeHeld_Block// "InputTag.MustBeHeld.Block" - 格挡输入// 技能/状态/事件标签Player_Ability_Block// "Player.Ability.Block" - 格挡技能标识Player_Status_Blocking// "Player.Status.Blocking" - 格挡中状态Player_Event_SuccessfulBlock// "Player.Event.SuccessfulBlock" - 成功格挡事件// 敌人标签Enemy_Status_Unblockable// "Enemy.Status.Unblockable" - 不可格挡攻击

2.2 输入处理:MustBeHeld 机制

文件WarriorAbilitySystemComponent.cpp

格挡使用MustBeHeld(持续按住)模式:按住激活,松开取消。区别于 Toggleable(按一次开/再按关)和普通技能(按一下触发一次)。

按下格挡键 → 激活 Block GA
voidUWarriorAbilitySystemComponent::OnAbilityInputPressed(constFGameplayTag&InInputTag){if(!InInputTag.IsValid())return;// 遍历所有已注册技能,通过 DynamicSpecSourceTags 匹配输入标签for(constFGameplayAbilitySpec&AbilitySpec:GetActivatableAbilities()){if(!AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InInputTag))continue;// Toggleable 类型走切换逻辑(格挡不是 Toggleable,走 else)if(InInputTag.MatchesTag(WarriorGameplayTags::InputTag_Toggleable)&&AbilitySpec.IsActive())CancelAbilityHandle(AbilitySpec.Handle);elseTryActivateAbility(AbilitySpec.Handle);// 格挡走这里}}
松开格挡键 → 取消 Block GA
voidUWarriorAbilitySystemComponent::OnAbilityInputReleased(constFGameplayTag&InInputTag){// 只处理 MustBeHeld 类型!普通技能松开不触发任何操作if(!InInputTag.IsValid()||!InInputTag.MatchesTag(WarriorGameplayTags::InputTag_MustBeHeld))return;for(constFGameplayAbilitySpec&AbilitySpec:GetActivatableAbilities()){if(AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(InInputTag)&&AbilitySpec.IsActive())CancelAbilityHandle(AbilitySpec.Handle);// 取消格挡 → 触发 EndAbility → 移除 Blocking 标签}}

实例:玩家按下右键 →Input_AbilityInputPressed(InputTag_MustBeHeld_Block)→ ASC 找到 Block GA →TryActivateAbility→ Block GA 激活并添加Player_Status_Blocking标签。
松开右键 →OnAbilityInputReleased→ 检测到 MustBeHeld 类型 → 取消 Block GA:CancelAbilityHandle →会触发 Block GA 的 EndAbility → 移除标签。


2.3 格挡判定核心是否在正面

文件WarriorFunctionLibrary.cpp

boolUWarriorFunctionLibrary::IsValidBlock(AActor*InAttacker,AActor*InDefender){check(InAttacker&&InDefender);// 计算攻击者和防御者的朝向向量点积constfloatDotResult=FVector::DotProduct(InAttacker->GetActorForwardVector(),InDefender->GetActorForwardVector());constfloatThreshold=-0.1f;// 点积 < -0.1 时格挡有效(两者面对面)returnDotResult<Threshold;}

通俗解释

想象两个人站在一起,每个人面朝一个方向。点积可以告诉我们他们的朝向关系:

  • 点积 = +1:完全同向(都朝同一方向看,玩家背对敌人)→ 格挡无效
  • 点积 = 0:互相垂直(侧对侧)→ 格挡无效
  • 点积 = -1:完全相对(面对面)→ 格挡有效

阈值-0.1意味着偏差角度约84°以内都算有效格挡,这给了玩家较大的容错空间。

格挡有效区域(约168°的正面弧度): 敌人 ↓ ← ← ↓ → → / \ / 格挡有效区 \ / \ 玩家 ←——————→ 玩家朝向 \ / \ 格挡有效区 / \ / → → ↑ ← ←

2.4 近战攻击的格挡判定

文件EnemyCombatComponent.cpp

当敌人的武器/拳头碰撞盒与玩家重叠时触发:

voidUEnemyCombatComponent::OnHitTargetActor(AActor*HitActor){// === 去重 ===if(OverlappedActors.Contains(HitActor))return;OverlappedActors.AddUnique(HitActor);boolbIsValidBlock=false;// === 三重判定 ===// ① 玩家是否正在格挡?constboolbIsPlayerBlocking=UWarriorFunctionLibrary::NativeDoesActorHaveTag(HitActor,WarriorGameplayTags::Player_Status_Blocking);// ② 敌人攻击是否不可格挡?constboolbIsMyAttackUnblockable=UWarriorFunctionLibrary::NativeDoesActorHaveTag(GetOwningPawn(),WarriorGameplayTags::Enemy_Status_Unblockable);// ③ 方向判定(仅在①②通过后)if(bIsPlayerBlocking&&!bIsMyAttackUnblockable)bIsValidBlock=UWarriorFunctionLibrary::IsValidBlock(GetOwningPawn(),HitActor);// === 结果分发 ===FGameplayEventData EventData;EventData.Instigator=GetOwningPawn();EventData.Target=HitActor;if(bIsValidBlock)// 格挡成功 → 不触发伤害,发送成功格挡事件给玩家UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(HitActor,WarriorGameplayTags::Player_Event_SuccessfulBlock,EventData);else// 格挡失败 → 正常伤害流程UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetOwningPawn(),WarriorGameplayTags::Shared_Event_MeleeHit,EventData);}

关键设计:格挡成功时,事件发送给玩家HitActor);格挡失败时,事件发送给敌人自身GetOwningPawn())。这是因为伤害计算 GA 监听的是Shared_Event_MeleeHit,该 GA 绑定在攻击者身上。


2.5 远程攻击的格挡判定

文件WarriorProjectileBase.cpp

远程攻击的格挡逻辑与近战类似,但有两个不同:

  1. 没有 Unblockable 检查:远程攻击总是可以被格挡(只要方向正确)
  2. 攻击者是投射物本身IsValidBlock(this, HitPawn)用投射物的朝向(飞行方向)计算
// 投射物命中回调中的格挡检查(简化)boolbIsValidBlock=false;constboolbIsPlayerBlocking=UWarriorFunctionLibrary::NativeDoesActorHaveTag(HitPawn,WarriorGameplayTags::Player_Status_Blocking);if(bIsPlayerBlocking)bIsValidBlock=UWarriorFunctionLibrary::IsValidBlock(this,HitPawn);// this = 投射物if(bIsValidBlock)// 格挡成功,不造成伤害SendGameplayEventToActor(HitPawn,Player_Event_SuccessfulBlock,Data);else// 正常伤害HandleApplyProjectileDamage(HitPawn,Data);

2.6 目标锁定中的格挡特殊处理

文件HeroGameplayAbility_TargetLock.cpp

在目标锁定状态下,每帧会强制旋转角色朝向锁定目标。但格挡时需要允许玩家自由调整朝向(以便面向其他方向的敌人格挡),因此做了特殊处理:

voidUHeroGameplayAbility_TargetLock::OnTargetLockTick(floatDeltaTime){// ...// 格挡或翻滚时不强制旋转constboolbShouldOverrideRotation=!UWarriorFunctionLibrary::NativeDoesActorHaveTag(GetHeroCharacterFromActorInfo(),WarriorGameplayTags::Player_Status_Rolling)&&!UWarriorFunctionLibrary::NativeDoesActorHaveTag(GetHeroCharacterFromActorInfo(),WarriorGameplayTags::Player_Status_Blocking);if(bShouldOverrideRotation){// 正常旋转朝向目标...}// 格挡时:不旋转,玩家可自由调整朝向以应对不同方向的攻击}

防御表演

这是 Sequence 的 Then 0(第一件事),做的是让英雄角色立即转向攻击者。

格挡成功瞬间,角色被推动微微后退一小步(0.2秒,力度80),模拟被敌人攻击击退的格挡后震动感
以及在角色身上播放魔法盾成功格挡的视觉特效和音效(火花、金属碰撞声等)。

三、完美格挡

概述:按下格挡键的瞬间挡住敌人攻击 = 完美格挡,
实现逻辑:

奖励慢动作演出 + 华丽特效 + 直接跳到终结技
具体如下:

Branch: Is Perfect Block?
├─ False → 什么都不做(普通格挡到此结束)
└─ True → 执行以下完美格挡专属流程 ↓

① Add Gameplay Tag to Actor if None
In Actor: Hero Character
Tag to Add: Player.Status.JumpToFinisher
→ 给英雄添加"可跳转终结技"状态标签

② Execute GameplayCue With Params On Owner
Gameplay Cue Tag: GameplayCue.FX.MagicShield.PerfectBlock
→ 播放完美格挡专属特效(比普通格挡更华丽的魔法盾闪光)

③ Set Global Time Dilation = 0.2
→ 全局时间缩放到 20%(慢动作!)

④ Delay: Duration = 0.08秒
→ 维持慢动作 0.08 秒(实际体感约 0.4 秒因为时间变慢了)

⑤ Set Global Time Dilation = 1.0
→ 恢复正常速度

⑥ Start Reset Jump to Finisher Timer
Target: self (GA_Hero_Block)
→ 启动一个计时器,超时后自动移除 JumpToFinisher 标签

总结:
完美格挡 = 普通格挡的所有反馈 + 三个额外奖励:

奖励效果
终结技窗口添加Player.Status.JumpToFinisher标签,在限定时间内按攻击可以触发终结技(这就是之前问的"格挡后特殊攻击"!)
慢动作演出全局时间降到 0.2 倍速持续约 0.08 秒,制造"子弹时间"的戏剧性停顿感
华丽特效播放PerfectBlock专属特效,比普通SuccessfulBlock更炫

四、防御反击(Counter Attack)系统分析

完美格挡后 → 会设定可以跳转到终结技能的tag
然后攻击的ga里会判定是否有这个tag
如果有这个tag,则下一次攻击是终结攻击即可

这个tag会在短暂后清除

敌人的无视防御的攻击

不可格挡(Unblockable)判定实现逻辑
一句话总结:

敌人的特定攻击技能在激活时临时给自身贴上Unblockable标签,碰撞判定时检查这个标签决定能不能被格挡,技能结束后撕掉标签。

[敌人 AI 行为树选中一个不可格挡攻击] ↓ 敌人攻击 GA 激活(如 GA_Giant_MeleeAttack_3 蓄力重击) ├─ AddTag: Enemy.Status.Unblockable → 给自身贴上标签 ↓ [碰撞盒碰到玩家] → EnemyCombatComponent::OnHitTargetActor ├─ 玩家有 Blocking 标签? ✓(玩家在格挡) ├─ 敌人有 Unblockable 标签? ✓ ← 关键检查 → 玩家格挡无效,照样受伤 ↓ 攻击 GA 结束 └─ RemoveTag: Enemy.Status.Unblockable → 撕掉标签

::: info 提问
为什么是这样的逻辑?按理来说应该是某个技能不可格挡,而非给怪物添加不可格挡的逻辑吧
:::

是的,从设计角度看"某个技能不可格挡"比"整个怪物不可格挡"更合理。
EnemyCombatComponent::OnHitTargetActor做格挡判定时,它能直接访问的只有敌人 Pawn被打的玩家这两个 Actor。想要获取ga本身,是比较麻烦的,通过tag可以简单一点。

五、动画混合

播放格挡动画时,设定了上半身的插槽

在玩家的动画里,实现了分层混合:这样可以实现上半身防御下半身走路

http://www.jsqmd.com/news/761060/

相关文章:

  • 华硕笔记本终极优化指南:用G-Helper实现AMD CPU降压调优
  • ESP32-P4开发板评测:7英寸HMI屏与AIoT应用实践
  • 如何用思维导图拆解项目范围
  • 3个致命误区导致国密支付上线失败!PHP工程师必查的国密证书链校验、时间戳RFC3161标准、随机数熵源合规性清单
  • Balena Etcher三步指南:免费开源工具,安全烧录系统镜像到SD卡和U盘
  • Dify对接MES/ERP非结构化日志的智能检索方案(含日志时间序列语义增强模块开源代码)
  • 从传感器开发到Modbus从机:用STM32 HAL库+FreeModbus快速搭建你的工业协议栈
  • Taotoken用量看板如何帮助团队清晰管理AI调用成本
  • OpenUI深度解析:AI驱动界面生成从原理到实战部署
  • 基于飞书与Claude Code的AI Agent自动化工作流构建指南
  • 为什么你的PHP AI校验总被绕过?7个被90%开发者忽略的安全盲区,今天必须修复
  • AI辅助开发:基于快马多模型能力打造你的智能终端,让xshell8具备AI思考力
  • 如何用开源工具让旧Mac重获新生?三步解锁硬件隐藏潜力
  • Docker化Emacs开发环境:跨版本测试与CI/CD集成实践
  • VIOLA框架:小样本视频理解的技术突破与实践
  • ai赋能嵌入式开发:让快马智能助手帮你完成stm32cubemx配置与代码生成
  • 终极Windows Defender控制:开源工具让你完全掌控系统安全
  • 多智能体协作平台AgentWall:从架构设计到工程实践
  • genshin-fps-unlock深度解析:突破《原神》60帧限制的架构实现与实战指南
  • 边缘计算中3D高斯泼溅技术的优化与实现
  • 解密BepInEx:突破性Unity游戏插件框架的实战应用与架构解析
  • OpenAgents智能体开发平台:从核心原理到实战部署
  • camh:轻量级跨平台摄像头框架,嵌入式视觉开发的高性能选择
  • 从APK签名到安装:一次完整的apktool反编译、修改与V1/V2签名实战记录
  • AI智能体记忆管理:基于文件系统的无侵入式记忆整理与提取方案
  • 多模型竞技场:用Python构建LLM谜语生成与解答评测系统
  • AI驱动的git-release-notes:自动化生成发布文档的智能工具
  • Dify国产化部署最后1公里:国产GPU(寒武纪MLU370)推理加速失效诊断(含onnxruntime-mlu编译日志逐行解密)
  • 军事AI决策系统:混合推理架构与实战优化
  • php函数版本更新的方法和使用工具