Minecraft BE 1.9.0 あたりでエンティティの追加ができるようになったらしいので、試してみた。その時に分かったことをメモしておこうと思う。プログラミングに明るい人向けだけど、何かの助けになれば。
こんなのを作った。プレイヤーのように振る舞う人型友好モブ。まだいろいろ機能足す必要があるけど……
以前挑戦したときも 1.9.0 だった気がしなくもないが、とにかくその時は上手いことモブを追加することができなかった覚えがある。その時の詰みポイントはリソースパックだった。
いろいろ重要な用語が出てくるので、羅列しておこう…
- エンティティ設定
- アニメーション設定、アニメーション名
- アニメーションコントローラ設定、アニメーションコントローラ名
- MoLang
環境
- Windows 10
- Minecraft Bedrock Edition v1.10.0
- VSCode(テキストエディタ)
リソースパックでオリジナルモブを描画する
以前挑戦したときにビヘイビアパックの作り方は把握したのだけど、リソースパックの設定がうまくいかず、ずっと透明人間がゾンビと戦う様を見ているだけだった。リソースパックの書き方のドキュメントなさすぎ……
最終的なエンティティ定義のjsonはこんな感じ。この後の節で詳しく見ていく。
{
"format_version": "1.10.0",
"minecraft:client_entity": {
"description": {
"identifier": "numani:starspread",
"materials": {
"default": "entity_alphatest"
},
"textures": {
"default": "textures/entity/starspread/moonlighter-skin"
},
"geometry": {
"default": "geometry.steve"
},
"animations": {
"idle": "animation.humanoid.base_pose",
"move": "animation.humanoid.move",
"look_at_target_default": "animation.humanoid.look_at_target.default",
"look_at_target_gliding": "animation.humanoid.look_at_target.gliding",
"look_at_target_swimming": "animation.humanoid.look_at_target.swimming",
"holding": "animtion.humanoid.holding",
"charging": "animation.humanoid.charging",
"damage_nearby_mobs": "animation.humanoid.damage_nearby_mobs",
"swimming": "animation.humanoid.swimming",
"use_item_progress": "animation.humanoid.use_item_progress",
"move_controller": "controller.animation.humanoid.move",
"look_at_target_controller": "controller.animation.humanoid.look_at_target"
},
"scripts": {
"pre_animation": [
"variable.tcos0 = (Math.cos(query.modified_distance_moved * 38.17) * query.modified_move_speed / variable.gliding_speed_value) * 57.3;",
"variable.tcos1 = -variable.tcos0;",
"variable.attack_body_rot_y = Math.sin(Math.sqrt(variable.attack_time) * 360.0) * 11.46;",
"variable.cos_attack_body_rot_y = Math.cos(variable.attack_body_rot_y);",
"variable.sin_attack_body_rot_y = Math.sin(variable.attack_body_rot_y);",
"variable.internal_swim_pos = Math.mod(query.modified_distance_moved, 26.0);",
"variable.attack = Math.sin((1.0 - (1.0 - variable.attack_time) * (1.0 - variable.attack_time)) * 180.0) * 57.3;",
"variable.attack2 = Math.sin(variable.attack_time * 180.0) * 57.3;",
"variable.z_bob = Math.cos(query.life_time * 103.13244) * 2.865 + 2.865;",
"variable.x_bob = Math.sin(query.life_time * 76.776372) * 2.865;"
],
"animate": [
"move_controller",
"look_at_target"
]
},
"render_controllers": [ "controller.render.steve" ],
"spawn_egg": {
"base_color": "#FFFFFF",
"overlay_color": "#000000"
},
"enable_attachables": true
}
}
}
詰まったところ
で、このjsonの中でも、以前試して上手くいかなかった状態から動く状態への主な差分は
- format_version が 1.10.0 である必要がある(以前は1.8.0にしてた)
- animation_controller オブジェクトは 1.10.0 では使わないので削除する
このあたり。
でも…… Minecon 2018 を機に公式から配布されている Minecon2018Resources
リソースパックだと、追加されている「サソリ」エンティティのjsonを見てみると、format_version が 1.8.0 になってた……なんで今回 1.10.0 に変えたことで上手くいったのかが不明な状態。公式さんもっとドキュメントちょうだい~
Minecon2018Resources
リソースパックの入手元はこちら
アニメーションつける
format_version を 1.10.0 にするなどしてエンティティが表示されたので、移動するときに歩きアニメーションが再生されるようにしたい。アニメーションの定義方法は結構複雑で、次のような手順で作成する。
- アニメーション設定を書く
- アニメーション設定をエンティティ設定から参照する
- エンティティ設定を参照しながらアニメーションコントローラ設定を書く
- アニメーションコントローラ設定をエンティティ設定から参照する
1. アニメーションを定義する
リソースパックの中に、animations/starspread.json
みたいな感じでアニメーション定義ファイルを作成する。ちなみに、starspreadっていうのは今回の追加モブのIDだけど、別に同じにしないといけないわけではない。あと今回の追加モブは他のアニメーション定義ファイルの内容を流用しているので、実際には作らなかった。
アニメーション定義ファイルの中身はこんな感じ。
{
"format_version": "1.8.0",
"animations": {
"animation.humanoid.move": {
"loop": true,
"bones": {
"rightArm": { "rotation": [ "variable.tcos1", 0.0, 0.0 ] },
"leftArm": { "rotation": [ "variable.tcos0", 0.0, 0.0 ] },
"rightLeg": { "rotation": [ "variable.tcos0 * 1.4", 0.0, 0.0 ] },
"leftLeg": { "rotation": [ "variable.tcos1 * 1.4", 0.0, 0.0 ] }
}
}
}
}
format_version
フィールドとか animations
オブジェクトは必須。その中の animation.humanoid.move
はアニメーション名で、自分で命名する。これはさっき見せたエンティティ設定の中か、またはアニメーションコントローラ設定で使ってる。
そしてアニメーション名のついたオブジェクトの中身は、アニメーションの内容を書く。rightArm
とか leftLeg
とかが、右腕や左足の動きを設定する。rotation
は回転。このアニメは両手両足を振りながら歩くモーションに使う目的で書かれているので、それぞれのパーツを回転させているというわけ。他にも scale
で拡縮したりできるのかな?
さらに rotation
の中には、 X, Y, Z 軸それぞれの回転角度を書くわけだけど、X軸(たぶん)のところに variable.tcos1 * 1.4
などと不思議な計算式が書かれている……
Minecraft BE のアドオンの中では、MoLangという文法で計算式を埋め込める部分がある。variable.tcos1
とかいうのは、変数にアクセスする式。この変数がどこで定義されているかというと……エンティティ設定の、minecraft:client_entity/description/scripts/pre_animation
のところを探すと、変数の値を編集するスクリプトが書かれているのが見つけられる。それは以下の部分。(jsonってコメントつけられないのだけど、省略や説明のためにつけてます。許してね)
{
"format_version": "1.10.0",
"minecraft:client_entity": {
"description": {
// (略)
"scripts": {
"pre_animation": [
"variable.tcos0 = (Math.cos(query.modified_distance_moved * 38.17) * query.modified_move_speed / variable.gliding_speed_value) * 57.3;",
"variable.tcos1 = -variable.tcos0;",
"variable.attack_body_rot_y = Math.sin(Math.sqrt(variable.attack_time) * 360.0) * 11.46;",
"variable.cos_attack_body_rot_y = Math.cos(variable.attack_body_rot_y);",
"variable.sin_attack_body_rot_y = Math.sin(variable.attack_body_rot_y);",
"variable.internal_swim_pos = Math.mod(query.modified_distance_moved, 26.0);",
"variable.attack = Math.sin((1.0 - (1.0 - variable.attack_time) * (1.0 - variable.attack_time)) * 180.0) * 57.3;",
"variable.attack2 = Math.sin(variable.attack_time * 180.0) * 57.3;",
"variable.z_bob = Math.cos(query.life_time * 103.13244) * 2.865 + 2.865;",
"variable.x_bob = Math.sin(query.life_time * 76.776372) * 2.865;"
],
// (略)
},
// (略)
}
}
}
たぶんjavascriptが書かれているのかな。このスクリプトが適切なタイミングで呼ばれる(いつ呼ばれるんだろう)ことで、アニメーションを制御しているというわけだ。
2. アニメーションをエンティティに割り当てる。
前節で書いたアニメーション設定は、まだエンティティに割り当てられていない状態。さっそく今回のエンティティに割り当ててみよう。最初に見せたエンティティ設定をまた見てみると、アニメーションを割り当てている部分は以下の通り。
{
"format_version": "1.10.0",
"minecraft:client_entity": {
"description": {
// (略)
// ここでアニメーションを設定
"animations": {
"idle": "animation.humanoid.base_pose",
"move": "animation.humanoid.move",
"look_at_target_default": "animation.humanoid.look_at_target.default",
"look_at_target_gliding": "animation.humanoid.look_at_target.gliding",
"look_at_target_swimming": "animation.humanoid.look_at_target.swimming",
"holding": "animtion.humanoid.holding",
"charging": "animation.humanoid.charging",
"damage_nearby_mobs": "animation.humanoid.damage_nearby_mobs",
"swimming": "animation.humanoid.swimming",
"use_item_progress": "animation.humanoid.use_item_progress",
"move_controller": "controller.animation.humanoid.move",
"look_at_target_controller": "controller.animation.humanoid.look_at_target"
},
// (略)
}
}
}
animations
オブジェクトの中に、使用したいアニメーションをリストアップする。フィールド名はここで適当に決めて書くが、フィールドの中身は先ほどのアニメーション設定で作成したアニメーション名を指定する。
例として、以下の部分を見てみよう:
"move": "animation.humanoid.move"
これで、「animation.humanoid.move
アニメーションを使います、それに move
という別名を付けます」という意味になる。エンティティ設定からアニメーションを参照するときは、こういう設定をリストアップする形で書くということ。
3. アニメーションコントローラを定義する
一部のアニメーション制御には、アニメーションコントローラが必要になることがある。一部のアニメーションっていうのは、アニメーションを再生する条件を決めたり、アニメーションとアニメーションの間を滑らかに繋ぐなど、複数のアニメーションの組み合わせを適切に制御しないといけないもの。
アニメーション設定ファイルは、リソースパックの animation_controllers
フォルダの中にjsonファイルを作って書く。以下のような感じ。
{
"format_version": "1.8.0",
"animation_controllers": {
"controller.animation.humanoid.riding": {
"states": {
"default": {
"transitions": [
{ "riding": "query.is_riding" }
]
},
"riding": {
"animations": [
{ "riding.arms": [] },
{ "riding.legs": [] }
],
"transitions": [
{ "default": "!query.is_riding" }
]
}
}
}
}
}
format_version
フィールドと animation_controllers
オブジェクトは必須。で、 controller.animation.humanoid.riding
とかいうのはアニメーションコントローラ名で、自分でつける。この名前はエンティティ設定の中で使う。ちなみに、このデータはバニラのデータの中身から入手していて、humanoidっていうのは人型モブの間で使いまわされているアニメーションコントローラ設定。
controller.animation.humanoid.riding
オブジェクトの中には states
オブジェクトが必須で、その中に実際のアニメーション制御の設定が書かれている。
states
というだけあって、アニメーションコントローラではステートマシンを使ってアニメーション同士の間を切り替えるための設定が書ける。states
オブジェクトの中身は default
, riding
オブジェクトなので、このアニメーションコントローラには default
, riding
の2つのステートがあるということになる。それぞれ、「騎乗していない状態」「騎乗している状態」のアニメーションを設定するというわけ。
ステートの中には、animations
, transitions
オブジェクトが書ける。エンティティが該当する状態にあれば、animations
の中に書いたアニメーションが再生されるというわけ。animations
の中では、エンティティ設定でつけたアニメーションの別名を使って参照する。
"animations": [
{ "riding.arms": [] },
{ "riding.legs": [] }
],
という書き方なので、rinding.arms
, riding.legs
という別名のついたアニメーションが再生される。フィールドの値として配列が渡されているが、なにやらアニメーションに対して引数が渡せるらしい。この辺の仕様はまだあまり調べてない。
transitions
オブジェクトのことだが、default
状態を表すオブジェクトの中には、以下の transitions
オブジェクトがある:
"transitions": [
{ "riding": "query.is_riding" }
]
ここには、どんな条件を満たしたときにどのステートへ遷移するか、を指定できる。
query.is_riding
はクエリと呼ばれるもので、この書き方なら今回のエンティティが他のエンティティに騎乗していればtrueを返す。そうしたら、晴れて riding
状態へ遷移する。
4. アニメーションコントローラを参照する
アニメーションコントローラ設定も、やはりエンティティ設定から参照しないと意味がない。アニメーションコントローラは、アニメーションと同じように、エンティティ設定の animations
オブジェクトの中から参照する。animations
オブジェクトの中には、アニメーションとアニメーションコントローラを混在させて書くというわけ。
最初の例から該当部分を抜き出すと以下の通り。
{
"format_version": "1.10.0",
"minecraft:client_entity": {
"description": {
// (略)
"animations": {
// (略)
"move_controller": "controller.animation.humanoid.move",
"look_at_target_controller": "controller.animation.humanoid.look_at_target"
},
// (略)
}
}
}
なるほど、先ほど登場した controller.animation.humanoid.move
アニメーションコントローラを参照している。
エンティティ設定のanimations
オブジェクトに指定されているアニメーションコントローラが更に、エンティティ設定の animations
オブジェクトの中身を参照しているため少し複雑だが、こんな感じで設定ができるはず。
つかれた
ほんとはビヘイビアパックのほうも弄ったんだけど、記事書くの疲れたからここまで。BE版アドオン制作は日本語の情報が皆無などころか、英語の情報も限りなく少ない……とはいえ、アドオン制作がサポートされたのはごく最近なので、当然といえば当然なんだけどね。
この記事が少しでもアドオン作りたい日本人の助けになれば……!