07 远程敌人

上一章:[[06-英雄战斗能力]] 返回:[[00-课程索引]]

核心架构

1
2
3
4
5
BT_Glacer → EQS寻找射击位置 → 激活 Enemy.Ability.Ranged
→ Projectile Ability 播放 Montage
→ Shared.Event.SpawnProjectile
→ Spawn AWarriorProjectileBase 并注入 EffectSpec
→ Hit/Overlap → 格挡分流或 Apply Spec → HitReact

P167 Ranged Enemy Section Overview(01:20)

提交a7fddbf ReadyForNextSection;章节概览。目标是复用现有 Enemy、AI、GAS 和 Damage 管线扩展 Glacer,而不是复制一套远程战斗框架。

P168 Ranged Enemy Starting Weapon(07:39)

提交30487a8 StartingWeaponStatsDone_NoCode;资产/Data Asset 课。

资产:BP_Glacer_WeaponBP_Gruntling_GlacerDA_GlacerGA_Glacer_SpawnWeapon

1
2
3
DA_Glacer → 授予 OnGiven SpawnWeapon Ability
→ Spawn/Attach BP_Glacer_Weapon
→ RegisterSpawnedWeapon(Enemy.Weapon, Weapon, true)

P169 Glacer Starting Stats(04:28)

与上一提交共同配置 CT_GlacerStatsGE_Glacer_StartUpDA_Glacer。Glacer 仍使用统一的 UWarriorAttributeSet,差异来自 CurveTable/GE 和 Ability Level。

P170 Glacer Hit React(08:38)

提交5835715 GlacerDeathHitReactDeathDone_NoCode;资产课。

DA_Glacer 组合共享 HitReact/Death Ability;本提交可确认修改了 Glacer 材质、Mesh、Death Ability 和溶解 Niagara。HitReact/Death 的具体动画资产关系没有出现在提交清单中,需打开 Glacer 蓝图与 Ability 图表确认;方向算法、Dead Tag 与 Damage 管线继续复用。

P171 Ranged Behavior Tree(05:15)

提交680a474 GlacerBehaviorTreeDone

资产:AIC_GlacerBT_Glacer。本节只建立 Glacer AIController/Behavior Tree 骨架,并复用 C++ 感知写入的 Blackboard.TargetActor。射击位置 EQS 在 P172 接入,远程 Ability 在 P173 接入,近战分支到 P181 才完成,因此不能把最终分支结构提前归到 P171。

P172 Find Shooting Position(08:13)

提交组合193f37d + ad7b412;EQS 资产课。

EQS_FindShootProjectileLocation 使用 EQSContext_TargetActor,从可导航候选中筛选合适射击点。距离、可见性和评分权重需打开资产确认,不应按常见做法杜撰具体数值。

P173 Projectile Ability(08:29)

提交9d4bdd9 ProjectileAbilityDone_NoCode

GA_Glacer_Projectile 使用 Enemy.Ability.Ranged;由 DA_Glacer.EnemyCombatAbilities 授予,BT 通过 TryActivateAbilityByTag 激活。Ability 播放 Cast Montage 并等待生成投射物事件。

P174 Projectile Class(12:56)

提交06f0e5a ProjectileClassDone

1
class AWarriorProjectileBase : public AActor

组件:

1
2
3
UBoxComponent* ProjectileCollisionBox;
UNiagaraComponent* ProjectileNiagaraComponent;
UProjectileMovementComponent* ProjectileMovementComp;

默认运动:

1
2
3
4
InitialSpeed=700
MaxSpeed=900
GravityScale=0
InitialLifeSpan=4秒

伤害策略:

1
2
3
4
5
enum class EProjectileDamagePolicy : uint8
{
OnHit,
OnBeginOverlap
};

Build.cs 加入 Niagara 模块。

P175 Spawning Projectile(07:56)

提交c89c7cc。新增 Shared.Event.SpawnProjectile

1
2
3
4
5
6
Cast Montage 的 AnimNotify
→ Shared.Event.SpawnProjectile
→ GA_Glacer_Projectile 等待到事件
→ 从武器发射 Socket 取位置
→ 计算朝向 TargetActor 的 Rotation
→ Spawn BP_Projectile_Glacer

Spawn 时必须设置 Instigator=Glacer,因为敌我判断、Source ASC 和伤害属性都依赖它。

P176 On Projectile Hit(09:14)

提交045cf8d

构造时绑定:

1
2
OnComponentHit.AddUniqueDynamic(this, &ThisClass::OnProjectileHit);
OnComponentBeginOverlap.AddUniqueDynamic(this, &ThisClass::OnProjectileBeginOverlap);

BeginPlay 根据 DamagePolicy 将 Pawn 响应设为 Block 或 Overlap。Hit 适合单发弹体;Overlap 可用于穿透/持续区域。

P177 Handle Projectile Hit(12:39)

提交3e039f7,并有行为树更新提交 7aa39b2

Hit 流程:

