2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Minecraft BE Addon講座#15】スクリプトの活用例~キルカウントを作ってみよう~

Last updated at Posted at 2021-10-03

本ページは アドオン講座#15、「スクリプト編、スクリプトの活用例」 です。
目次や、この講座については#1の記事をご覧ください。

スクリプト編で理解してほしいこと!

2022年4月19日のアップデート、Minecraft 1.18.30(Bedrock)(ベータ版では2022年1月27日のMinecraft Beta 1.18.20.21)をもって ScriptingAPIは削除されました。
本Addon講座#14、#15で解説されているScriptingAPIは、これ以降のバージョンで使用できません。
スクリプトの実行は、代わりにGameTestFrameworkが担っていくようです。

ScriptingAPIは執筆現在、Minecraft Bedrock Edition for Windows10(通称:win10版)でしか動作しません。(でした)
条件を満たさない場合、iOS版、Android版などでは実行できないのでご注意ください。
この制限について、またこの制限の対策について、詳しくは#14「サーバーとクライアントの違い」をご覧ください。

スクリプト編はこのアドオン講座の他の応用編と異なり、.jsonファイルではなく、Javascriptを使用することもあり、レベルとしては応用編の中でも発展寄りになります。
しかし、Javascriptに少しでも触れたことがある方にとってはスクリプト編の方が簡単に感じるかもしれません。

スクリプト編は2ページからなり、前半#14はスクリプトの書き方(記法)と仕組みの理解について扱い、後半#15ではどのように使うのか実際にアドオンを作ってみます。

