前回 は SDLMain.cpp
の main
関数の SDL2 を軸にした簡単な流れだけ見ました。
int main(int Argc, char* Args[]) { // Ignore first param (executable path) std::vector<std::wstring> CmdLineParams; for (int i = 1; i < Argc; ++i) { CmdLineParams.push_back(FStringUtils::Widen(Args[i])); } FCommandLine::Get().Init(CmdLineParams); FSettings::Get().Init(); Main = std::make_unique<FMain>(); Main->InitPlatform();
上記は `main のイベントループに入る前のコードです。
コマンドライン引数を FCommandLine
に格納、設定ファイルの指定があれば FSettings
に格納しています。
どちらもシングルトンの実装がされていて Get
でインスタンスを取得できます。
FMain
これが本体です。
コンストラクタ / デストラクタ
DirectX の部分を除けば以下のように小さなコードです。
FMain::FMain() noexcept(false): bIsFullScreen(false) { FDebugLog::Init(); FDebugLog::AddTarget(FDebugLog::ELogTarget::DebugOutput); FDebugLog::AddTarget(FDebugLog::ELogTarget::Console); FDebugLog::AddTarget(FDebugLog::ELogTarget::File); } FMain::~FMain() { FDebugLog::Close(); Game = nullptr; }
bIsFullScreen
, Game
のメンバ変数定義は以下です。
public: /** True if main window is currently in fullscreen mode */ bool bIsFullScreen; private: std::unique_ptr<FGame> Game;
InitPlatform
以下のコードは全体ですがログ出力は省いています。
void FMain::InitPlatform() { // Init EOS SDK EOS_InitializeOptions SDKOptions = {}; SDKOptions.ApiVersion = EOS_INITIALIZE_API_LATEST; SDKOptions.AllocateMemoryFunction = nullptr; SDKOptions.ReallocateMemoryFunction = nullptr; SDKOptions.ReleaseMemoryFunction = nullptr; SDKOptions.ProductName = SampleConstants::GameName; SDKOptions.ProductVersion = "1.0"; SDKOptions.Reserved = nullptr; SDKOptions.SystemInitializeOptions = nullptr; SDKOptions.OverrideThreadAffinity = nullptr; EOS_EResult InitResult = EOS_Initialize(&SDKOptions); if (InitResult != EOS_EResult::EOS_Success) return; EOS_EResult SetLogCallbackResult = EOS_Logging_SetCallback(&EOSSDKLoggingCallback); const bool bCreateSuccess = FPlatform::Create(); }
- EOS_InitializeOptions | Epic Online Services ドキュメンテーション
- EOS_Initialize | Epic Online Services ドキュメンテーション
- EOS_Logging_SetCallback | Epic Online Services ドキュメンテーション
EOS_Initialize
に関しては以下の説明がありますので、 EOS_Shutdown
もここで追っておきます。
This function must only be called one time and must have a corresponding EOS_Shutdown call
EOS_Shutdown が呼ばれる箇所
main
関数において、メインループ終了後に Main->OnShutdown();
が呼び出されます。この内部でさらに Game->OnShutdown();
が呼び出されます。
Game
のクラスである FGame
の親クラスの FBaseGame::OnShutdown
に処理が移譲されており、そこで Authentication->Shutdown();
が呼び出されます。
Authentication
は FAuthentication
のインスタンスであり、その Shutdown
メソッド内で EOS_Shutdown()
が呼び出されています。
void FAuthentication::Shutdown() { RemoveNotifyLoginStatusChanged(); RemoveConnectAuthExpirationNotification(); if (FPlatform::GetPlatformHandle()) { EOS_Platform_Release(FPlatform::GetPlatformHandle()); } EOS_EResult ShutdownResult = EOS_Shutdown(); if (ShutdownResult != EOS_EResult::EOS_Success) // ログ FPlatform::Release(); }
ここの最後で呼び出されている FPlatform
は FMain::InitPlatform
メソッド内でも FPlatform::Create
が呼び出されており、シャットダウン処理はこの FAuthentication::Shutdown
が担っているようです。
FPlatform
以下のコメントが書かれているため、EOS の Platform 関連にアクセスするようです。
Platform インターフェース | Epic Online Services ドキュメンテーション
/** * Creates EOS SDK Platform */ class FPlatform
bool FPlatform::Create() { bIsInit = false; // Create platform instance EOS_Platform_Options PlatformOptions = {}; PlatformOptions.ApiVersion = EOS_PLATFORM_OPTIONS_API_LATEST; PlatformOptions.bIsServer = FCommandLine::Get().HasFlagParam(CommandLineConstants::Server); PlatformOptions.EncryptionKey = SampleConstants::EncryptionKey; PlatformOptions.OverrideCountryCode = nullptr; PlatformOptions.OverrideLocaleCode = nullptr; PlatformOptions.Flags = EOS_PF_WINDOWS_ENABLE_OVERLAY_D3D9 | EOS_PF_WINDOWS_ENABLE_OVERLAY_D3D10 | EOS_PF_WINDOWS_ENABLE_OVERLAY_OPENGL; // Enable overlay support for D3D9/10 and OpenGL. This sample uses D3D11 or SDL. PlatformOptions.CacheDirectory = FUtils::GetTempDirectory(); // コマンドラインパラメータから ProductId を取得し設定 // コマンドラインパラメータから SandboxId を取得し設定 // コマンドラインパラメータから DeploymentId を取得し設定 // コマンドラインパラメータから ClientId を取得し設定 // コマンドラインパラメータから ClientSecret を取得し設定 // (未設定など)不正な値があれば失敗 if (bHasInvalidParamProductId || bHasInvalidParamSandboxId || bHasInvalidParamDeploymentId || bHasInvalidParamClientCreds) { return false; } EOS_Platform_RTCOptions RtcOptions = { 0 }; RtcOptions.ApiVersion = EOS_PLATFORM_RTCOPTIONS_API_LATEST; // 以下は Windows で SDL を利用している場合 std::string XAudio29DllPath = SDL_GetBasePath(); XAudio29DllPath.append("/xaudio2_9redist.dll"); EOS_Windows_RTCOptions WindowsRtcOptions = { 0 }; WindowsRtcOptions.ApiVersion = EOS_WINDOWS_RTCOPTIONS_API_LATEST; WindowsRtcOptions.XAudio29DllPath = XAudio29DllPath.c_str(); RtcOptions.PlatformSpecificOptions = &WindowsRtcOptions; PlatformOptions.RTCOptions = &RtcOptions; PlatformHandle = EOS_Platform_Create(&PlatformOptions); if (PlatformHandle == nullptr) return true; bIsInit = true; return true;
期待通り EOS の Platform のインスタンスを作成し保持していることが確認できます。
- EOS_Platform_Options | Epic Online Services ドキュメンテーション
- EOS_Platform_RTCOptions | Epic Online Services ドキュメンテーション
- EOS_Platform_Create | Epic Online Services ドキュメンテーション
EOS_Windows_RTCOptions
に関してはドキュメントを見つけられませんでしたが、SDK にヘッダファイルは含まれていました。 EOS_Platform_RTCOptions::PlatformSpecificOptions
に設定するものなので、Windows 用の設定ということなのだと思います。
ここでいう
RTC
はドキュメントから明示的になにかわからなかったのですが、WebRTC のことなのでしょうか。
EOS_Platform_Create
のドキュメントには、これが single instance であることと、EOS_Platform_Release
に渡して開放することが記載されています。
すでに FAuthentication::Shutdown()
の中で EOS_Platform_Release(FPlatform::GetPlatformHandle());
が呼び出されていることを確認できています。
ついでに FPlatform::Release
も見ておきます。
void FPlatform::Release() { bIsInit = false; PlatformHandle = nullptr; bIsShuttingDown = true; }
init 関数 (SDLMain.cpp)
続いてイベントループの直前の init
関数を見ます。
int main(int Argc, char* Args[]) { // Main->InitPlatform(); まで //Start up SDL and create window if (!Init()) { printf("Failed to initialize!\n"); } else { // ここからイベントループ
SDL2 の初期設定のコードが多いので、その辺は省略し、FMain
クラスに関わる部分を中心にみていきます。
bool Init() { // SDL_Init, SDL_GL_SetAttribute の設定 int Width, Height; Main->GetDefaultSize(Width, Height); Main->InitCommandLine(); SDL_Window* Window = nullptr; //Create window if (Main->bIsFullScreen) // Window = SDL_CreateWindow が分岐でそれぞれ実行 //Create context SDL_GLContext GLContext = SDL_GL_CreateContext(Window); //Initialize GLEW glewExperimental = GL_TRUE; GLenum glewError = glewInit(); //Vsync settings //First try to use adaptive VSync if (SDL_GL_SetSwapInterval(-1) < 0) // Vsync の設定を何度か行う //init True Type Fonts lib if (TTF_Init() != 0) if (!Main->bIsFullScreen) { int MinWidth = 1024; int MinHeight = 768; Main->GetMinimumSize(MinWidth, MinHeight); SDL_SetWindowMinimumSize(Window, MinWidth, MinHeight); } SDL_GetWindowSize(Window, &Width, &Height); //Initialize OpenGL if (!InitGraphics(Window, Width, Height)) return false; Main->Initialize(Window, GLContext, Width, Height); return true; }
FMain::InitCommandLine
void FMain::InitCommandLine() { bIsFullScreen = HasFullScreenCommandLine(); } bool FMain::HasFullScreenCommandLine() { return FCommandLine::Get().HasFlagParam(CommandLineConstants::Fullscreen); }
コマンドライン引数から fullscreen
を取得しているだけです。
FMain::Initialize
void FMain::Initialize(SDL_Window* Window, SDL_GLContext InGLContext, int Width, int Height) { SDLWindow = Window; GLContext = InGLContext; Game = std::make_unique<FGame>(); CreateDeviceDependentResources(); CreateWindowSizeDependentResources(); Game->UpdateLayout(Width, Height); Game->Init(); } void FMain::CreateDeviceDependentResources() { if (Game) Game->Create(); } void FMain::CreateWindowSizeDependentResources() { Vector2 Size; int Width = 0, Height = 0; SDL_GetWindowSize(SDLWindow, &Width, &Height); Size = Vector2(float(Width), float(Height)); float aspectRatio = float(Size.x) / float(Size.y); }
Window サイズに関する処理もしていますが、 Game
インスタンスの作成・保持や初期化を行っています。
続いて Game
のクラスである FGame
を見ていきます。
FGame
コンストラクタ / デストラクタ
FGame::FGame() noexcept(false) : FBaseGame() { Menu = std::make_shared<FMenu>(Console); Level = std::make_unique<FLevel>(); CustomInvites = std::make_unique<FCustomInvites>(); CreateConsoleCommands(); } FGame::~FGame() { } void FGame::CreateConsoleCommands() { FBaseGame::CreateConsoleCommands(); if (Console) { const std::vector<const wchar_t*> ExtraHelpMessageLines = { L" SIMULATECUSTOMINVITE - simulates an incoming custom invite, debug testing only", }; AppendHelpMessageLines(ExtraHelpMessageLines); } }
FGame
が FMenu
, FLevel
, FCustomInvites
の管理をしているようです。
さらに Console
は FConsole
の shared_ptr
のため、 FConsole
の管理もしているようです。
続いて、親クラスの FBaseGame
です。
FBaseGame のコンストラクタ / デストラクタ
FBaseGame::FBaseGame() noexcept(false): TheImpl(std::make_unique<FBaseGame::Impl>(this)) { Input = std::make_unique<FInput>(); Console = std::make_shared<FConsole>(); Users = std::make_unique<FUsers>(); TextureManager = std::make_unique<FTextureManager>(); Authentication = std::make_shared<FAuthentication>(); Friends = std::make_unique<FFriends>(); Metrics = std::make_unique<FMetrics>(); PlayerManager = std::make_unique<FPlayerManager>(); VectorRender = std::make_unique<FVectorRender>(); EosUI = std::make_unique<FEosUI>(); } FBaseGame::~FBaseGame() { }
親クラスもまとめて見ると、FGame
は、ほとんどを管理しています。そりゃそうですよね😅
メンバ初期化で指定されている TheImpl
は FBaseGame::Impl
というクラスになっておりコメントを見るに singleton として動くようです。
FBaseGame::Create
Game->Create()
も実態としては FBaseGame
の方になっています。
void FBaseGame::Create() { TheImpl->UISpriteBatch = std::make_unique<FSDLSpriteBatch>(); Menu->CreateFonts(); Menu->Create(); Level->Create(); VectorRender->Create(); }
構成要素の Create が順次呼び出されているようです。
UpdateLayout / Init
Game->UpdateLayout()
, Game->Init()
も同様に FBaseGame
の方にあります。
void FBaseGame::UpdateLayout(int Width, int Height) { if (VectorRender) { //recreate vector render bool bIsEnabled = VectorRender->IsDebugRenderEnabled(); VectorRender = std::make_unique<FVectorRender>(); VectorRender->Create(); VectorRender->SetDebugRenderEnabled(bIsEnabled); } if (Menu) { Menu->UpdateLayout(Width, Height); } } void FBaseGame::Init() { Metrics->Init(); Menu->Init(); EosUI->Init(); FGameEvent Event(EGameEventType::CheckAutoLogin); OnGameEvent(Event); } void FBaseGame::OnGameEvent(const FGameEvent& Event) { PlayerManager->OnGameEvent(Event); Friends->OnGameEvent(Event); Menu->OnGameEvent(Event); Authentication->OnGameEvent(Event); Metrics->OnGameEvent(Event); Level->OnGameEvent(Event); EosUI->OnGameEvent(Event); }
VectorRender
は FBaseGame
のコンストラクタで生成されていましたが、コメント通りですが recreate されているようです。
そして、FGameEvent
が出てきました。まだ内容はわかりませんがイベントループに関連する部分になりそうです。
まとめ
今回は main
関数の中の、イベントループに入る直前までの設定や初期化の部分を中心にみていきました。
SDL2 と DirectX に別れた main から、実際の main を司る FMain
, EOS Platform インターフェースを wrap するFPlatform
、そしておそらくこのサンプルゲームの中心になる FGame
, FBaseGame
を順に追っていき、FGame
がゲームの UI やイベントを管理しているであろうところまでいきました。
次回はイベントループに入っていきたいと思います。