UE4 EQS C++自定义节点编写
程序员文章站
2022-06-10 23:28:07
...
基础应用就不说了,直接看官方文档。
这篇主要讲怎么在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元素
-
下面以一个例子说明,这个例子会生成一个敌人的集合。
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
:
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
的关键代码,供各位参考。
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);
}
}
}
上一篇: 吃什么食物对不孕症有好处