ue5 UGameplayTask和 UGameplayBehavior 和USmartObjectBehaviorDefinition

发布时间:2023年12月28日

ue5.3.2

UAbilityTask太庞大暂时不说

UGameplayTask是基础 ,对应的应该有UGameplayTasksComponent

启动一个Task,通过调用UGameplayTasksComponent的static方法来run一个task,还有蓝图方法

代码如下

	UFUNCTION(BlueprintCallable, DisplayName="Run Gameplay Task", meta=(ScriptName="RunGameplayTask"), Category = "Gameplay Tasks", meta = (AutoCreateRefTerm = "AdditionalRequiredResources, AdditionalClaimedResources", AdvancedDisplay = "AdditionalRequiredResources, AdditionalClaimedResources"))
	static GAMEPLAYTASKS_API EGameplayTaskRunResult K2_RunGameplayTask(TScriptInterface<IGameplayTaskOwnerInterface> TaskOwner, UGameplayTask* Task, uint8 Priority, TArray<TSubclassOf<UGameplayTaskResource> > AdditionalRequiredResources, TArray<TSubclassOf<UGameplayTaskResource> > AdditionalClaimedResources);

	static GAMEPLAYTASKS_API EGameplayTaskRunResult RunGameplayTask(IGameplayTaskOwnerInterface& TaskOwner, UGameplayTask& Task, uint8 Priority, FGameplayResourceSet AdditionalRequiredResources, FGameplayResourceSet AdditionalClaimedResources);
	

EGameplayTaskRunResult UGameplayTasksComponent::RunGameplayTask(IGameplayTaskOwnerInterface& TaskOwner, UGameplayTask& Task, uint8 Priority, FGameplayResourceSet AdditionalRequiredResources, FGameplayResourceSet AdditionalClaimedResources)
{
	const FText NoneText = FText::FromString(TEXT("None"));

	if (Task.GetState() == EGameplayTaskState::Paused || Task.GetState() == EGameplayTaskState::Active)
	{
		// return as success if already running for the same owner, failure otherwise 
		return Task.GetTaskOwner() == &TaskOwner
			? (Task.GetState() == EGameplayTaskState::Paused ? EGameplayTaskRunResult::Success_Paused : EGameplayTaskRunResult::Success_Active)
			: EGameplayTaskRunResult::Error;
	}

	// this is a valid situation if the task has been created via "Construct Object" mechanics
	if (Task.GetState() == EGameplayTaskState::Uninitialized)
	{
		Task.InitTask(TaskOwner, Priority);
	}

	Task.AddRequiredResourceSet(AdditionalRequiredResources);
	Task.AddClaimedResourceSet(AdditionalClaimedResources);
	Task.ReadyForActivation();

	switch (Task.GetState())
	{
	case EGameplayTaskState::AwaitingActivation:
	case EGameplayTaskState::Paused:
		return EGameplayTaskRunResult::Success_Paused;
		break;
	case EGameplayTaskState::Active:
		return EGameplayTaskRunResult::Success_Active;
		break;
	case EGameplayTaskState::Finished:
		return EGameplayTaskRunResult::Success_Active;
		break;
	}

	return EGameplayTaskRunResult::Error;
}

??另外一种调用的方法就是手动的调用void UGameplayTask::InitTask方法也可以

void UGameplayTask::InitTask(IGameplayTaskOwnerInterface& InTaskOwner, uint8 InPriority)
{
	Priority = InPriority;
	TaskOwner = &InTaskOwner;
	TaskState = EGameplayTaskState::AwaitingActivation;

	if (bClaimRequiredResources)
	{
		ClaimedResources.AddSet(RequiredResources);
	}

	// call owner.OnGameplayTaskInitialized before accessing owner.GetGameplayTasksComponent, this is required for child tasks
	InTaskOwner.OnGameplayTaskInitialized(*this);

	UGameplayTasksComponent* GTComponent = InTaskOwner.GetGameplayTasksComponent(*this);
	TasksComponent = GTComponent;
	bOwnedByTasksComponent = (TaskOwner.GetObject() == GTComponent);

	// make sure that task component knows about new task
	if (GTComponent && !bOwnedByTasksComponent)
	{
        //Task初始化事件通知
		GTComponent->OnGameplayTaskInitialized(*this);
	}
}

调用的时候需要指定IGameplayTaskOwnerInterface& Owner,目前所知道的被AAIController控制的APawn都会默认创建UGameplayTasksComponent对象,而UGameplayTasksComponent就是继承了IGameplayTaskOwnerInterface接口。所以NPC具有运行UGameplaytask的天然优势。

