【Unity】UI Toolkitを使用してターン制コマンドバトルのUIを作った

環境

  • Unity 2021.311f1
  • Windows 11 Home

UXMLドキュメント&スタイルシート

UXMLドキュメント

Assets/UIDocuments/Battle.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
    <Style src="project://database/Assets/UIDocuments/Battle.uss" />
    <ui:VisualElement name="canvas" class="canvas">
        <ui:VisualElement name="PlayerStatusWindow" class="window playerStatusWindow">
            <ui:Label text="ゆうしゃ" name="PlayerName" class="windowLabel" />
            <ui:GroupBox name="HPContainer" class="statsContainer hpContainer">
                <ui:Label text="HP" name="HPLabel" />
                <ui:Label text="68" name="HP" />
            </ui:GroupBox>
            <ui:GroupBox name="MPContainer" class="statsContainer">
                <ui:Label text="MP" name="MPLabel" />
                <ui:Label text="75" name="MP" />
            </ui:GroupBox>
            <ui:GroupBox name="LevelContainer" class="statsContainer">
                <ui:Label text="Lv" name="LevelLabel" />
                <ui:Label text="22" name="Level" />
            </ui:GroupBox>
        </ui:VisualElement>
        <ui:VisualElement name="MessageWindow" class="window messageWindow">
            <ui:Label name="MessageText" class="messageText" />
        </ui:VisualElement>
        <ui:VisualElement name="CommandWindow" class="window commandWindow">
            <ui:Label text="コマンド" name="CommandLabel" class="windowLabel" />
            <ui:GroupBox name="Command1" class="command commandAttack">
                <ui:Label text="▶" name="CommandCursor1" class="commandCursor" />
                <ui:Button text="こうげき" name="CommandButton1" class="commandButton" />
            </ui:GroupBox>
            <ui:GroupBox name="Command2" class="command">
                <ui:Label text="▶" name="CommandCursor2" class="commandCursor" />
                <ui:Button text="じゅもん" name="CommandButton2" class="commandButton" />
            </ui:GroupBox>
            <ui:GroupBox name="Command3" class="command">
                <ui:Label text="▶" name="CommandCursor3" class="commandCursor" />
                <ui:Button text="ぼうぎょ" name="CommandButton3" class="commandButton" />
            </ui:GroupBox>
            <ui:GroupBox name="Command4" class="command">
                <ui:Label text="▶" name="CommandCursor4" class="commandCursor" />
                <ui:Button text="どうぐ" name="CommandButton4" class="commandButton" />
            </ui:GroupBox>
        </ui:VisualElement>
        <ui:VisualElement name="DetailCommandWindow" class="window detailCommandWindow">
            <ui:GroupBox name="DetailCommandLabelContainer" class="detailCommandLabelContainer">
                <ui:Label text="ゆうしゃのじゅもん" name="DetailCommandLabel" class="detailCommandLabel" />
            </ui:GroupBox>
            <ui:GroupBox name="DetailCommandContainer" class="detailCommandContainer">
                <ui:GroupBox name="DetailCommand1" class="command detailCommand">
                    <ui:Label text="▶" name="DetailCommandCursor1" class="commandCursor" />
                    <ui:Button text="ファイア" name="DetailCommandButton1" class="commandButton" />
                </ui:GroupBox>
                <ui:GroupBox name="DetailCommand2" class="command detailCommand">
                    <ui:Label text="▶" name="DetailCommandCursor2" class="commandCursor" />
                    <ui:Button text="アイス" name="DetailCommandButton2" class="commandButton" />
                </ui:GroupBox>
                <ui:GroupBox name="DetailCommand3" class="command detailCommand">
                    <ui:Label text="▶" name="DetailCommandCursor3" class="commandCursor" />
                    <ui:Button text="パワーアップ" name="DetailCommandButton3" class="commandButton" />
                </ui:GroupBox>
                <ui:GroupBox name="DetailCommand4" class="command detailCommand">
                    <ui:Label text="▶" name="DetailCommandCursor4" class="commandCursor" />
                    <ui:Button text="ヒール" name="DetailCommandButton4" class="commandButton" />
                </ui:GroupBox>
            </ui:GroupBox>
        </ui:VisualElement>
        <ui:VisualElement name="CostWindow" class="costWindow window">
            <ui:GroupBox name="CostLabelContainer" class="costLabelContainer">
                <ui:Label text="しょうひMP" name="CostLabel" class="costLabel" />
            </ui:GroupBox>
            <ui:GroupBox name="CostContainer" class="costContainer">
                <ui:GroupBox name="Cost" class="cost">
                    <ui:Label text="3" name="CostPoint" />
                    <ui:Label text="/" name="Separator" />
                    <ui:Label text="75" name="HavePoint" />
                </ui:GroupBox>
            </ui:GroupBox>
        </ui:VisualElement>
        <ui:VisualElement name="EnemyImage" class="enemyImage" />
    </ui:VisualElement>
</ui:UXML>

スタイルシート

Assets/UIDocuments/Battle.uss
* {
    color: white;
    font-size: 20px;
    -unity-font-style: bold;
}

.canvas {
    width: 100%;
    height: 100%;
    background-color: black;
    position: absolute;
    visibility: visible;
}

