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();