六一的部落格


关关难过关关过,前路漫漫亦灿灿。




说明

AI / EQS / Find Ammo Pickup

寻找弹药补给的EQS: EQS_FindAmmoPickup

判断是否需要领取弹药补给的Decorator类名STUNeedAmmoDecorator

指定武器类型, 自然弹药不是无限模式, 而这里暂时设置为当弹药耗尽才去领取补给

要求武器类型和弹药补给类型一致

可以参考上节当作练习, 再和给出的答案进行比较


创建EQS资产并测试


预备工作

  1. 设置弹药补给材质为黑色

    • 双击打开 Content/Pickups/BP_STUAmmoPickup
    • 内容浏览器选中 Content/Materials/M_BaseColor
    • 应用材质


  2. 拖动复制弹药补给为3个


  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资产

  1. 暂时停止当前逻辑

  2. 添加节点: Selector

    NPC寻找弹药补给, 或等待

  3. 添加节点: Wait

    设置时长为2s

  4. 添加序列

  5. 为序列添加任务: RunEQSQuery

  6. 设置EQS

    • Details > EQS > EQSRequest > Query Template , 设置为EQS_FindAmmoPickup
    • Details > Blackboard > Blackboard Key , 设置为AimLocation


  7. 为序列添加任务: MoveTo

    Details > Blackboard > Blackboard Key , 设置为AimLocation



查看

  1. 按下 F8 或者点击 Eject
  2. 拖动距离NPC最近的弹药补给: NPC寻找并去到此时距离最近的弹药补给位置
  3. 点击 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

  1. 为Sequence添加Decorator

  2. 设置STUHealthPercentDecorator

    -
    Observer aborts Self
    Weapon Type BP_STULauncherWeapon



调整行为树

由于弹药初始不为空, 需要射击逻辑

  1. 将本节新建序列移动到拾取生命补给逻辑之后: 优先级其次

  2. 恢复行为树框架

  3. 确保射击服务有目标


  4. 暂时不更换武器


  5. 步枪伤害设为0

    BP_STURifleWeapon > Damage Amount


  6. 可以在拾取生命补给和拾取弹药补给移动之前添加Wait任务, 用来判断NPC发现补给情况



恢复设置

  • 更换武器
  • 弹药补给材质

使用EQS寻找弹药补给



说明

AI / EQS / Find Ammo Pickup

寻找弹药补给的EQS: EQS_FindAmmoPickup

判断是否需要领取弹药补给的Decorator类名STUNeedAmmoDecorator

指定武器类型, 自然弹药不是无限模式, 而这里暂时设置为当弹药耗尽才去领取补给

要求武器类型和弹药补给类型一致

可以参考上节当作练习, 再和给出的答案进行比较


创建EQS资产并测试


预备工作

  1. 设置弹药补给材质为黑色

    • 双击打开 Content/Pickups/BP_STUAmmoPickup
    • 内容浏览器选中 Content/Materials/M_BaseColor
    • 应用材质


  2. 拖动复制弹药补给为3个


  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资产

  1. 暂时停止当前逻辑

  2. 添加节点: Selector

    NPC寻找弹药补给, 或等待

  3. 添加节点: Wait

    设置时长为2s

  4. 添加序列

  5. 为序列添加任务: RunEQSQuery

  6. 设置EQS

    • Details > EQS > EQSRequest > Query Template , 设置为EQS_FindAmmoPickup
    • Details > Blackboard > Blackboard Key , 设置为AimLocation


  7. 为序列添加任务: MoveTo

    Details > Blackboard > Blackboard Key , 设置为AimLocation



查看

  1. 按下 F8 或者点击 Eject
  2. 拖动距离NPC最近的弹药补给: NPC寻找并去到此时距离最近的弹药补给位置
  3. 点击 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

  1. 为Sequence添加Decorator

  2. 设置STUHealthPercentDecorator

    -
    Observer aborts Self
    Weapon Type BP_STULauncherWeapon



调整行为树

由于弹药初始不为空, 需要射击逻辑

  1. 将本节新建序列移动到拾取生命补给逻辑之后: 优先级其次

  2. 恢复行为树框架

  3. 确保射击服务有目标


  4. 暂时不更换武器


  5. 步枪伤害设为0

    BP_STURifleWeapon > Damage Amount


  6. 可以在拾取生命补给和拾取弹药补给移动之前添加Wait任务, 用来判断NPC发现补给情况



恢复设置

  • 更换武器
  • 弹药补给材质