UE4 Actor生命周期 SpawnActor DestroyActor剖析

32 篇文章 43 订阅

原创文章,转载请注明出处。

AActor很重要,只要是我们世界大纲列表内的物体,全部都是AActor。

目录

第一部分,从编辑器点击Play开始分析World里面全部的Actor的Spawn流程,分析到调用BeginPlay结束

1>下面从点击场景中的Play/PlayInEditor/Play In Standalone开始,代码执行的顺序,只是大致的。后续引擎版本变化可能会不同(猜测)。我从堆栈拷贝的代码,从下往上看。

2>上面生成了熟悉的GameInstance,GameMode怎么生成出来的?

3>上面生成了熟悉的GameInstance,那么GameMode怎么生成出来的?贴出堆栈的图

4>那么场景中的Actor列表怎么个调用BeginPlay的流程呢?

   1)会判断Actors列表是否已经被初始化过了。if( !AreActorsInitialized() ),看来这个方法会被调用多次

   2)没有被初始化的时候呢,有下面的代码,下面的则就是真正的初始化部分了。跟进去看一看

    3)对每个Actor上的全部组件进行初始化。上图,但是注意此时Actor的BeginPlay还没有执行。还没到Actor的BeginPlay,还在下头

     4)ActorBeginPlay调用的位置如下

第二部分,从代码层面调用SpawnActor,对其做分析

1>编码测试

2>源码分析

1)SpawnActor源码分析

2)Actor->PostSpawnInitialize源码分析

3)void AActor::PostActorConstruction()源码分析这个里面会初始化Actor下面的组件, 主要是下面的代码调用的BeginPlay

4)void AActor::DispatchBeginPlay(bool bFromLevelStreaming)源码分析

第三部分,从代码层面调用DestroyActor,对其做分析

1>编码测试

2>源码分析

1) void AActor::Destroyed()源码分析

2) RouteEndPlay(EEndPlayReason::Destroyed)源码分析



第一部分,从编辑器点击Play开始分析World里面全部的Actor的Spawn流程,分析到调用BeginPlay结束

在剖析Actor生命周期之前,我跟了一下Editor和Standlone的代码,找到了场景中actor列表的初始化的地方。

1>下面从点击场景中的Play/PlayInEditor/Play In Standalone开始,代码执行的顺序,只是大致的。后续引擎版本变化可能会不同(猜测)。我从堆栈拷贝的代码,从下往上看。

8 StartPlayInEditorGameInstance中调用了创建GameMode创建GameMode,这个是在GameInstance里面创建的
UGameInstance::CreateGameModeForURL(FURL InURL, UWorld * InWorld)
UWorld::SetGameMode(const FURL & InURL)

7 下面文章中还会接着介绍这个里面的代码
UGameInstance::StartPlayInEditorGameInstance(ULocalPlayer * LocalPlayer, const FGameInstancePIEParameters & Params)

6 这个方法就是在创建GameInstance,并且有趣的是上面几个方法也是在这里面调用的
//在这个里面创建了GameMode,就是上面的代码
UEditorEngine::CreateInnerProcessPIEGameInstance(FRequestPlaySessionParams & InParams, const FGameInstancePIEParameters & InPIEParameters, int InPIEInstanceIndex)

5>
UEditorEngine::OnLoginPIEComplete_Deferred(int LocalUserNum, bool bWasSuccessful, FString ErrorString, FPieLoginStruct DataStruct)

4>创建一个Editor新窗口的实例
UEditorEngine::CreateNewPlayInEditorInstance(FRequestPlaySessionParams & InRequestParams, const bool bInDedicatedInstance, const EPlayNetMode InNetMode)

3>拿PlayInEditor举例, 该方法中会判断一些条件,比如开着Matinee了,不让播放
UEditorEngine::StartPlayInEditorSession(FRequestPlaySessionParams & InRequestParams) 

2>从StartQueuedPlaySessionRequest过来的,并且从这个方法里面判断是哪一种, 是Standalone?还是Play/PlayInEditor?
UEditorEngine::StartQueuedPlaySessionRequestImpl()

1>点击编辑器中的Play按钮的时候调用的
UEditorEngine::StartQueuedPlaySessionRequest()

2>上面生成了熟悉的GameInstance,GameMode怎么生成出来的?

创建GameMode GetGameInstance()->CreateGameModeForURL
bool UWorld::SetGameMode(const FURL& InURL)
{
	if( IsServer() && !AuthorityGameMode )
	{
		AuthorityGameMode = GetGameInstance()->CreateGameModeForURL(InURL, this);
		if( AuthorityGameMode != NULL )
		{
			return true;
		}
		else
		{
			UE_LOG(LogWorld, Error, TEXT("Failed to spawn GameMode actor."));
			return false;
		}
	}

	return false;
}


