見出し画像

【Minecraft】プレイヤーやエンティティを物理法則に従って動かすコマンド

この記事は Minecraft Command Advent Calendar 2023 8 日目の記事です

もう2年前になるんですが、私は魅せコマで物理法則に従って動くグラップリングフックを作りました。

このグラップリングフックの作り方を3回に分けて解説していきます。
その今回は第1回ということで、「プレイヤーやエンティティをガタガタさせずに、物理法則に従って動かす方法」について解説していきたいと思います。

●フックショットや移動スキルを作ったけど、視点がガタガタする…
●グラップリングフックや突進を作りたいけどプレイヤーやエンティティをダイナミックに動かす方法がわからない…

といった方にオススメの記事になっています。

各コマンドの使い方を解説したりする記事ではないので、実際に作りたい方はMinecraft wiki(英語版)のコマンド解説を片手に読んでください。
あとMinecraft 1.20.2時点の情報です。ご了承ください。


エンティティを動かす方法について

まずは基本。物理法則とか関係なく普通にエンティティを動かす方法から行きましょう。

teleportコマンド

一番手軽なのはteleportコマンドです。指定した位置にエンティティを動かすことができます。
エンティティの場合はteleportコマンドでもなめらかに動いてくれます。
試しに毎tick 0.1m上にテレポートさせてみても、ガタガタせず動いてくれますね。

/teleport @e[type=armor_stand] ~ ~0.1 ~

1.20.2で追加されたマクロ関数を使えば、スコアボードの値の座標にテレポートさせることもできます。

※1.20.2以前でもPosというNBTに直接代入することでもスコアボードの値の座標にテレポートさせることができます。

ただteleportコマンドを使った銃弾は壁を貫通しないように毎tick当たり判定をチェックする必要があります。

エンティティを物理法則に従って動かす方法について

teleportコマンドで物理法則に従って動かしたい場合は当たり判定や空気抵抗などを自前で計算しなければなりません。
しかしマイクラはゲームエンジンなので物理エンジンが搭載してあります。それを使いましょう。
※マイクラはゲームエンジンです。異論があればぜひ聞きたいのでコメント欄に書いてください。

Motion

Minecraft上でどのようにモブが動いているかご存じでしょうか。
色々と例外はあるのですが、基本的には全てのエンティティは「Motion」というNBTをもっており、ここにエンティティの現在の速度が保存されています。
現在の速度を元に毎tickエンティティの位置を計算して移動することでエンティティ達は動いています。
※プレイヤーやそれを乗せたブタや馬の歩行はMotionで動いていませんが、重力による落下などはMotionで動いています。

NBTなのでdataコマンドを使ってこの数字は書き換えられます。 試しに以下のコマンドを入力してアーマースタンドを動かしてみましょう。

/data merge entity @e[type=armor_stand,sort=nearest,limit=1] {Motion:[0.0,3.0,3.0]}

空気抵抗や重力が考慮されるので、等加速度直線運動みたいな、物理法則に従って動きをしてくれます。
これを活用すると色々それっぽいことができます。 例えば、

●シンプルに上向きの力を加えて、モブをジャンプさせる
●モブが見ている向きに力を加えて、ダッシュやバックステップをさせる
●世界のどこか一点の向きに力を加えて、ブラックホールに吸い込む

といった感じです。ブラックホールについては例をご用意しました。


# x0 y0 z0地点がforceloadコマンドなどで読み込まれている状態で実行する
execute facing entity @p eyes positioned as 0.0 0.0 0.0 run summon marker ^ ^ ^1 {Tags:["test"]}
data modify entity @e[type=villager,limit=1,sort=nearest] Motion set from entity @e[type=marker,tag=test,limit=1] Motion
kill @e[type=marker,tag=test,limit=1]

Motionを使って弾丸を作ると、召喚時に速度を与えれば残りの処理は必要なく、ブロックに当たったら勝手に止まる、重力で自然に落下する弾が作れます。
 ※重力が不要な場合はNoGravityのNBTで消せます

じゃあ全部Motionで動かせばいいじゃん!

…とはいきません。この方法には一つ重要な欠点があります…プレイヤーのデータはdataコマンドで書き換えられないため、プレイヤーのMotionも直接コマンドで変更することができないのです。
※Motionの欠点として、一部の特殊なエンティティやNoAIのモブが飛ばせないことも挙げられます。

プレイヤーを動かす方法

では本題。プレイヤーをdataコマンドなしで動かすにはどうすればいいんでしょうか?

teleportコマンド

一番簡単なのは先ほども登場したteleportコマンドですね。テレポート先をプレイヤーのちょっと上にしてリピートコマンドブロックで実行してみます。

しかし、単にテレポートさせるだけでは動画のようにプレイヤーの視点がガタガタしてしまいます。エンティティを移動させたときはなめらかに動いていたはずなんですが…

プレイヤーの視点がガタガタする原因

この原因は2つあります。

①重力やプレイヤー操作の移動(ジャンプ、WASDキーによる歩行など)と、テレポート移動が競合してしまう

