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

今回は UI の構成要素を見ていきたいと思います。
前回FMenu には親クラスも合わせて以下の Dialog が含まれていることが分かりました。

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

FMenu::UpdateLayout

// custom invite / console frame
#define CUSTOMINVITE_CONSOLE_FRAME_WIDTH   .7f
#define CUSTOMINVITE_CONSOLE_FRAME_HEIGHT  1.f
#define CUSTOMINVITE_CONSOLE_FRAME_SCALE   Vector2(CUSTOMINVITE_CONSOLE_FRAME_WIDTH, CUSTOMINVITE_CONSOLE_FRAME_HEIGHT)

// friends frame
#define FRIENDS_FRAME_WIDTH                    1.f - CUSTOMINVITE_CONSOLE_FRAME_WIDTH
#define FRIENDS_FRAME_HEIGHT               1.f
#define FRIENDS_FRAME_SCALE                    Vector2(FRIENDS_FRAME_WIDTH, FRIENDS_FRAME_HEIGHT)

// custom invite dialog (inside custom invite / console frame)
#define CUSTOMINVITES_DIALOG_PERCENT_X     1.f
#define CUSTOMINVITES_DIALOG_PERCENT_Y     .2f
#define CUSTOMINVITE_DIALOG_SCALE          Vector2(CUSTOMINVITES_DIALOG_PERCENT_X, CUSTOMINVITES_DIALOG_PERCENT_Y)

// console dialog (inside custom invite / console frame)
#define CONSOLE_DIALOG_PERCENT_X           1.f
#define CONSOLE_DIALOG_PERCENT_Y           1.f - CUSTOMINVITES_DIALOG_PERCENT_Y
#define CONSOLE_DIALOG_SCALE               Vector2(CONSOLE_DIALOG_PERCENT_X, CONSOLE_DIALOG_PERCENT_Y)

// freinds dialog (inside friends frame)
#define FRIENDS_USAGE_PERCENT_X                1.f
#define FRIENDS_USAGE_PERCENT_Y                1.f
#define FRIENDS_DIALOG_SCALE               Vector2(FRIENDS_USAGE_PERCENT_X, FRIENDS_USAGE_PERCENT_Y)

void FMenu::UpdateLayout(int Width, int Height)
{
    WindowSize = Vector2((float)Width, (float)Height);
    Vector2 LayoutPositionMarker{ };

    BackgroundImage->SetPosition(LayoutPositionMarker);
    BackgroundImage->SetSize(Vector2((float)Width, ((float)Height) / 2.f));

    // move the layout cursor down 10px, recalculate available percentage
    LayoutPositionMarker += Vector2(10.f, 120.f);
    const Vector2 MainFrameOrigin{ LayoutPositionMarker };
    const Vector2 MainFrameSize{ WindowSize - MainFrameOrigin };

    const Vector2 CustomInviteConsoleFrameOrigin{ MainFrameOrigin };
    const Vector2 CustomInviteConsoleFrameSize{ Vector2::CoeffProduct(CUSTOMINVITE_CONSOLE_FRAME_SCALE, MainFrameSize) };

    const Vector2 CustomInviteDialogSize = Vector2::CoeffProduct(CUSTOMINVITE_DIALOG_SCALE, CustomInviteConsoleFrameSize);
    if (CustomInvitesDialog)
    {
        CustomInvitesDialog->SetSize(CustomInviteDialogSize);
        CustomInvitesDialog->SetPosition(CustomInviteConsoleFrameOrigin);
    }
    LayoutPositionMarker.y += CustomInviteDialogSize.y;

    const Vector2 ConsoleWidgetSize = Vector2::CoeffProduct(CONSOLE_DIALOG_SCALE, CustomInviteConsoleFrameSize);
    if (ConsoleDialog)
    {
        ConsoleDialog->SetSize(ConsoleWidgetSize);
        ConsoleDialog->SetPosition(LayoutPositionMarker);
    }

    LayoutPositionMarker = MainFrameOrigin;
    LayoutPositionMarker.x += CustomInviteConsoleFrameSize.x;

    const Vector2 FriendsFrameOrigin{ LayoutPositionMarker };
    const Vector2 FriendsFrameSize = Vector2::CoeffProduct(FRIENDS_FRAME_SCALE, MainFrameSize);

    const Vector2 FriendsWidgetSize = Vector2::CoeffProduct(FRIENDS_DIALOG_SCALE, FriendsFrameSize);
    if (FriendsDialog)
    {
        FriendsDialog->SetPosition(LayoutPositionMarker);
        FriendsDialog->SetSize(FriendsWidgetSize);
    }

    if (PopupDialog)
    {
        PopupDialog->SetPosition(Vector2((WindowSize.x / 2.f) - PopupDialog->GetSize().x / 2.0f, (WindowSize.y / 2.f) - PopupDialog->GetSize().y));
    }

    if (ExitDialog)
    {
        ExitDialog->SetPosition(Vector2((WindowSize.x / 2.f) - ExitDialog->GetSize().x / 2.0f, (WindowSize.y / 2.f) - ExitDialog->GetSize().y - 200.0f));
    }

    if (AuthDialogs)
    {
        AuthDialogs->UpdateLayout();
    }
}

