【Minecraft BE Addon講座#12】状態(state)の変化(トランジション)とMoLang

本ページはアドオン講座#12、「アニコン編、状態(state)の変化(トランジション)とMoLang」です。
目次や、この講座については[#1の記事](https://qiita.com/pencilya/items/7b7e68265151f42a51ac)をご覧ください。

#アニコン編で理解してほしいこと！
**マイクラBEのゲーム、企画制作に必須**ともいえる**アニメーションコントローラー**。ここではアニコンとは何なのかから、アニコンを活用した**コマンドブロックにないコマンド実行条件の設定**まで、**何かしらのゲームを作れるレベルへの到達を目標**に解説していきます。

* (前提#3~#4)基礎編
* (前提#5)コンポネ編、entity.jsonの書き方
* (前提#8)ファンクション編復習、コマンドの実行主の理解
* (#11)アニコンとは？
* (#11)アニコンの追加方法
* (#11)アニコンの常時実行
* (#12)状態(state)とは？
* (#12)状態の切り替え(トランジション)方法
* (#12)条件の設定とMoLang
* (#12)[実践]空中ジャンプを作ってみよう
* (#13)3発動、on_entryとon_exitとアニメーション

#アニコン編、編集セクション
[前回#11](https://qiita.com/pencilya/items/0c7e5bdc285aca55ee4c)では、アニコンの登録、追加、実行までを解説しましたが、今回#12と次回#13ではアニコンを編集していきます。

今回の内容が理解できれば、アニコンを使ってできるだいたいのことができるようになります。
次回は、だいぶ発展的で、普段アニコンを使っている人でも知らない可能性があるくらいの内容です。
もちろん、使えるようになれば、さらに幅が広がる内容でもありますが、今回#12を乗り切れば一応アニコン一人前だと思います！

要するに！読者のみなさん！難しいけど！挫折しないで！！

#状態(state)とは？
[前回#11](https://qiita.com/pencilya/items/0c7e5bdc285aca55ee4c)や[基礎編#3](https://qiita.com/pencilya/items/a2b6debdf3e0a860688c)でも示したように、アニコンとは、本来アニメーションを切り替えるものです。
今はこのアニメーションを実行している、それが状態(state)です。
**アニコンの仕事は状態(state)を切り替えること**なのです。

なぜここまでしつこく状態という言葉に(state)と付け加えているかというと[^1]、アニコンの中身に`state`という単語が出てくるからです。
これがまさに、先程から説明している状態(state)のことです。

[^1]: ここで、この講座の英語の取り扱い方について軽く触れておきます。英語の中でも、発音でなじみのある単語となじみのない単語があります。例えば、`animation`という単語は、英語で書くよりも、発音で書く、つまりカタカナで「アニメーション」の方が、あっそれかぁと意味をつかみやすいと思っています。(「ビヘイビア」など。また、英語にすると長くて読む気のなくなる単語`component`,`transition`などもカタカナにしました。)こういった単語についてはしつこくカタカナで書いています。対して、`state`はカタカナ「ステート」と書いても、？？？となるので、直訳「状態」を採用しています。漢字の方が分かりやすいよね…？

もちろん、状態を**切り替える**からには複数の状態が必要です。
ここで、[前回#11](https://qiita.com/pencilya/items/0c7e5bdc285aca55ee4c)で作成したアニコンを見てみましょう。

```behavior/animation_controllers/system.animation_controllers.json
{
	"format_version" : "1.10.0",
	"animation_controllers" : {
		"controller.animation.system" : {
			"initial_state" : "default",
			"states" : {
				"charge" : {
					"animations" : [ "third_person_crossbow_equipped" ],
					"transitions" : [
						{
							"default" : "query.get_equipped_item_name != 'crossbow' || (query.item_remaining_use_duration <= 0.0 && !query.item_is_charged)"
						},
						{
							"hold" : "query.item_is_charged"
						}
					]
				},
				"default" : {
					"transitions" : [
						{
							"hold" : "query.item_is_charged"
						},
						{
							"charge" : "query.item_remaining_use_duration > 0.0"
						}
					]
				},
				"hold" : {
					"animations" : [ "crossbow_hold" ],
					"transitions" : [
						{
							"default" : "query.get_equipped_item_name != 'crossbow' || (query.item_remaining_use_duration <= 0.0 && !query.item_is_charged)"
						},
						{
							"charge" : "query.item_remaining_use_duration > 0.0"
						}
					]
				}
			}
		}
	}
}
```

5行目と6行目にstateという単語があります。
5行目`"initial_state"`は直訳で「初期状態」です。
(イニシャル＝名前の最初の文字のことを言いますよね…？)
5行目を翻訳すると、「初期状態は`"default"`とする」ってことです。

6行目から`"states"`のカッコが始まります。
このカッコの中に状態を入れていくわけです。
このサンプルでは`"charge"`、`"default"`、`"hold"`の三つの状態があると分かります。

そしてそれぞれの状態のときにアニメーションを実行しているのも分かるでしょう。
`"charge"`のときはアニメーション`"third_person_crossbow_equipped"`、`"hold"`のときはアニメーション`"crossbow_hold"`を実行しています。

#状態の切り替え(トランジション)方法
状態(state)のことはよく分かったと思うので、今度はアニコンの本業、切り替えです。
この切り替えの機能のことを**トランジション(transition)**と言います。
また、上記の例を見てみましょう。

9行目に`"transition"`とあります。
この`"transition"`は`"charge"`状態の中に書いてあるので、`"charge"`状態のときに、書いてある条件を満たしたらトランジションしますよ、という意味です。

次は11行目です。

```(抜粋)behavior/animation_controllers/system.animation_controllers.json
"default" : "query.get_equipped_item_name != 'crossbow' || (query.item_remaining_use_duration <= 0.0 && !query.item_is_charged)"
→"移動先状態" : "条件"
```

左が移動先の状態(state)、右がトランジションする条件です。
この場合、条件を満たしたら`"charge"`から`"default"`にトランジションするよってことです。

#条件の設定とMoLang
では、そのトランジションする条件は？というと、上記抜粋のものを翻訳すると、
「クロスボウをもっていない」または「｛アイテムを使い続けている時間が0.0以下｝かつ｛持っているアイテムをチャージしていない｝」のとき、`"default"`にトランジションするよ、となります。

これらの条件はMoLangというマイクラ独自の言語で書かれています。
基本的にはプログラミングでいう条件式(`if`などで使われる)と扱いは同じです。
条件に入る値が`true`もしくは`1.0`つまり「真」ならトランジション、`false`もしくは`0.0`つまり「偽」ならトランジションしないということになります。

ここにMoLangで使う頻度の高い基本的な条件記号をまとめておきます。

| MoLang | 意味 | 解説 |
|:-:|:-:|:-:|
| &&  | かつ  | 記号の右と左両方が「真」だと「真」になる。  |
| ｜｜(半角)  | または  | 記号の右と左どちらか一方か両方が「真」だと「真」になる。  |
| ==  | 等しい  | 等しいときに「真」になる。  |
| !  | 否定  | 記号の直後にあるものを「真」と「偽」をひっくり返す  |
| !=  | 等しくない  | 等しくないときに「真」になる。  |

なんだか「集合と命題」みたいじゃないですか…？(笑)

この例ではちょっと複雑なので、ここからはアニコンを自分で自由に変更してみましょう。

#空中ジャンプを作ってみよう！
今回は多段ジャンプを作ってみましょう。
実装するのは、「りんごをかじったら空中ジャンプする」です。

材料は以下の通りです。

> 1. アニコンは`"controller.animation.system"`
> * 初期状態は`"default"`
> * 状態は`"default"`と`"jump"`のふたつ
> * りんごをもっている＝`query.get_equipped_item_name == 'apple'`
> * アイテムを使っている(食べている)＝`query.is_using_item`
> * 空中ジャンプは浮遊エフェクトの付与で行う。

では実際に書いてみましょう！

```behavior/animation_controllers/system.animation_controllers.json
{
	"format_version" : "1.10.0",
	"animation_controllers" : {
		"controller.animation.system" : {
↑材料１
			"initial_state" : "default",
↑材料２
			"states" : {
↑材料３
				"default" : {
					"transitions" : [
						{ "jump" : "(query.get_equipped_item_name == 'apple') && (query.is_using_item)"}
↑材料４、５
					]
				},
				"jump" : {
	                "on_entry": [
                        "/effect @s levitation 1 38 true",
                        "/say Jump!"
↑材料６
                     ],
					"transitions" : [
						{ "default" : "!query.is_using_item" }
					]
				}
			}
		}
	}
}
```

できました～～～
動作確認用にこのジャンプを使ったら`Jump!`と言うようにしておきました。

ポイントは4つ！

* 条件と条件を結ぶのは**「かつ」(&&)**です。「または」(||)だと、りんごを持つだけで飛んでしまったり、別の食べ物を食べたときにも飛んでしまったりします。
* アニメーションではなくコマンドを実行するときは、**"on_entry"**の中に、**スラッシュ付きで**記述します。
* **アニコン内でのコマンド実行は`@s`**指定すると、条件に該当する人のみを対象にします。これが、**アニコンがそれぞれのエンティティで実行されている**根拠です。`@p`は基本的に使いません。
* 目的の状態(state)になったら、**必ず元の状態(state)に戻るように**しましょう。適切な条件を書いて戻らなければ、2回以上発動できなかったり、連続で発動したりします。ここでは「アイテムを使っていなければ」デフォルトに戻るという条件を書いています。[^2]

[^2]: アイテムを持ち変える(選択スロットを変える)だけでアイテムを使っていない判定になるので、「持っているアイテムがりんごじゃなければ」という条件は省略しています。また、このような特殊な場合を除いて、戻るときの条件は、最初のトランジションの条件の否定を書きます。「かつ(&&)」を使っている場合は否定にすると「または(||)」に変わるので注意です(＝[ド・モルガンの法則](https://daigaku-juken.net/%E3%80%8C%E3%81%8B%E3%81%A4%E3%80%8D%E3%80%8C%E3%81%BE%E3%81%9F%E3%81%AF%E3%80%8D%E3%81%AE%E5%90%A6%E5%AE%9A/))。

ちなみにこの空中ジャンプの機構は、私が製作に携わった"MineSmash"というゲームにも使われているものです。

https://twitter.com/pencilya_/status/1431527463696232448

今回のアニコンを基礎として、コマンドを書き足せば、この動画のように、飛べる回数に制限を付けて、残り飛べる回数を経験値のレベルで示す、なんてこともできるわけです。

#クエリ(query)の検索
今回は「りんごを持っている」と「アイテムを使っている」を検知する材料をこちらで用意しましたが、実際にアドオンを作るとなると、これは**自分で必要なものを探す必要**があります。

このようなエンティティにまつわる情報を取得するものを**クエリ(query)**と言います。

クエリは以下のページの下部にまとまっています。

https://bedrock.dev/docs/stable/MoLang

が、しかし、英語なのに加えて、何を返すのかもわからないクエリ、引数を必要とするクエリ、実際には動かないクエリなどが混ざっており、ここから探すのは一苦労なので、よく使うクエリだけ抜粋しました。

| クエリ | 返り値 | 説明 |
|:--|:-:|:--|
| query.get_equipped_item_name  | アイテム名、ex)'apple', 'redstone_block', ...  | 手に持っているアイテム。主に`query.get_equipped_item_name == 'item'`の形で使う  |
| query.is_using_item  | 1.0 か0.0 | アイテムを使っている(右クリック、食べるなど)とき1.0  |
| variable.attack_time  | 1.0 ～0.0  | 殴っている(左クリック)とき1.0、これだけクエリではなく変数(variable)  |
| query.is_moving  | 1.0 か0.0  | 動いているとき1.0  |
| query.is_sneaking  | 1.0 か0.0  | スニークしているとき1.0  |

最初のうちはこれらだけで大体のことは済むと思います。
さらに別な条件が必要！探してみたい！という方は、サイトや、リソースのアニメーションコントローラーなどを見てみてください。

今回はここでおしまいです！
[次回#13](https://qiita.com/pencilya/items/103bb885fa9e4876d25c)では、さらっと流した`"on_entry"`の意味や発動の調整、アニメーションの活用方法を解説していきます。

アニコン編もついに終盤です！
お疲れ様でした！

↓目次#1へ↓

https://qiita.com/pencilya/items/7b7e68265151f42a51ac