20201104 UE4 EQS高级应用 自定义EQS节点 Custom EQS Node

基础应用就不说了,直接看官方文档

这篇主要讲怎么在C++层定义Generator、Test和Context。

决策对象

EQS的作用是做出最优决策,目前支持的决策对象包括:

  • Actor
  • Point
  • Direction

Generator

按照上面所说,生成器可以生成可选的集合。

对于一个自定义C++ Generator来说,最起码需要实现两个函数:

  • 构造函数
    • 标明集合的类型(Actor, Point, Direction…)
  • GenerateItems() 往集合里添加元素
    • QueryInstance.AddItemData<UEnvQueryItemType_Actor>(Element); 添加点元素
    • QueryInstance.AddItemData<UEnvQueryItemType_Point>(Element); 添加Actor元素

下面以一个例子说明,这个例子会生成一个敌人的集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
UEnvQueryGenerator_Enemy::UEnvQueryGenerator_Teammate(const FObjectInitializer& ObjectInitializer /*= FObjectInitializer::Get()*/)
{
ItemType = UEnvQueryItemType_Actor::StaticClass();
}

void UEnvQueryGenerator_Enemy::GenerateItems(FEnvQueryInstance& QueryInstance) const
{
TWeakObjectPtr<UObject> OwnerPtr = QueryInstance.Owner;
UObject* Owner = OwnerPtr.Get();
MyCharactor* MyChar = Cast<MyCharactor>(Owner);
if (MyChar != nullptr)
{
TArray<ACharactor*> EnemyArray = MyChar->GetEnemys();
for(const ACharactor* Enemy : EnemyArray)
{
QueryInstance.AddItemData<UEnvQueryItemType_Actor>(Enemy);
}
}
}

筛选特定点的集合,可以参考UE4自带的源码EnvQueryGenerator_SimpleGrid,原理上差不多,只是AddItemData的时候添加的是一个点FNavLocation

Context

Context的本质是返回一个参数,这个参数可以在Test中使用。

自定义C++ Context类只需要实现ProvideContext()函数。在这个函数中,调用对应的UEnvQueryItemType::SetContextHelper()方法来设置数据。

举个例子,我们需要拿到场上的一杯饮料作为参数,那么新建一个UEnvQueryContext_Drink:

1
2
3
4
5
6
7
8
9
10
void UEnvQueryContext_Drink::ProvideContext(FEnvQueryInstance& QueryInstance, FEnvQueryContextData& ContextData) const
{
AActor* QueryOwner = Cast<AActor>(QueryInstance.Owner.Get());
UMyWorld* World = QueryOwner->GetWorld();
ADrink* Drink = World->FindFirstDrink();
if(Drink != nullptr)
{
UEnvQueryItemType_Actor::SetContextHelper(ContextData, Drink);
}
}

读者可以参考UE4源码UEnvQueryContext_Querier,返回的是查询者本身。

Test

Test的目的有两个:Filter过滤不符合条件的,或者Score计算分值。

最简单的可以参考EnvQueryTest_GameplayTag

主要需要实现构造函数,以及函数RunTest()

在构造函数中,需要标明本Test的性能开销EEnvTestCost,以及标明输入参数的合法类型UEnvQueryItemType

这里可以直接贴出EnvQueryTest_GameplayTag的关键代码,供各位参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
UEnvQueryTest_GameplayTags::UEnvQueryTest_GameplayTags(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
Cost = EEnvTestCost::Low;
SetWorkOnFloatValues(false);
bUpdatedToUseQuery = false;

ValidItemType = UEnvQueryItemType_ActorBase::StaticClass();
}

void UEnvQueryTest_GameplayTags::RunTest(FEnvQueryInstance& QueryInstance) const
{
UObject* QueryOwner = QueryInstance.Owner.Get();
if (QueryOwner == nullptr)
{
return;
}

BoolValue.BindData(QueryOwner, QueryInstance.QueryID);
bool bWantsValid = BoolValue.GetValue();

// loop through all items
for (FEnvQueryInstance::ItemIterator It(this, QueryInstance); It; ++It)
{
// 使用`GetItemActor`获取集合的元素
AActor* ItemActor = GetItemActor(QueryInstance, It.GetIndex());
IGameplayTagAssetInterface* GameplayTagAssetInterface = Cast<IGameplayTagAssetInterface>(ItemActor);
// 判断是否有某个tag,如果有,那么SetScore为true
if (GameplayTagAssetInterface != NULL)
{
bool bSatisfiesTest = SatisfiesTest(GameplayTagAssetInterface);

// bWantsValid is the basically the opposite of bInverseCondition in BTDecorator. Possibly we should
// rename to make these more consistent.
It.SetScore(TestPurpose, FilterType, bSatisfiesTest, bWantsValid);
}
else // If no GameplayTagAssetInterface is found, this test doesn't apply at all, so just skip the item.
{ // Currently
It.ForceItemState(EEnvItemStatus::Passed);
}
}
}
Buy Me A Coffee / 捐一杯咖啡的钱
分享这篇文章~
0%
//