UE4 Actor生命周期 SpawnActor DestroyActor剖析
今天xbox开发机挂掉了。系统坏了,发现一个解决办法。错误代码E200 E305 分享链接O网页链接O网页链接点击打开链接点击打开链接转载自http://weibo.com/3185590710/profile?rightmod=1&wvr=6&mod=personnumber&is_search=1&key_word=%E5%BC%80%E5%8F%91%E6%9
原创文章,转载请注明出处。
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,还在下头
2)Actor->PostSpawnInitialize源码分析
3)void AActor::PostActorConstruction()源码分析这个里面会初始化Actor下面的组件, 主要是下面的代码调用的BeginPlay
4)void AActor::DispatchBeginPlay(bool bFromLevelStreaming)源码分析
第三部分,从代码层面调用DestroyActor,对其做分析
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();
}
文章到此结束。谢谢,有问题请指正。
创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 <( ̄︶ ̄)>
这里是一个专注于游戏开发的社区,我们致力于为广大游戏爱好者提供一个良好的学习和交流平台。我们的专区包含了各大流行引擎的技术博文,涵盖了从入门到进阶的各个阶段,无论你是初学者还是资深开发者,都能在这里找到适合自己的内容。除此之外,我们还会不定期举办游戏开发相关的活动,让大家更好地交流互动。加入我们,一起探索游戏开发的奥秘吧!
更多推荐
所有评论(0)