【マイクラ統合版】自作コマンドの作り方、全部教えます。ScriptAPIからWebSocketまで【総集編記事版】

はじめに

マインクラフト統合版には、コマンドを自作できるシステムが存在します。それも1つや2つではなく、ScriptAPIを用いたものからWebSocketServer、果てには外部ライブラリを用いたものまで、様々です。コマンドの自作としては代表的なものとしては、CustomCommandが知られていますが、広義の意味でのカスタムコマンドというのは、それにとどまりません。

本動画では、コマンドの自作について、チャット検知やWebSocketServerなど幅広い観点から網羅的&体系的にまとめることを目的とします。本動画が皆様のお役に立てれば幸いです。

本記事は、私の動画の記事版です

YouTube動画

以下は、本稿のメインである、YouTube動画です。1時間13分あります。一度に一気見するのもいいですし、何回かに分けて視聴するのもいいと思います:

動画の資料・リンクなど

【Discord】https://discord.gg/cQXHmAT4cE

動画資料:

【ScriptAPI】https://lemon-slime.com/?p=1254

【チャット検知】https://lemon-slime.com/?p=1404

【scriptevent】https://lemon-slime.com/?p=1492

【CustomCommand1】https://lemon-slime.com/?p=2188

【CustomCommand2】https://lemon-slime.com/?p=2218

【CustomCommand3】https://lemon-slime.com/game/custom-command-no-namespace/

【WebSocketServer】https://lemon-slime.com/game/custom-command-no-namespace/

【日本語コマンド】https://lemon-slime.com/?p=1694

【BedrockPy】https://lemon-slime.com/game/bedrockpy-overall/

各種サイト:

【vscode】https://code.visualstudio.com/

【node.js】https://nodejs.org/en/download/prebuilt-installer

【uuidジェネレーター】https://www.uuidgenerator.net/

【Python】https://www.python.org/downloads/

【bedrockpy公式リファレンス】https://bedrockpy.readthedocs.io/en/latest/api.html

動画字幕

以下、動画字幕です。長いです:

