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

今回からイベントループに入っていきますが、SDL2 を使った GUI プログラミングに不慣れなので少しずつ見ていきます。

イベントループ部分

int main(int Argc, char* Args[]) {
    // 略

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

上記は main のイベントループ部分のみ抜粋したものです。

ProcessSDLEvents メソッドがイベントハンドルをしているようです。

ProcessSDLEvents

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;
}

FInput::ProcessSDLEvent

ここでは、 SDL_EventFUIEvent に変換しています。詳細は省略しますが、 FUIEvent は以下の種類があり、 SDL_Event の特定の操作イベントが割り当てられます。

enum class EUIEventType : unsigned char
{
    None,
    MousePressed,
    MouseReleased,
    MouseWheelScrolled,
    KeyPressed,
    KeyReleased,
    TextInput,
    CopyText,
    SelectAll,
    PasteText,
    FullscreenToggle,
    SearchText,
    FocusGained,
    FocusLost,
    FriendInviteSent,
    InviteToSession,
    Last = FriendInviteSent
};

else 部分

else 部分は QUIT か Window 操作時に関するものです。 QUIT の場合は true が返却され、イベントループを抜けるようになっています。

InputEvent が None 以外のとき

FGame::Get().GetMenu()->OnUIEvent(InputEvent) が呼び出されています。

前回見たとおりで FGame は多くのゲームの要素を管理しており、 GetMenu では FMenuインスタンスを取得します。

FMenu::OnUIEvent

実際には親クラスである FBaseMenu::OnUIEvent が呼び出されます。

void FBaseMenu::OnUIEvent(const FUIEvent& Event)
{
#ifdef EOS_DEMO_SDL
    // FullScreenToggle に関するコードがあるが省略
    else
#endif
    if (Event.GetType() == EUIEventType::MousePressed)
    {
        for (DialogPtr NextDialog : Dialogs)
        {
            if (NextDialog->CheckCollision(Event.GetVector()))
            {
                NextDialog->OnUIEvent(Event);
            }
            else
            {
                NextDialog->SetFocused(false);
            }
        }
    }
    else
    {
        for (DialogPtr NextDialog : Dialogs)
        {
            NextDialog->OnUIEvent(Event);
        }
    }

    if (AuthDialogs) AuthDialogs->OnUIEvent(Event);
}

Dialogs 配列の内部

FBaseMenu の内部では以下が Dialogs 配列に追加されています。

  • ConsoleDialog
  • FriendsDialog
  • ExitDialog (非表示)
  • PopupDialog (非表示)

また FMenu の内部では以下が追加されています。

  • CustomInvitesDialog

CheckCollision

NextDialog->CheckCollisionDialog::CheckCollision に実装されており、Dialog 内部の Widgets (UI 要素) の座標とマウスポイントの座標がオーバーラップしているかどうかを判定しています。

AuthDialogs

AuthDialogsDialogs 配列に入っていないことからも、少し特別な扱いになっています。

void FBaseMenu::CreateAuthDialogs()
{
    AuthDialogs = std::make_shared<FAuthDialogs>(
        FriendsDialog,
        L"Friends",
        BoldSmallFont->GetFont(),
        SmallFont->GetFont(),
        TinyFont->GetFont());
    
    AuthDialogs->Create();
}

AuthDialogs は生成時に FriendsDialog も受け取るようになっています。

void FBaseMenu::Create()
{
    BackgroundImage->Create();

    TitleLabel->Create();
    TitleLabel->SetFont(BoldLargeFont->GetFont());

    FPSLabel->Create();
    FPSLabel->SetFont(SmallFont->GetFont());

    CreateConsoleDialog();
    CreateAuthDialogs();
    CreateExitDialog();
    CreatePopupDialog();
}
void FMenu::Create()
{
    CreateFriendsDialog();

    // creates console dialog, which is needed for creating custom invites dialog
    FBaseMenu::Create();

    CreateCustomInvitesDialog();
}

上記のように、FriendsDialog だけは FMenu で生成され、その後 FBaseMenuAuthDialog を含む他の Dialog が生成されています。

まとめ

今日はイベントループに入り、イベントをまず受け取っている FMenu の構成要素を眺めました。

次は FMenu の構成要素がどのように UI にセットされているかなど見てみたいと思います。