跳至主要內容

F13.BeginPlay|启动事件

Mr.Si大约 2 分钟u++

头像

C++中的BeginPlay是不是就是蓝图中的EventBeginPlay

头像

回答问题之前我们先看看C++的BeginPlay

新建一个测试actor

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyTest.generated.h"

UCLASS()
class EXORCIST_API AMyTest : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AMyTest();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;
		
};
头像
我们已经看到了继承重写的BeginPlay函数,现在我们往定义中添加一些测试代码
void AMyTest::BeginPlay()
{
	Super::BeginPlay();
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Blue, TEXT("C++ BeginPlay"));
}
头像
接着我们在派生的蓝图EventBeginPlay中也写一份打印,你觉得打印顺序是什么?
头像
蓝图派生自C++的actor,那么应该先打印C++的内容然后打印BP的内容。
头像

确实,按照我们的惯性思维这里的EventBeginPlayC++BeginPlay认为是一个东西,所以应该先打印父项部分再打印子项,可事实真的这样吗?

头像
为什么先打印的BP?
头像
说明我们调用C++BeginPlay 之前 已经调用了BP部分的函数。我们可以去源码中康康猫腻在哪!
头像

可是继承的父类Actor中 BeginPlay只是一个虚函数,而且没有宏标记!他是怎么映射到蓝图中的?

头像

起初我也以为是父类标记了BeginPlayUPROPERTY,子类可以省略不写。现在看来并不是这样。我们康康定义的源码写了什么!

void AActor::BeginPlay()
{
	TRACE_OBJECT_LIFETIME_BEGIN(this);

	ensureMsgf(ActorHasBegunPlay == EActorBeginPlayState::BeginningPlay, TEXT("BeginPlay was called on actor %s which was in state %d"), *GetPathName(), (int32)ActorHasBegunPlay);
	SetLifeSpan( InitialLifeSpan );
	RegisterAllActorTickFunctions(true, false); // Components are done below.

	TInlineComponentArray<UActorComponent*> Components;
	GetComponents(Components);

	for (UActorComponent* Component : Components)
	{
		// bHasBegunPlay will be true for the component if the component was renamed and moved to a new outer during initialization
		if (Component->IsRegistered() && !Component->HasBegunPlay())
		{
			Component->RegisterAllComponentTickFunctions(true);
			Component->BeginPlay();
			ensureMsgf(Component->HasBegunPlay(), TEXT("Failed to route BeginPlay (%s)"), *Component->GetFullName());
		}
		else
		{
			// When an Actor begins play we expect only the not bAutoRegister false components to not be registered
			//check(!Component->bAutoRegister);
		}
	}

	if (GetAutoDestroyWhenFinished())
	{
		if (UWorld* MyWorld = GetWorld())
		{
			if (UAutoDestroySubsystem* AutoDestroySys = MyWorld->GetSubsystem<UAutoDestroySubsystem>())
			{
				AutoDestroySys->RegisterActor(this);
			}			
		}
	}

	ReceiveBeginPlay();

	ActorHasBegunPlay = EActorBeginPlayState::HasBegunPlay;
}
头像

我发现他结尾调用了ReceiveBeginPlay!这个函数的声明的UPROPERTY元数据属性中就有 meta=(DisplayName = "BeginPlay")!!!

meta=(DisplayName = "BeginPlay") 用于修改显示节点名。

头像
没错,这就解释了为什么继承的Actor执行后会先调用蓝图部分的函数再调用C++。

因为我们加了Super::BeginPlay();所以执行顺序如下:

  1. 重写后的ActorBeginPlay
  2. 执行BeginPlay中的ReceiveBeginPlay();——》BP BeginPlay
  3. 执行打印——》C++BeginPlay