自定义生成器限制条件
2023年11月9日 2024年1月11日
说明
AI / EQS / Custom Test
验证EQS寻找补给时会把不可用补给考虑在内
补给不可用逻辑
补给被领取后, 设置在碰撞检测中忽略补给, 同时设置补给不可见, 使用定时器恢复碰撞和不可见选项
存在的问题
补给虽然无法参与碰撞, 在场景中也不可见, 但仍作为Actor存在于场景中, 于是NPC可以正常捕获
验证
-
初始状态
-
设置场景中一个补给不可见
-
选中BP_STUAmmoPickup1
-
设置CollisionComponent不可见
Details > Rendering > Visible
取消勾选: 注意到, 碰撞胶囊已不可见 -
设置StaticMesh不可见
Details > Rendering > Visible
取消勾选
-
-
移动EQS_TestPawn, 仍可以捕获不可见补给
-
恢复补给状态
考虑解决方案
- 自定义生成器: 筛选可见Actor
- 自定义生成器限制条件: 筛选可见Actor
考虑如何实现自定义生成器限制条件
- 判断组件可见性
根组件的选择很灵活, 在一些方案里, 即使根组件不可见, Actor也许能被正常观察; 而根组件可见, Actor也不一定能被正常观察到 - 为补给添加接口: 判断补给是否可用
相应地, 该限制条件只能用于补给
创建C++类
- | |
---|---|
基类 | EnvQueryTest |
名称 | EnvQueryTest_PickupCouldBeTaken |
路径 | AI/EQS |
属性 | Public |
学习EnvQueryTest及其派生类
派生类命名规则
引擎使用该模板识别限制条件在EQS蓝图编辑器的名称
EnvQueryTest_子类别
筛选Actor接口
EnvQueryTest
每次EQS运行时, 调用该函数; 在该函数中, 对捕获Actor进行筛选
public
1// Function that does the actual work 2virtual void RunTest(FEnvQueryInstance& QueryInstance) const { checkNoEntry(); }
筛选Actor逻辑
参考限制条件Trace EnvQueryTest_Trace
构造函数
1UEnvQueryTest_Trace::UEnvQueryTest_Trace(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 2{ 3 Cost = EEnvTestCost::High; 4 ValidItemType = UEnvQueryItemType_VectorBase::StaticClass(); 5 SetWorkOnFloatValues(false); 6 7 Context = UEnvQueryContext_Querier::StaticClass(); 8 TraceData.SetGeometryOnly(); 9}
-
Cost
说明限制条件逻辑的复杂度, 复杂度越高, 开销就越大
使用枚举类型EEnvTestCost对其进行赋值
复杂度参考用于待测试Actor的数学运算 -
ValidItemType
说明测试对象的数据类别, 数组或者元素- UEnvQueryItemType_VectorBase 数组 UEnvQueryItemType_ActorBase 元素 -
SetWorkOnFloatValues
限制条件部分参数的模式由FilterType决定, SetWorkOnFloatValues使用枚举类型EEnvTestFilterType对FilterType进行设置- Minimum 数值 Maximum 数值 Range 数值 Match 布尔类型 参数为true时, FilterType值为Range; 参数为false, FilterType值为Match: 这两个相当于是两种默认类型
如果FilterType为Range, 通常可以在蓝图编辑器中根据需求设置数值的其他模式Minimum和Maximum
1void UEnvQueryTest::SetWorkOnFloatValues(bool bWorkOnFloats) 2{ 3 bWorkOnFloatValues = bWorkOnFloats; 4 5 // Make sure FilterType is set to a valid value. 6 if (bWorkOnFloats) 7 { 8 if (FilterType == EEnvTestFilterType::Match) 9 { 10 FilterType = EEnvTestFilterType::Range; 11 } 12 } 13 else 14 { 15 if (FilterType != EEnvTestFilterType::Match) 16 { 17 FilterType = EEnvTestFilterType::Match; 18 } 19 20 // Scoring MUST be Constant for boolean tests. 21 ScoringEquation = EEnvTestScoreEquation::Constant; 22 } 23 24 UpdatePreviewData(); 25}
UEnvQueryTest_Trace::RunTest
-
FEnvQueryInstance::ItemIterator
EQS专用的迭代器类型 -
获取Actor位置
1const FVector ItemLocation = GetItemLocation(QueryInstance, It.GetIndex()) + FVector(0, 0, ItemZ);
-
通用模板: 遍历捕获目标
1for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) 2{ 3 const auto ItemActor = GetItemActor(QueryInstance, It.GetIndex()); // 后续需对ItemActor作出修改时, 移除const限定符 4 5 // ... 6}
-
设置Actor通过筛选
- TestPurpose 枚举类型TEnumAsByteEEnvTestPurpose, 取值 Filter Only
,Score Only
,Filter and Score
FilterType Score 如果时布尔类型, 为true, 通过筛选; 为false, 未通过. 如果是数值, 为权重值 bExpected 为true, Score决定筛选情况; 为false, 对Score对应的结果取反 1It.SetScore(TestPurpose, FilterType, true, true);
-
设置Actor未通过筛选
- InStatus 使用枚举类型EEnvItemStatus对其赋值, 表示是否通过筛选: Failed
或Passed
Score 使用默认值 1It.ForceItemState(EEnvItemStatus::Failed);
修改补给基类
- 提供测试机制: 返回设置值
- 通过定时器状态判断: 若定时器开启, 认为补给无效
添加属性: 开启测试
protected
ShootThemUp: Pickups/STUBasePickup.h
1UPROPERTY(EditAnywhere, BlueprintReadWrite) 2bool EnableTest = true;
添加属性: 测试结果
protected
ShootThemUp: Pickups/STUBasePickup.h
1UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "EnableTest")) 2bool CouldBeTakenTest = true;
添加属性: 存放定时器描述符
private
ShootThemUp: Pickups/STUBasePickup.h
1FTimerHandle RespawnTimerHandle;
屏蔽定时器描述符变量定义
ShootThemUp: Pickups/STUBasePickup.cpp
1// PickupWasTaken
添加接口: 判断补给是否可用
public
ShootThemUp: Pickups/STUBasePickup.h
1bool CouldBeTaken() const;
ShootThemUp: Pickups/STUBasePickup.cpp
1bool ASTUBasePickup::CouldBeTaken() const 2{ 3 if (EnableTest) 4 { 5 return CouldBeTakenTest; 6 } 7 else 8 { 9 return !GetWorldTimerManager().IsTimerActive(RespawnTimerHandle); 10 } 11}
实现EnvQueryTest_PickupCouldBeTaken
构造函数
- |
---|
低开销 |
单个元素 |
使用布尔类型 |
public
ShootThemUp: AI/EQS/EnvQueryTest_PickupCouldBeTaken.h
1UEnvQueryTest_PickupCouldBeTaken(const FObjectInitializer &ObjectInitializer);
ShootThemUp: AI/EQS/EnvQueryTest_PickupCouldBeTaken.cpp
1#include "EnvironmentQuery/Items/EnvQueryItemType_ActorBase.h" 2 3UEnvQueryTest_PickupCouldBeTaken::UEnvQueryTest_PickupCouldBeTaken(const FObjectInitializer &ObjectInitializer) // 4: Super (ObjectInitializer) 5{ 6 Cost = EEnvTestCost::Low; 7 ValidItemType = UEnvQueryItemType_ActorBase::StaticClass(); 8 SetWorkOnFloatValues(false); 9}
RunTest
public
ShootThemUp: AI/EQS/EnvQueryTest_PickupCouldBeTaken.h
1virtual void RunTest(FEnvQueryInstance& QueryInstance) const override;
ShootThemUp: AI/EQS/EnvQueryTest_PickupCouldBeTaken.cpp
1#include "Pickups/STUBasePickup.h" 2 3void UEnvQueryTest_PickupCouldBeTaken::RunTest(FEnvQueryInstance& QueryInstance) const 4{ 5 for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) 6 { 7 const auto ItemActor = GetItemActor(QueryInstance, It.GetIndex()); 8 const auto PickupActor = Cast<ASTUBasePickup>(ItemActor); 9 if (!PickupActor) continue; 10 11 const auto CouldBeTaken = PickupActor->CouldBeTaken(); 12 if (CouldBeTaken) 13 { 14 It.SetScore(TestPurpose, FilterType, true, true); 15 } 16 else 17 { 18 It.ForceItemState(EEnvItemStatus::Failed); 19 } 20 } 21}
使用PickupCouldBeTaken
-
为EQS_FindAmmoPickup的生成器添加PickupCouldBeTaken
Add Test > Pickup Could be Taken
-
选中EQS_TestPawn, 合适补给被标记为绿色
-
选择一个被标记为绿色的补给, 使之状态无效
勾选Enable Test
, 取消Could Be Taken Test
勾选 -
移动EQS_TestPawn, 该补给不再有效
注意
-
通过勾选和取消勾选生成器的限制条件, 可以对若干指定项进行测试, 且立即生效
-
当前,
PickupCouldBeTaken > Details > Filter > Bool Match
是否勾选无区别. 而在限制条件的设计中, 期望其勾选状态的改变应该导致相反的结果
EnvQueryTest取反逻辑
-
获取BoolMatch值: 决定是否对筛选结果取反
EnvQueryTest_Trace
BoolValue是一个特殊类型, 通过成员函数GetValue获取布尔值1UObject* DataOwner = QueryInstance.Owner.Get(); 2BoolValue.BindData(DataOwner, QueryInstance.QueryID); 3 4bool BoolMatchValue = BoolValue.GetValue();
-
使用SetScore统一设置是否通过筛选
Score 筛选结果 true 通过筛选 false 未通过筛选 Bool Match
是否对结果取反 true false 对结果取反 1It.SetScore(TestPurpose, FilterType, Score, BoolMatchValue);
实现EnvQueryTest_PickupCouldBeTaken结果取反
CouldBeTaken | |
---|---|
true | 可以被拾取 |
false | 无法被拾取 |
Bool Match |
- |
---|---|
true | |
false | 对结果取反 |
1void UEnvQueryTest_PickupCouldBeTaken::RunTest(FEnvQueryInstance& QueryInstance) const 2{ 3 const auto DataOwner = QueryInstance.Owner.Get(); 4 BoolValue.BindData(DataOwner, QueryInstance.QueryID); 5 6 const auto BoolMatchValue = BoolValue.GetValue(); 7 for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It) 8 { 9 const auto ItemActor = GetItemActor(QueryInstance, It.GetIndex()); 10 const auto PickupActor = Cast<ASTUBasePickup>(ItemActor); 11 if (!PickupActor) continue; 12 13 const auto CouldBeTaken = PickupActor->CouldBeTaken(); 14 It.SetScore(TestPurpose, FilterType, CouldBeTaken, BoolMatchValue); 15 } 16}
查看取反效果
EQS_FindAmmoPickup > PickupCouldBeTaken > Details > Filter > Bool Match
默认勾选
取消勾选
为EQS_FindHealthPickup生成器添加限制条件PickupCouldBeTaken
设置补给默认关闭测试
默认关闭测试. 之前默认开启. 不用挨个关闭补给测试
ShootThemUp: Pickups/STUBasePickup.h
1UPROPERTY(EditAnywhere, BlueprintReadWrite) 2bool EnableTest = false;