Epic Online Services サンプルの AuthAndFriends のコードリーディング ①

サンプルのアプリケーションもさくっと読めなかったので、手間ですが紐解いていくことにしました。

対象は AuthAndFriends です。
Auth and Friends サンプル | Epic Online Services ドキュメンテーション

ディレクトリ構成

EOS-SDK-18153445-v1.14.1/
  + SDK/
    + Lib/EOSSDK-Win64-Shipping.lib
  + Samples/
    + Achievements/ (他のサンプル達、ここでは代表して Achievements のみ記載)
    + AuthAndFriends/ (今回の対象のサンプル)
    + Shared/
      + Assets/ (サウンド・画像・フォントなど)
      + External/
        + curl/
        + DirectXTK/
        + GLEW/
        + httplib/
        + rapidjson/
        + SDL2/
        + UTF8-CPP/
      + Source/
        + Core/
        + Graphics/
        + Input/
        + Main/
        + Math/
        + Steam/
        + Utils/
        + BaseGame.h / cpp
        + BaseLevel.h / cpp
        + BaseMenu.h / cpp
        + pch.h / cpp
    + Samples.sln
    + Samples.props

インクルードファイル

今回はビルド構成は Debug_SDL の場合を見ていきます。

f:id:you1dan:20220111170430p:plain

  • $(EOSSDKSamplesRoot)/AuthAndFriends/Source;
  • $(EOSSDKSamplesRoot)/Shared/Source;
  • $(EOSSDKSamplesRoot)/Shared/Source/Main;
  • $(EOSSDKSamplesRoot)/Shared/Source/Core;
  • $(EOSSDKSamplesRoot)/Shared/Source/Graphics;
  • $(EOSSDKSamplesRoot)/Shared/Source/Graphics/GUI;
  • $(EOSSDKSamplesRoot)/Shared/Source/Input;
  • $(EOSSDKSamplesRoot)/Shared/Source/Utils;
  • $(EOSSDKSamplesRoot)/Shared/External/SDL2/include;
  • $(EOSSDKSamplesRoot)/Shared/External/GLEW/include;
  • $(EOSSDKSamplesRoot)/Shared/External/SDL2/SDL2_ttf/include;
  • $(EOSSDKSamplesRoot)/Shared/External/UTF8-CPP/source;
  • $(EOSSDKIncludes)

上記ですが、もし DirectX のビルド構成に変えると、 $(EOSSDKSamplesRoot)/Shared/Source/Main/Windows;$(EOSSDKSamplesRoot)/Shared/External/DirectXTK/Include が増える代わりに SDL2 用のものが除外されます。

GLEW というのは OpenGL の拡張のようです。一旦深入りはしません。
GLEW: The OpenGL Extension Wrangler Library

追加のライブラリ

リンカーの設定を見ていきます。

f:id:you1dan:20220111170926p:plain

  • $(EOSSDKLibs);
  • $(EOSSDKSamplesRoot)\Shared\External\SDL2\Lib\x64\Release;
  • $(EOSSDKSamplesRoot)\Shared\External\GLEW\lib\Release\x64;
  • $(EOSSDKSamplesRoot)\Shared\External\SDL2\SDL2_ttf\lib\x64;

f:id:you1dan:20220111171239p:plain

  • EOSSDK-Win64-Shipping.lib;
  • SDL2.lib;
  • SDL2main.lib;
  • GlU32.Lib;
  • OpenGL32.Lib;
  • glew32s.lib;
  • SDL2_ttf.lib;

GIU32.libOpenGL32.lib に関しては Windows の一部として入っているものなので、追加のライブラリディレクトリとしては指定していませんでした。実態は以下にあります。

C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86

エントリポイント

int main で検索すると、Shared/Source/Main/SDL/SDLMain.cpp が見つかります。

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();

    //Start up SDL and create window
    if (!Init())
    {
        printf("Failed to initialize!\n");
    }
    else
    {
        //Main loop flag
        bool bQuit = false;

        //Event handler
        SDL_Event Event;

        //Enable text input
        SDL_StartTextInput();

        //While application is running
        while (!bQuit)
        {
            //Handle events on queue
            while (SDL_PollEvent(&Event) != 0)
            {
                bQuit = ProcessSDLEvents(&Event);
            }

            Main->Tick();

            //Update screen
            SDL_GL_SwapWindow(Main->GetWindow());
        }

        //Disable text input
        SDL_StopTextInput();

        //Check if we need to delay shutting down
        Main->OnShutdown();
        while (Main->IsShutdownDelayed())
        {
            //We only update & render (no inputs, no events, etc)
            Main->Tick();

            //Update screen
            SDL_GL_SwapWindow(Main->GetWindow());
        }
    }

    //Free resources and close SDL
    Shutdown();

    Main.reset();

#if defined (_DEBUG) && defined (DUMP_MEM_LEAKS)
    _CrtDumpMemoryLeaks();
#endif // _DEBUG

    return 0;
}

SDL2 関連でいえば SDL_GL_SwapWindow が今の自分ではまだ理解できませんが、描画をしているようです。

そこを除けば、初期化、イベントループ、終了処理という一連の流れが実装されていることが分かります。

ProcessSDLEvents

まずは処理対象のイベントを見ておきます。

void ProcessWindowEvent(SDL_WindowEvent *WinEvent)
{
    switch (WinEvent->event)
    {
        case SDL_WINDOWEVENT_RESIZED:
        case SDL_WINDOWEVENT_SIZE_CHANGED:
        {
            UpdateWindowSize(WinEvent->data1, WinEvent->data2);
            break;
        }
        case SDL_WINDOWEVENT_CLOSE:
        {
            SDL_Event QuitEvent;
            QuitEvent.type = SDL_QUIT;
            SDL_PushEvent(&QuitEvent);
            break;
        }
    }
}

bool ProcessSDLEvents(SDL_Event *Event)
{
    FUIEvent InputEvent = FInput::ProcessSDLEvent(*Event);
    if (InputEvent.GetType() != EUIEventType::None)
    {
        FGame::Get().GetMenu()->OnUIEvent(InputEvent);
    }
    else
    {
        switch (Event->type)
        {
            case SDL_QUIT:
            {
                return true;
            }
            case SDL_WINDOWEVENT:
            {
                ProcessWindowEvent(&Event->window);
                break;
            }
            default:
                break;
        }
    }

    return false;
}

まずは FUIEvent を拾った場合以外についてですが、 SDL_QUIT か Window 関連のイベントを拾っているようです。window が閉じる場合にも SDL_QUIT に変換するというのは常套手段なんでしょうか。

ここから

この先は F が prefix についているクラス FInputFMain を見ていくことになるので、今回はここまでにしておきます。

まとめ

SDL2 のサンプルを前回作っていたので、今回のサンプルもどこから読んでいけばいいのか理解できました。

引き続き理解を深めていければと思います。