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

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出来的一个Actorreturn 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 gameActorComp->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));
#elseULevel* 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_SPAWNACTORTIMERFScopedSpawnActorTimer 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;}
判断坐标信息是否是有效的,无效不能spawnelse 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 Statif (!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 okif (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 failsif (SpawnParameters.bNoFail){// maybe upgrade to disallow failif (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding){CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;}else if (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding){CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;}}// use override if set, else fall back to actor's preferenceESpawnActorCollisionHandlingMethod 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 spawnedif (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 spawningUE_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_SPAWNACTORTIMERSpawnTimer.SetActorName(Actor->GetFName());
#endif清除actor的名字如果编辑器模式下
#if WITH_EDITORActor->ClearActorLabel(); // Clear label on newly spawned actors
#endif // WITH_EDITORif ( GUndo ){ModifyLevel( LevelToSpawnIn );}在这将生成的actor就加到我们上面Level里面的Actors列表对象里面了。头文件里面是两个TArray
一个列表,一个GC用的LevelToSpawnIn->Actors.Add( Actor );LevelToSpawnIn->ActorsForGC.Add(Actor);#if PERF_SHOW_MULTI_PAWN_SPAWN_FRAMESif( Cast<APawn>(Actor) ){FString PawnName = FString::Printf(TEXT("%d: %s"), ThisFramePawnSpawns.Num(), *Actor->GetPathName());ThisFramePawnSpawns.Add(PawnName);}
#endif// 赋值spawn信息Actor->SpawnCollisionHandlingMethod = CollisionHandlingMethod;#if WITH_EDITORif (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 spawnOnActorSpawned.Broadcast(Actor);#if WITH_EDITORif (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) componentsDispatchOnComponentsCreated(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 FinishSpawningGSpawnActorDeferredTransformCache.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 deserializedconst bool bDeferBeginPlayAndUpdateOverlaps = (bExchangedRoles && RemoteRole == ROLE_Authority) && !GIsReinstancing;if (bActorsInitialized){这个比较熟悉吧,初始化这个actor下的所有componentsInitializeComponents();// actor should have all of its components created and registered now, do any collision checking and handling that we need to doif (World){switch (SpawnCollisionHandlingMethod){case ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn:{// Try to find a spawn positionFVector 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 normalbreak;}}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 runbRunBeginPlay = (ParentActor->HasActorBegunPlay() || ParentActor->IsActorBeginningPlay());}}#if WITH_EDITORif (bRunBeginPlay && bIsEditorPreviewActor){bRunBeginPlay = false;}
#endif重头戏在这呢,通知调用beginplayif (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 workMarkPendingKill();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在这里调用的BeginPlayBeginPlay();
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||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 trueWorld->DestroyActor(this, true); }if (!IsPendingKill()){// Initialize overlap stateUpdateInitialOverlaps(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 BeaconClientsreturn false;}如果正在执行BeginPlay呢,那么在后续Begin完成之后再对其进行删除if (ThisActor->IsActorBeginningPlay()){FSetActorWantsDestroyDuringBeginPlay SetActorWantsDestroyDuringBeginPlay(ThisActor);return true;}}else{ThisActor->Modify();}// Prevent recursionFMarkActorIsBeingDestroyed 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_EDITORif( 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_EDITORif( 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 levelsif ( GIsEditor ){if (!IsGameWorld()){ThisActor->InvalidateLightingCache();}#if WITH_EDITORGEngine->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下面的所有组件也去EndPlayEndPlay(EndPlayReason);}// Behaviors specific to an actor being unloaded due to a streaming level removalif (EndPlayReason == EEndPlayReason::RemovedFromWorld){ClearComponentOverlaps();bActorInitialized = false;if (UWorld* World = GetWorld()){World->RemoveNetworkActor(this);}}// Clear any ticking lifespan timersif (TimerHandle_LifeSpanExpired.IsValid()){SetLifeSpan(0.f);}}UninitializeComponents();
}

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

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

UE4 Actor生命周期 SpawnActor DestroyActor剖析相关推荐

  1. Actor生命周期理解

    Actor生命周期理解 镇图:Actor内功心法图 Actor的生命周期可以用Hooks体现和控制,下面是默认的Actor Hooks的方法,我们可以选择性的进行重写: def preStart(): ...

  2. Akka的Actor生命周期《Eight》译

    Actor在创建时出现,然后在用户请求时停止.每当一个Actor停止时,它的所有孩子也会被递归停止.此行为极大地简化了资源清理,并有助于避免资源泄漏,例如由打开的套接字和文件引起的资源泄漏.事实上,处 ...

  3. 以Transaction的生命周期为线索剖析Libra核心组件

    以Transaction的生命周期为线索剖析Libra核心组件 账号模型 Transaction与Move合约 Transaction的生命周期 AC服务 Mempool服务 Mempool内的Tra ...

  4. 【Akka】Akka Actor生命周期

    1.概述 转载:Akka Actor生命周期 用于自我学习. Actor的生命周期是使用Hooks体现和控制的,我们可以重写相关的Hooks,从而实现对Actor生命周期各环节的细粒度控制.各事件发生 ...

  5. Akka的Hello World(二)Akka的Actor生命周期

    (〇)介绍 每当一个Actor停止时,它的所有孩子也会被递归地停止.此行为极大地简化了资源清理,并有助于避免资源泄漏,例如由打开sockets 和文件引起的资源泄漏.实际上,处理低级多线程代码时常常被 ...

  6. abp模块生命周期设计思路剖析

    abp中将生命周期事件抽象为4个接口: //预初始化 public interface IOnPreApplicationInitialization {void OnPreApplicationIn ...

  7. UE5——Actor生命周期——销毁

    Actor类 判断对象是否被标记为销毁状态 LevelActor类 意思是不允许从客户端删除Actor 准备销毁Actor 销毁 通知蓝图 通知网络驱动动Actor已经被销毁了 从关卡列表中移除掉 通 ...

  8. vue- Vue-Cli脚手架工具安装 -创建项目-页面开发流程-组件生命周期-03

    目录 本博客环境 Vue-Cli 项目环境搭建 与 python 基础环境对比 环境搭建 创建启动 vue 项目 命令创建项目(步骤小多) 启动 vue 项目(命令行方式) 启动 vue 项目(pyc ...

  9. 剖析Fragment的Pause生命周期全过程

    前言 之前遇到一个问题,与Fragment的Pause生命周期有关,所以就研究了一下Fragment的Pause生命周期特点.就有关这篇笔记. 我们知道Fragment的生命周期是依赖Activity ...

最新文章

  1. 【bzoj2751】[HAOI2012]容易题(easy) 数论-快速幂
  2. MySQL高可用的几种方案
  3. [latex]图片动态缩放的PDF动画示例
  4. 在Linux机器上配置NUD
  5. 【语义分割】OCRNet:Object-Context Representations for Semantic Segmentation
  6. 1043 输出PATest(PAT乙级 C++)
  7. TCP/IP协议模型和OSI协议模型的概念
  8. nopcommerce 二次开发
  9. 从request中获取上一个请求的url
  10. mysql查看和修改密码策略
  11. 梅特勒托利多xk3124电子秤说明书_梅特勒托利多电子称设置方法
  12. MCS-51单片机的内部结构
  13. 通达OA-医疗卫生行业系统解决方案
  14. 华为交换机配置IPSG防止DHCP动态主机私自更改IP地址
  15. CSS中absolute和relative
  16. LM1875功放板设计实例
  17. [转]多普达818、828+升级中文WM6.1及必装软件全过程讲解
  18. Java 算法题目 走楼梯
  19. 数据挖掘的10个常见问题
  20. python Xarray处理设置2维数组作为coordinates

热门文章

  1. 【独立版】八戒农场V1.3.8智慧农场系统,支持小程序和公众号H5双版本
  2. web服务器学习小结
  3. php 发送邮件端口,PHP 使用 PHPMailer 发送邮件
  4. jQuery制作树状表格
  5. php中tp_thinkphp中M()方法有什么用
  6. 用Python软件绘制正方形(红绿色)
  7. DOM change 事件
  8. 用友U8 ERP 平台打印控件(word打印设置)
  9. python case when用法_case when
  10. 洛谷 P5738歌唱比赛 题解