创建GameMode的代码,可以发现其实这个平常我们在worldSetting里面对每一个umap设置的GameMode
就是在这被生成出来的。转定义就可以发现Settings->DefaultGameMode;是个TSubclassOf<>,
看到这是不是就知道咋回事了。
AGameModeBase* UGameInstance::CreateGameModeForURL(FURL InURL, UWorld* InWorld)
{
	省略了部分代码

	// Get the GameMode class. Start by using the default game type specified in the map's worldsettings.  It may be overridden by settings below.
	TSubclassOf<AGameModeBase> GameClass = Settings->DefaultGameMode;

	省略了部分代码

    最后其实看到这个GameMode呢也是被Spawn出来的一个Actor
	return World->SpawnActor<AGameModeBase>(GameClass, SpawnInfo);
}

3>上面生成了熟悉的GameInstance,那么GameMode怎么生成出来的?贴出堆栈的图

接着UGameInstance::StartPlayInEditorGameInstance讲

1>生成GameInstance之后,在该函数中生成了GameMode

2>接着生成完GameMode之后呢,调用了PlayWorld->CreateAISystem(); 创建AI系统

3>PlayWorld->InitializeActorsForPlay(URL); 主要讲一下这块,这个里头就是世界大纲里面所有的Actors的初始化

4>FNavigationSystem::AddNavigationSystemToWorld 添加AI导航网格

5>PlayWorld->BeginPlay();场景中初始化时候的Actor的BeginPlay在这

4>那么场景中的Actor列表怎么个调用BeginPlay的流程呢?

在场景开始时候,对所有的Actor进行初始化 UWorld::InitializeActorsForPlay

   1)会判断Actors列表是否已经被初始化过了。if( !AreActorsInitialized() ),看来这个方法会被调用多次

   2)没有被初始化的时候呢,有下面的代码,下面的则就是真正的初始化部分了。跟进去看一看

// Route various initialization functions and set volumes.
for( int32 LevelIndex=0; LevelIndex<Levels.Num(); LevelIndex++ )
{
	ULevel*	const Level = Levels[LevelIndex];
	Level->RouteActorInitialize();
}

    3)对每个Actor上的全部组件进行初始化。上图,但是注意此时Actor的BeginPlay还没有执行。还没到Actor的BeginPlay,还在下头

AActor::InitializeComponents()-> Actor中的初始化Actor上面的所有组件

GetComponents获取到所有的组件 初始化他们
void AActor::InitializeComponents()
{
	QUICK_SCOPE_CYCLE_COUNTER(STAT_Actor_InitializeComponents);

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

	for (UActorComponent* ActorComp : Components)
	{
		if (ActorComp->IsRegistered())
		{
			if (ActorComp->bAutoActivate && !ActorComp->IsActive())
			{
				ActorComp->Activate(true);
			}

			if (ActorComp->bWantsInitializeComponent && !ActorComp->HasBeenInitialized())
			{
				// Broadcast the activation event since Activate occurs too early to fire a callback in a game
				ActorComp->InitializeComponent();
			}
		}
	}
}

     4)ActorBeginPlay调用的位置如下

     真正场景中ActorsBeginPlay的流程如下面的堆栈图->Actor
     UWorld调用BeginPlay, 再到WorldSetting中,对所有的Actors进行BeginPlay的调用。有兴趣的BeginPlay里面的代码也建议看看。

第二部分,从代码层面调用SpawnActor,对其做分析

1>编码测试

我在代码里面写了一个SpawnActor,跟进去看看

我写的SpawnActor测试代码
void ABOCPLUSPLUSGameModeBase::BeginPlay()
{
	Super::BeginPlay();

	AActor* pActor = GWorld->SpawnActor(AActor::StaticClass());
	pActor->SetActorLocation(FVector::ZeroVector);
}

2>源码分析

下图是调用的堆栈

下面是SpawnActor里面的实现,贴出来吧,代码比较多。我写点注释记录一下

1)SpawnActor源码分析

AActor* UWorld::SpawnActor( UClass* Class, FTransform const* UserTransformPtr, const FActorSpawnParameters& SpawnParameters )
{
	SCOPE_CYCLE_COUNTER(STAT_SpawnActorTime);
	CSV_SCOPED_TIMING_STAT_EXCLUSIVE(ActorSpawning);

#if WITH_EDITORONLY_DATA
    判断当前的Level是否有值
	check( CurrentLevel ); 	
    //判断是不是在编辑器内,如果不是check会直接导致崩溃
	check(GIsEditor || (CurrentLevel == PersistentLevel));
#else
	ULevel* CurrentLevel = PersistentLevel;
#endif

	判断描述Actor信息类的Class是否是空的
	if( !Class )
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because no class was specified") );
		return NULL;
	}

	SCOPE_TIME_GUARD_NAMED_MS(TEXT("SpawnActor Of Type"), Class->GetFName(), 2);

#if ENABLE_SPAWNACTORTIMER
	FScopedSpawnActorTimer SpawnTimer(Class->GetFName(), SpawnParameters.bDeferConstruction ? ESpawnActorTimingType::SpawnActorDeferred : ESpawnActorTimingType::SpawnActorNonDeferred);
