游戏的加载流程
先看下官方文档的图
原图链接:https://docs.unrealengine.com/4.27/zh-CN/InteractiveExperiences/Framework/GameFlow/
事件的一般顺序为初始化引擎、创建并初始化 GameInstance、加载关卡,最后开始游戏。事件的一般顺序为初始化引擎、创建并初始化 GameInstance、加载关卡,最后开始游戏。
先看GameEngin::Init`
//加载并应用用户游戏设置
GetGameUserSettings()->LoadSettings();
GetGameUserSettings()->ApplyNonResolutionSettings();
//创建游戏实例。对于GameEngine,这应该是唯一创建的GameInstance。
{
FSoftClassPath GameInstanceClassName = GetDefault<UGameMapsSettings>()->GameInstanceClass;
UClass* GameInstanceClass = (GameInstanceClassName.IsValid() ? LoadObject<UClass>(NULL, *GameInstanceClassName.ToString()) : UGameInstance::StaticClass());
if (GameInstanceClass == nullptr)
{
UE_LOG(LogEngine, Error, TEXT("Unable to load GameInstance Class '%s'. Falling back to generic UGameInstance."), *GameInstanceClassName.ToString());
GameInstanceClass = UGameInstance::StaticClass();
}
GameInstance = NewObject<UGameInstance>(this, GameInstanceClass);
GameInstance->InitializeStandalone();
}
`可以看到,应用了用户设置之后,就看上创建了GameInstance;接着调用了GameInstance->InitializeStandalone();
继续接着看InitializeStandalone()里面干了什么了。
void UGameInstance::InitializeStandalone(const FName InPackageName, UPackage* InWorldPackage)
{
//创建世界上下文。这应该是为这个游戏实例创建的唯一WorldContext。
WorldContext = &GetEngine()->CreateNewWorldContext(EWorldType::Game);
WorldContext->OwningGameInstance = this;
//在独立的环境中,从一开始就创建一个虚拟世界,以避免在LoadMap使我们真正的世界到来之前没有世界的问题
UWorld* DummyWorld = UWorld::CreateWorld(EWorldType::Game, false, InPackageName, InWorldPackage);
DummyWorld->SetGameInstance(this);
WorldContext->SetCurrentWorld(DummyWorld);
Init();
}
void UWorld::InitializeNewWorld(const InitializationValues IVS)
{
if (!IVS.bTransactional)
{
ClearFlags(RF_Transactional);
}
PersistentLevel = NewObject<ULevel>(this, TEXT("PersistentLevel"));
PersistentLevel->Initialize(FURL(nullptr));
PersistentLevel->Model = NewObject<UModel>(PersistentLevel);
PersistentLevel->Model->Initialize(nullptr, 1);
PersistentLevel->OwningWorld = this;
// Create the WorldInfo actor.
FActorSpawnParameters SpawnInfo;
}
void UGameInstance::Init()
{
ReceiveInit();
if (!IsRunningCommandlet())
{
UClass* SpawnClass = GetOnlineSessionClass();
OnlineSession = NewObject<UOnlineSession>(this, SpawnClass);
if (OnlineSession)
{
OnlineSession->RegisterOnlineDelegates();
}
if (!IsDedicatedServerInstance())
{
TSharedPtr<GenericApplication> App = FSlateApplication::Get().GetPlatformApplication();
if (App.IsValid())
{
App->RegisterConsoleCommandListener(GenericApplication::FOnConsoleCommandListener::CreateUObject(this, &ThisClass::OnConsoleInput));
}
}
FNetDelegates::OnReceivedNetworkEncryptionToken.BindUObject(this, &ThisClass::ReceivedNetworkEncryptionToken);
FNetDelegates::OnReceivedNetworkEncryptionAck.BindUObject(this, &ThisClass::ReceivedNetworkEncryptionAck);
}
SubsystemCollection.Initialize(this);
}
可以看到,创建了WorldContext 和 DummyWorld;
UWorld::InitializeNewWorld里面创建了关卡
还创建了了在线会话。
下面继续看World::BeginPlay
void UWorld::BeginPlay()
{
AGameModeBase* const GameMode = GetAuthGameMode();
if (GameMode)
{
GameMode->StartPlay();
if (GetAISystem())
{
GetAISystem()->StartPlay();
}
}
OnWorldBeginPlay.Broadcast();
}
开始了GameMode::StartPlay();
void AGameModeBase::StartPlay()
{
GameState->HandleBeginPlay();
}
void AGameStateBase::HandleBeginPlay()
{
bReplicatedHasBegunPlay = true;
GetWorldSettings()->NotifyBeginPlay();
GetWorldSettings()->NotifyMatchStarted();
}
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;
}
}
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;
}
}
从以上源码可以看出,最后面调用Actor::BeginPlay.
现在回到我们的ActionRPG.
现在就看BP_MainMenuGameMode里面的流程。
这个就比较简单了,就是打开了WB_Title这个面板。
点击面板中的开始游戏。
可以看到移除所有面板,播放一个加载界面,就开始打开ActionRPG主场景了。