UE4BeginPlay执行流程

UE4 BeginPlay执行流程



Created with Raphaël 2.3.0 开始 UGameInstance::StartPlayInEditorGameInstance() 调用UWorld::BeginPlay() UWorld::BeginPlay()调用AGameModeBase::StartPlay() AGameModeBase::StartPlay()调用AGameStateBase::HandleBeginPlay() AGameStateBase::HandleBeginPlay()调用AWorldSettings::NotifyBeginPlay() AWorldSettings::NotifyBeginPlay()遍历所有Actor并调用AActor::DispatchBeginPlay(bool bFromLevelStreaming)) AActor::DispatchBeginPlay(bool bFromLevelStreaming)) 调用AActor::BeginPlay() 结束
Actor 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;
	}
}
  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值