#endif
    判断Actor是不是被废弃的,废弃的不允许生成
	if( Class->HasAnyClassFlags(CLASS_Deprecated) )
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because class %s is deprecated"), *Class->GetName() );
		return NULL;
	}
    带有抽象反射信息的不允许生生
	if( Class->HasAnyClassFlags(CLASS_Abstract) )
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because class %s is abstract"), *Class->GetName() );
		return NULL;
	}
    必须为AActor类型
	else if( !Class->IsChildOf(AActor::StaticClass()) )
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because %s is not an actor class"), *Class->GetName() );
		return NULL;
	}
    判断类的模板信息是否正确
	else if (SpawnParameters.Template != NULL && SpawnParameters.Template->GetClass() != Class)
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because template class (%s) does not match spawn class (%s)"), *SpawnParameters.Template->GetClass()->GetName(), *Class->GetName());
		if (!SpawnParameters.bNoFail)
		{
			return NULL;
		}
	}
判断类是否允许在构造函数中spawn,默认为false应该是。可以改成true。spawnInfo里头
	else if (bIsRunningConstructionScript && !SpawnParameters.bAllowDuringConstructionScript)
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because we are running a ConstructionScript (%s)"), *Class->GetName() );
		return NULL;
	}
这个UWorld在被销毁过程中,同样也是不允许被Spawn的
	else if (bIsTearingDown)
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because we are in the process of tearing down the world"));
		return NULL;
	}
判断坐标信息是否是有效的,无效不能spawn
	else if (UserTransformPtr && UserTransformPtr->ContainsNaN())
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because the given transform (%s) is invalid"), *(UserTransformPtr->ToString()));
		return NULL;
	}

	ULevel* LevelToSpawnIn = SpawnParameters.OverrideLevel;
	if (LevelToSpawnIn == NULL)
	{
		// Spawn in the same level as the owner if we have one. @warning: this relies on the outer of an actor being the level.
		LevelToSpawnIn = (SpawnParameters.Owner != NULL) ? CastChecked<ULevel>(SpawnParameters.Owner->GetOuter()) : CurrentLevel;
	}

    Actor的名称,就是在世界中叫啥名
	FName NewActorName = SpawnParameters.Name;
	AActor* Template = SpawnParameters.Template;

模板是否有效
	if( !Template )
	{
		// Use class's default actor as a template.
		Template = Class->GetDefaultObject<AActor>();
	}
	check(Template);

名字是否为空
	if (NewActorName.IsNone())
	{
		// If we are using a template object and haven't specified a name, create a name relative to the template, otherwise let the default object naming behavior in Stat
		if (!Template->HasAnyFlags(RF_ClassDefaultObject))
		{
			NewActorName = MakeUniqueObjectName(LevelToSpawnIn, Template->GetClass(), *Template->GetFName().GetPlainNameString());
		}
	}
	else if (StaticFindObjectFast(nullptr, LevelToSpawnIn, NewActorName))
	{
		// If the supplied name is already in use, then either fail in the requested manner or determine a new name to use if the caller indicates that's ok

		if (SpawnParameters.NameMode == FActorSpawnParameters::ESpawnActorNameMode::Requested)
		{
			NewActorName = MakeUniqueObjectName(LevelToSpawnIn, Template->GetClass(), *NewActorName.GetPlainNameString());
		}
		else
		{
			if (SpawnParameters.NameMode == FActorSpawnParameters::ESpawnActorNameMode::Required_Fatal)
			{
				UE_LOG(LogSpawn, Fatal, TEXT("An actor of name '%s' already exists in level '%s'."), *NewActorName.ToString(), *LevelToSpawnIn->GetFullName());
			}
			else if (SpawnParameters.NameMode == FActorSpawnParameters::ESpawnActorNameMode::Required_ErrorAndReturnNull)
			{
				UE_LOG(LogSpawn, Error, TEXT("An actor of name '%s' already exists in level '%s'."), *NewActorName.ToString(), *LevelToSpawnIn->GetFullName());
			}
			return nullptr;
		}
	}

	// See if we can spawn on ded.server/client only etc (check NeedsLoadForClient & NeedsLoadForServer)
	if(!CanCreateInCurrentContext(Template))
	{
		UE_LOG(LogSpawn, Warning, TEXT("Unable to spawn class '%s' due to client/server context."), *Class->GetName() );
		return NULL;
	}

