欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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);
		}
	}
}
相关标签: UE4 UE4 游戏