使用EQS寻找弹药补给
2023年11月8日 2024年1月11日
说明
AI / EQS / Find Ammo Pickup
寻找弹药补给的EQS: EQS_FindAmmoPickup
判断是否需要领取弹药补给的Decorator类名STUNeedAmmoDecorator
指定武器类型, 自然弹药不是无限模式, 而这里暂时设置为当弹药耗尽才去领取补给
要求武器类型和弹药补给类型一致
可以参考上节当作练习, 再和给出的答案进行比较
创建EQS资产并测试
预备工作
-
设置弹药补给材质为黑色
- 双击打开
Content/Pickups/BP_STUAmmoPickup
- 内容浏览器选中
Content/Materials/M_BaseColor
- 应用材质
- 双击打开
-
拖动复制弹药补给为3个
-
确保发射器的弹药不是无限模式
BP_STULauncherWeapon > Default Ammo > Infinite
, 取消勾选
创建EQS资产, 命名为EQS_FindAmmoPickup
Content/AI/EQS
目录下
可以使用Ctrl-W对EQS_FindHealthPickup进行拷贝, 并修改筛选Actor类型
设置EQS_TestPawn应用EQS_FindAmmoPickup, 查看
Details > EQS > Query Template
, 选择EQS_FindAmmoPickup
添加生成器: 设置补给类型
Generators > Actors Of Class
- | ||
---|---|---|
Searched Actor Class | 指定Actor类型 | STUAmmoPickup |
Search Radius | 搜索半径 | 1500 |
为生成器添加限制条件: 视线可见
Add Test > Trace
要求无阻挡
- | |
---|---|
Filter > Bool Match | 取消勾选 |
为生成器添加限制条件: 距离最近
Add Test > Distance
- | ||
---|---|---|
Filter > Filter Type | Minimum | |
Filter > Float Value Min | 0 | |
Score > Scoring Factor | -1 | 距离越近, 权重越大 |
为生成器添加限制条件: 存在可到达路径
Add Test > Path Finding
在行为树中使用EQS资产
-
暂时停止当前逻辑
-
添加节点: Selector
NPC寻找弹药补给, 或等待 -
添加节点: Wait
设置时长为2s -
添加序列
-
为序列添加任务: RunEQSQuery
-
设置EQS
Details > EQS > EQSRequest > Query Template
, 设置为EQS_FindAmmoPickupDetails > Blackboard > Blackboard Key
, 设置为AimLocation
-
为序列添加任务: MoveTo
Details > Blackboard > Blackboard Key
, 设置为AimLocation
查看
- 按下
F8
或者点击Eject
- 拖动距离NPC最近的弹药补给: NPC寻找并去到此时距离最近的弹药补给位置
- 点击
Pause simulation
, 将所有弹药补给移动到墙后, 点击Resume play-in-editor session
序列停止, NPC循环执行Wait任务
实现STUNeedAmmoDecorator
创建C++类
- | |
---|---|
基类 | BTDecorator |
名称 | STUNeedAmmoDecorator |
路径 | AI/Decorators |
属性 | Public |
武器组件添加接口: 指定类型武器弹药是否为空
课程中只要检测到弹药不是满的, 就会去领取弹药补给; 为此, 将ASTUBaseWeapon::IsAmmoFull从protected移动到public
public
ShootThemUp: Components/STUWeaponComponent.h
1bool IsAmmoEmpty(TSubclassOf<ASTUBaseWeapon> WeaponType) const;
ShootThemUp: Components/STUWeaponComponent.cpp
1bool USTUWeaponComponent::IsAmmoEmpty(TSubclassOf<ASTUBaseWeapon> WeaponType) const 2{ 3 for (const auto Weapon : Weapons) 4 { 5 if (Weapon && Weapon->IsA(WeaponType)) 6 { 7 return Weapon->IsAmmoEmpty(); 8 } 9 } 10 return false; 11}
添加构造函数
public
ShootThemUp: AI/Decorators/STUNeedAmmoDecorator.h
1USTUNeedAmmoDecorator();
ShootThemUp: AI/Decorators/STUNeedAmmoDecorator.cpp
1USTUNeedAmmoDecorator::USTUNeedAmmoDecorator() 2{ 3 NodeName = "Need Ammo"; 4}
添加属性: 设置武器类型
protected
ShootThemUp: AI/Decorators/STUNeedAmmoDecorator.h
1class ASTUBaseWeapon; 2 3UPROPERTY(EditAnywhere, BlueprintReadWrite) 4TSubclassOf<ASTUBaseWeapon> WeaponType;
覆写条件
protected
ShootThemUp: AI/Decorators/STUNeedAmmoDecorator.h
1virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override;
参考拾取弹药逻辑: 要求游戏角色存活
ShootThemUp: Pickups/STUAmmoPickup.cpp
GivePickupTo
ShootThemUp: AI/Decorators/STUNeedAmmoDecorator.cpp
1#include "Weapon/STUBaseWeapon.h" 2#include "AIController.h" 3#include "STUUtils.h" 4#include "Components/STUHealthComponent.h" 5#include "Components/STUWeaponComponent.h" 6 7bool USTUNeedAmmoDecorator::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const 8{ 9 const auto Controller = OwnerComp.GetAIOwner(); 10 if (!Controller) return false; 11 12 const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(Controller->GetPawn()); 13 if (!HealthComponent || HealthComponent->IsDead()) return false; 14 15 const auto WeaponComponent = STUUtils::GetSTUPlayerComponent<USTUWeaponComponent>(Controller->GetPawn()); 16 if (!WeaponComponent) return false; 17 18 return WeaponComponent->IsAmmoEmpty(WeaponType); 19}
使用STUNeedAmmoDecorator
BT_STUCharacter
-
为Sequence添加Decorator
-
设置STUHealthPercentDecorator
- Observer aborts Self Weapon Type BP_STULauncherWeapon
调整行为树
由于弹药初始不为空, 需要射击逻辑
-
将本节新建序列移动到拾取生命补给逻辑之后: 优先级其次
-
恢复行为树框架
-
确保射击服务有目标
-
暂时不更换武器
-
步枪伤害设为0
BP_STURifleWeapon > Damage Amount
-
可以在拾取生命补给和拾取弹药补给移动之前添加Wait任务, 用来判断NPC发现补给情况
恢复设置
- 更换武器
- 弹药补给材质