はじめに
みなさん、こんにちは!
突然ですが、「API Script」をご存じでしょうか?
「API Script」というのは、マインクラフトの統合版において、プログラミング言語(Javascript)を用いてゲームシステムをカスタマイズできる機能のこと。Javascriptの知識さえあれば、マインクラフトのシステムをある程度いじることができるんです。普通のゲームはそんなことできません。マインクラフトのこういうところが私は好きです。
これは、APIスクリプト(ScriptAPI)の一例で、「ジャンプしたらkillコマンドが発動する」ことを意味しています。このようにしてゲームシステムに直接干渉するわけですね。
さて、本稿では、この「API Script」を使ったアドオンの制作において、ある程度よく用いられるフレーズを備忘録もかねて5つまとめてみたいと思います。あくまで「経験上」よく使われるか否かが論点であり、主観が多分に含まれていることに予めご了承ください。
APIスクリプトの基本
本記事ではAPIスクリプトの基本事項については触れません。APIスクリプトの概要や基本事項、事前準備などについてはこちらの記事で詳細にまとめております。APIスクリプトの基本的な情報を知りたい方はまずこちらの記事を参照し、後で本記事に戻ることをお勧めします。
以下では、APIスクリプトの基本を理解なさっていて、事前準備が完了していることを前提にお話しします。
本題
それでは、APIスクリプトの記述においてよく使われるフレーズをまとめます。
import文
API Scriptはimport文から始まります。これがなければただのjsファイルです。具体的には:
import * as server from '@minecraft/server';
のように記述し、これによって、マインクラフトの世界に干渉できるようになります。今のは一例で、人によっては、
from '@minecraft/server' import {world,system};
のように書くこともあるらしいですね。好みが別れるところです。
言うまでもないですが、ここで同時に変数の宣言を行っています。以下で「server」という変数が突拍子もなく出てきますが、ここで宣言されたものです。
毎ティック実行
コマンドブロックに「常時実行」というモードがありますよね。これと同じように、常時プログラムを動かしたいときに重宝する関数があります。
server.system.runInterval(() => {
//ここに内容を記述
});
それがsystemクラスのrunInterval()です。これは、関数と間隔(int)を引数とすることで、名前の通り、一定周期で関数を実行することができます。上のように間隔を明記しない場合は常に実行します。常にプレイヤーの動きを監視するのにも使えます。ジャンプしたらkillされるAPI Script:
にもこの関数が使われていますね。
ちなみに、「常時実行」と言えば、functionsファイル内のtick.jsonを想起させます。ご存じのない方に向けて一応説明しますと、tick.jsonはワールド内で常時実行される関数スクリプト(一連のコマンド群で、mcfunction形式で記述されたもの)を定義します。このtick.jsonを用いることでも簡単に常時実行を達成することはできますが、こちらはあらかじめ定義されたコマンドしか使えないので一長一短です。必要に応じてrunInterval()とtick.jsonを使い分ける感じですね。
各プレイヤーについて実行
マインクラフトの主人公は、プレイヤーです。このプレイヤーを主体として何かを実行したいときもありますよね。そんな時に重宝するフレーズがあります。
for (const player of server.world.getAllPlayers()){
//ここに内容を記述
}
それが、
for (const player of server.world.getAllPlayers())
というもの。worldクラスのgetAllPlayers()関数は、ワールド内にいる全プレイヤーを取得し、配列形式で出力します。したがって、各プレイヤーについて何かを処理したいときは、これとfor文を組み合わせれば良いのです。ちなみに、プレイヤーの名前を取得したいときは、player.nameでOKです。
for文の中では、同時に変数playerを宣言しています。このplayerは、playerクラスを有し、コマンドを実行したり(注:下記も参照のこと)、イベント処理をしたりと大きな役割を担います。
コマンド実行
マインクラフトのコマンドは非常に優秀で、大抵のことはコマンドで成し遂げられます。そんなコマンドをAPI Scriptの味方につければ正に鬼に金棒。API Script上でコマンドを実行するにはどうすればよいでしょうか?
その答えを握るのが、runCommand()関数です。しかし利用できる状況が限られます。というのも、このrunCommand()関数、クラスがdimensionまたはplayerでないと使えないのです。(注:ちゃんと確認したわけではないので、間違っていたらごめんなさい)なので、回りくどいですが、実践的には以下のように使われます。
server.world.getDimension("overworld").runCommand("give @a diamond 3")
for (const player of server.world.getAllPlayers()){
player.runCommand("give @a diamond 3")
}
実はこれ、落とし穴があります。というのも、ターゲットセレクターの「@s」(自分自身)が使えないのです。その理由は、コマンドの発動者が明確ではなく、「自分」が定義されていないからです。では、上の例2において、「自分」だけにダイアモンドを与えたい場合はどうすればよいでしょうか?
・・・答えはplayer.nameが握っていまして、
for (const player of server.world.getAllPlayers()){
player.runCommand(`give ${player.name} diamond 3`)
}
のように直接コマンドにプレイヤー名を代入すればよいのです。
マイクラのコマンドでできることは多岐にわたります。コマンドでできること(例:プレイヤーにダイアモンドを与える、メッセージを表示するなど)は、色々コードを書くよりもrunCommand()関数一発で行ったほうが早いことが往々にしてあります。
なお、よく似た関数に、runCommandAsync()があります。こちらは、runCommand()とは異なり、非同期でコマンドを実行することができます。基本はどちらを使っても良いですが、runCommand()でうまくコマンドを実行できない場合、runCommandAsync()を使うとうまくいくことが偶にあります(経験則)。
手に持っているアイテムを取得
先日のアップデートで、API Scriptの界隈にも変貌が訪れました。というのも、手に持っているアイテムを簡単に取得できるようになったのです。ターゲットセレクターhasitemと同じような機能ですね。
例えば、現在プレイヤーが持っているアイテムをメッセージ欄に表示する場合には、
let item_name = player.getComponent("inventory").container.getItem(player.selectedSlotIndex).typeId;
server.world.sendMessage(item_name);
のように記述すればOKです(注:変数playerは別に定義する必要があります)。ここのポイントは
getItem(player.selectedSlotIndex)
ですね。getItem()関数は、スロット番号(integer)を引数として、インベントリ上でそのスロットに存在するアイテムを返す関数です。今まではスロット番号を具体的に記述する(0番、1番・・・etc)必要があったのですが、アップデートを機にplayer.selectedSlotIndexと記述することで現在選択しているスロット番号を代入することができるようになりました。
おしまい
・・・と、ここまでAPI Scriptについて語ってきました。
API Scriptを始めとするアドオン制作については、私のYoutubeチャンネルでもまとめていますので、お時間が宜しければ是非ご覧ください。その他、技術的な視点からマインクラフトに関する情報を発信していますので、ご興味がありましたら是非訪れてみてください!