【統合版アドオン】part-3│「カスタムUI」、ScriptAPIだけで作れます!

スポンサーリンク

初めに

どうも餅わらびすとです。この記事では究極の機能、ScriptAPI(gametestとも言う)でのカスタムUIを作る方法について解説します。

どんなことが出来るようになるかというと、こんな感じ。

%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2024-03-27-131900.png
%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2024-03-26-183816.png

見るからに使えそうなUI!

この記事ではこのUIを使えるようになることを目標にします。
「まずScriptAPIについて知らない」、という方はこちらの、「【統合版アドオン】part-1│究極の機能、ScriptAPIを徹底解説!」で基本的な使い方を解説しているので、見ていない方は見てください。ここでは、リンクの記事の内容を理解していることを前提にしているので、記事を見た後に来るのを推奨します。

この記事が役に立つものになれば嬉しいです。


~今回の説明に使用させていただいたサイト~
https://jaylydev.github.io/scriptapi-docs/#documentation


~前回~
https://minecraft-mcworld.com/70279/

必要なクラスのインポート

始める前に前提として、UIを作るのに必要なクラスを@minecraft/server-uiモジュールからインポートします。

import { ActionFormData, ModalFormData } from "@minecraft/server-ui";

今回インポートするのはこの2つです。
事前にマニフェストで使うモジュールを指定するのも忘れないでください。

UIの構築

UIは、作りたいUIのクラスでインスタンスを生成することで作ることが出来ます。

//ActionFormの場合
const form = new ActionFormData();

//ModalFormの場合
const form = new ModalFormData();

newというのは、クラスからインスタンスを生成する際に使う演算子です。
ActionFormData();の部分は、「【統合版アドオン】part-3.5│クラスからインスタンスを生成する」で似たようなものを解説しています。

UIに要素をいれる

UIを構築した次には、UIを構成する要素を決めます。

そういっても、決め方は他のクラスの処理とあんまり変わりません。

%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2024-03-26-224400.png

ActionFormDataではこんなメソッドがあったりしますが、これもそのままです。
1つ違うところを言えば、これらの構成メソッドはActionFormDataを返すので、

const form = new ActionFormData();
form.title("タイトル")
    .body("ボディ")
    .button("ボタン")

のようにメソッドを連続して書くことができます。
ModalFormDataも同じです。

ちなみにActionFormDataでは、buttonの第2引数にテクスチャのパスを入れることでボタンの横に画像を表示させることが可能です。見やすくしたいときに使ってください。
例:.button(“リンゴ”, “textures/items/apple”);

%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2024-03-27-131451.jpg


あとは実際に色々試してみてください。

UIを表示する

UIを作っても、プレイヤーに表示しなければ意味はありません。

作ったUIは、

フォーム.show(プレイヤー).then(仮引数 => {...});

で表示することが出来ます。

%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88-2024-03-26-231112-1.png

showの返り値を見てみると、メソッドにthenがないじゃないか!となるかもしれませんが、これはthenがPromiseのオブジェクトにあるメソッドだからです。
Promiseは複雑になるのでここでは解説しませんが(というか解説できない)、知りたければここを見てください。

thenについては、こっちのサイトにいろいろ載っているので興味があれば見てみてください。

複雑そうなこといってますが、とりあえずは前回のイベントについての記事と同じ要領で、仮引数のところにActionFormResponseが入るものだと考えていてください。

UIごとの処理

ActionForm

ActionFormDataでは、ActionFormResponseのselectionというプロパティに、押したボタンのインデックス(順番)の数値が入っています。0から始まるものです。

例えば、

const form = new ActionFormData();
form.title("タイトル")
    .button("リンゴ")
    .button("ゴリラ")

というフォームだったとしたら、

「リンゴ」を押したらselectionには0が入り、「ゴリラ」を押したらselectionに1が入るというイメージです。
他にもプロパティはありますが、そこまで複雑でない内容なので割愛します。

ModalForm

ModalFormDataでは、ModalFormResponseのformValuesというプロパティに、設定したUI要素の順でプレイヤーが選択した値が並んだ配列が入っています。

例えば、

const form = new ModalFormData();
form.title("タイトル")
    .textField("テキストフィールド","テキストが入れられるよ")
    .toggle("トグル")

というフォームで、プレイヤーがテキストフィールドに「こんにちは」と入力して、トグルをONにしていたとしたら、
formValuesには、[“こんにちは”, true]という配列が入っています。

これにもプロパティはありますが、そこまで複雑でない内容なので割愛します。

サンプルコード

ボタン1を押したら0 0 0に、ボタン2を押したら20 20 20に、ボタン3を押したら行きたい座標に自由に行けるフォームを出すフォームを、チャットへ.formと送信したらプレイヤーに出すコード

import { world, system } from "@minecraft/server";
import { ActionFormData, ModalFormData } from "@minecraft/server-ui";

function showActionForm(player) {
    const form = new ActionFormData()
        .title("タイトル")
        .body("ボディ")
        .button("ボタン1")
        .button("ボタン2")
        .button("ボタン3")
        .show(player).then(re => {
            if (re.cancelationReason === "UserBusy") showActionForm(player);
                switch (re.selection) {
                    case 0:
                        player.teleport({ x:0, y:0, z:0 });
                        break;
                    case 1:
                        player.teleport({ x:20, y:20, z:20 });
                        break;
                    case 2:
                        showModalForm(player);
                        break;
                }
        });
}

function showModalForm(player) {
    const form = new ModalFormData()
        .title("タイトル")
        .textField("x", "x座標を入力してください")
        .textField("y", "y座標を入力してください")
        .textField("z", "z座標を入力してください")

        .show(player).then(re => {
            player.teleport({ x: Number(re.formValues[0]), y: Number(re.formValues[1]), z: Number(re.formValues[2]) });
        });
}

world.beforeEvents.chatSend.subscribe(ev => {
    if (ev.message === ".form") {
        ev.cancel = true;
        system.run(() => { showActionForm(ev.sender); });
    }
});

最後に

ここまで読んでいただきありがとうございます!役に立ったと思ってもらえたら嬉しいです。

なにか質問があるときはこちらのディスコードサーバーから受け付けています。どんな人も大歓迎!一応ここでも受け付けますが内容が複雑になると判断した質問はサーバーでしか受け付けません。

まだScriptAPIについて知らないことも少なくないので、間違っているところがあれば教えてほしいです。

それでは良いアドオンライフを!

>> 次回の記事

※投稿記事に含まれるファイルやリンクにより発生した被害についてクラフターズコロニーは責任を取りません
投稿通報

コメント

  1. switchのcaseとの間が全角空白になってます。

    • 報告ありがとうございます。
      修正致しました。

  2. ブロックをクリックしたら、モーダルフォームを表示するコードはどう書けばいいですか?

    • ページにもある通り、そのような質問はこちらでは受け付けておりません

  3. 自分用メモ

    part-2 世界のイベントを自在にコントロールする
    https://minecraft-mcworld.com/70279/

    part-3.5 クラスからインスタンスを生成する
    https://minecraft-mcworld.com/70591/

  4. サンプルコードは、Index.jsに入れますか?それともmain.jsに入れますか?

    • 好みですね
      manifest.jsonで指定したファイルにさえ書けば実行できます
      別ファイルでも指定ファイルから取り込めば可能

コメント通報