順番に見ていきます。

WindowSize = Vector2((float)Width, (float)Height);
Vector2 LayoutPositionMarker{ };

引数から得られた WidthHeightWindowSize に入れています。おそらくそのままの意味だと思います。
LayoutPositionMarker を初期化しています。このベクトルに、各 Dialog の配置のベース情報を更新していくようです。

BackgroundImage->SetPosition(LayoutPositionMarker);
BackgroundImage->SetSize(Vector2((float)Width, ((float)Height) / 2.f));

BackgroundImageDialog ではないのでここまでで紹介していませんが FBaseMenu のコンストラクタで以下のように初期化されます。

BackgroundImage = std::make_shared<FSpriteWidget>(Vector2(0.f, 0.f), Vector2(1024.f, 384.f), DefaultLayer, L"Assets/menu_background.dds");

画像としては以下の部分になります。 FSpriteWidget のコンストラクタの第一引数は「位置」であり、第二引数は「サイズ」なので、この背景画像は (0,0) の位置に配置され、幅 1024px, 高さ 384px ということになります。ただ、以下のアプリケーションの画像部分はそこまでの高さはありません。あくまでこの Widget のサイズです。

f:id:you1dan:20220114212456p:plain

ちなみに、BackgroundImage に対して SetPositionSetSize をしていますが、他にもタイトルラベル等もあり、それらもある程度 BackgroundImage が (0,0) に配置される前提でハードコードされているようです。(Window サイズも最小サイズが 1024x768 になっています)

// move the layout cursor down 10px, recalculate available percentage
LayoutPositionMarker += Vector2(10.f, 120.f);
const Vector2 MainFrameOrigin{ LayoutPositionMarker };
const Vector2 MainFrameSize{ WindowSize - MainFrameOrigin };

続いて MainFrame ですが、これは MainFrame というものが存在しているわけではなく BackgroundImage エリアとの区別のために存在しているようです。配置としては、 LayoutPositionMarker はこの直前で (0, 0) なので、ここ MainFrameOrigin は (10, 120) を起点としていることになります。

f:id:you1dan:20220114214055p:plain

上図のように、以降の常時表示される要素は MainFrame から計算されることになります。

const Vector2 CustomInviteConsoleFrameOrigin{ MainFrameOrigin };
const Vector2 CustomInviteConsoleFrameSize{ Vector2::CoeffProduct(CUSTOMINVITE_CONSOLE_FRAME_SCALE, MainFrameSize) };

const Vector2 CustomInviteDialogSize = Vector2::CoeffProduct(CUSTOMINVITE_DIALOG_SCALE, CustomInviteConsoleFrameSize);
if (CustomInvitesDialog)
{
    CustomInvitesDialog->SetSize(CustomInviteDialogSize);
    CustomInvitesDialog->SetPosition(CustomInviteConsoleFrameOrigin);
}
LayoutPositionMarker.y += CustomInviteDialogSize.y;

Vector2::CoeffProduct は成分ごとの積です。

#define CUSTOMINVITE_CONSOLE_FRAME_WIDTH    .7f
#define CUSTOMINVITE_CONSOLE_FRAME_HEIGHT  1.f
#define CUSTOMINVITE_CONSOLE_FRAME_SCALE   Vector2(CUSTOMINVITE_CONSOLE_FRAME_WIDTH, CUSTOMINVITE_CONSOLE_FRAME_HEIGHT)

// custom invite dialog (inside custom invite / console frame)
#define CUSTOMINVITES_DIALOG_PERCENT_X     1.f
#define CUSTOMINVITES_DIALOG_PERCENT_Y     .2f
#define CUSTOMINVITE_DIALOG_SCALE          Vector2(CUSTOMINVITES_DIALOG_PERCENT_X, CUSTOMINVITES_DIALOG_PERCENT_Y)

CustomInviteConsoleFrame は、CustomInviteDialog とこの後で出てくる ConsoleDialog の両方の領域です。
そのサイズは高さは MainFrame と同じ、幅は 0.7 倍されています。以下のような領域ですね。

続いて CustomInviteDialogCustomInviteConsoleFrame に対して幅は同じ、高さは 0.2 倍となっています。
まとめると以下のような感じです。

