初めに
どうも餅わらびすとです。この記事では究極の機能、ScriptAPI(gametestとも言う)を解説します。
ScriptAPIというのは、JavaScriptという言語を使ってプログラムしていく、マイクラアドオンの機能です。
複雑な計算をしたり、マイクラ内のイベントのデータを読み取って処理をしたり、functionなどでは出来ないような色々な機能が沢山あり、アドオンの幅を広げてくれます。プラグインのようなことも実現可能!
役に立つ記事になれば嬉しいです。
目次
・manifest.json
・書く前に知っておいた方が良いこと
・ScriptAPIのつくりを理解する
・よく使うやつ
・エラーの見方
・最後に
~個人的おすすめテキストエディタ~
- pc: vscode
- android: コードエディタ
- ios: LiquidLogic
~見る前に最低限必要な知識~
- JSONの構造、用語
- 記号の名称
- マイクラアドオン構造の基本的な知識
manifest.json
まず、ScriptAPIを使う際には、マニフェストを変える必要があります。
こちらがサンプルコードです。
{
"format_version": 2
"header": {
"name": "ビヘイビアテストBP",
"description": "ビヘイビアテストBP",
"min_engine_version": [1,20,40],
"uuid": "d8229b16-c69a-40f3-baa6-926f96a5ca6c",
"version": [1,0,0]
},
"modules": [
{
"type": "data",
"uuid": "81344285-81fc-44b7-b8da-6f6f61288c4b",
"version": [1,0,0]
},
{
"type": "script",
"language": "javascript",
"uuid": "440ac9d1-d6f8-4eff-a5aa-29ed51b893f5",
"entry": "scripts/main.js",
"version": [1,0,0]
}
],
"dependencies": [
{
"module_name": "@minecraft/server",
"version": "1.8.0"
}
]
}
modules
まずはmodulesの中から見ていきます。
このマニフェストでは、scriptapiを使わないマニフェストと違い、typeがdataではない、typeがscriptの新しいオブジェクトが入っています。
しかし新しいオブジェクトといえども、中のuuidを変える以外はコピペで大丈夫です。
一応ほかの構成を見てみると、
・languageは使うプログラミング言語、
・entryは実行するscriptのファイルのパス、・versionは存在意義がわからん奴
となっています。言語がpythonやらcやらjavaやらに変えられるわけでもないですし、entryはファイルの名前さえ変えなければそのままだし、versionは数字変えても意味ないですしコピペです。
dependencies
次に、dependenciesです。
ここでは変数や関数などをまとめたモジュールというものの、種類やバージョンを指定しているオブジェクトが入っています。
主に使うもののmodule_nameは、
・@minecraft/server
・@minecraft/server-ui
の二つがあります。
serverの方では、ワールドに関することができ、uiの方ではフォームというui的なものを作ることができます。
この例では@minecraft/serverで1.8.0というバージョンを使用していますが、バージョンはアプデするごとに増えていきます。
そして、scriptapiにはbetaバージョンというそのときのテスト段階のバージョンがあります。この記事を書いているときのマイクラバージョン1.20.60では1.9.0-betaです。こちらは、現段階ではチャットの内容をとれたりエンティティへ右クリックをしたというイベントを検知(取得)できたり、まだ1.8.0のような安定版ではできないことができます。このbeta機能は、バージョンの名前を
<使用したいベータバージョン>-beta
として、ワールドの実験機能の設定からベータAPIをオンにすることで使用できます。マインクラフトpreviewなどのマイクラ自体のプレビューは必要ありません。
uiはあんま変わりません。大体1.1.0と1.2.0-beta。
serverもserver-uiも、このページからその時のマイクラのバージョンを選び、そのページの横にあるモジュール一覧から確認してください。
ちなみに、バージョンが増えるということは1.8.0みたいな安定版は新しいバージョンが出ても使えるわけですが、betaを使用する場合は毎回変える必要があります。
書く前に知っておいた方がいいこと
「早速scriptapiを書いていこう!」と言いたい流れですが、一文だけ覚えても文法を覚えなければ応用活用が利きません。
応用が効くようにしたいということで、ScriptAPIに使用されている、JavaScriptという言語の、基礎について覚えましょう。
しかしこれは基礎なので複雑な処理をしたい時や今回紹介したことのもっと詳しい仕様を知りたいという場合は、サイト、youtubeや生成aiなんかに聞きましょう。ちなみに、生成aiにはscriptapiはほとんど出来ないので、scriptapiとしてではなくjavascriptとしての質問をしましょう。
既にJavaScriptについて知っている、という方は飛ばして貰って大丈夫です。
コメント
jsでは、コメントは
//このようにスラッシュをコメントにしたいところから2つつけるか、
/* スラッシュとアスタリスクで囲むとできます。 */
/*
このコメントの場合は、
複数行にわたって
書くこともできます
*/
コメントをするとコメントをした範囲がコードとして認識されなくなるため、処理のメモに使えます。
データ型
プログラミングにおいて、データには種類があります。ScriptAPIで主なものを5つ挙げると、
・String(文字列)
・Number(数値)
・Boolean(真偽値)
・Object(オブジェクト)
・undefined
があります。
Stringは、“このようにダブルクォーテーションか,シングルクォーテーションで囲むことにより” 表すことができます。
+で2つの文字列を合わせる、といったことも可能です。(例:”あいさつは” + “大事だよ”)
Numberは、そのまま、0や100のようにして表せます。
+ や -、 *、 /、 % などで計算ができます。右から、意味は加算、減算、乗算、除算、剰余演算となっています。
Booleanは、trueとfalseで表すことができます。
そのまんまです。はいかいいえ的な
Objectは、[]で囲まれたものと{}で囲まれたものがあります。
[]のほうはArray(配列)と言い、複数のデータをコンマで区切って入れることができます。(例:[“一つ目のデータ”, 2, 3]) そして、その中のデータを要素と呼びます。
データは、オブジェクトの名前[番号]で取り出せます。しかし番号は0から始まるので、頭の中ではオブジェクトの名前[番号 – 1]と考えるようにしましょう。
{}のほうは、「プロパティ」というものを用いて、データを保存したり関数(後に説明します)を入れることができます。
//例
{
apple: "red",
grape: function() {
world.sendMessage("purple");
}
}
//こうでもok
{ apple: "red", grape: function() { world.sendMessage("purple"); } }
appleがプロパティの名前で、grapeがメソッド(プロパティに関数を入れたもの)の名前です。
プロパティは、オブジェクトの名前.プロパティの名前で呼び出せ、メソッドはオブジェクトの名前.メソッドの名前()で呼び出せます。
undefinedは、値が定義されていないものなどに入ったりするグローバル変数です。
そのまんまです。
定数・変数
この2つは簡単。
//定数の宣言
const /*定数の名前*/ = /*入れたいデータ*/;
//変数の宣言
let /*変数の名前*/ = /*入れたいデータ*/;
こんな感じです。
名前を日本語にするのは非推奨です。これは大抵の命名に当てはまります。初めに数字をつけるのはNGです。これも大抵の命名に当てはまります。
変数は定義した以降に、変数の名前 = 入れたいデータ、とすると再代入できます。
これらをどんなときに使うかというと、
object.red.fruit.apple
こんな感じの、「objectというオブジェクトのredというプロパティのオブジェクトのfruit(以下略)…」みたいに複雑な値があるとします。
これを毎回このまんま書くのは長ったらしいし面倒です。
これを定数を使うと
const apple = object.red.fruit.apple;
これからはappleと書くだけであの長いコードと同じ意味になります。
関数
関数とは、いくつかの処理を1つにまとめたものです。マイクラにもコマンドをまとめたfunctionがありますね。そんなものです。
関数は、
function /*関数の名前*/(/*仮引数*/) {
/*処理*/
}
と書いて定義できます。
呼び出すときは、関数の名前(引数) で出来ます。
まず、関数の名前です。これは何でもいいです。
そして処理。これはまんま処理です。return 値 をつけると、処理の中で出した値などを、関数を呼び出した場所に入れることができます。そのときに処理が止まるので、returnの値を設定せずにreturnだけで書いて、この条件で処理を止める、のようなこともできます。
仮引数と引数は、言葉で説明しづらいですが
//関数を定義
function plus10(number) {
const n = number + 10;
return n;
}
//関数の呼び出し
world.sendMessage(plus10(20)); //ワールドに30とチャットされる
こんな関係です。
仮引数はコンマをつけて増やすことができます。引数側もコンマをつけてください。
ちなみに、関数はアロー関数という書き方もあります。これは、
const /*関数変数名*/ = (/*仮引数*/) => {
/*処理*/
}
で書けます。
if文とかfor文とか
if文は、名前から何となく分かるかもしれませんが条件です。
if (/*条件*/) {
/*処理*/
}
if文の中の処理は、()の中にtrueが入ったときに実行されます。
例えば、1 === 1を入れると、1 === 1はtrueなので処理が実行されます。
結果は、!をつけると反転できます。trueがfalseになったりfalseがtrueになったり、、、if (!(1 === 1))
条件は、&&で、どちらもtrueならという条件にできif (true && true)、||で、どちらかがtrueならという条件にできますif (true || false)。
for文は、反復処理ができます。
for (let i = 0; i === /*実行したい数*/; i++) {
}
for (const /*定数名*/ of /*配列*/) {
/*処理*/
}
上は特に言うことなしです。そのまんま。
下は、配列の要素1つ1つに処理を行います。
クラス・インスタンス
これで基礎は最後です。
よく言われる例えで、クラスは設計図、インスタンスは設計図にもとずいてあるオブジェクト。
こういう感じです。scriptapiでクラスの宣言するのは個人的には高度なことでもしない限り使わないと思うので省略します。使いたくなった場合に調べてください。
ScriptAPIの書き方を理解する
やっと出てきます。scriptapi。
まず、ビヘイビアのファイルにscriptsというフォルダを作ってください。
作り終えたら、その中にmain.jsというファイルを作ってください。jsonとjsでは違うので、注意してください。ちなみに、jsonは「JavaScript Object Notation」の略だそうです。ながいね。
そうしたら、中にこう書いてみてください。
末尾に書いてある;はコロンではなくセミコロンなので注意してください。
import { world, system } from "@minecraft/server";
system.runInterval(function() {
for (const player of world.getPlayers()) {
const playerName = player.name;
player.sendMessage(playerName);
}
},20);
そして、保存してマイクラで開いてみてください。
どうでしょうか?1秒ごとに自分の名前がチャットに送られていると思います。
それではこのコードの意味を解いていきます。
注意
この解説のクラスのリンクなどは、少し経ったらバージョンが古くなっています。
なので、こちらのサイトから常にその時の新しいバージョンのものを確認してください。リファレンスの読み方は変わりません。
import { world, system } from “@minecraft/server”;
ここでは、@minecraft/serverモジュールから、Worldクラスのworldというインスタンス、Systemクラスのsystemというインスタンスの2つをインポートしています。
system.runInterval(function() {…},20);
ここでは、インポートしたsystemを使用しています。
systemはSystemクラスのインスタンスなので、Systemクラスのメソッドが使えます。
SystemクラスのrunIntervalというメソッドを見てみましょう。
初めに、Remarksという、その機能の説明を見てみます。
翻訳すると、「インターバルで一連のコードを実行する。」と書いてあるようです。
聞き慣れない言葉はインターバルぐらいで、意味は、繰り返し一連のコードを実行する、ということらしいです。
それでは、上から見ていきましょう。初めに、runInterval(callback, tickInterval?): number
とあります。この文から、引数が最大2つ使え、そして2つ目の引数の最後に?がついているため、tickIntervalという引数は、なくても使えることが分かります。
callbackという引数の説明には、callback: (() => void)とあります。ここには関数が入るということです。下の文は、翻訳すると、「このインターバルが発生したときに実行される関数コード。」とあります。
続いて、tickIntervalという引数の説明を見ていきましょう。
こちらには、Optional tickInterval: numberとあります。Optionalからも、tickIntervalがなくとも良いことがわかりました。そして、tickIntervalにはnumberを入れれば良いことも分かりました。
下の文も翻訳して、見てみると「コールバックが呼び出される間隔。」らしいです。名前がtickのintervalなのでtick単位で時間を入れればよさそうです。
そしてReturnを見ると、このメソッドを実行したときに返ってくる値はnumberで、説明によると「この関数の実行をインターバルで停止させるためにclearRunメソッドで使用できる不透明なハンドル。」みたいです。繰り返しをやめるメソッドを使用するのに必要ということです。
つまり、system.runInterval(function() {…},20);は、20ティックごとに{}の中身を実行するという意味だったということです!
for (const player of world.getPlayers()) {…}
ここでは、インポートしたworldを使っています。
worldは、WorldクラスのインスタンスなのでWorldクラスのメソッドが使えます。
WorldクラスのgetPlayersというメソッドを見てみましょう。
今回も初めに、Remarksを見ていきます。
このメゾットは、「フィルタ条件の EntityQueryOptions セットを介して定義された一連の条件に基づくプレーヤーのセットを返します。」らしいです。
それでは、上から見ていきましょう。初めに、
getPlayers(options?): Player[]
とあります。これから、引数にはoptionsというものが入れられるということが分かります。これも?があるため無くとも使えます。
options引数の説明には、options: EntityQueryOptionsとあります。EntityQueryOptionsとは何でしょうか?となって、クリックしてページへ飛んでみると、
色々出てきました。EntityQueryOptionsとはエンティティを取得する際、条件を付けられるインターフェイスのようです。とりあえずはインターフェースはクラスのような概念(設計図)であり、オブジェクトのような実態はないものと考えてください。このページは、まだ下に続いています。
1つ例を見てみます。このclosestというプロパティには、numberが入るようです。
Remarksを翻訳すると、「返すエンティティの数を制限し、このプロパティで指定された最も近いN個のエンティティを選ぶ。location 値もクエリオプションオブジェクトで指定する必要があります。」になります。ここでlocationも設定して、比較の中心を決める必要もあるようです。コマンドのセレクターで言うcのようなものみたいですね。
こういうわけで、EntityQueryOptionsを引数に入れた場合のメソットの例は、以下のようになります。
world.getPlayers({location: {x: 0, y: 0, z: 0}, closest: 1});
話をgetPlayersに戻します。
optionsの次には、Return、つまり返ってくる値が書かれています。
見てみると、どうやらPlayer[]が返ってくるようです。
これは、Playerクラスのインスタンスが入った配列ということです。
つまりfor (const player of world.getPlayers()) {…}は、playerにgetPlayersで取得した配列の各要素を代入し、{}の中でその個々の要素に処理を行っているという意味だったということです。
forの処理の中
const playerName = player.name;
player.sendMessage(playerName);
恐らくここまで読めたならばなんとなくやってることは分かると思うので、詳しいところは省略します。
nameプロパティを見ると、Readonlyと書かれていますね。これは値を再代入できない、ということです。
nameTagとかなら再代入して名前を変えることができます。
そして、sendMessage。これはまんまで、メッセージを送れます。
messageに入れられる値がいろいろごちゃってますが、これはstring,RawMessage,(string,RawMessage)[]のどれでも入れられるという意味です。
つまりこのコードは、プレイヤーの名前を本人にチャットで送るという処理だったといぅことです。
こんな感じで、リファレンスを見れば何をどうすればどうなるのか、ということが分かります。なので、なにか実装したい機能があったらリファレンスをまず見てみましょう。今の見方で応用も効くはずなので、リファレンス見て色々使えそうな機能で遊んでみてください。分からない処理や単語は調べてみましょう。これを繰り返したらその内ScriptAPIが書けるようになっていると思います。
よく使うやつメモ
World
System
Dimension
Entity
ItemStack
Block
↑大体のことはこのクラスから処理できる
worldのafterEvents、beforeEventsが個人的に強い。イベント系はとにかく色々出来る
マイクラ内のdevelopment_behavior_packsにアドオンフォルダを入れて、そのアドオン入りワールドで/reload使うとfunctionとscriptapiのファイルが更新されるからいちいちワールドを変えなくとも良い
エラーの見方
エラーは、設定にあるクリエイターという欄にあるコンテンツログGUIの有効化をONにすることで見ることができます。
ScriptAPIに限らず、様々な要素のエラーが出てくるのでONにしておいて損は無いです。
エラーは、大抵の場合は翻訳すれば言っていることが分かると思います。
最後に
ここまで読んでくださりありがとうございました!
役に立ったと思ってもらえたら嬉しいです。
なにか質問があるときはこちらのディスコードサーバーから受け付けています。どんな人も大歓迎!
それでは良いアドオンライフを!
コメント
神ですね。ありがとうございます。
その言葉が嬉しいです!
良かったです
scriptAPIのオートコンプリート機能があれば使い方を教えてほしいです。コードエディタはVScodeを使っています。
npmをインストールし、https://www.npmjs.com/package/@minecraft/serverこのページからバージョンを選んで、パッケージのインストールのコマンドをコマンドプロンプトにコピペして実行すれば補完が出てくるようになります