根据传入的Spawn参数,比如检测周围如果有碰撞信息了,碰撞重叠之后是否继续spawn?
	FTransform const UserTransform = UserTransformPtr ? *UserTransformPtr : FTransform::Identity;

	ESpawnActorCollisionHandlingMethod CollisionHandlingOverride = SpawnParameters.SpawnCollisionHandlingOverride;

	// "no fail" take preedence over collision handling settings that include fails
	if (SpawnParameters.bNoFail)
	{
		// maybe upgrade to disallow fail
		if (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding)
		{
			CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
		}
		else if (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding)
		{
			CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
		}
	}

	// use override if set, else fall back to actor's preference
	ESpawnActorCollisionHandlingMethod const CollisionHandlingMethod = (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::Undefined) ? Template->SpawnCollisionHandlingMethod : CollisionHandlingOverride;

	// see if we can avoid spawning altogether by checking native components
	// note: we can't handle all cases here, since we don't know the full component hierarchy until after the actor is spawned
	if (CollisionHandlingMethod == ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding)
	{
		USceneComponent* const TemplateRootComponent = Template->GetRootComponent();

		// Note that we respect any initial transformation the root component may have from the CDO, so the final transform
		// might necessarily be exactly the passed-in UserTransform.
		FTransform const FinalRootComponentTransform =
			TemplateRootComponent
			? FTransform(TemplateRootComponent->GetRelativeRotation(), TemplateRootComponent->GetRelativeLocation(), TemplateRootComponent->GetRelativeScale3D()) * UserTransform
			: UserTransform;

		FVector const FinalRootLocation = FinalRootComponentTransform.GetLocation();
		FRotator const FinalRootRotation = FinalRootComponentTransform.Rotator();

		if (EncroachingBlockingGeometry(Template, FinalRootLocation, FinalRootRotation))
		{
			// a native component is colliding, that's enough to reject spawning
			UE_LOG(LogSpawn, Log, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *FinalRootLocation.ToString(), *Class->GetName());
			return nullptr;
		}
	}

	真正的Spawn在这呢,其实发现很有趣,调用的就是NewObject。但是前期要进行上面那么多的检测
所以我们在对A类生成的时候,一定要调用SpawnActor。不能直接调用NewObject!
	AActor* const Actor = NewObject<AActor>(LevelToSpawnIn, Class, NewActorName, SpawnParameters.ObjectFlags, Template);

判断是否spawn成功了,内存是否申请成功
	check(Actor);

#if ENABLE_SPAWNACTORTIMER
	SpawnTimer.SetActorName(Actor->GetFName());
#endif

清除actor的名字如果编辑器模式下
#if WITH_EDITOR
	Actor->ClearActorLabel(); // Clear label on newly spawned actors
#endif // WITH_EDITOR

	if ( GUndo )
	{
		ModifyLevel( LevelToSpawnIn );
	}

在这将生成的actor就加到我们上面Level里面的Actors列表对象里面了。头文件里面是两个TArray
一个列表,一个GC用的
	LevelToSpawnIn->Actors.Add( Actor );
	LevelToSpawnIn->ActorsForGC.Add(Actor);

#if PERF_SHOW_MULTI_PAWN_SPAWN_FRAMES
	if( Cast<APawn>(Actor) )
	{
		FString PawnName = FString::Printf(TEXT("%d: %s"), ThisFramePawnSpawns.Num(), *Actor->GetPathName());
		ThisFramePawnSpawns.Add(PawnName);
	}
#endif

	// 赋值spawn信息
	Actor->SpawnCollisionHandlingMethod = CollisionHandlingMethod;

#if WITH_EDITOR
	if (SpawnParameters.bHideFromSceneOutliner)
	{
		FSetActorHiddenInSceneOutliner SetActorHidden(Actor);
	}
	Actor->bIsEditorPreviewActor = SpawnParameters.bTemporaryEditorActor;
#endif //WITH_EDITOR

主要在这里面进行的Actor里面的初始化
	Actor->PostSpawnInitialize(UserTransform, SpawnParameters.Owner, SpawnParameters.Instigator, SpawnParameters.IsRemoteOwned(), SpawnParameters.bNoFail, SpawnParameters.bDeferConstruction);

	if (Actor->IsPendingKill() && !SpawnParameters.bNoFail)
	{
		UE_LOG(LogSpawn, Log, TEXT("SpawnActor failed because the spawned actor %s IsPendingKill"), *Actor->GetPathName());
		return NULL;
	}

	Actor->CheckDefaultSubobjects();

	// Broadcast notification of spawn
	OnActorSpawned.Broadcast(Actor);

#if WITH_EDITOR
	if (GIsEditor)
	{
		GEngine->BroadcastLevelActorAdded(Actor);
	}
#endif

	// Add this newly spawned actor to the network actor list. Do this after PostSpawnInitialize so that actor has "finished" spawning.
	AddNetworkActor( Actor );

	return Actor;
}

2)Actor->PostSpawnInitialize源码分析

先贴出堆栈,比较明了