void AAIController::OnPossess(APawn* InPawn)
{
	// don't even try possessing pending-kill pawns
	if (InPawn != nullptr && !IsValid(InPawn))
	{
		return;
	}

	Super::OnPossess(InPawn);

	if (GetPawn() == nullptr || InPawn == nullptr)
	{
		return;
	}

	// not calling UpdateNavigationComponents() anymore. The PathFollowingComponent 
	// is now observing newly possessed pawns (via OnNewPawn)

	if (PathFollowingComponent)
	{
		PathFollowingComponent->Initialize();
	}

	if (bWantsPlayerState)
	{
		ChangeState(NAME_Playing);
	}

	// a Pawn controlled by AI _requires_ a GameplayTasksComponent, so if Pawn 
	// doesn't have one we need to create it
	if (CachedGameplayTasksComponent == nullptr)
	{
		UGameplayTasksComponent* GTComp = InPawn->FindComponentByClass<UGameplayTasksComponent>();
		if (GTComp == nullptr)
		{
			GTComp = NewObject<UGameplayTasksComponent>(InPawn, TEXT("GameplayTasksComponent"));
			GTComp->RegisterComponent();
		}
		CachedGameplayTasksComponent = GTComp;
	}

..........................
}

void UGameplayTask::ReadyForActivation()

?如果想立刻执行,而不是根据优先级排序执行

就要确保

FORCEINLINE bool RequiresPriorityOrResourceManagement() const { return bCaresAboutPriority == true || RequiredResources.IsEmpty() == false || ClaimedResources.IsEmpty() == false; }

这个返回是false就行

void UGameplayTask::ReadyForActivation()
{
    //没有UGameplayTasksComponent 就endtask
	if (UGameplayTasksComponent* TasksPtr = TasksComponent.Get())
	{
        //是否需要按权重执行
		if (RequiresPriorityOrResourceManagement() == false)
		{
			PerformActivation();
		}
		else
		{
			TasksPtr->AddTaskReadyForActivation(*this);
		}
	}
	else
	{
		EndTask();
	}
}

?先说不按权重的执行

void UGameplayTask::PerformActivation()
{
    //如果此Task是在运行状态就直接Return
	if (TaskState == EGameplayTaskState::Active)
	{
		UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Warning
			, TEXT("%s PerformActivation called while TaskState is already Active. Bailing out.")
			, *GetName());
		return;
	}
    
	TaskState = EGameplayTaskState::Active;
    //激活task
	Activate();

	// Activate call may result in the task actually "instantly" finishing.
	// If this happens we don't want to bother the TaskComponent
	// with information on this task
	if (IsFinished() == false)
	{    
        //激活事件通知
		TasksComponent->OnGameplayTaskActivated(*this);
	}
}

void UGameplayTask::Activate()
{
    //一般都要重载这个函数
	UE_VLOG(GetGameplayTasksComponent(), LogGameplayTasks, Verbose
		, TEXT("%s Activate called, current State: %s")
		, *GetName(), *GetTaskStateName());
}

//本来以为Task的Active函数就是执行逻辑了,没想到到这里又根据不同属性分发到不同的容器中
//那就看看这三个容器分别是干啥的
void UGameplayTasksComponent::OnGameplayTaskActivated(UGameplayTask& Task)
{
	// process events after finishing all operations
	FEventLock ScopeEventLock(this);
    //容器1,总容器来一个收一个
	KnownTasks.Add(&Task);

	if (Task.IsTickingTask())
	{
		check(TickingTasks.Contains(&Task) == false);
        //容器2,看来准备在tick中用
		TickingTasks.Add(&Task);

		// If this is our first ticking task, set this component as active so it begins ticking
		if (TickingTasks.Num() == 1)
		{
			UpdateShouldTick();
		}
	}
	//这个能网络同步,牛
	if (Task.IsSimulatedTask())
	{
        //容器3,能网络同步到客户端
		const bool bWasAdded = AddSimulatedTask(&Task);
		check(bWasAdded == true);
	}

	IGameplayTaskOwnerInterface* TaskOwner = Task.GetTaskOwner();
	if (!Task.IsOwnedByTasksComponent() && TaskOwner)
	{
		TaskOwner->OnGameplayTaskActivated(Task);
	}
}

?KnownTasks就是收录了所有active的task,只是收集记录,并没有操作task

TickingTasks存放需要tick的task

然后在

void UGameplayTasksComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
	SCOPE_CYCLE_COUNTER(STAT_TickGameplayTasks);

	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// Because we have no control over what a task may do when it ticks, we must be careful.
	// Ticking a task may kill the task right here. It could also potentially kill another task
	// which was waiting on the original task to do something. Since when a tasks is killed, it removes
	// itself from the TickingTask list, we will make a copy of the tasks we want to service before ticking any

	int32 NumTickingTasks = TickingTasks.Num();
	int32 NumActuallyTicked = 0;
	switch (NumTickingTasks)
	{
	case 0:
		break;
	case 1:
		{
			UGameplayTask* TickingTask = TickingTasks[0];
			if (IsValid(TickingTask))
			{
				TickingTask->TickTask(DeltaTime);
				NumActuallyTicked++;
			}
		}
		break;
	default:
		{
			static TArray<UGameplayTask*> LocalTickingTasks;
			LocalTickingTasks.Reset();
			LocalTickingTasks.Append(TickingTasks);
			for (UGameplayTask* TickingTask : LocalTickingTasks)
			{
				if (IsValid(TickingTask))
				{
					TickingTask->TickTask(DeltaTime);
					NumActuallyTicked++;
				}
			}
		}
		break;
	};

	// Stop ticking if no more active tasks
	if (NumActuallyTicked == 0)
	{
		TickingTasks.SetNum(0, false);
		UpdateShouldTick();
	}
}

SimulatedTasks,网络同步给simulate端

bool UGameplayTasksComponent::AddSimulatedTask(UGameplayTask* NewTask)
{
	if (NewTask == nullptr)
	{
		return false;
	}

	if (SimulatedTasks.Find(NewTask) == INDEX_NONE)
	{
		SimulatedTasks.Add(NewTask);
		SetSimulatedTasksNetDirty();

		if (IsUsingRegisteredSubObjectList() && IsReadyForReplication())
		{
			AddReplicatedSubObject(NewTask, COND_SkipOwner);
		}

		return true;
	}

	return false;
}

	/** Tasks that run on simulated proxies */
	UPROPERTY(ReplicatedUsing = OnRep_SimulatedTasks)
	TArray<TObjectPtr<UGameplayTask>> SimulatedTasks;

void UGameplayTasksComponent::OnRep_SimulatedTasks(const TArray<UGameplayTask*>& PreviousSimulatedTasks)
{
	if (IsUsingRegisteredSubObjectList())
	{
		// Find if any tasks got removed
		for (UGameplayTask* OldSimulatedTask : PreviousSimulatedTasks)
		{
			if (OldSimulatedTask)
			{
				const bool bIsRemoved = SimulatedTasks.Find(OldSimulatedTask) == INDEX_NONE;
				if (bIsRemoved)
				{
					RemoveReplicatedSubObject(OldSimulatedTask);
				}
			}
		}
	}
    //同步过来的Task并没有自动RunTask,要么重载OnRep_SimulatedTasks手动RunTask,
    //要么在TickTask中重载RunTask
	for (UGameplayTask* SimulatedTask : GetSimulatedTasks())
	{
		if (SimulatedTask)
		{
			// If the task needs to be ticked and isn't yet.
			if (SimulatedTask->IsTickingTask() && TickingTasks.Contains(SimulatedTask) == false)
			{
				SimulatedTask->InitSimulatedTask(*this);

				TickingTasks.Add(SimulatedTask);

				// If this is our first ticking task, set this component as active so it begins ticking
				if (TickingTasks.Num() == 1)
				{
					UpdateShouldTick();
				}
			}

			// See if it's a new task that needs to be registered
			if (IsUsingRegisteredSubObjectList())
			{
				const bool bIsNew = PreviousSimulatedTasks.Find(SimulatedTask) == INDEX_NONE;
				if (bIsNew)
				{
					AddReplicatedSubObject(SimulatedTask, COND_SkipOwner);
				}
			}
		}
	}
}

?总结

在comp中调用方法把task和owner结合起来,并run task

comp只是管理task,比如需要tick的,需要同步的simulate task,和一些事件的分发

目前没有看到task是异步的,都是在game线程执行

貌似异步的现象,这个是被蓝图异步节点封装的Task

class GAMEPLAYTASKSEDITOR_API UK2Node_LatentGameplayTaskCall : public UK2Node_BaseAsyncTask

深度的源码我没有看,猜测是

执行的逻辑是具有异步特性的,完成和失败是需要回调通知

如果继承UGameplayTask并且写了static的BlueprintCallable方法就能被UK2Node_LatentGameplayTaskCall所捕获,就能在蓝图中直接调出异步节点。神奇,但是并没有深度看源码。

UAITask_UseGameplayBehaviorSmartObject

?? ?UAITask_UseGameplayBehaviorSmartObject* UseSOTask = NewBTAITask<UAITask_UseGameplayBehaviorSmartObject>(OwnerComp);
?? ?UseSOTask->SetClaimHandle(ClaimHandle);
?? ?UseSOTask->ReadyForActivation();

文章来源:https://blog.csdn.net/opk8848/article/details/135265812
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。