はじめに
このブログでは、統合版の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 がありません");
}
}
});
完成品
完成品はこちらです
動作チェック
プログラムが正しく動作するか確認しましょう。
- タグ「worldEdit」を自身に付与する。
- 木の斧で、ブロック破壊、ブロック接触を行う。
- チャット欄で「;set 好きなブロックID」を実行する。
コメント