void AActor::PostSpawnInitialize(FTransform const& UserSpawnTransform, AActor* InOwner, APawn* InInstigator, bool bRemoteOwned, bool bNoFail, bool bDeferConstruction)
{
	
	UWorld* const World = GetWorld();
	bool const bActorsInitialized = World && World->AreActorsInitialized();

	CreationTime = (World ? World->GetTimeSeconds() : 0.f);

	此处判断必须是由服务器创建的
	check(GetLocalRole() == ROLE_Authority);
	ExchangeNetRoles(bRemoteOwned);

设置拥有者
	SetOwner(InOwner);

设置谁导致的这个Actor的生成。这块比较绕。这样想就明白了了
我觉得UE4设置InOwner,InInstigator就是在射击游戏的原型上衍生出来的。
比如人拿枪,人开枪,枪发射子弹。假如子弹就是这个被生成的断点Actor
那么InOwner是谁?是抢,子弹属于枪的
那么InInstigator是谁?是人,人开枪才有了子弹。
当然怎么设置取决于我们
	SetInstigator(InInstigator);

坐标的处理。。。。
	USceneComponent* const SceneRootComponent = FixupNativeActorComponents(this);
	if (SceneRootComponent != nullptr)
	{
		check(SceneRootComponent->GetOwner() == this);

		// Determine if the native root component's archetype originates from a converted (nativized) Blueprint class.
		UObject* RootComponentArchetype = SceneRootComponent->GetArchetype();
		UClass* ArchetypeOwnerClass = RootComponentArchetype->GetOuter()->GetClass();
		if (UBlueprintGeneratedClass* ArchetypeOwnerClassAsBPGC = Cast<UBlueprintGeneratedClass>(ArchetypeOwnerClass))
		{
			// In this case, the Actor CDO is a non-nativized Blueprint class (e.g. a child class) and the component's archetype
			// is an instanced default subobject within the non-nativized Blueprint's CDO. If the owner class also has a nativized
			// parent class somewhere in its inheritance hierarchy, we must redirect the query by walking up the archetype chain.
			if (ArchetypeOwnerClassAsBPGC->bHasNativizedParent)
			{
				do 
				{
					RootComponentArchetype = RootComponentArchetype->GetArchetype();
					ArchetypeOwnerClass = RootComponentArchetype->GetOuter()->GetClass();
				} while (Cast<UBlueprintGeneratedClass>(ArchetypeOwnerClass) != nullptr);
			}
		}

		if (Cast<UDynamicClass>(ArchetypeOwnerClass) != nullptr)
		{
			// For native root components either belonging to or inherited from a converted (nativized) Blueprint class, we currently do not use
			// the transformation that's set on the root component in the CDO. The reason is that in the non-nativized case, we ignore the default
			// transform when we instance a Blueprint-owned scene component that will also become the root (see USCS_Node::ExecuteNodeOnActor; in
			// the case of dynamically-spawned Blueprint instances, 'bIsDefaultTransform' will be false, and the scale from the SCS node's template
			// will not be applied in that code path in that case). Once a Blueprint class is nativized, we no longer run through that code path
			// when we spawn new instances of that class dynamically, but for consistency, we need to keep the same transform as in the non-
			// nativized case. We used to ignore any non-default transform value set on the root component at cook (nativization) time, but that 
			// doesn't work because existing placements of the Blueprint component in a scene may rely on the value that's stored in the CDO,
			// and as a result the instance-specific override value doesn't get serialized out to the instance as a result of delta serialization.
			SceneRootComponent->SetWorldTransform(UserSpawnTransform);
		}
		else
		{
			// In the "normal" case we do respect any non-default transform value that the root component may have received from the archetype
			// that's owned by the native CDO, so the final transform might not always necessarily equate to the passed-in UserSpawnTransform.
			const FTransform RootTransform(SceneRootComponent->GetRelativeRotation(), SceneRootComponent->GetRelativeLocation(), SceneRootComponent->GetRelativeScale3D());
			const FTransform FinalRootComponentTransform = RootTransform * UserSpawnTransform;
			SceneRootComponent->SetWorldTransform(FinalRootComponentTransform);
		}
	}

	// Call OnComponentCreated on all default (native) components
	DispatchOnComponentsCreated(this);

	// Register the actor's default (native) components, but only if we have a native scene root. If we don't, it implies that there could be only non-scene components
	// at the native class level. In that case, if this is a Blueprint instance, we need to defer native registration until after SCS execution can establish a scene root.
	// Note: This API will also call PostRegisterAllComponents() on the actor instance. If deferred, PostRegisterAllComponents() won't be called until the root is set by SCS.
	bHasDeferredComponentRegistration = (SceneRootComponent == nullptr && Cast<UBlueprintGeneratedClass>(GetClass()) != nullptr);

注册组件了。第一部分也出现过
	if (!bHasDeferredComponentRegistration)
	{
		RegisterAllComponents();
	}

#if WITH_EDITOR
	// When placing actors in the editor, init any random streams 
	if (!bActorsInitialized)
	{
		SeedAllRandomStreams();
	}
#endif

	看看有没有啥东西把我们给删了?
	if( IsPendingKill() && !bNoFail )
	{
		return;
	}

	通知一下,我这个Actor已经spawn出来了。
	PostActorCreated();

	执行本地和BP构造脚本。
	if (!bDeferConstruction)
	{
        在这呢在这呢
		FinishSpawning(UserSpawnTransform, true);
	}
	else if (SceneRootComponent != nullptr)
	{
		// we have a native root component and are deferring construction, store our original UserSpawnTransform
		// so we can do the proper thing if the user passes in a different transform during FinishSpawning
		GSpawnActorDeferredTransformCache.Emplace(this, UserSpawnTransform);
	}
}

3)void AActor::PostActorConstruction()源码分析
这个里面会初始化Actor下面的组件, 主要是下面的代码调用的BeginPlay