f:id:you1dan:20220114215747p:plain

LayoutPositionMarker.y += CustomInviteDialogSize.y; 最後に、LayoutPositionMarker の Y 座標を CustomInviteDialog のそれで更新しています。

const Vector2 ConsoleWidgetSize = Vector2::CoeffProduct(CONSOLE_DIALOG_SCALE, CustomInviteConsoleFrameSize);
if (ConsoleDialog)
{
    ConsoleDialog->SetSize(ConsoleWidgetSize);
    ConsoleDialog->SetPosition(LayoutPositionMarker);
}

LayoutPositionMarker = MainFrameOrigin;
LayoutPositionMarker.x += CustomInviteConsoleFrameSize.x;
// console dialog (inside custom invite / console frame)
#define CONSOLE_DIALOG_PERCENT_X           1.f
#define CONSOLE_DIALOG_PERCENT_Y           1.f - CUSTOMINVITES_DIALOG_PERCENT_Y
#define CONSOLE_DIALOG_SCALE               Vector2(CONSOLE_DIALOG_PERCENT_X, CONSOLE_DIALOG_PERCENT_Y)

続いて、ConsoleDialog ですが、先程の内容からの延長ですが、 CustomInviteConsoleFrame に対して、幅は同じですが、高さが CustomInviteDialog と分割するような指定になっています。

注意すべきは、 LayoutPositionMarker の値が、 MainFrame の Y 座標、CustomInviteConsoleFrame の X 座標になっています。

f:id:you1dan:20220114220314p:plain

const Vector2 FriendsFrameOrigin{ LayoutPositionMarker };
const Vector2 FriendsFrameSize = Vector2::CoeffProduct(FRIENDS_FRAME_SCALE, MainFrameSize);

const Vector2 FriendsWidgetSize = Vector2::CoeffProduct(FRIENDS_DIALOG_SCALE, FriendsFrameSize);
if (FriendsDialog)
{
    FriendsDialog->SetPosition(LayoutPositionMarker);
    FriendsDialog->SetSize(FriendsWidgetSize);
}
// friends frame
#define FRIENDS_FRAME_WIDTH                    1.f - CUSTOMINVITE_CONSOLE_FRAME_WIDTH
#define FRIENDS_FRAME_HEIGHT               1.f
#define FRIENDS_FRAME_SCALE                    Vector2(FRIENDS_FRAME_WIDTH, FRIENDS_FRAME_HEIGHT)

// freinds dialog (inside friends frame)
#define FRIENDS_USAGE_PERCENT_X                1.f
#define FRIENDS_USAGE_PERCENT_Y                1.f
#define FRIENDS_DIALOG_SCALE               Vector2(FRIENDS_USAGE_PERCENT_X, FRIENDS_USAGE_PERCENT_Y)

続いて FriendsFrameFriendsDialog です。
FriendsDialogFriendsFrame に対して幅、高さともに等倍なので同じサイズです。
FriendsFrameMainFrame に対して CustomeInviteConsoleFrame と幅を分け合う形になっています。

上述の通り、 LayoutPositionMarkerCustomInviteConsoleFrame の右上端に移動しているので、そこから配置されることになります。

if (PopupDialog)
{
    PopupDialog->SetPosition(Vector2((WindowSize.x / 2.f) - PopupDialog->GetSize().x / 2.0f, (WindowSize.y / 2.f) - PopupDialog->GetSize().y));
}

if (ExitDialog)
{
    ExitDialog->SetPosition(Vector2((WindowSize.x / 2.f) - ExitDialog->GetSize().x / 2.0f, (WindowSize.y / 2.f) - ExitDialog->GetSize().y - 200.0f));
}

PopupDialogExitDialog に関しては常時表示されているわけではなく、なにかの通知の場合に表示されます。なので、それぞれ、WindowSize に対して計算されています。

PopupDialog ExitDialog
f:id:you1dan:20220114221348p:plain f:id:you1dan:20220114221400p:plain

AuthDialog

if (AuthDialogs)
{
    AuthDialogs->UpdateLayout();
}

最後に AuthDialog ですが、見ての通りでこれだけ UpdateLayout が呼び出されています。

AuthDialog は前回も触れましたが FriendsDialog に依存しており、内部では ParentDialog として保持しています。
加えて、それに対して Depth も浅い部分に AuthDialog の UI 要素を描画しているため FriendsDialog 内に AuthDialog の内容が描画されることになります。

まとめ

サンプルアプリケーションの UI 要素について、どのように配置されているかをざっと眺めました。

次は、今回のサンプルのメインの部分である AuthDialog の方に入っていければと思います。