文章目录
UE4 BeginPlay执行流程
源代码
UGameInstance::StartPlayInEditorGameInstance:
FGameInstancePIEResult UGameInstance::StartPlayInEditorGameInstance(ULocalPlayer* LocalPlayer, const FGameInstancePIEParameters& Params)
{
if (!Params.EditorPlaySettings)
{
return FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_InvalidEditorPlaySettings", "Invalid Editor Play Settings!"));
}
if (PIEStartTime == 0)
{
PIEStartTime = Params.PIEStartTime;
}
BroadcastOnStart();
UEditorEngine* const EditorEngine = CastChecked<UEditorEngine>(GetEngine());
// for clients, just connect to the server
if (Params.NetMode == PIE_Client)
{
FString Error;
FURL BaseURL = WorldContext->LastURL;
FString URLString(TEXT("127.0.0.1"));
uint16 ServerPort = 0;
if (Params.EditorPlaySettings->GetServerPort(ServerPort))
{
URLString += FString::Printf(TEXT(":%hu"), ServerPort);
}
if (Params.EditorPlaySettings->IsNetworkEmulationEnabled())
{
if (Params.EditorPlaySettings->NetworkEmulationSettings.IsEmulationEnabledForTarget(NetworkEmulationTarget::Client))
{
URLString += Params.EditorPlaySettings->NetworkEmulationSettings.BuildPacketSettingsForURL();
}
}
if (EditorEngine->Browse(*WorldContext, FURL(&BaseURL, *URLString, (ETravelType)TRAVEL_Absolute), Error) == EBrowseReturnVal::Pending)
{
EditorEngine->TransitionType = ETransitionType::WaitingToConnect;
}
else
{
return FGameInstancePIEResult::Failure(FText::Format(NSLOCTEXT("UnrealEd", "Error_CouldntLaunchPIEClient", "Couldn't Launch PIE Client: {0}"), FText::FromString(Error)));
}
}
else
{
// we're going to be playing in the current world, get it ready for play
UWorld* const PlayWorld = GetWorld();
FString ExtraURLOptions;
if (Params.EditorPlaySettings->IsNetworkEmulationEnabled())
{
NetworkEmulationTarget CurrentTarget = Params.NetMode == PIE_ListenServer ? NetworkEmulationTarget::Server : NetworkEmulationTarget::Client;
if (Params.EditorPlaySettings->NetworkEmulationSettings.IsEmulationEnabledForTarget(CurrentTarget))
{
ExtraURLOptions += Params.EditorPlaySettings->NetworkEmulationSettings.BuildPacketSettingsForURL();
}
}
// make a URL
FURL URL;
// If the user wants to start in spectator mode, do not use the custom play world for now
if (EditorEngine->UserEditedPlayWorldURL.Len() > 0 || Params.OverrideMapURL.Len() > 0)
{
FString UserURL = EditorEngine->UserEditedPlayWorldURL.Len() > 0 ? EditorEngine->UserEditedPlayWorldURL : Params.OverrideMapURL;
UserURL += ExtraURLOptions;
// If the user edited the play world url. Verify that the map name is the same as the currently loaded map.
URL = FURL(NULL, *UserURL, TRAVEL_Absolute);
if (URL.Map != PIEMapName)
{
// Ensure the URL map name is the same as the generated play world map name.
URL.Map = PIEMapName;
}
}
else
{
// The user did not edit the url, just build one from scratch.
URL = FURL(NULL, *EditorEngine->BuildPlayWorldURL(*PIEMapName, Params.bStartInSpectatorMode, ExtraURLOptions), TRAVEL_Absolute);
}
// If a start location is specified, spawn a temporary PlayerStartPIE actor at the start location and use it as the portal.
AActor* PlayerStart = NULL;
if (!EditorEngine->SpawnPlayFromHereStart(PlayWorld, PlayerStart))
{
// failed to create "play from here" playerstart
return FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_FailedCreatePlayFromHerePlayerStart", "Failed to create PlayerStart at desired starting location."));
}
if (!PlayWorld->SetGameMode(URL))
{
// Setting the game mode failed so bail
return FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_FailedCreateEditorPreviewWorld", "Failed to create editor preview world."));
}
FGameInstancePIEResult PostCreateGameModeResult = PostCreateGameModeForPIE(Params, PlayWorld->GetAuthGameMode<AGameModeBase>());
if (!PostCreateGameModeResult.IsSuccess())
{
return PostCreateGameModeResult;
}
// Make sure "always loaded" sub-levels are fully loaded
PlayWorld->FlushLevelStreaming(EFlushLevelStreamingType::Visibility);
PlayWorld->CreateAISystem();
PlayWorld->InitializeActorsForPlay(URL);
// calling it after InitializeActorsForPlay has been called to have all potential bounding boxed initialized
FNavigationSystem::AddNavigationSystemToWorld(*PlayWorld, LocalPlayers.Num() > 0 ? FNavigationSystemRunMode::PIEMode : FNavigationSystemRunMode::SimulationMode);
// @todo, just use WorldContext.GamePlayer[0]?
if (LocalPlayer)
{
FString Error;
if (!LocalPlayer->SpawnPlayActor(URL.ToString(1), Error, PlayWorld))
{
return FGameInstancePIEResult::Failure(FText::Format(NSLOCTEXT("UnrealEd", "Error_CouldntSpawnPlayer", "Couldn't spawn player: {0}"), FText::FromString(Error)));
}
}
UGameViewportClient* const GameViewport = GetGameViewportClient();
if (GameViewport != NULL && GameViewport->Viewport != NULL)
{
// Stream any levels now that need to be loaded before the game starts
GEngine->BlockTillLevelStreamingCompleted(PlayWorld);
}
if (Params.NetMode == PIE_ListenServer)
{
// Add port
uint16 ServerPort = 0;
if (Params.EditorPlaySettings->GetServerPort(ServerPort))
{
URL.Port = ServerPort;
}
// start listen server with the built URL
PlayWorld->Listen(URL);
}
PlayWorld->BeginPlay();
}
// Give the deprecated method a chance to fail as well
FGameInstancePIEResult StartResult = FGameInstancePIEResult::Success();
if (StartResult.IsSuccess())
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
StartResult = StartPIEGameInstance(LocalPlayer, Params.bSimulateInEditor, Params.bAnyBlueprintErrors, Params.bStartInSpectatorMode) ?
FGameInstancePIEResult::Success() :
FGameInstancePIEResult::Failure(NSLOCTEXT("UnrealEd", "Error_CouldntInitInstance", "The game instance failed to Play/Simulate In Editor"));
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
return StartResult;
}
UWorld::BeginPlay
void UWorld::BeginPlay()
{
AGameModeBase* const GameMode = GetAuthGameMode();
if (GameMode)
{
GameMode->StartPlay();
if (GetAISystem())
{
GetAISystem()->StartPlay();
}
}
OnWorldBeginPlay.Broadcast();
#if WITH_CHAOS
if(PhysicsScene)
{
PhysicsScene->OnWorldBeginPlay();
}
#endif
}
AGameModeBase::StartPlay
void AGameModeBase::StartPlay()
{
GameState->HandleBeginPlay();
}
AGameStateBase::HandleBeginPlay
void AGameStateBase::HandleBeginPlay()
{
bReplicatedHasBegunPlay = true;
GetWorldSettings()->NotifyBeginPlay();
GetWorldSettings()->NotifyMatchStarted();
}
AWorldSettings::NotifyBeginPlay
void AWorldSettings::NotifyBeginPlay()
{
UWorld* World = GetWorld();
if (!World->bBegunPlay)
{
for (FActorIterator It(World); It; ++It)
{
SCOPE_CYCLE_COUNTER(STAT_ActorBeginPlay);
const bool bFromLevelLoad = true;
It->DispatchBeginPlay(bFromLevelLoad);
}
World->bBegunPlay = true;
}
}
FActorIterator
是actor的遍历器,它继承自 TActorIteratorBase<FActorIterator>
,TActorIteratorBase
里面存储了World的指针,并重写了++
符号,所以这样写能遍历Actor
ActorIteratorBase
重写的方法:
void operator++()
{
// Use local version to avoid LHSs as compiler is not required to write out member variables to memory.
AActor* LocalCurrentActor = nullptr;
int32 LocalIndex = State->Index;
TArray<UObject*>& LocalObjectArray = State->ObjectArray;
TArray<AActor*>& LocalSpawnedActorArray = State->SpawnedActorArray;
UWorld* LocalCurrentWorld = State->CurrentWorld;
while(++LocalIndex < (LocalObjectArray.Num() + LocalSpawnedActorArray.Num()))
{
if (LocalIndex < LocalObjectArray.Num())
{
LocalCurrentActor = static_cast<AActor*>(LocalObjectArray[LocalIndex]);
}
else
{
LocalCurrentActor = LocalSpawnedActorArray[LocalIndex - LocalObjectArray.Num()];
}
State->ConsideredCount++;
ULevel* ActorLevel = LocalCurrentActor ? LocalCurrentActor->GetLevel() : nullptr;
if ( ActorLevel
&& static_cast<const Derived*>(this)->IsActorSuitable(LocalCurrentActor)
&& static_cast<const Derived*>(this)->CanIterateLevel(ActorLevel)
&& ActorLevel->GetWorld() == LocalCurrentWorld)
{
// ignore non-persistent world settings
if (ActorLevel == LocalCurrentWorld->PersistentLevel || !LocalCurrentActor->IsA(AWorldSettings::StaticClass()))
{
State->CurrentActor = LocalCurrentActor;
State->Index = LocalIndex;
return;
}
}
}
State->CurrentActor = NULL;
State->ReachedEnd = true;
}
AActor::DispatchBeginPlay
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();
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;
}
}