if (bRunBeginPlay)
{
	SCOPE_CYCLE_COUNTER(STAT_ActorBeginPlay);
	DispatchBeginPlay();
}
void AActor::PostActorConstruction()
{
	UWorld* const World = GetWorld();
	bool const bActorsInitialized = World && World->AreActorsInitialized();

	if (bActorsInitialized)
	{
		PreInitializeComponents();
	}

	// If this is dynamically spawned replicated actor, defer calls to BeginPlay and UpdateOverlaps until replicated properties are deserialized
	const bool bDeferBeginPlayAndUpdateOverlaps = (bExchangedRoles && RemoteRole == ROLE_Authority) && !GIsReinstancing;

	if (bActorsInitialized)
	{
		这个比较熟悉吧,初始化这个actor下的所有components
		InitializeComponents();

		// actor should have all of its components created and registered now, do any collision checking and handling that we need to do
		if (World)
		{
			switch (SpawnCollisionHandlingMethod)
			{
			case ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn:
			{
				// Try to find a spawn position
				FVector AdjustedLocation = GetActorLocation();
				FRotator AdjustedRotation = GetActorRotation();
				if (World->FindTeleportSpot(this, AdjustedLocation, AdjustedRotation))
				{
					SetActorLocationAndRotation(AdjustedLocation, AdjustedRotation, false, nullptr, ETeleportType::TeleportPhysics);
				}
			}
			break;
			case ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding:
			{
				// Try to find a spawn position			
				FVector AdjustedLocation = GetActorLocation();
				FRotator AdjustedRotation = GetActorRotation();
				if (World->FindTeleportSpot(this, AdjustedLocation, AdjustedRotation))
				{
					SetActorLocationAndRotation(AdjustedLocation, AdjustedRotation, false, nullptr, ETeleportType::TeleportPhysics);
				}
				else
				{
					UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *AdjustedLocation.ToString(), *GetClass()->GetName());
					Destroy();
				}
			}
			break;
			case ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding:
				if (World->EncroachingBlockingGeometry(this, GetActorLocation(), GetActorRotation()))
				{
					UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *GetActorLocation().ToString(), *GetClass()->GetName());
					Destroy();
				}
				break;
			case ESpawnActorCollisionHandlingMethod::Undefined:
			case ESpawnActorCollisionHandlingMethod::AlwaysSpawn:
			default:
				// note we use "always spawn" as default, so treat undefined as that
				// nothing to do here, just proceed as normal
				break;
			}
		}

		if (!IsPendingKill())
		{
			PostInitializeComponents();
			if (!IsPendingKill())
			{
				if (!bActorInitialized)
				{
					UE_LOG(LogActor, Fatal, TEXT("%s failed to route PostInitializeComponents.  Please call Super::PostInitializeComponents() in your <className>::PostInitializeComponents() function. "), *GetFullName());
				}

				bool bRunBeginPlay = !bDeferBeginPlayAndUpdateOverlaps && (BeginPlayCallDepth > 0 || World->HasBegunPlay());
				if (bRunBeginPlay)
				{
					if (AActor* ParentActor = GetParentActor())
					{
						// Child Actors cannot run begin play until their parent has run
						bRunBeginPlay = (ParentActor->HasActorBegunPlay() || ParentActor->IsActorBeginningPlay());
					}
				}

#if WITH_EDITOR
				if (bRunBeginPlay && bIsEditorPreviewActor)
				{
					bRunBeginPlay = false;
				}
#endif

重头戏在这呢,通知调用beginplay
				if (bRunBeginPlay)
				{
					SCOPE_CYCLE_COUNTER(STAT_ActorBeginPlay);
					DispatchBeginPlay();
				}
			}
		}
	}
	else
	{
		// Set IsPendingKill() to true so that when the initial undo record is made,
		// the actor will be treated as destroyed, in that undo an add will
		// actually work
		MarkPendingKill();
		Modify(false);
		ClearPendingKill();
	}
}

4)void AActor::DispatchBeginPlay(bool bFromLevelStreaming)源码分析

到这一步其实就到了Beginplay真正的调用位置了

void AActor::DispatchBeginPlay(bool bFromLevelStreaming)
{
	UWorld* World = (!HasActorBegunPlay() && !IsPendingKill() ? GetWorld() : nullptr);

	if (World)
	{
		ensureMsgf(ActorHasBegunPlay == EActorBeginPlayState::HasNotBegunPlay, TEXT("BeginPlay was called on actor %s which was in state %d"), *GetPathName(), (int32)ActorHasBegunPlay);
		const uint32 CurrentCallDepth = BeginPlayCallDepth++;

		bActorBeginningPlayFromLevelStreaming = bFromLevelStreaming;
		ActorHasBegunPlay = EActorBeginPlayState::BeginningPlay;

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
在这里调用的BeginPlay在这里调用的BeginPlay在这里调用的BeginPlay在这里调用的BeginPlay
		BeginPlay();
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

		ensure(BeginPlayCallDepth - 1 == CurrentCallDepth);
		BeginPlayCallDepth = CurrentCallDepth;

		if (bActorWantsDestroyDuringBeginPlay)
		{
			// Pass true for bNetForce as either it doesn't matter or it was true the first time to even 
			// get to the point we set bActorWantsDestroyDuringBeginPlay to true
			World->DestroyActor(this, true); 
		}
		
		if (!IsPendingKill())
		{
			// Initialize overlap state
			UpdateInitialOverlaps(bFromLevelStreaming);
		}

		bActorBeginningPlayFromLevelStreaming = false;
	}
}

