【ScriptAPI】簡単なワールドエディットを作ってみよう (完成品有)

スポンサーリンク

はじめに

このブログでは、統合版のScriptAPIを使用して簡単なワールドエディット機能を作成します。
バージョンは ScriptAPI 2.0.0-alpha (1.14.0-beta) です。

簡単ワールドエディットの概要

機能:ワールド内で指定した2つの座標の範囲を、特定のブロックで埋め立てる (fill)。
使用者:タグ「worldEdit」を持つプレイヤー。

手順:
1.座標の選択:
  ・座標1:木の斧でブロックを破壊することで、始点座標を設定する。
  ・座標2:木の斧でブロックに触れることで、終点座標を設定する。

2.コマンドの実行:
  ・特定のコマンド(;set <blockId>)を入力すると、指定した2つの座標の範囲を指定したブロックで埋め立てる。

プログラムの作成

1.必要なクラスのインポート
まず、以下のクラスをインポートします。

import { BlockTypes, system, world } from "@minecraft/server";

2.座標1を指定する処理
プレイヤーが木の斧でブロックを破壊すると、破壊したブロックの座標を「座標1」として選択できるようにします。

ブロック破壊前イベントで、破壊したブロックの座標をプレイヤーのダイナミックプロパティに保存しています。

/* 座標1を指定するプログラム */

// ビフォーイベント:プレイヤーがブロックを破壊する前に発生
world.beforeEvents.playerBreakBlock.subscribe(ev => {
    const { player, itemStack, block } = ev;
    const { x, y, z } = block.location;

    // "worldEdit" タグを持っている かつ 破壊アイテムが木の斧のとき
    if (player.hasTag("worldEdit") && itemStack && itemStack.typeId === "minecraft:wooden_axe") {

        // 破壊をキャンセルする
        ev.cancel = true;
        // プレイヤーのダイナミックプロパティ "pos1" にブロックの座標を保存する
        player.setDynamicProperty("pos1", block.location);

        // ビフォーイベント内では setActionBar() が動かないため system.run() で遅延実行する
        system.run(() => {
            // ブロック座標をアクションバーに表示
            player.onScreenDisplay.setActionBar(`座標1を (${x}, ${y}, ${z}) に設定しました`);
        });
    }
});

3.座標2を指定する処理
プレイヤーが木の斧でブロックに触れると、そのブロックの座標を「座標2」として選択できるようにします

ブロック接触前イベントで、触れたブロック座標をプレイヤーのダイナミックプロパティに保存しています。

/* 座標2を指定するプログラム */

// ビフォーイベント:プレイヤーがブロックに触れる前に発生
world.beforeEvents.playerInteractWithBlock.subscribe(ev => {
    const { player, itemStack, block } = ev;
    const { x, y, z } = block.location;

    // "worldEdit" タグを持っている かつ 接触アイテムが木の斧のとき
    if (player.hasTag("worldEdit") && itemStack && itemStack.typeId === "minecraft:wooden_axe") {

        // 破壊をキャンセルする
        ev.cancel = true;
        // プレイヤーのダイナミックプロパティ "pos2" にブロックの座標を保存する
        player.setDynamicProperty("pos2", block.location);

        // ビフォーイベント内では setActionBar() が動かないため system.run() で遅延実行する
        system.run(() => {
            // ブロック座標をアクションバーに表示
            player.onScreenDisplay.setActionBar(`座標2を (${x}, ${y}, ${z}) に設定しました`);
        });
    }
});

4.fill関数の作成
指定した範囲を指定のブロックで埋め立てるfill関数を作成します。

範囲内のブロック1つ1つを、forで巡回し、setType()でブロックを設置します。
さらに設置したブロック数を返り値として返します。