重力やプレイヤー操作の移動はクライアント側で行われるにの対し、テレポートはサーバー側で行われるために同期ズレが起きてしまいます。
逆にプレイヤー以外のエンティティは、基本的にサーバー側のみで移動が行われるためテレポートしてもガタガタしません。

②tick間のアニメーションの補完がされない

Minecraftでは位置が1秒間に20回しか更新されません。
そのため、そのまま画面に表示してしまうとこの動画の1番下の人のようにガタガタしてしまいます。

それを防ぐために、プレイヤーの通常移動やエンティティの移動ではいい感じにアニメーションしてtickの間の移動を補完しています。
しかし、プレイヤーのテレポートではこの補完はされないのでガタガタしてしまう、というわけです。

まとめると、視点をガタガタさせないためには、以下の二つを満たすような方法で移動させる必要があります。

💡ガタガタさせずに移動させるのに必要なこと
●(クライアント側で処理される)重力やプレイヤー操作による移動を防ぐこと
●アニメーションの補完がされること

これを実現する方法をこれから紹介します。

spectateコマンド

spectateコマンドでエンティティに憑依させたプレイヤーはエンティティについていきます。
そのためプレイヤー操作による移動を防ぎつつ、エンティティの移動のアニメーションの補完も使うことができます。
具体的には以下のような手順でコマンドを実行すると視点をガタガタさせずに移動させることができます。

📝spectate式プレイヤー移動の手順
① アーマースタンドなど適当なエンティティを用意する。
② gamemodeコマンドでプレイヤーをスペクテイターモードにする
③ spectateコマンドでスペクテイターモードのプレイヤーの視点をアーマースタンドの視点にする
④ アーマースタンドをMotionなりテレポートなりで移動させる

しかし、移動や視点変更が全くできなくなってしまいます。
これを逆手に取ってムービーシーンを作るときに使えます。
Chuzume氏の昔のフックショットはこれを使っています。

エンティティに乗せる

ガタガタもしない、移動や視点変更ができなくもならない移動方法は無いのか…と考えていきついた方法はエンティティに乗せることです。
プレイヤーにエンティティに乗せてしまえばこっちのもんで、

ride式のメリット
●エンティティの移動のアニメーションの補完が使える。
●プレイヤーはエンティティの上から移動できなくなる。
 ⇒クライアント側によるプレイヤー移動を防げる。
●その上プレイヤーのMotionからプレイヤーが移動しようとしていることを検知できる(WASDキー検知)。
 ⇒それに応じてエンティティを動かせば(若干のラグはありますが)移動も阻害しない。
●spectateと違って視点変更できる

などなどおいしいところ盛りだくさんです。
1.19.3まではあらかじめ用意した豚などに乗ってもらう(そしてそれをpredicateやadvancementで検知する)必要がありましたが、1.19.4で追加されたrideコマンドを用いればその必要もありません。
しかし結構大きなデメリットもあります。

👾ride式のデメリット
●見た目が何かに乗っている姿勢になる。
●ジャンプやスニークはできない。
 ⇒ジャンプパッドや二段ジャンプには向かない。
●乗られるエンティティをMotionで動かしても上のエンティティの当たり判定は考慮されない。
 ⇒工夫無しだとどうしてもブロックに埋まる。

Delta

Deltaという、エンティティに乗せずにMotionのようにプレイヤーの速度を変えられるデータパックライブラリがあります。
Motionのようにスムーズにプレイヤーを動かせるので非常に便利です。
ただ、これだけ便利だと色々と代償にしたデメリットがあります。 毎tick連続して使っているとバグるがその最たるものでしょう。そのためグラップリングフックには向きません。
しかし、ride式と違ってジャンプやスニークができなくなったりしないため、
使い方はこちらの記事からどうぞ。

その他

Chuzume氏が昔やっていたように圧縮したスライムやノックバック、爆風、浮遊エフェクトを利用してプレイヤーを動かすこともできますが、安定した制御が難しいのでDelta以外はほとんど実用例は(たぶん)ありません。
※浮遊エフェクトは制御が比較的容易ですが、浮遊はクライアント側で行われるが、持続時間の制御はサーバー側で行われるため同期ズレもまた容易に起きます。

まとめ

今回の話をまとめるとこんな感じです。

①エンティティをガタガタさせずに動かしたい
 ⇒teleportコマンドでもOK
②エンティティを物理法則な感じに動かしたい

 ⇒Motionを使おう
車やグラップリングフックのように、プレイヤーに常に力を加え続けたい
 ⇒rideコマンドでエンティティに乗せよう。
ジャンプやダッシュのように、プレイヤーに一瞬だけ力を加えたい
 ⇒Deltaを使おう
⑤必要に応じてDeltaとrideコマンドを使い分けよう

プレイヤーを動かす方法については、大体これで列挙できたかなと思います。まぁ、ソースコード読んだわけじゃないので、指摘などあれば気軽にコメントお願いします。
では次回(12/12)、「グラップリングフックの物理演算編」でお会いしましょう。

参考リンク


この記事が気に入ったらサポートをしてみませんか?