このスクリプト編では、Javascriptを使うことで、.jsonなどの限られた機能だけではできなかった検知や、それを利用したシステムの製作ができるようになることを目的としています。

  • (前提#3~#4)基礎編
  • (前提#8)ファンクション編復習、コマンドの実行主の理解
  • (#14)ScriptingAPIとは?
  • (#14)サーバーとクライアントの違い
  • (#15)[実践]キルカウントを作ってみよう

[実践]キルカウントを作ってみよう

Java版にはスコアボードの機能としてキルカウントがありますが、BEにはdummy型しかありません。
再現するには「武器を1確(一回当たったら倒れる)にしてスコアやらタグやらでmobやらを経由して@p」という誤検知しやすく制限の多い方法を使うしかないのです。

しかし、ScriptingAPIでは攻撃したときの検知、エンティティが倒されたときの検知ができ、攻撃者と被害者のプレイヤーの両方の情報(コンポネ)が取得できるため、スクリプト完結で誤判定のないキルカウントを作ることができます

一応解説のためのアドオン講座なので、解説を挟みながら作っていきます。

①フォルダ・ファイルの用意

まずはフォルダ・ファイルの用意です。
フォルダはbehavior/scripts/serverのように用意します。
これはバニラにもあるフォルダなので分かると思います。
behavior/scripts/clientは使用しないので、コピーしてきた場合は消しておきましょう。

ファイルは**名前自由で拡張子.js**です。
今回はserver.jsとしましょう。

②お膳立て(毎回コピペするやつ)

②-1.スクリプト登録

エディタでserver.jsを開きましょう。
開いたらまず1行目に

behavior/scripts/server/server.js
const system = server.registerSystem(0,0);

を書きます。
これは、マイクラ側に 「このファイルはスクリプトのサーバーのファイルです」と登録する文1になります。

ちなみに、サーバーではなく、クライアントの場合は、

behavior/scripts/client/client.js
const clientSystem = client.registerSystem(0,0);

です。
(変数、定数ってクライアントとサーバーで連動するんでしょうか…?有識者の方いましたら教えてください…!)

②-2.組み込み関数

次に、大元の関数(組み込み関数)を書きます。

behavior/scripts/server/server.js
//ワールド始動時実行
system.initialize = function() {
  //イベントの登録、コンポーネントの登録、クエリの登録、イベントの検知登録
};

//更新されるたび(=tickごと?)実行
system.update = function() {
  //更新
};

//ワールド終了時?実行
system.shutdown = function() {
  //片付け
};

この3つのどれかを起点に実行は始まります。
updateshutdownは使ったことがないので、どういう用途なのか、どういう挙動なのかよく分かりません。。。
updateはワールドに入った後で、エンティティを出したり動かしたりするなど、イベントの検知以外のタイミングで実行するときに使うようです。
shutdownに関しては例がありませんでした。

今回は「エンティティが倒された」というイベントの検知で実行をするので、initializeのみ使用します。

②-3.イベントの検知登録(listenForEvent)

behavior/scripts/server/server.js
system.initialize = function() {
  //エンティティが攻撃を受けたら関数「system.entity_hurt」を実行する
  this.listenForEvent ("minecraft:entity_hurt", (eventData) => this.entity_hurt(eventData));
  //エンティティが倒されたら関数「system.entity_death」を実行する
  this.listenForEvent("minecraft:entity_death", (eventData) => this.entity_death(eventData));
};

このように書きます。
エンティティが倒されたときのイベント"minecraft:entity_death"をlisten(検知)しています。

参考に、攻撃を受けたときのlistenForEventも書いてみました。
検知したときに実行する関数名は自由です2
今回は"minecraft:entity_death"の方しか使わないので、"minecraft:entity_hurt"の方は消しておきましょう。

③実行する処理

behavior/scripts/server/server.js
//ここまでのまとめ
const system = server.registerSystem(0,0);
system.initialize = function() {
  this.listenForEvent("minecraft:entity_death", (eventData) => this.entity_death(eventData));
};
system.entity_death = function(eventData) {
  //ここにエンティティが倒されたときに実行する処理を書いていくよ
}

③-1.コンポーネントのデータを得る

ScriptingAPIではエンティティのコンポネの値を取得することができます。
コンポーネントについて#5でプレイヤーの名前表示を出したり消したりしましたが、そのときに使った"minecraft:nameable"というコンポーネントを使います。

behavior/scripts/server/server.js
let hurter = this.getComponent(eventData.data.entity, "minecraft:nameable").data.name;

"minecraft:nameable"nameには名前が入っています3
これで変数hurterに「eventDataのentity」の名前が代入されました。

「eventDataのentity(eventData.data.entity)」はイベントを起こしたエンティティ、つまり今回は倒されたエンティティを指します。
じゃあ倒したエンティティの名前はどうやって取得するかというと、「eventDataの中のkiller(eventData.data.killer)」から取得します4

behavior/scripts/server/server.js
let hurter = this.getComponent(eventData.data.entity, "minecraft:nameable").data.name;
let killer = this.getComponent(eventData.data.killer, "minecraft:nameable").data.name;

これで変数killerに「eventDataのkiller」の名前、つまり倒したエンティティの名前が代入されました。

③-2.コマンドの実行

次はスクリプト内でのコマンドの実行です。
コマンドの実行は"minecraft:execute_command"というイベントで行われます5
①イベントデータを作り、②イベントの内容を設定(=実行するコマンドを記述)し、③イベントを実行する、という3stepを踏みます。

behavior/scripts/server/server.js
let execute_command = this.createEventData("minecraft:execute_command");
execute_command.data.command = `/scoreboard players add @a[name="${hurter}"] deathcount 1`;
this.broadcastEvent("minecraft:execute_command", execute_command);

それぞれの行が3stepに対応しています。
複数コマンドを実行する場合は②③を繰り返します。

behavior/scripts/server/server.js
let execute_command = this.createEventData("minecraft:execute_command");
execute_command.data.command = `/scoreboard players add @a[name="${hurter}"] deathcount 1`;
this.broadcastEvent("minecraft:execute_command", execute_command);
execute_command.data.command = `/scoreboard players add @a[name="${killer}"] killcount 1`;
this.broadcastEvent("minecraft:execute_command", execute_command);

①はコマンドの文を代入できるようにイベントの型を作っている、っていう感覚ですかね。
作った型はexecute_command.data.commandに再代入すれば使いまわせるって感覚です。

もちろん作ったイベントデータを代入する変数名はexecute_commandではなくexecuteCommandでもcommandEventでもなんでも大丈夫です。
ただしそのイベントデータを使うところでは最初に代入した変数名と同じ名前にしなければならないので注意。
プログラムの変数やマイクラのスコアボードと考え方は同じです。

<注意>
コマンドに使うセレクタの名前指定は@a[name="名前"]という方法で行ってください
/scoreboard players add 名前 deathcount 1/scoreboard players add @p[name="名前"] deathcount 1ではできません。
理由はよく分からないのですが、エンティティが倒された瞬間に実行するコマンドなので、名前@p[name="名前"]では、倒されたエンティティが見つからないようです(これに気付くのに2か月くらいかかりました)。

<追加情報>
ScriptingAPIは座標(x,y,z)=(0,0,0)を中心に実行されるようです。
@pや相対座標(チルダ~~~)を使う場合、この影響を受けることになります。

④まとめ

behavior/scripts/server/server.js
//スクリプトの登録
const system = server.registerSystem(0,0);

//ワールドを開いたとき実行
system.initialize = function() {
  //イベント検知の登録
  this.listenForEvent("minecraft:entity_death", (eventData) => this.entity_death(eventData));
};

//エンティティが倒されたとき実行
system.entity_death = function(eventData) {
  //名前の取得
  let hurter = this.getComponent(eventData.data.entity, "minecraft:nameable").data.name;
  let killer = this.getComponent(eventData.data.killer, "minecraft:nameable").data.name;
  //コマンドの実行
  let execute_command = this.createEventData("minecraft:execute_command");
  execute_command.data.command = `/scoreboard players add @a[name="${hurter}"] deathcount 1`;
  this.broadcastEvent("minecraft:execute_command", execute_command);
  execute_command.data.command = `/scoreboard players add @a[name="${killer}"] killcount 1`;
  this.broadcastEvent("minecraft:execute_command", execute_command);
}

ワールドに入ったら、
/scoreboard objectives add killcount dummy
/scoreboard objectives add deathcount dummy
を実行します。
/scoreboard objectives setdisplay sidebar [表示したいobject名]
も実行して、倒したり、倒されたりしてみてください。
倒せばkillcountが増え、倒されればdeathcountが増えるのが分かると思います。

これで一応完成です!

⑤さらに極めてみる

ついでにキルデス通知も作ってみましょう。

behavior/scripts/server/server.js
const system = server.registerSystem(0,0);

system.initialize = function() {
  this.listenForEvent("minecraft:entity_death", (eventData) => this.entity_death(eventData));
};

system.entity_death = function(eventData) {
  let hurter = this.getComponent(eventData.data.entity, "minecraft:nameable").data.name;
  let execute_command = this.createEventData("minecraft:execute_command");
  execute_command.data.command = `/scoreboard players add @a[name="${hurter}"] deathcount 1`;
  this.broadcastEvent("minecraft:execute_command", execute_command);

  //倒したエンティティがいるなら
  if(eventData.data.killer){
    let killer = this.getComponent(eventData.data.killer, "minecraft:nameable").data.name;
    execute_command.data.command = `/scoreboard players add @a[name="${killer}"] killcount 1`;
    this.broadcastEvent("minecraft:execute_command", execute_command);
    
    execute_command.data.command = `/execute @a[name="${hurter}"] ~~~ playsound note.didgeridoo @s`;
    this.broadcastEvent("minecraft:execute_command", execute_command);
    execute_command.data.command = `/execute @a[name="${killer}"] ~~~ playsound note.harp @s`;
    this.broadcastEvent("minecraft:execute_command", execute_command);
    execute_command.data.command = `/titleraw @a[name="${hurter}"] actionbar {"rawtext":[{"text":"§l§b${killer}§r§lに倒された"}]}`;
    this.broadcastEvent("minecraft:execute_command", execute_command);
    execute_command.data.command = `/titleraw @a[name="${killer}"] actionbar {"rawtext":[{"text":"§l§c${hurter}§r§lを倒した"}]}`;
    this.broadcastEvent("minecraft:execute_command", execute_command);
  }
}

ifを使うことで、溶岩などでHPがゼロになったときに、(「名無しに倒された」という)通知が出ないようになっています。
アクションバーに倒した/倒されたプレイヤー名が出るほか、倒した/倒されたときにSE(効果音)がなるようにしました。

スクリプト編終了!

応用編の中でも難易度の高いスクリプト編でしたが、どうでしたか?
難しいと言いつつ2回分で全内容が終了してしまいました。

ここまで複数の応用編で、学びながら実際にアドオンを作っていくセクションがありましたが、それらは私が今までに作ってきたアドオンを簡易化したものです。

後半の回(今回#15)で作成した機能に、チームの概念を導入して、FF表示やチームメンバーのキル/デス通知、キルレの計算などを実装したものが、配布している「ExtraDeathMessage」というアドオンになります。

さらにこれを発展させてPvPに特化したものが「JustBattlePvP」というアドオンになり、

これをベースに雪合戦に特化したものが

爆弾設置/防衛ゲームに特化したものが

(未配布)

これらの企画の基礎には、アドオンの力があるのです!
そしてこのアドオン講座で解説したことは、実際に配布できるアドオンの技術につながっています!

このアドオン講座で得た知識で、よりおもしろいアドオンが生まれたらいいな!と思っています。

最後まで読んでくれたみなさんが、作りたいものが作れますように!
お疲れ様でした!!

↓目次#1へ↓

  1. 引数の(0, 0)はスクリプトエンジンのバージョンらしいです。どの例を見ても(0, 0)なので、あまり気にせず使いましょう。

  2. Javascript的には関数名はentityDeathの方がしっくりくるかもしれないですね。今回はイベント名にそろえています。

  3. 参考:https://bedrock.dev/docs/stable/Scripting#minecraft%3Anameable

  4. 参考:https://bedrock.dev/docs/stable/Scripting#minecraft%3Aentity_death

  5. 参考:https://bedrock.dev/docs/stable/Scripting#minecraft%3Aexecute_command

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?