第三部分,从代码层面调用DestroyActor,对其做分析

1>编码测试

AActor* pActor = GWorld->SpawnActor(AActor::StaticClass());
	if (pActor)
	{
		pActor->SetActorLocation(FVector::ZeroVector);

代码中删除Actor的方式
		//GWorld->DestroyActor(pActor);
		pActor->Destroy();
	}
}

2>源码分析

bool UWorld::DestroyActor( AActor* ThisActor, bool bNetForce, bool bShouldModifyLevel )
{
	SCOPE_CYCLE_COUNTER(STAT_DestroyActor);
	CSV_SCOPED_TIMING_STAT_EXCLUSIVE(ActorDestroying);

判断Actor是否是有效的
	check(ThisActor);
	check(ThisActor->IsValidLowLevel());
	//UE_LOG(LogSpawn, Log,  "Destroy %s", *ThisActor->GetClass()->GetName() );

	SCOPE_CYCLE_UOBJECT(ThisActor, ThisActor);

	if (ThisActor->GetWorld() == NULL)
	{
		UE_LOG(LogSpawn, Warning, TEXT("Destroying %s, which doesn't have a valid world pointer"), *ThisActor->GetPathName());
	}

	如果已经在要删除的列表中,不往下处理
    if (ThisActor->IsPendingKillPending())
	{
		return true;
	}

	WorldSetting这个Actor是不允许被销毁的,请记住这一点
	// seamless travel and network games.
	if (GetWorldSettings() == ThisActor)
	{
		return false;
	}

	// In-game deletion rules.
	if( IsGameWorld() )
	{

		const bool bIsNetworkedActor = ThisActor->GetLocalRole() != ROLE_None;

		删除Actor是不允许从客户端进行删除的
		const bool bCanDestroyNetworkActor = ThisActor->GetLocalRole() == ROLE_Authority || bNetForce || ThisActor->bNetTemporary;
		if (bIsNetworkedActor && !bCanDestroyNetworkActor)
		{
			return false;
		}

		const bool bCanDestroyNonNetworkActor = !!CVarAllowDestroyNonNetworkActors.GetValueOnAnyThread();
		if (!bIsNetworkedActor && !bCanDestroyNonNetworkActor)
		{
			return false;
		}

		if (ThisActor->DestroyNetworkActorHandled())
		{
			// Network actor short circuited the destroy (network will cleanup properly)
			// Don't destroy PlayerControllers and BeaconClients
			return false;
		}

如果正在执行BeginPlay呢,那么在后续Begin完成之后再对其进行删除
		if (ThisActor->IsActorBeginningPlay())
		{
			FSetActorWantsDestroyDuringBeginPlay SetActorWantsDestroyDuringBeginPlay(ThisActor);
			return true;
		}
	}
	else
	{
		ThisActor->Modify();
	}

	// Prevent recursion
	FMarkActorIsBeingDestroyed MarkActorIsBeingDestroyed(ThisActor);

通知纹理流管理器这个Actor被删除了
	IStreamingManager::Get().NotifyActorDestroyed( ThisActor );

	这个里面会有很多的代码,在这里面进行删除
	ThisActor->Destroyed();

	将所有附加到这个Actor上面的Actor脱离
	TArray<AActor*> AttachedActors;
	ThisActor->GetAttachedActors(AttachedActors);

	if (AttachedActors.Num() > 0)
	{
		TInlineComponentArray<USceneComponent*> SceneComponents;
		ThisActor->GetComponents(SceneComponents);

		for (TArray< AActor* >::TConstIterator AttachedActorIt(AttachedActors); AttachedActorIt; ++AttachedActorIt)
		{
			AActor* ChildActor = *AttachedActorIt;
			if (ChildActor != NULL)
			{
				for (USceneComponent* SceneComponent : SceneComponents)
				{
					ChildActor->DetachAllSceneComponents(SceneComponent, FDetachmentTransformRules::KeepWorldTransform);
				}
#if WITH_EDITOR
				if( GIsEditor )
				{
					GEngine->BroadcastLevelActorDetached(ChildActor, ThisActor);
				}
#endif
			}
		}
	}

	判断我们这个actor是否是attach到其他的actor下面了
	USceneComponent* RootComp = ThisActor->GetRootComponent();
	if( RootComp != nullptr && RootComp->GetAttachParent() != nullptr)
	{
		AActor* OldParentActor = RootComp->GetAttachParent()->GetOwner();
		if (OldParentActor)
		{
			OldParentActor->Modify();
		}

从父节点上脱离
		ThisActor->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);

#if WITH_EDITOR
		if( GIsEditor )
		{
			GEngine->BroadcastLevelActorDetached(ThisActor, OldParentActor);
		}
#endif
	}

	ThisActor->ClearComponentOverlaps();

	通知设置owner为空
	if( ThisActor->GetOwner() )
	{
		ThisActor->SetOwner(NULL);
	}

	ULevel* const ActorLevel = ThisActor->GetLevel();
	if (ActorLevel)
	{
		ActorLevel->CreateReplicatedDestructionInfo(ThisActor);
	}

	// Notify net drivers that this guy has been destroyed.
	if (FWorldContext* Context = GEngine->GetWorldContextFromWorld(this))
	{
		for (FNamedNetDriver& Driver : Context->ActiveNetDrivers)
		{
			if (Driver.NetDriver != nullptr && Driver.NetDriver->ShouldReplicateActor(ThisActor))
			{
				Driver.NetDriver->NotifyActorDestroyed(ThisActor);
			}
		}
	}
	else if (WorldType != EWorldType::Inactive && !IsRunningCommandlet())
	{
		// Inactive worlds do not have a world context, otherwise only worlds in the middle of seamless travel should have no context,
		// and in that case, we shouldn't be destroying actors on them until they have become the current world (i.e. CopyWorldData has been called)
		UE_LOG(LogSpawn, Warning, TEXT("UWorld::DestroyActor: World has no context! World: %s, Actor: %s"), *GetName(), *ThisActor->GetPathName());
	}

	// Remove the actor from the actor list.
	RemoveActor( ThisActor, bShouldModifyLevel );

	// Invalidate the lighting cache in the Editor.  We need to check for GIsEditor as play has not begun in network game and objects get destroyed on switching levels
	if ( GIsEditor )
	{
		if (!IsGameWorld())
		{
			ThisActor->InvalidateLightingCache();
		}
		
#if WITH_EDITOR
		GEngine->BroadcastLevelActorDeleted(ThisActor);
#endif
	}
		
	清空这个actor上面的所有组件
	ThisActor->UnregisterAllComponents();

	将这个Actor和Actor上的所有组件标记为将要删除
	ThisActor->MarkPendingKill();
	ThisActor->MarkPackageDirty();
	ThisActor->MarkComponentsAsPendingKill();

	取消注册actor的tick函数
	const bool bRegisterTickFunctions = false;
	const bool bIncludeComponents = true;
	ThisActor->RegisterAllActorTickFunctions(bRegisterTickFunctions, bIncludeComponents);

	// Return success.
	return true;
}