茜
コマンドを、
茜
作りたーい!
茜
ご存じの通り、マインクラフトにはホンマぎょうさんコマンドあるやん?
葵
ホント、覚えられないくらいには。
茜
でもさ、これ微妙に痒いところに手が届かへんのも事実。
葵
あー、分かるかも。コマンド触れば触るほど、「こんなコマンドがあればいいのに」って思うことあるよね
茜
で、一般的にはそんなのをfunctionとかコマンドの組み合わせとかで無理やり実装するところやけど、
茜
実はこんなコマンドって自作できるのってあんまり知られてへんよな
葵
確かに?界隈の人以外はね..
茜
ってことで、この動画ではコマンドを自作する方法をいくつかまとめてみよう、って趣旨。
葵
そういえばコマンド自作する方法ってどんなのがあったっけ?
茜
パッとあげると、こんな感じ。
茜
まだまだあるかもしれへんけど、ここらへんの方法が王道かもな
茜
で、基本的にコマンドの自作には「ScriptAPI」といってJavaScriptでゲームシステムをいじる機能が必要になるから、
茜
その部分の解説を含めた、こんな構成でお届けします…
葵
要はコマンド自作の総集編ってことね
茜
で、まず解説に入る前にそもそも「自作のコマンドって何なん?」っていう疑問が払拭されへんとモチベーションもわかんと思うから、
茜
まずは完成品を見せるわ
茜
一目で分かる例が、こちら
葵
「test」?
葵
確かにカスタムコマンドだね
茜
あとは、こんな感じのチャット欄に指定の語句があれば何かを実行するようなシンプルなやつとか、
茜
後半部分では日本語で何らかのコマンドを実行するような例もやっていくで
茜
さて、概要の解説はこれくらいにして、まずはこの動画の核となるScriptAPIについて話すわ
茜
まずは必要なものがいくつかあるからこれをインストールしよう
茜
詳細は概要欄を見てね
葵
なんかソフトの一覧にマインクラフトが入ってるんだけど
茜
このチャンネルの視聴者の大多数は持ってると思うから解説は省くわ
茜
次のvscodeはプログラミングに特化したテキストエディタで、
茜
必須ではないけどあったほうが断然楽やで
茜
概要欄に貼ってあるリンクからダウンロードできるで
葵
ゲーム制作で使ったやつだ
茜
汎用性が高いよな
茜
最後にnode.js。
葵
これはあんまり聞かないなぁ
茜
本来javascriptっていう言語は
茜
Webページ上で動作するスクリプトなんやけど、
茜
これをパソコン上で使えるようにするのがnode.jsの役割。
葵
ふーん…
茜
これも概要欄からアクセス可能。
茜
ダウンロードボタンを押して、
茜
msiファイルをダブルクリックして実行すれば完了。
茜
今回解説する工程ではnode.jsは必須やで
茜
これがダウンロードできたら次に進もう
茜
まずはいつも通り、com.mojangフォルダを開こう
茜
実はこの場所、例の1.21.120のアップデートで
茜
変更になってるから気つけてな
葵
結構奥の方…
茜
せやな、迷いやすいから注意。
茜
開けたらdevelopment_behavior_packsを開こう
茜
そしたら右クリックしてフォルダを新規作成。
茜
フォルダの名前はお好みで。
茜
今回はtestとしたで
茜
そしたらこれを開いてmanifest.jsonを作ろう
茜
右クリックして新規作成からテキストドキュメントを選択。
茜
するとテキストファイルが作成されるから、これを改名して
茜
manifest.jsonとしよう
葵
聞いたことあるな…
茜
これはアドオンの要項を記述する、必須のファイルだから
茜
本チャンネルでも何回か取り上げてるで
茜
これをvscodeで開いて、編集しよう
茜
内容はこのサイトに書いてある通りで、コピー&ペーストすればOK。
茜
何点か補足すると、
茜
ここでアドオンの名前と説明を定義しているので、
茜
アレンジはお好みで。
茜
これがそのまま表示名になるで
茜
このuuidはアドオン固有の文字列。
茜
これはそのまま使うというよりも、
茜
uuidジェネレーターとかを使って生成・変更するのがええかもな
葵
そのまま使うとどうなるの?
茜
このアドオン自体は動くんやけど、配布するときにちょっと面倒なことになるな
茜
最後にminecraft/serverのバージョンについて。
茜
動画作成時点で有効なバージョンはこんな感じで、
茜
基本的に最新のバージョンになればなるほどいろんな機能が使えるようになるんやけど
茜
ベータ版は挙動が安定しないから使用には注意が必要やな
茜
それが完了したらフォルダを新規作成して
茜
scriptsと改名しよう
葵
たしかここにスクリプトを書いていくんだっけ?
茜
せやな、scriptsフォルダを開いて
茜
新規作成からテキストドキュメントを選んで
茜
これをmain.jsに改名しよう
茜
これをvscodeで開いたら、
茜
上のメニュー欄からTerminal、New Terminalの順に選択。
茜
するとターミナルが開くので、
茜
まずはこのアドオンがあるフォルダに移動しよう
茜
続いてnpmコマンドでマインクラフトのモジュールをインストール。
茜
こんな感じになれば成功。
茜
ちなみにこの一連の操作はコマンドプロンプトでもできるで
葵
もはやマイクラの動画とは思えない…
茜
ちょっと専門的すぎたな…
茜
でも事前準備はこれで終わり。
茜
あとはmain.jsに何かスクリプトを書いていこう
茜
スクリプトの内容については前回の動画でお話ししているので
茜
この動画では割愛。
茜
こんな感じで簡単にスクリプトが書けたら保存して、
茜
アドオンの有効化を行ってからゲームをロードすると…
茜
無事、メッセージ欄がHelloで埋め尽くされるで
茜
manifest.jsonのversionについてなんやけど、
茜
ここをベータ版にすると最新の機能、
茜
例えば自作のコマンドの制作なんかができるようになるんやけど
茜
その場合はベータAPIをオンにする必要があるから注意な
茜
以上が、大まかなScriptAPIの全体像。
茜
続いて、この環境構築に基づいてチャット検知をやってみるで
茜
で、ここが重要やねんけど、
茜
今回チャット欄の監視を行うにあたって
茜
ここがベータ版じゃないと動かへんのよ
葵
最新機能なんだ
茜
せやで
茜
で、ここのバージョンは頻繁に変わるから、
茜
公式のリファレンスでバージョンを確認してね
茜
1.21.120以降は単に「beta」と指定することもできるで
茜
あと、ベータ版を使う場合はワールド起動時に「ベータAPI」を
茜
オンにする必要があるのも注意。
葵
忘れそうだ…
茜
後でもう一回話すで
茜
それが完了したら、フォルダを新規作成して、
茜
scriptsという名前のフォルダを作るで
茜
これを開いて、テキストファイルを新規作成。
茜
main.jsとするで
茜
これを開いて、スクリプトを書いていこう!
葵
ようやくコマンドが作れるのね…
茜
せやな、
茜
大まかな流れはこんな感じで、
茜
何らかのメッセージが送信されたときにそれを検知して
茜
処理につなげていくという流れやな
茜
まずは簡単な例を書いていくで
茜
まずは、いつも通りモジュールの呼び出しから
茜
これでマイクラの世界に干渉できるようになるで
茜
続いて、チャットが送信されたときに発火する関数を定義。
葵
chatSendなんてあるんだ
茜
これで、チャットの送信を監視できんねん
茜
便利やからめっちゃ使うで!
茜
subscribeで関数の実行を予約したら、
茜
この関数の名前をevとするで
茜
ev.messageでメッセージの内容を取得して、
茜
もしこのメッセージが「!killall」だったら、
茜
このメッセージの送信者、ev.senderのもとで
茜
runCommand関数でコマンドを実行。
茜
これを保存して、
茜
さっき言ったようにベータAPIをオンにしたら、
茜
ゲーム内で「!killall」が使えるようになるはずやで
茜
実際に使ってみよう
葵
あ、モブが倒された
茜
こんな感じでちゃんと機能するな
茜
さっき書いたスクリプトはうp主のサイトにもあるから、
茜
コピー&ペーストするのが早いで
茜
ここではrunCommand関数を使って処理を行ったけど、
茜
sendMessage関数を使ってメッセージを送信することもできるで
茜
これを実行すると、こんな感じでメッセージが返ってくるで
葵
山びこ山みたい
茜
それ以外にも前回紹介したやり方でUIを出すとか、
茜
色んなスクリプトが考えられるな
葵
面白そう!
茜
みんなもオリジナルのコマンドを作ってみよう!
茜
で、普通のブログとか動画はここで終わるところやけど、
茜
ここで終わらないのが当チャンネル。
茜
普通のコマンドだとこんな感じでオプションの設定、
茜
いわゆる引数を付けられるんやけど、
茜
オリジナルのコマンドにもこれを実装してみよう、という話。
葵
なるほど
茜
説明に入る前に、前提知識から。
茜
例えばこんな感じに文章があったとするやん?
葵
ぶん、しょう?
茜
オレンジのところに文字が入って、白は空白。
葵
なるほど
茜
で、こんな文章について、
茜
javascriptに標準搭載されてるsplit関数を使って
茜
空白で分割すると、
茜
こんな感じになんねん
葵
それぞれの文字に番号が振られるのか
茜
せやで、そしてそれぞれを配列に格納。
茜
これは空白に限らずいろんな文字で分割出来て、
茜
この文章を点で分割するとこんな感じになるな
茜
このsplit関数を使うと引数を取得出来て、
茜
その例はこんな感じ。
葵
これは何?
茜
足し算を行うaddコマンドを実装する例。
茜
実際に使ってみるで
茜
引数の数字を2つ入力すると・・・
葵
あ、足し算ができてる!
茜
もちろん、他の数字でも・・・
葵
これも大丈夫そう
茜
小数の足し算もOK
茜
ということでこれの解説を。
葵
一行目はさっきと変わらないね
茜
せやな
茜
だけど2行目はstartsWithが使われてるな
葵
ほんとだ。これは何?
茜
これは、文字列が指定の語句から始まっているかどうかを判定する、
茜
javascriptの標準ライブラリの関数。
茜
今回は、メッセージが「!add」から始まっている時にtrueを返すで
茜
3行目ではさっき話したsplit関数を使って、
茜
空白でメッセージを分割して配列に格納。
茜
4行目で足し算を行う、という流れやな
茜
この辺の詳しい流れはうp主のサイトにも書いてるで
茜
そしてこれを応用すると、
茜
引数を無限に増やすことができんねん
葵
え、すご!
茜
実際に使ってみるで
茜
今回はこんな感じで5つの引数を置いてみよう
葵
あ、合計が出た
茜
こんな感じで引数の数に関わらず
茜
コマンドを実行することができんねん
茜
さっきと違うのは、引数の処理でfor文を使ってるところやな
茜
このlengthで配列の要素の数を取得してるで
葵
なるほど、面白いね!
茜
みんなもカスタマイズして遊んでみてね!
茜
で、さらに応用するとこんな感じで
茜
コマンドの中で任意のコマンドを実行できるんよ
葵
え、どういうこと?
茜
executeコマンドをイメージすると分かりやすいかも
葵
なるほど
茜
これはwaitコマンドといって、
茜
コマンドの実行を遅らせるコマンドやねん
茜
試しにこれを使ってみるで
茜
こんな感じのコマンドを実行すると・・・
茜
ちょっと遅れてメインのコマンドが発動すんねん
茜
これは任意のコマンドについて有効で・・・
茜
giveコマンドを遅延させることもできるな
茜
ここでは実行が60ティック、3秒遅れるで
葵
すごい!
茜
今回の場合、空白で分割するのはあかんのよ
葵
というと?
茜
giveコマンドの中にも空白があるやろ?
葵
ああ、waitコマンドの引数とgiveコマンドの引数を
葵
区別しなきゃいけないのか…
茜
せやな、
茜
今回はこういう感じで分割したいから、
茜
for文でコマンド部分の文字列を合体してるで
葵
なるほど…
茜
ここまで色々コマンドの作成方法について話したけど、
茜
チャット欄に依存する以上、
茜
コマンドブロックを使った実行の自動化が難しいんだよね
葵
確かに…
茜
この問題の解決策はおよそ2種類あって、
茜
続いて、そのうちの1つの方法について話すわ
茜
前回はチャット欄を監視してコマンドを自作したけど、
茜
それやとコマンドブロックが使えへんから、
茜
自動化が難しいんよ
茜
せやけど、今回解説する方法はチャット欄に依存せえへんから
茜
コマンドブロックが使えるというメリットがあんねん
葵
なるほど…
茜
で、その方法なんやけど…
葵
タイトルにも書いてあるし、何ならここにもあるよ?
茜
せやな、鍵を握るのがこのscripteventコマンド。
葵
でも、そんなのあったっけ?
茜
それがあんねん
茜
こんな感じでヘルプにも出てくるんやけど、
茜
影が薄いのか、あまり知られてへんみたいやな
葵
だって説明見てもよく分かんないこと書いてあるし…
葵
せめて「コマンドを自作できます」とか書かれていればいいのに…
茜
ま、その辺分かりづらいのはあるよな
茜
まずはこのコマンドがどういうものなのかを話すわ
葵
お願いします!
茜
そもそも、scripteventコマンドの構文はこんな感じ。
茜
最初にコマンド名であるscripteventをおいて、
茜
その後にidを、
茜
そして最後にメッセージを書くで
葵
このidって何? あらかじめ決まってるの?
茜
いや、自分で決められんねん
葵
どういうこと?
茜
このidだったらこういう処理をする…
茜
っていうのを、あらかじめ自分で決めんねん
葵
へぇ…
茜
百聞は一見に如かずやな、実際にコマンドを使ってみるで
茜
今回はあらかじめ、killallというidを入力すると
茜
killコマンドが発動するようにしてるで
茜
そしてこのコマンドを実行すると…
葵
モブが倒されたね
茜
こんな感じで機能するわけやな
茜
このidの部分には名前空間が必要やから注意
葵
これ、どうやってidを定義するの?
茜
ここで必要になるのが、ScriptAPI。
茜
このチャンネルでは何回か取り上げてるけど、
茜
プログラミングをしてゲームのシステムをいじる機能やな
茜
環境構築についてはこの動画とかあの動画とか
茜
前回の動画とかうp主のブログで詳しく説明してるから、
茜
そっちをご覧ください
茜
いつも通りmain.jsにアクセスして
茜
モジュールを呼び出したら、
茜
こんな感じでスクリプトを書いていくで
茜
まずは、scripteventが発火したときにトリガーする関数を定義。
茜
関数の引数はevとするで
茜
ev.idで送信されたscripteventのidを取得して
茜
もしこれがkillallやったら、
茜
scripteventの送信者、ev.sourceEntityのもとで
茜
runCommand関数でコマンドを実行。
葵
これで完成?
茜
もうちょっとやりたいことがあんねんけど、
茜
とりあえず、このスクリプトの挙動を確認してみよう!
茜
scripteventコマンドを実行するで
葵
ちゃんと動くね!
茜
次にコマンドブロックで動くか見てみるで
茜
同じようにscripteventコマンドを入力。
茜
そしてコマンドを実行すると…
葵
え、killallが効いてない…
茜
こんな感じでエラーが発生すんねん
葵
え、scripteventはコマンドブロックでも使えるんじゃないの?
茜
無条件で使えるわけとちゃうねん
茜
考えてみれば当たり前のことで、
茜
今回、コマンドを実行したエンティティについて
茜
runCommand関数で処理を行ってるけど、
茜
ブロックはエンティティとちゃうからエラーが発生するわけやな
葵
スクリプトがコマンドブロックに対応してないのか…
茜
せやな、
葵
じゃあ、どうすればいいの?
茜
実行者を場合分けして、
茜
それぞれについて処理を行っていくで
茜
ev.sourceTypeでコマンドの実行元を取得して、
茜
それがentityだったら普通に処理を実行。
茜
そうでなくて、実行元がBlockやった場合、
茜
ev.sourceBlockで実行元のブロックを取得して、
茜
そのブロックのディメンション内でrunCommandを実行。
葵
回りくどいね
茜
しゃーなしやな
茜
実際に動かしてみるで
茜
コマンドブロックを起動すると…
葵
あ、モブが倒されてる
茜
エラーも発生してへんな
茜
え、これを応用すると足し算コマンドが作れんねん
葵
足し算?
茜
実際に使ってみるで
茜
今回は、idとしてaddを入力。
茜
そして次のメッセージ欄に、足し算する数字を入力。
茜
今回は、小数の入った2つの数字を足し算するで
茜
そしてコマンドを実行すると…
葵
結果がでてるね
茜
こんな感じで機能するわけやな
茜
さっきと違うのは、このev.messageで
茜
メッセージ部分の文字を取得してるとこやな
葵
ホントだ
茜
これをsplit関数を使って空白で分割して、
茜
それらの数字を使って足し算をしてるで
茜
後半部分はさっきとあまり変わらへんのやけど、
茜
この辺の流れだけちょっとちゃうな
葵
ホントだ、どういうこと?
茜
sendMessage関数はクラスがプレイヤーじゃないと使えへんから、
茜
そのブロックがあるディメンションにいるプレイヤーを取得して、
茜
各プレイヤーについてメッセージ送信してんねん
葵
へぇ…
茜
こんな感じで、scripteventを使えば
茜
コマンドを「自作」できんねん
葵
結構、面白いね
茜
せやろ、これを使えば自由度がかなり上がると思うわ
茜
コマンドブロックが使えるのもええよな
茜
で、これとは別にコマンドそのものを自作する方法もあるから、
茜
そっちも解説するわ
茜
できることならこんな感じで、直接実行するタイプのオリジナルコマンド、作りたいやんか
葵
あー、まあ確かにね。でもそんなの聞いたことないよ?
茜
それができんねん。これ見てほしいんやけど、
葵
アップデート?
茜
せやで。この前の1.21.80のアプデで、CustomCommandが追加されてんねん
葵
CustomCommand?
茜
文字通り、自分でオリジナルのコマンドを作れるシステムやな。楽しそうやろ?
葵
え、それすごいじゃん!
葵
でも何で誰も何も言わないんだろう?
茜
まあ、アップデートのTechnical Updatesの部分なんて普通の人は注目せえへんからな
葵
確かに統合版スーパーフラットのインパクトが強すぎて。
茜
ただ、ScriptAPI触っている人の中ではある程度知れ渡っているのか、高度なアドオンとかだとカスタムコマンドは少し見かけるかな?
茜
ま、早速オリジナルのコマンド作っていくわ
茜
まずは今回やりたいことを見せるわ
茜
こんな感じで、「lq:test」という自作のコマンドを定義して、
茜
それを実行したらこんな感じでメッセージを表示する、っていう簡単なコマンドを作っていくわ
葵
スラッシュから始まる普通のコマンドを作るんだね
茜
せやな、その辺がscripteventとはちゃうところやし、
茜
ビックリマークから始まる、「疑似コマンド」とも当然一線を画すところやな
葵
正真正銘の「オリジナルコマンド」だ
茜
そういうこと!
茜
で、カスタムコマンドを使うのにあたって必要なものがいくつかあるんやけど、
茜
まずは、当然やけどScriptAPIの環境。
葵
まあ、スクリプト書いて定義するんだから当然だよね
茜
で、ちょっと気をつけて億点として、従来はこのカスタムコマンド、ベータ版の機能やったんよ
葵
元動画もベータ版として解説してたよね
茜
それが、1.21.120のアップデートで正式版として実装されるようになったから、
茜
カスタムコマンドの定義にベータAPIとかは特に不要。
葵
それは大きいね
茜
ということで、まずはScriptAPIの環境を整えよう
茜
それが準備できたらアドオンのフォルダ内にscriptsフォルダを作って、
茜
その中にmain.jsを作って、テキストエディタで開こう
茜
そしたらここにコードを記述。
茜
まずはいつも通り、import文でマイクラのモジュールを呼び出してそれを変数serverとしよう
茜
続いてカスタムコマンドを定義するスクリプトを記述。
茜
serverのsystemモジュールのbeforeEvents、startupクラスを使って、ワールド初期時に発火する関数を定義
茜
この関数の引数をevとして、registerCommand関数でカスタムコマンドを登録。
茜
続いてこのカスタムコマンドの概要を定義していくわ
茜
まずはname。言うまでもなく、このコマンドの名前やな
葵
これって名前空間が必要なの?
茜
せやねん、(名前空間):(コマンド名)の形で定義。公式リファレンスにさりげなく書いてるで
茜
続いて、description。これもそのままの意味で、コマンドの説明文を定義。
茜
今回は「これはテスト用のコマンドです」という説明にしておこう
葵
コマンド名の右側に出てくるこの文章のこと?
茜
せやな。アドオンを配布する場合はあったほうがええと思うわ
茜
次に、permissionLevel。これはいわゆるコマンドの「権限レベル」の設定やな
茜
CommandPermissionLevelクラスを設定することになってて、有効なのはこんな感じ。
茜
今回は誰でも使える「Any」を設定しておこう
茜
mandatoryParametersとoptionalParametersは後で話すわ
茜
続いて、このカスタムコマンドがゲーム内で実行されたときに何が起きるかを定義していこう
茜
コマンドの実行起点であるoriginと、これは後で話すんやけどargを引数にとって、発火する関数を定義。
茜
試しに実行したエンティティを起点にsendMessage関数で「テスト用文章」というメッセージを表示してみよう
茜
これが終わったらあとはゲーム内で挙動を確認するだけやな
茜
マイクラを起動して、このアドオンを有効化しよう
茜
ということでゲーム内でさっき定義したコマンドを入力してみると..
葵
あ、ちゃんとコマンド一覧に登録されてる
茜
説明文もOKやな
茜
そしてこれを実行すると…
葵
メッセージが出てるね
茜
こんな感じでちゃんとスクリプトが動いてることが分かるな
葵
これってメッセージの表示以外にもコマンドの実行とかできるのかな?
茜
試してみよっか
茜
sourceEntityの下でrunCommand関数でコマンドを実行してみよう
茜
この状態でさっきと同じように、「lq:test」を実行。
葵
なんかエラーが出てるよ
茜
実はrunCommand関数、beforeEventsプロパティと一緒には使えへんのよ
葵
あ、そうなんだ。ちょっと不便。
茜
どうしても使いたい場合、runTImeout関数を使って意図的に関数の実行を遅延させるっていう手があるな
茜
実際にスクリプトを書くとしたら、こんな感じ。
茜
この関数、遅延させるティック数を引数にとる必要があるから、何らかの数値を指定しよう
茜
これでコマンドを実行させると…
葵
あ、コマンドが実行されてる
茜
一応、こんな感じで使う方法はあるにはある、っていう感じやな
葵
でもそれ、functionとどう違うの?
茜
functionは予め定義しておいた一連のコマンド群を順々に実行するっていう処理過程しかとらないけど、
茜
今回使ったCustomCommandはScriptAPIを介したプログラミング機構を持ってるから
茜
条件分岐とか繰り返し処理がだいぶやりやすいっていう利点があるな
葵
あー、確かに。似てるようで全然違うんだ
茜
単純な処理はfunctionに任せて、複雑なことがやりたいならCustomCommandを使うのがええかもしれへん
茜
最後に、引数について。
葵
あー、そういえばこのコマンドに引数ってなかったね
茜
そう、今回定義したのは「lq:test」で完結する簡単なコマンドやったけど、
茜
これも設定出来たらだいぶ自由度が上がると思うわ
茜
で、この引数の鍵を握るのが、さっきスルーしたmandatoryParametersとoptionalParameters。
葵
楽しそう
茜
あと、さっき使わなかった関数の引数argも引数の設定になると関わってくる感じやな
茜
次はこのあたりの説明をしていくわ
茜
まずは今回やりたいことを見せるわ
茜
前回追加したオリジナルのコマンドに、
茜
適当な文字列を追加すると、
茜
その入力にあった文字が出力されるっていう仕組みを作っていくわ
葵
それが引数になるんだ
茜
そういうことやな
茜
で、そもそも引数って何?っちゅう話なんやけど…
葵
あれでしょ?コマンドの後ろにつくやつ。
茜
マイクラ上やとそうなんやけど、より一般的に説明するならコマンドや関数を実行する際にそれに与える値のことやな
茜
で、カスタムコマンドにそれを実装して、よりコマンドの自由度を上げようっていうのがこの動画の趣旨。
茜
さて、スクリプトなんやけど、前回軽く説明した通り、mandatoryParametersは必須となる引数の設定で、
茜
optionalParametersは任意の引数の設定。どっちもlist形式で指定することになっとるな
葵
つまり、上のやつはないとエラーを吐くけど、下のやつはなくてもいいってこと?
茜
そういうことやな。まずは必須となる引数から設定していくわ
茜
引数の設定にあたって必要となる要素は、nameとtypeの2つ。
茜
この2つの要素を持ったObject型を、中かっこを用いて記述しよう
茜
nameの部分は、この引数の名前。適当な文字列でOK
葵
名前空間も要らないんだ
茜
続いて、typeはserverモジュールのCustomCommandParamTypeを指定することになっとるから注意しよう
茜
有効な引数のtypeはこんな感じ。
茜
そしてよく使う引数のtypeと、その意味、例を表にするとこんな感じ。
葵
結構色々あるんだね
茜
もちろんこれ以外にもあって、詳しいことは公式のリファレンスにも書いてあるから見てみるとええかもな
茜
今回は簡単な例として、String、つまり文字列を引数のtypeとするわ
葵
これで設定は終わりかな?
茜
このカスタムコマンドが受け入れる引数の設定はそう。あともう一つやることがあって、
茜
この引数をもとに「何を実行するか」っていうのを改めて定義する必要があんねん
茜
そのカギを握るのが、前回スルーした、この変数arg。
葵
あー、定義するだけしておいて、一切使ってなかったから何でだろうって思ってた
茜
ってことで今回は簡単な例として、この引数を受け入れてその引数をそのまま表示するプログラムを書いていこう
茜
とはいっても特に難しいことはなくて、このargに引数の情報が含まれているから、それをsendMessageで表示するだけやな
葵
このStringっていう関数は?
茜
これは、ある型の変数を、強制的に文字列型に変換する関数。sendMessage関数は文字列型しか受け入れへんから、一応書いてるって感じ
茜
この「型」っていう概念が後々重要になるから、覚えておくとええかも
茜
ってことで、これを保存してゲーム内での挙動を見てみよう
茜
チャット欄を開いて、前回定義したコマンドに新しく文字列を引数として挿入してみよう
葵
なんかコマンドの説明部分も引数の存在が前提となってるね
茜
せやねん、これは後で話すんやけど「このコマンドはどうやって使うのか」っていうのが一目で明確になるのが
茜
このカスタムコマンドのええ所やと思うわ
茜
で、これを実行すると
葵
あ、引数が表示されてる
茜
ちゃんと引数の設定ができることが確認できるな
茜
次に、OptionalParameters、つまり任意の引数の設定もやってみよう
葵
引数の設定の方法はさっきと同じかな?
茜
せやな、nameとtypeの両方が必要。
茜
さっきと違う名前にしとくと区別がついて分かりやすいかもしれへん
茜
二つ目の引数のtypeは、さっきとちゃうLocation、つまり座標にしてみよう
葵
あー、fillコマンドとかで見る~ ~ ~みたいなやつか
茜
同じようにこの引数を受け取って何が実行されるかを定義していくんやけど、
茜
複数の引数を設定するときは、registerCommand側の引数を増やさなあかんねん
葵
あ、そうやって定義するんだ
茜
変数argは既に使われてるから、ここではarg2を2つ目の引数を格納する変数として新たに定義しよう
茜
そんで同じように、座標の情報を含むarg2を使った処理を行っていこう
茜
座標のx座標を取得して、それを出力する例はこんな感じ。
茜
y座標やz座標も同じ。
茜
ちなみにこれ自体は数値型やから、String関数がないとエラーを吐くで
茜
ゲーム内で挙動を確認してみよう
茜
まずは一つ目の引数、文字列を入力して、その次に任意の引数、座標を入力。
葵
あ、数字が出力されてる
茜
これが現在のx座標に相当するな
茜
ただ、このままやと小数点の入った数字で使いづらいと思うから、多くの場合Math.floor関数で処理をしたほうがええと思うわ
茜
あと、PlayerSelectorでターゲットセレクターを引数にとれんねんけど、
茜
この場合argにはObject型が入って、String型しか受け付けへんsendMessageとは相性が悪いから、
茜
そういう場合、JSON.stringifyで無理やりObject型をString型に変換できるっていうのは覚えとくとええかも
葵
あ、こんな感じで中身を直接見れるんだね
茜
これはカスタムコマンドに限らず、ScriptAPI全般について言えることやな
茜
というところで引数の解説はおしまい。
茜
さっき説明したように、CustomCommandの場合、入力時にヒントが表示されるからプレイヤーに優しいっていうメリットがあるし
茜
これがScripteventや疑似コマンドにはない、唯一無二の魅力となってる面があるな
茜
元来ベータ版として運用されてきたけど、これが正式版として気楽に使えるようになったのも追い風やと思うわ
葵
このカスタムコマンドだけどさ、名前空間が必要なんだよね…
葵
でさ、巷の凄いアドオン見てるとさ、カスタムコマンドはコマンドでもこんな感じで名前空間内やつ見るんだよ
茜
あー、たまにあるよなぁ
葵
これさ、どうやって作ってるのかお姉ちゃん知ってる?
茜
あー、これ、意外と解説されない事項やから、ちょっとこれを機にスクリプト解説するわ
葵
あ、お姉ちゃん知ってるんだ、お願いしますっ!
茜
まずは何をやりたいのかってことを話すわ
茜
こんな感じでコマンド一覧を開くと、
茜
名前空間がない、単なる「test」というコマンドがあるのが分かる?
葵
あー、あるね、こういうのが欲しい
茜
これこそ、今回追加していくコマンド。
茜
そして、これを実行すると、こんな感じで何らかのアクションを起こす、という一連の流れまでを実装するわ
茜
ということで、早速スクリプトを書いていくわ
茜
名前空間がないとは言っても、基本的な部分は普通のカスタムコマンドと一緒。
茜
まずはimport文を普通に定義して…
茜
system、beforeEventsのstartupを用いてワールド起動時にカスタムコマンドを登録する処理を記述。
茜
で、後々のことを考えてちょっと書き方を変えるんやけど
茜
まずは登録したいコマンドについて仮で良いので名前空間を定義
葵
え?どういうこと?名前空間はないんじゃないの?
茜
この事情については後で話すわ
茜
で、続いて登録したいコマンドの名前を定義。
茜
これは「test」とか適当な文字列にしておこう
茜
さらにこの登録するコマンドについて、説明文を宣言。
茜
これはそのまま、コマンドの入力画面で表示される文言になるで
葵
でも今回は、これらをコマンドの登録する過程で決めるんじゃなくて先に定数で置いちゃうんだね
茜
実は、これが「名前空間なし」のコマンドとちょっと関わってくんねん
茜
さて、これらの宣言が終わったら、続いて同じようにコマンドを実行したときに発火する関数も予め定義しておこう
茜
ここではこのコマンドの関数を「commandCallback」とでもして、引数にoriginを設定。
茜
このコマンドの実行者をさっき決めた引数、origin.sourceEntityを用いて取得。
茜
このエンティティが存在しないことも考慮して、一応if文を挟んでおこう
葵
あー、コマンドブロックで実行することもあるもんね
茜
それが終わったら、んー、適当にそのエンティティについてコマンドを実行する感じでええかな
茜
say testとでもしておこう
葵
シンプルぅ!
茜
さて、これで発火する関数の定義は終わり。最後にこれらを結び付けてカスタムコマンドの登録処理をやっていこう
葵
そうそう、どうやって名前空間なくすんだろう、普通にやるとエラーになるし…
茜
まずは普通にカスタムコマンドを登録する処理を記述。これはこのまえやったのと殆ど変わらへんな
茜
このカスタムコマンドの名前についてだけど、まずは普通にさっき決めた名前空間ごと定義してしまおう
葵
あー、結局名前空間入ったコマンドは作るんだ
茜
せやな、名前空間は後程無くしていくで
茜
descriptionもさっき決めた定数をそのまま代入。
茜
permissionLevel、つまり権限もお好みで。
茜
今回はこれは本題ではないので、簡単に使えるようAnyに設定。
茜
引数も特にええかな
茜
で、これが終わったら実行時に発火する関数を定義。これはさっき記述したので、それを入れよう
葵
んー、一応それっぽいスクリプトはできたけど、これは前回のスクリプトの書き方変えただけで本質的には一緒だよね?
葵
どうやって名前空間なくすの?
茜
実は、これと同じようなスクリプトをもう一個書けばええねん
葵
え、どういうこと?
茜
でもただ書くといってもな、今度は名前空間なしで登録。
茜
つまり、nameの部分はcommandNameだけ。
茜
description以下は同様。
葵
え、つまり2つ書くということ?
茜
せやな、こうやって書くことで、1つ目はコマンドの正式版として、そして2つ目はそのコマンドの短縮版として認識されんねん
茜
で、このように書く意図があるからこそ、最初にコマンドの名前とか説明とか複数回書くことが予想される部分は定数にしたってこと
茜
以上でスクリプトの全文は終了。ちなみに、このスクリプトについては、概要欄のリンクからコピペできるから参考にしてみてね
茜
で、実際にこれを書いてゲームを実行すると、こんな感じで名前空間がない、シンプルな「test」というコマンドが登録されてることが分かる?
葵
あー、あるね…、そしてよく見ると下にも名前空間付きのやつがある…
茜
せやな、つまり名前空間無しのコマンドを登録しようとした場合、必然的に名前空間つきのものもできてしまう、って感じやな
茜
まぁ実用上はどっちか好きなほう実行すればええんやし、あんまり問題ないと思うわ
茜
で、これを実行したときの結果は冒頭の通り。ちゃんとコマンドが登録されて実行されることが分かるな
茜
名前空間がないコマンドを作れると、アドオン使う方もだいぶ使いやすくなるし、洗練された印象があるから
茜
是非一度作ってみるとええかもしれへんな
葵
これできると、だいぶ上級者って感じするよね
茜
そして、以上がCustomCommandに関する概要。
茜
ScriptAPIでサポートされているオリジナルコマンドのシステムというのは、大体これくらいで、
茜
次は外部のライブラリに依存した、カスタムコマンドのやり方についても話すわ
葵
というと?
茜
その代表的な例は、冒頭で見せたような日本語でコマンドを実行するようなシステムの構築。
茜
こういうのはScriptAPIだけじゃだめで、外部のライブラリを使わなあかんのやけど、
茜
そのための知識を話す必要があるから、次にその話をするわ
葵
楽しそう!
茜
日本語でのコマンド実行だけど、後述する通りマイクラが公式にサポートしてるわけじゃないから、
茜
現実的にはAIを呼び出して、文脈に応じた処理を自動実行する必要があるわけ。
茜
ということで、マイクラ内にAIを呼び出さないと始まらへんから、先にAIを呼び出すシステムを構築しよう
茜
先に挙動を見せるわ
茜
メッセージ欄にこんな感じでメッセージを送信すると、
茜
AIからの返答が返ってくるっていう感じやな
葵
え、すごい!
茜
勿論、簡単な挨拶だけじゃなくて、
茜
「エンドラを討伐したい」と入力すると…
茜
ちゃんとしたアドバイスが返ってくるし、
茜
クリーパーに拠点を爆破されたという愚痴にも…
茜
いい感じのアドバイスが返ってくるで
茜
「コマンドブロックが欲しい」と入力すると…
茜
こんな感じで入力するべきコマンドを教えてくれんねん
葵
へえ、有能じゃん
茜
ここらへんは流石AIって感じやな
茜
当然、「あなたはだあれ?」みたいな何でもない質問にも
茜
ちゃんとした回答が返ってくるで
葵
頼りになるサポーターって感じだね
茜
サバイバルはもちろんクリエイティブでも役立つし、
茜
何より話し相手として機能するのは強いよな
葵
これって最近流行りのChatGPTなのかな?
茜
せやで、chatGPTをそのままminecraftに持ってきた感じやな
茜
今回はこのAIチャットBotの作り方を解説するで
葵
いつものようにScriptAPIを使うのかな?
茜
ホントはそれがええんやけど、今回はちょっと問題があんねん
葵
え、問題?
茜
ScriptAPIだと普通、importでマイクラのモジュールを持ってくるんやけど
茜
今回みたいな完全に外部のモジュールを
茜
importするのはできへんのよ
茜
つまりScriptAPIは使えへんってわけ
葵
え、じゃあムリってこと?
茜
さっき実際にできたやろ
葵
じゃあ、どうやるんだろう?
茜
結論から言うとこれはアドオンじゃなくて、
茜
WebSocketServerという仕組みを使ってんねん
葵
どういうこと?
茜
今回はマインクラフトとサーバーとの間で
茜
リアルタイムで常時情報のやり取りをしている感じやな
茜
具体的にはチャット欄に文字が送信されたときに、
茜
その情報をサーバーに送ったうえで、
茜
サーバー側でAIの返答を用意してそれを返す、という仕組み
茜
まずはこのサーバーの部分を作っていこう!
茜
まず必要なものはこんな感じ。
茜
何らかのテキストエディタと、node.jsを前提に解説するで
葵
え、node.jsっているの?
茜
今回はjavascriptでサーバーを構築していくから必須やけど、
茜
当然pythonとかCとか他の言語で構築する場合は不要やな
茜
概要欄にそれぞれのリンクがあるで
茜
まずは適当な作業フォルダを作って、それを開こう
葵
これは何でもいいの?
茜
せやで、自分で分かりやすいパスやとええかもな
茜
このフォルダ内で右クリックしてファイルを作成しよう
茜
名前は何でもええんやけど、今回はmain.jsとするで
茜
これを開いたらファイルメニューを選択して
茜
中にあるopen folderを選択。
茜
この作業フォルダを選択しよう
茜
次にTerminalからnew terminalを選択して、
茜
ターミナルを表示しよう
茜
そしてここにnpm initと入力。
茜
色々出てくるけど、全部エンターでOK
茜
ちなみにこの処理にはnode.jsが要るから注意な
茜
続いてnpm install ws uuidと入力
葵
これはどういう意味?
茜
今回必要になる二つのモジュールをインストールするコマンドやねん
茜
これが完了したら、色々ファイルが生成されるで
茜
これを確認したら、早速スクリプトを書いていこう
葵
どんなスクリプトを書くの?
茜
まずはサーバーを構築して、マイクラとの交信を可能にするで
茜
まずはさっきinstallした二つのモジュールを読み込み、
茜
WebSocketServerを作っていこう
茜
定数を頭文字からとってwssとして、
茜
port番号を定義。
茜
次にサーバーへ接続したときに発火する関数を定義しよう
茜
引数をsocketとして、
茜
接続成功時にメッセージを送信。
茜
これで無事接続できたかどうか分かるわけやな
葵
これって実際に動くのかな?
茜
じゃあ、ちょっとやってみようか
茜
その前に一つやることがあって、
茜
コマンドプロンプトを起動してこんなコマンドを入力しよう
葵
何この暗号…
茜
これでマイクラがlocalhostと接続できるようになんねん
茜
マイクラを起動して、適当にワールドを作成。
茜
今回はコマンドを使うから、チートはオンにしよう
茜
vscode側でF5キーを押して、Node.jsを選択しよう
茜
そしてこの緑のマークを押すとこのスクリプトを実行できるで
茜
ワールドの起動が完了したら、チャット欄を開いて
茜
connectコマンドを使ってさっき作ったサーバーと接続。
茜
ポート番号はさっき設定したものと同じにしよう
葵
なんか出てきたよ?
茜
ちゃんと接続できたことが分かるな
茜
サーバー側の表示もOK
茜
今回はプレイヤーのチャット欄を監視するわけやけど、
茜
このイベントを予めsubscribeしてマイクラ側に送信しよう
茜
送信するときのフォーマットは決まってるから
茜
この通りに書いていこう
茜
messageTypeはcommandRequest、
茜
messagePurposeはsubscribeとしよう
茜
そしてこのJsonObjectをsend関数で送信。
茜
続いてプレイヤーがmessageを送信したときに発火する関数を定義
茜
rawDataとisBinaryを引数として
茜
まずはJson形式で送られてきたrawDataを変換。
茜
これを定数dataに格納しよう
茜
続いてコマンドとして出力するためのオブジェクトを定義。
茜
このフォーマットも大体決まってるからその通りに記述。
茜
これでコマンドの実行をマイクラ側に指示することができんねん
葵
結構大事だ
茜
せやねん
茜
具体的なコマンドの内容はこんな感じで記述しよう
茜
今回はテストとしてsayコマンドで試してみるで
茜
最後にこのオブジェクトをJson形式に変換して、
茜
send関数を使ってマイクラ側に送信しよう
茜
一旦このスクリプトを動かして、
茜
試しになにかメッセージを送信すると…
葵
なんか出てきた
茜
こんな感じでsayコマンドが自動実行されるわけやな
茜
続いて今回のメイン、aiを呼び出す関数を作っていこう
茜
まずは非同期実行の関数、open aiを宣言。
茜
引数はaiに送信するメッセージとしよう
茜
続いてchatGPTを外部から呼び出すのに必要な、
茜
apiキーをここで設定。
茜
apiキーはこのサイトで入手できるし、
茜
方法について検索すると結構色々出てくるで
茜
ここにapiキーを入力したら、
茜
fetch関数を使ってchatGPTからの返答を取得しよう
茜
URLはこちら
茜
続いてこのURLにリクエストを送信。
茜
内容はある程度決まってるから、この通りに書こう
茜
一応補足すると、これは呼び出すchatGPTのモデル。
茜
GPT 4oがおススメかな…
茜
そしてこれはシステムとして送信するメッセージ。
茜
要は、あなたはマイクラのアドバイザーですよ、といってるわけやな
茜
これが終わったらjson形式で返答を取得して、
茜
messageのcontent部分を抽出したものを取得し、
茜
最後にreturnしてこの関数部分は終了
茜
続いてさっきの部分に戻って、
茜
マイクラで送信されたメッセージを取得。
茜
どんなメッセージに対しても無条件でaiの返答が返ってきたら困るから
茜
どりあえずメッセージがはてなで終わるものに制限して、
茜
さっき作った関数を呼び出し。
茜
これが非同期実行であることを考慮して、
茜
aiの返答、responseを引数として発火する関数を定義
茜
とりあえずこの返答をコントロールに表示してみよう
茜
他の部分もif文の中に入れてしまおう
茜
そしてマイクラでメッセージを送信すると
葵
あ、コンソールに返答が表示されてる
茜
あとはこれをマイクラ内で表示するだけやな
茜
今回はtellrawコマンドで表示していくんやけど、
茜
長すぎる文章は表示できへんみたいやから、
茜
点や改行で分割してそれぞれについて表示していくわ
葵
なるほど
茜
いまやってるのはaiの返答を分割するところやな
茜
for文を使ってそれぞれについてtellrawを実行していこう
茜
ちなみにメッセージの送信者はbody.senderで取得できるで
茜
実際にマイクラでメッセージを送信すると…
葵
あ、ちゃんと返答が表示されてる
茜
もちろん、他の文章でもOK
茜
これ、実際に動くと結構楽しいのでみんなもやってみてね
葵
これってアドオンじゃないんだよね…
茜
せやねん、だけどアドオンじゃないからこそ、
茜
本来できなかったことができるようになるという点で、
茜
かなり自由度が上がんねん
茜
WebSocketServer、みんなも触ってみてね
茜
そしてこの方法を用いて、実際に日本語でコマンドを実行できるシステムを作っていこう!
葵
ホントにそんなことできるの?
茜
百聞は一見に如かずやな、ここに「ダイアモンドが欲しい」と入力するやろ?
茜
そしたら実際にダイアモンドが手に入んねん
茜
確かにインベントリ内にもダイアモンドが入ってるな
茜
同じように、エメラルドが欲しいと入力すると…
茜
ちゃんとgiveコマンドが実行されてエメラルドが手に入んねん
茜
コマンドの実行結果もOK
葵
すごいね、どうやってるんだろう?構文解析?
茜
その辺の事情は後で話すわ
茜
実はgiveコマンド以外も使うことができて、例えばここに「ゾンビを倒したい」と入力すると…
葵
あ、ゾンビが倒されて腐肉が落ちてる
茜
同じように、アイテムを消去したいと入力すると…
葵
あ、きれいになくなった!
茜
コマンド自体の実行結果もOK
茜
こんな感じで、発言に応じて意図をくみ取ったコマンドを実行できんねん
茜
最後に、ここにいる村人に、毒の効果を付けてみよう
葵
え、そんなこともできるの?
茜
それができんねん、とりあえず「村人を毒にしたい」と入力
茜
すると実際にコマンドが実行されて、
茜
村人が毒になってることが分かるな
葵
いろいろできるんだね
茜
次はこのやり方について話すわ
茜
で、おそらく方法はいくつかあるんやけど、まずパッと思いつくのが葵もさっき言ってた構文解析的なやり方
茜
要は日本語の文章を解析して、「欲しい」という単語があったら機械的にgiveコマンドに変換するっていう感じやな
葵
それが自然に見えるけど…
茜
だけど「欲しい」じゃなくて「入手したい」とか「得たい」とか別の語句だった時にはこの方法は使えへん
茜
そうじゃなくても、例えば「頭が赤い魚を食べる猫」っていう文章があったとするやん?
葵
有名な文章だね
茜
たったこれだけ文章なのにいくつかの解釈ができんねん
葵
日本語って難しい…
茜
で、それを踏まえると統計学的手法、とりわけニューラルネットワークを用いた方法が取り得るわけやな
茜
今回はchatGPTの力を借りてこれを実装してみよう
葵
そういえばこの前、chatGPTをマインクラフト内で呼び出す方法について解説してたよね
茜
せやな、あそこで使ったスクリプトをちょっと変えて、自然言語でコマンドを実行できるようにしてみよ!
茜
基本的にこの前のスクリプトのうち、大きく変えるのはsystemに与えるmessage部分かな
葵
あ、これをコマンド生成に特化させなきゃいけないのか
茜
とはいえ、それを長々と書くのは無理があるので、ここでは引数としてsystemにmessageを送るにとどめておこう
葵
そういうのは別のファイルにまとめておくといいのかな?
茜
せやな、それを踏まえてまずはfsモジュールをもってこよう
茜
require関数で定数fsを定義。
茜
続いてファイル名を引数としてその中身を取り出す関数を作っていこう
茜
関数名はreadFileContent、引数名はfilePath
茜
定数dataを宣言して、ここに非同期実行であることを考慮しながらファイルの中身を格納しよう
茜
最後にtoString関数でこれをstring型にしたら、それをreturn。
茜
一応、try catch文で予期せぬエラーにも対応しておこう
茜
それが完了したら、aiを呼び出す前にreadFileContent関数を呼び出し。
茜
とりあえずファイル名をsystem.txtとして、その内容を取得して引数に代入しよう
葵
これでaiにそのメッセージが送れるのね
茜
せやで、最後に送られてきたコマンドをそのままcommandLineに入れてコマンドを実行。
茜
send関数でマイクラに送信するのも忘れずに。
茜
最後にAIに送信するメッセージを定義して、これをsystem.txtとしよう
葵
なんか、思ったより長いね
茜
まあ、どうしてもaiにマイクラのコマンドについて教えなあかんからしゃーなしやな
茜
最初に役割と制約を与えて、次にマイクラの主要なコマンドの例を教えてる感じ。
葵
確かにずらっとコマンドが並んでるね
茜
chatGPTのapiがknowledgeに対応していたら楽なんやけどな
茜
最後に各種ターゲットセレクターについての基本的な知識を与えて、終了。
茜
さて、実際にこのweb socket serverと接続してみよう
茜
やり方は前回と同じ。
茜
続いて「ダイアが欲しい」とこちらの意思を伝えると…
葵
あ、コマンドが実行されてる
茜
こんな感じで、日本語のメッセージからコマンドが実行されてることが分かるな
茜
ちょっと変わったメッセージも試してみよう
茜
villagerを上空に移動させたいと入力すると…
葵
へー、すごい。色々できるんだね
葵
でも、これが実用化したら世の中のコマンド解説者の立つ瀬がなくなると思うんだけど…
茜
まあ、まだ大丈夫なんとちゃう?
茜
たぶんexecuteとかscoreboardとかはまだムリやろ
葵
教えるだけでは…
茜
こんな感じで、外部ライブラリ使えばチャット欄の検知から日本語でのコマンド実行も可能になんねん
茜
外部ライブラリつながりで、もう一つ面白い方法があるから紹介するわ
茜
ちょっとこの画面見てほしいんやけど..
葵
なんか左上に表示されてるね?破壊したブロック?
茜
せやで、こんな感じで破壊したブロックとプレイヤーを表示したり、
茜
チャット欄を監視して、特定の語句があったら何かを実行する、
茜
カスタムコマンドみたいなことをやりたくなること、あらへん?
葵
あー、イベントをトリガーして何かを実行するやつね。それってコマンドでできないの?
茜
マインクラフトのコマンドシステムって、プログラミングとちゃうから条件分岐とか制御構文は苦手やねん
葵
じゃあ、カスタムコマンドとかブロックの破壊をトリガーするのはムリなのか..
葵
あれは? ScriptAPI。こういうときこそ、出番でしょ?
茜
確かにこれはJavaScriptというプログラミング的アプローチをとるからそういうのも可能ではあるんやけど…
茜
ちょっと複雑なこと…例えば外部のAPIとかライブラリを用いた発展的なことをやろうとすると案外力不足なんよ…
茜
で、今回の動画はタイトルにもある通り、ScriptAPIとは別の方法でイベントのトリガーと実行をやってみよう、って話。
茜
結論を言うと、鍵を握るのがbedrockpy。
葵
なにそれ?アップルパイの仲間?
茜
食べ物の話してどうすんねん…
茜
これは、Pythonっちゅうプログラミング言語のライブラリの1つで、
茜
簡単に言えばPythonを用いてゲームに干渉し、いろんなイベントをトリガーすることが可能になんねん
葵
え、Pythonでゲームを動かせるの?
茜
そういうことになるな
茜
冒頭の映像も、実はPythonでコマンドを実行して作ってんねん
葵
へー、ScriptAPIじゃないんだ。
葵
でもさ、Pythonでゲームを動かして何が楽しいの?
茜
一番大きいのは外部のライブラリを使える点やろうな
茜
例えばChatGPTのAPIを使うことでマイクラ内でAIと会話できるシステムをPythonで構築したり
茜
ファイルの読み書きも可能になるから、データの保存とか、あるいはデータベースを参照してワールドに変化を加えることもできるかも
葵
面白そう。ScriptAPIじゃできないところだね
茜
百聞は一見に如かずということで、まずは簡単なスクリプトを書いてみよう
茜
で、まず必要になるのがPythonの開発環境。
葵
このチャンネルでPython触るの初めてかな?
茜
せやな、まだインストールしてへん人はまずはこのページからインストールしよう
茜
Pythonは今はやりの言語やし、何かとあると便利かも。
茜
で、あともう一つはこのライブラリそのもの。
茜
Pythonが使える環境ではpipも使えると思うから、ターミナルでこのコマンドを実行してインストールしよう
茜
そしたら適当な場所を開いてpythonのファイルを新規作成。
茜
(適当な名前).pyという名前であればOK。
茜
そしたらこれをVSCodeのようなテキストエディタで開いて
茜
最小構成となるスクリプトを書こう
茜
まずは先ほどインストールしたライブラリから必要なmoduleをimport。
茜
続いてServerオブジェクトを作成して、それを変数appに格納
茜
最後にapp.startでサーバーを起動。今回は公式リファレンスに沿ってポート番号を6464としてみよう
茜
スクリプト全文はこんな感じ。概要欄にあるサイトからコピペできるで
葵
これで終わり?何か特別なことをしているようには見えないけど。
茜
スクリプトの最小構成としては、そう。まずはちゃんと動くか見てみよう
茜
ターミナルを開いてこのコマンドを実行するか、
茜
VSCode右上の矢印マークを押せばOK。
茜
ターミナルの方には何も表示されてへんとは思うけど、
茜
マインクラフトのゲーム内で、こんな感じのconnectコマンドを実行して…
茜
「サーバーへの接続を確立しました」と表示されれば成功。
茜
それが分かれば一旦実行中のターミナルを中止して…
茜
次にイベントをトリガーするスクリプトを書いてみよう
葵
どんなスクリプトを書くの?
茜
まずはさっき見せたように、簡単にブロックの破壊をトリガーして、メッセージとしてそれを送信する例。
茜
非同期で実行する関数block_brokenを定義して、引数をctxとしよう
茜
続いてブロックを破壊したプレイヤー名を取得してそれを変数に格納。
茜
プレイヤー名はこんな感じで取得できるで
葵
なんでそんなの分かるの?
茜
この事情は後で話すわ
茜
同じように、破壊されたブロックも取得して変数に格納
茜
今回は、ctx.id。
茜
次に、これらの変数を元に出力する機構を作っていこう
茜
ctx.server.runで任意のコマンドを実行出来て、
茜
この引数にsayコマンドを指定する感じでええかな
葵
あ、f文字列だ
茜
Pythonらしいよな
茜
そしたらこれを保存して、同じように実行しよう
茜
接続が切断されているので、再度connectコマンドを実行するのも忘れずに…
茜
これでブロックを壊すとメッセージが送信されてるのが分かるやろ?
葵
ホントだ。sayコマンドが実行されてるのかな?
茜
こんな感じでPythonを使ってゲームに干渉できてることが分かるな
茜
同じように、プレイヤーのメッセージを監視してそれに応じて何らかの処理を行う関数も書いてみよう
茜
非同期で実行する関数、player_messageを定義して、引数をctxに設定。
茜
さっきも使ったctx.server.runでダイアモンドを1つ与えるコマンドを実行してみよう
茜
ここでメッセージを送信すると…
葵
あ、ダイアモンドが増えてる
茜
これはメッセージの文言によらず、メッセージの送信をトリガーしたら必ず特定の処理を行う例やな
葵
これを応用してカスタムコマンドを作りたいね
茜
やってみよっか
茜
さっきの関数に戻って、まずは送信されたメッセージ自体を取得して変数に格納しておこう
葵
これはctx.messageなんだ
茜
そしてif文を用いてもしこのメッセージが「?」で始まるなら…
茜
このコマンドを実行するよう定義すると、
茜
例えば「?テスト」というメッセージだとコマンドは実行されるし、
茜
「test」だと実行されへん
葵
メッセージに応じて処理を変えることもできるんだね
茜
で、このif文の中身を変えてメッセージが「?diamond」であるかどうかを判定するようにすると…
茜
完全一致で「はてなdiamond」というメッセージでないとダイアモンドはもらえへん
葵
これが事実上のカスタムコマンドとして動作するんだね
茜
せやな
茜
他にもこんなのをトリガーできるよ、という例として…
茜
アイテムを右クリックして使用したことを検知してみよう
葵
ScriptAPIで簡単にできるよ…?
茜
Pythonでやるからええんよ..
茜
ここでは非同期関数、item_usedを定義しよう
茜
で、ここでアイテムを使用したプレイヤーとか、使用したアイテム名を取得したいなあ、って思うやん?
葵
もちろん。どうするの?
茜
それが分からへんのよ…
葵
ダメじゃん
茜
で、そんなときにどうすればええのか、って話。
茜
まずはprint(ctx.data)と書いてみよう
葵
データを出力するのかな?
茜
そういうこと。
茜
で、ゲーム内でなんでもいいから使えるものを手に取って
茜
右クリックして使った後にターミナルを見ると…
葵
なんか色々表示されてるね
茜
ここに表示されてるObjectを元にこれを解析すればええことが分かるな
茜
ということで、改めてスクリプトを書き直そう
茜
使用したアイテムは、さっきのdataのitemというキーのidに保存されていることが分かるから、このようにして取得。
茜
同じように、player名も取得して変数に代入しよう
葵
これはさっきと同じだね
茜
このprintは要らんから削除して、代わりにこれらの変数からコマンドを実行するスクリプトを書いてみよう
茜
この状態でくもの目を使うと…
葵
あ、左上にメッセージが出てきたね
茜
カスタムアイテムのチョコミントアイスも…
葵
あ、こっちも表示されてる。名前空間はない感じかな?
茜
たしかdataにnamespaceが含まれてたと思うから、そこから抽出することで名前空間も表示できそうやな
茜
で、最後に何がトリガーできるの?という問題。
茜
これに関してはbedrockpyの公式リファレンスにほとんど書いてて、他にもブロックの設置とかワールドへの参加とかトリガーできるみたいやで
葵
割とできること多そう
茜
これと外部のライブラリを組み合わせたらだいぶ自由度が広がると思うわ
茜
うp主もこれを使ったプロジェクトを2つほど計画中だったり。
茜
ということで、Pythonを使ってマイクラを操作できるbedrockpyの紹介でした
葵
ScriptAPIと使い分けつつ…って感じかな?
茜
それもええかもな。ただ、これ自体はアドオンじゃない点には注意。
茜
結構楽しいので、皆も触ってみてね!
葵
でもさ、ここで定義した疑似カスタムコマンドって普通にScriptAPIで定義するのとどう違うの?
茜
これについてはさっきも軽く触れたけど、大元がPythonなんよ、
茜
だから、本来ScriptAPIではできひんかったこと、例えばAIの導入とかDiscord、XのAPIと組み合わせて何かやるみたいな、
茜
より複雑で外部のライブラリを呼び出すようなカスタムコマンドを定義できるっていうのは強みかもしれへんな
葵
あー、それはだいぶ自由度が高いね
茜
という感じで、カスタムコマンドの概要についてはこんなところかな
茜
ここで話したことはあくまで基礎で、これを使って色々試してみることでオリジナルのアドオンだったりソフトだったりができると思うわ
葵
こうしてみると、コマンドって奥が深いんだね、
葵
普通に色々コマンドを実行して遊ぶのも楽しいけど、自分でコマンドを作るという分野も結構興味深いものがある…
茜
みんなもオリジナルのコマンドを定義して遊んでみてね!
茜
ということで、今回の動画はこれでおしまい
葵
ここまでのご視聴、お疲れ様です
茜
質問や雑談のためのDiscordチャンネルもあるので、是非覗いてみてください!
茜
それじゃー、
茜
ばいばーい
葵
ばいばーい