1
2
3
4
5
6
7
8
BP_OnSpawnProjectileHitFX(ImpactPoint)
→ OtherActor 转 Pawn
→ IsTargetPawnHostile
→ 检查 Player.Status.Blocking
→ IsValidBlock
├─ 有效:Send Player.Event.SuccessfulBlock
└─ 无效:HandleApplyProjectileDamage
→ Destroy

Overlap 路径不检查格挡,使用 OverlappedActors 去重且不立即销毁,适合穿透型投射物。

P178 Projectile Spec Handle(10:58)

提交dd65656

1
2
UPROPERTY(BlueprintReadOnly, meta=(ExposeOnSpawn="true"))
FGameplayEffectSpecHandle ProjectileDamageEffectSpecHandle;

设计重点:Spec 在发射 Ability 中构造,保留 Instigator、Ability Level 和 SetByCaller 数据;投射物只携带和应用,不在命中时重新猜测伤害。

P179 Make Projectile Spec Handle(03:00)

提交c26343c

调用:

1
2
UWarriorEnemyGameplayAbility::MakeEnemyDamageEffectSpecHandle(
EffectClass, DamageScalableFloat);
1
2
3
4
5
MakeEffectContext
→ SetAbility / AddSourceObject / AddInstigator
→ MakeOutgoingSpec(AbilityLevel)
→ Shared.SetByCaller.BaseDamage = FScalableFloat.GetValueAtLevel(Level)
→ Spawn Actor 参数注入 ProjectileDamageEffectSpecHandle

P180 Projectile Sound FX(06:53)

提交ca472d8;表现资产课。

BP_Projectile_Glacer 配置发射、飞行、命中声音和 Niagara Trail。C++ 扩展点:

1
2
UFUNCTION(BlueprintImplementableEvent)
void BP_OnSpawnProjectileHitFX(const FVector& HitLocation);

P181 Glacer Melee Ability(08:17)

提交be3a362 GlacerMeleeAbilityDone_NoCode

资产:GA_Glacer_Melee_1、Melee Montage、BT_GlacerDA_Glacer

1
2
目标距离适合远程 → Enemy.Ability.Ranged
目标过近/近战条件 → Enemy.Ability.Melee

两者都由 Tag 激活,行为树不依赖具体 Ability 类。

P182 Unblockable Attack(09:36)

提交e208658,后续 cf91f82 调整 EnemyCombatComponent。

状态 Tag 的实际拼写:

1
Enemy.Status.Unbloackable

Enemy Combat:

1
2
3
4
5
6
7
const bool bBlocking = NativeDoesActorHaveTag(HitActor, Player_Status_Blocking);
const bool bUnblockable = NativeDoesActorHaveTag(GetOwningPawn(), Enemy_Status_Unbloackable);

if (bBlocking && !bUnblockable)
{
bIsValidBlock = IsValidBlock(GetOwningPawn(), HitActor);
}

警告 Cue 使用正确拼写:GameplayCue.FX.UnblockableWarning

[!warning] 不可格挡 Tag 只在敌人近战 UEnemyCombatComponent 路径检查;AWarriorProjectileBase::OnProjectileHit 没有检查它,因此不能说所有投射物天然不可格挡。

P183 Combat Test Map(08:03)

提交0ec06f2;地图/行为树集成测试。

测试覆盖:

  • Glacer EQS 射击选位。
  • Ranged/Melee 分支切换。
  • 投射物 Hit/Overlap 策略。
  • 普通格挡与不可格挡攻击。
  • 多敌人导航、锁定、受击、死亡和 UI。

P184 Section Wrap Up(00:52)

提交7f7e2fc ReadyForBossSection;总结课。

1
2
3
4
5
6
7
flowchart LR
BT[BT_Glacer] --> EQS[EQS射击点]
BT --> GA[Enemy.Ability.Ranged]
GA --> Event[SpawnProjectile Event]
Event --> Projectile[Projectile + EffectSpec]
Projectile --> Block[格挡分流]
Projectile --> Damage[统一伤害Execution]

EffectSpec 传递链

1
2
3
4
5
6
7
8
9
CT_GlacerStats/FScalableFloat
→ Enemy Ability.MakeEnemyDamageEffectSpecHandle
→ EffectContext(Ability/SourceObject/Instigator)
→ SetByCaller BaseDamage
→ ExposeOnSpawn 注入 Projectile
→ Hit/Overlap
→ FunctionLibrary.ApplyGameplayEffectSpecHandleToTargetActor
→ SourceASC.ApplyGameplayEffectSpecToTarget
→ GEExecCalc_DamageTaken

Hit 与 Overlap 对比

行为 OnHit OnBeginOverlap
Pawn 响应 Block Overlap
命中 FX 当前 C++ 未调用
格挡判定
命中后销毁
去重 依赖销毁 OverlappedActors
适用 单发弹体 穿透/持续范围

本章源码检查

  • Spawn 时设置正确 Instigator
  • Spec 有效后再解引用 SpecHandle.Data
  • 世界静态物不尝试读取 ASC。
  • 明确选择 Hit 或 Overlap 策略。
  • 不把近战不可格挡规则误认为已覆盖投射物。

本文从 LearnByCompany 原始文档 自动同步。