1) void AActor::Destroyed()源码分析

void AActor::Destroyed()
{
	RouteEndPlay(EEndPlayReason::Destroyed);

告诉蓝图Destroyed了,这个就是蓝图中可覆盖的那个方法
	ReceiveDestroyed();
广播一下,其他地方可监听
	OnDestroyed.Broadcast(this);
}

2) RouteEndPlay(EEndPlayReason::Destroyed)源码分析

void AActor::RouteEndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if (bActorInitialized)
	{
		if (ActorHasBegunPlay == EActorBeginPlayState::HasBegunPlay)
		{
调用EndPlay,并且也会通知Actor下面的所有组件也去EndPlay

			EndPlay(EndPlayReason);
		}

		// Behaviors specific to an actor being unloaded due to a streaming level removal
		if (EndPlayReason == EEndPlayReason::RemovedFromWorld)
		{
			ClearComponentOverlaps();

			bActorInitialized = false;
			if (UWorld* World = GetWorld())
			{
				World->RemoveNetworkActor(this);
			}
		}

		// Clear any ticking lifespan timers
		if (TimerHandle_LifeSpanExpired.IsValid())
		{
			SetLifeSpan(0.f);
		}
	}

	UninitializeComponents();
}

文章到此结束。谢谢,有问题请指正。

创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 <( ̄︶ ̄)>

  • 18
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
UE中的Actor生命周期包括以下几个阶段:创建、初始化、开始播放和销毁。 首先,在创建阶段,UE会根据蓝图或代码创建Actor对象,并将其添加到场景中。在这个阶段,Actor的构造函数会被调用。 接下来,在初始化阶段,UE会调用Actor的InitializeComponents()函数来初始化Actor上的所有组件。这个函数会遍历Actor上的所有组件,并根据需要激活和初始化它们。 然后,在开始播放阶段,UE会调用Actor的BeginPlay()函数。在这个函数中,可以编写自定义的逻辑来处理Actor开始播放时的操作。例如,可以在这里初始化变量、加载资源或执行其他必要的操作。 最后,在销毁阶段,当Actor不再需要时,UE会调用ActorDestroy()函数来销毁它。在这个函数中,可以进行一些清理工作,例如释放资源、取消注册组件等。 需要注意的是,UE中的Actor生命周期是由UE引擎自动管理的,开发者可以在相应的函数中编写自己的逻辑,但不能直接控制Actor的创建和销毁时机。 综上所述,UE中的Actor生命周期包括创建、初始化、开始播放和销毁这几个阶段,每个阶段都有相应的函数可以进行自定义操作。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [Unreal Engine中的Actor:理解、生成与消亡](https://blog.csdn.net/Jason6620/article/details/128399837)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [UE4 Actor生命周期 SpawnActor DestroyActor剖析](https://blog.csdn.net/u011718663/article/details/50202307)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WhiteTian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值