/*
 fillを行う関数
   dimension - ディメンション (overworld, nether, the_end)
   start - 始点の座標
   end - 終点の座標
   blockId - 設置ブロックID
   返り値 - 設置したブロック数
*/
function fill(dimension, start, end, blockId) {
    const minX = Math.min(start.x, end.x);
    const maxX = Math.max(start.x, end.x);
    const minY = Math.min(start.y, end.y);
    const maxY = Math.max(start.y, end.y);
    const minZ = Math.min(start.z, end.z);
    const maxZ = Math.max(start.z, end.z);

    // 設置数の初期化
    let count = 0;

    try {
        for (let x = minX; x <= maxX; x++) {
            for (let y = minY; y <= maxY; y++) {
                for (let z = minZ; z <= maxZ; z++) {
                    const location = { x, y, z };

                    try {
                        // ブロックの設置
                        dimension.setBlockType(location, blockId);
                        // 設置数を増加させる
                        count++;
                    } catch {
                        // 非読込チャンクの場合エラーが発生するため無視する
                    }
                }
            }
        }
    } catch {
        // 膨大な範囲の場合エラーが発生するため無視する
    }
    return count;
}

5.埋め立てコマンドを実行する処理
チャット欄でコマンドを入力すると、指定されたブロックIDで範囲を埋め立てるようにします。

チャット送信前イベントで、メッセージを受け取り、埋め立てコマンドを実行します。
選択された2つの座標を、プレイヤーのダイナミックプロパティから取得して、先ほど作成したfill()に渡して実行します。

/* 座標1から座標2の範囲をfillするプログラム */

// ビフォーイベント:プレイヤーがチャットを送る前に発生
world.beforeEvents.chatSend.subscribe(ev => {
    const { sender: player, message } = ev;

    // メッセージを空白で分割し配列にする
    const messages = message.split(" ");
    const command = messages[0]; // コマンド
    const blockId = messages[1];

    // "worldEdit" タグを持っている かつ コマンドが ";set" のとき
    if (player.hasTag("worldEdit") && command === (";set")) {
        // メッセージ送信をキャンセル
        ev.cancel = true;

        // プレイヤーのダイプロ "pos1" と "pos2" から座標を取得
        const pos1 = player.getDynamicProperty("pos1");
        const pos2 = player.getDynamicProperty("pos2");

        // pos1 と po2 があるとき
        if (pos1 && pos2) {

            // ブロックID が入力されている場合
            if (blockId) {
                // 入力されたブロックIDが存在する場合
                if (BlockTypes.get(blockId)) {

                    system.run(() => { // fill() を実行するために遅延実行

                        // fill() を呼び出して埋め立て、設置数を返り値として受け取る
                        const count = fill(player.dimension, pos1, pos2, blockId);
                        // 設置メッセージを送る
                        player.sendMessage(`[WE] ${count}個のブロックで満たしました`);
                    });

                } else { // 入力されたブロックIDが存在しない場合
                    player.sendMessage(`§c[WE] ブロックID "${blockId}" が見つかりません`);
                }

            } else { // ブロックID が入力されていない場合
                player.sendMessage("§c[WE] ブロックID がありません");
            }

        } else if (!pos1 && pos2) { // pos1 がないとき
            player.sendMessage("§c[WE] 座標1 がありません");
        } else if (pos1 && !pos2) { // pos2 がないとき
            player.sendMessage("§c[WE] 座標2 がありません");
        } else { // pos1 と po2 がないとき
            player.sendMessage("§c[WE] 座標1 座標2 がありません");
        }
    }
});

完成品

完成品はこちらです

動作チェック

プログラムが正しく動作するか確認しましょう。

  1. タグ「worldEdit」を自身に付与する。
  2. 木の斧で、ブロック破壊、ブロック接触を行う。
  3. チャット欄で「;set 好きなブロックID」を実行する。
%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-09-15-014935.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-09-15-014952.jpg
%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-09-15-015019.jpg
%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-09-15-015041.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-09-15-015056-1.jpg
※投稿記事に含まれるファイルやリンクにより発生した被害についてクラフターズコロニーは責任を取りません
投稿通報

コメント

コメント通報