使用している素材等

Special Thanks

本サイト内では、記事内の画像・サムネイル画像などで以下の素材を使用していることがあります

【立ち絵】 琴葉姉妹 – ユメのオワリ様

今回解説したスクリプトを使って、友人・コミュニティー内で24時間いつでも遊べるマルチサーバーを構築したい場合は、VPS(レンタルサーバー)の導入が近道です。

私の開発・検証環境では「シンVPS」を使用しています。

シンVPS コントロールパネル画像

以前は自宅PCでホストしていましたが、PCの電源を切り忘れるストレスや、電気代を考慮して移行しました。シンVPSを選んだ決め手は、圧倒的なコストパフォーマンスです。

  • 月額の安さ 4GBプランでも月額1,200円程度(長期契約なら1,000円以下)と維持費が安く済みます。
  • 高速な読み込み 全プランでNVMe SSDを採用しており、ワールドの読み込みや重いコマンド処理でもラグを感じにくいです。
※最低利用期間が3ヶ月からという縛りはありますが、サーバーを長期で安定運用するならむしろ割安になります。

以下のリンクから申し込むと初回利用料金が10%OFFになります。
(1ヶ月以上の契約対象。「安くて速い」サーバーを探している方はぜひ試してみてください)

シンVPS 公式サイトを見てみる
(10%OFF適用)
マルチサーバーの立て方はこちら
最新情報をチェックしよう!