.window {
    background-color: black;
    border-color: white;
    border-width: 3px;
    border-radius: 4px 4px 4px 4px;
}

.playerStatusWindow {
    width: 120px;
    position: absolute;
    left: 5%;
    top: 5%;
    right: 80%;
    bottom: 60%;
}

.windowLabel {
    position: absolute;
    left: 50%;
    top: -20px;
    background-color: black;
    padding-right: 5px;
    -unity-text-align: upper-center;
    margin-right: auto;
    margin-left: auto;
    right: auto;
    translate: -50% 0;
    padding-left: 5px;
    align-items: flex-end;
}

.statsContainer {
    flex-direction: row;
    height: 25%;
    padding: 0 5% 0 5%;
    align-items: center;
    justify-content: space-between;
}

.hpContainer {
    margin-top: 10%;
}

.messageWindow {
    right: 25%;
    left: 25%;
    top: 63%;
    bottom: 5%;
    position: absolute;
    align-items: flex-end;
    justify-content: flex-start;
    flex-wrap: wrap-reverse;
}

.messageText {
    left: -2.5%;
    top: 2.5%;
    width: 95%;
    height: 95%;
    align-items: auto;
    padding: 0;
    white-space: normal;
}

.commandWindow {
    position: absolute;
    left: 5%;
    top: 60%;
    right: 77%;
    bottom: 8%;
}

.command {
    align-items: center;
    flex-direction: row;
    height: 20%;
    justify-content: flex-start;
    margin: 0;
    padding: 0 5% 0 5%;
}

.commandAttack {
    margin-top: 10%;
}

.commandButton {
    border-color: black;
    background-color: black;
    padding: 0 2px 0 0;
    -unity-text-align: lower-left;
    top: auto;
    cursor: initial;
    scale: 1 1;
    margin: 0;
}

.commandCursor {
    margin-bottom: 30px;
    visibility: hidden;
}

.detailCommandWindow {
    position: absolute;
    left: 20%;
    top: 65%;
    right: 45%;
    bottom: 6%;
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
}

.detailCommandLabelContainer {
    width: 100%;
    height: 30%;
    margin: 0;
    padding: 0;
    border-color: white;
    border-bottom-width: 2px;
}

.detailCommandLabel {
    -unity-text-align: middle-center;
    white-space: normal;
}

.detailCommandContainer {
    padding-left: 0;
    padding-top: 0;
    flex-direction: row;
    align-items: stretch;
    flex-wrap: wrap;
    width: 100%;
    height: 70%;
    margin: 0;
    padding-bottom: 0;
}

.detailCommand {
    margin: 8px 0 0 4px;
    padding: 0;
    height: 35%;
    width: 46%;
}

.costWindow {
    position: absolute;
    left: 53%;
    top: 75%;
    right: 30%;
    bottom: 6%;
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
}

.costLabelContainer {
    width: 100%;
    height: 43%;
    margin: 0;
    padding: 0;
    border-bottom-color: white;
    border-bottom-width: 2px;
}

.costLabel {
    -unity-text-align: middle-center;
    white-space: normal;
    padding: 0;
}

.costContainer {
    flex-direction: column;
    align-items: flex-end;
    flex-wrap: wrap-reverse;
    justify-content: center;
    width: 100%;
    height: 59%;
    margin: 0;
    padding: 0;
}

.cost {
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
    flex-direction: row;
    align-items: center;
    justify-content: center;
}

.enemyImage {
    position: absolute;
    left: 30%;
    top: 10%;
    right: 30%;
    bottom: 40%;
    background-image: url('project://database/Assets/Images/dragon.png');
}

Unityで表示

プロジェクトを作成

UXMLドキュメント及びスタイルシートを作成

Assets/UIDocumentsフォルダを作成し、上記Battle.uxmlとBattle.ussをコピー。

PanelSettingsを作成

Projectウィンドウ上で右クリック→[Create]>[UI Toolkit]>[Panel Settings Asset]を選択。

Scale Modeを「Scale With Screen Size」

Reference Resolution を X:800 Y450に設定。

敵の画像を追加

いらすとやからドラゴンのイラストをダウンロードしAssets/Images/dragon.pngとして保存。

シーンにUIDocumentを追加

Hierarchyウィンドウ上で右クリック→[Create Empty]を選択。

作成したゲームオブジェクトに「UI Document」をアタッチし、先ほど作成したPanel SettingsとUXMLドキュメントをプロパティにセット。

UI Toolkitを使用してみた感想

良かった点

  • ブラックボックスになりやすいUI部分をコードで管理できる。
    → コードなのでGithub上でも確認できる、それによってレビューもしやすい。
  • UIBuilderを使用してGUIで作成することも可能。
    → 初心者でも始めやすい。
    → 設定する必要がないのにデフォルト値で設定したり、インラインスタイルを意図せず作成する等が起きがちなので注意。

悪かった点

  • まだ動作が不安定な部分がある。
    → 親要素を非表示にしたのに子要素が表示されっぱなし、文字が一部だけ太字になってない等
  • C#と連携するためにはコードを書く必要がある。
    → プロパティやUnityEventをインスペクターから使用するのと比べるとコード量は結構増える。
    → メンバーがUniTask、UniRxを使用できるとやりやすいかも。

参考文献

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です