Minecraftのエンダーパールは走りながら投げても飛距離に影響しない (1.9 ~ 1.20)

ダッシュジャンプでなければならないのである(1.9 ~ 1.20)

要約

Q. 1.16.1 において、ジャンプなし、ダッシュだけしている時にエンダーパールを投げても、棒立ち状態でエンダーパールを投げた時と比べて飛距離が変わらないのはなぜ?

A. ただ走っているだけではサーバーにプレイヤーの速さを伝えることができないから!

概要

エンダーパールの初速はプレイヤーの velocity によって決定されるはずだが、(1.20以前のバージョンでは)ダッシュしているだけではエンダーパールの初速に影響を与えられない事象が発生する理由を説明する

また、1.21 より、この挙動に一部変更が入り、ダッシュしているだけでもエンダーパールの初速に影響を与えられるようになったため、この旨についても説明する。

おことわり

  • FabricMC/yarn によるマッピングを使用してソースコードの動的/静的解析を行った
    • 1.16.1+build.21
    • 1.20.1+build.10
    • 1.21+build.9
  • これらの挙動は 1.9 以降のMinecraftで確認することができ、1.20 系まではある程度同じ挙動であることが確認できる
  • 1.21 での差分点については記事後半で説明する
  • この記事は以下の動画の最後、「バージョンごとの差異」についてのアンサーとしての立ち位置の記事となる
    • かなり素晴らしい動画なので是非見てみてください

www.youtube.com

サイドの説明

このゲームは、大きく分けてクライアント側とサーバー側で処理が分かれている。

サーバー側

基本的なゲームロジックに関わる計算はサーバー側で処理される。今回の議題であるエンダーパールの各挙動もサーバー側で計算される。

クライアント側

ピクセルの描画や、20fpsより高い分解能を得るための補完などはクライアント側で処理される。基本的には、サーバー側の挙動をコピーして描画するのが主な役割。

例外として、一部の座標や一部の velocity の計算など、このゲームの根幹だと判断されなかった処理についてはクライアント側で行われることもある。特に、今回はクライアントとサーバーの挙動の差異が起因してエンダーパールの挙動に影響を与えている。

エンダーパールの初速について

ref: net.minecraft.entity.projectile.ProjectileEntity#setProperties

要約する*1が、エンダーパールの初速は サーバー側のプレイヤーの velocity (速度) を元に決定される。プレイヤーの velocity がそのままエンダーパールの velocity に加算される。

あくまでクライアント側の velocity ではないことがポイントとなる。

サーバー側での velocity 更新の動き

ref: net.minecraft.server.network.ServerPlayNetworkHandler#onPlayerMove

サーバーがクライアントからプレイヤーの動きに関するパケットを受け取った時、基本的には*2以下のどちらかの場合で のみ velocity が設定/加算される。

  • ジャンプした時( ref: net.minecraft.entity.player.PlayerEntity#jump )
    • y 方向に 0.42 加算される
    • さらにダッシュしていた場合、定数と角度で計算した値が x, z 方向の velocity として加算される
      • 定数を元に計算されることが重要!
        • つまり、俊敏のポーションやソウルスピードなどの影響を受けることはなく、ダッシュジャンプ時の横方向の速さは常に一定!!!!
      • 実装としてはこんなかんじ
        • x += -sin(角度のラジアン) * 0.2
        • z += cos(角度のラジアン) * 0.2
  • 入力に関与しない自然な移動が発生した時(net.minecraft.entity.LivingEntity#travel 周辺、いろいろ )
    • 具体的には
      • 落下(重力)
        • 例: 落ちている間、y 方向の velocity は毎tick加算される
      • 別のなにかから動力を受ける
        • 例: TNT に吹き飛ばされる
      • 水流に触れ、流されている
      • ツタなどに触れている状態で下方向に降下している
      • など

そうでない場合、例えば単にダッシュしはじめた場合、クライアント側の velocity は適切に設定されるが、サーバー側の velocity は 0 のままになっている!!! velocity が 0 なのに、座標が連続的に変わっていくような挙動をみせることになる。

例えば、俊敏のポーションやソウルスピードによって速さを得ていたとしても、サーバー側の velocity は 0 のままである。

減衰について

ref: net.minecraft.entity.LivingEntity#travel

また、上述したベロシティ加算の動きをしていない場合、基本的にはサーバー側の velocity は 1 tick ごとに減衰し、(何もしなければ)最終的には 0 に収束する。

例えば、ダッシュジャンプを一回行った後にダッシュを継続していたとしても、サーバー側の velocity が高く設定されるのはジャンプを行ったタイミングのみで、その後はゆっくりと 0 に近づいていく。

また、1 tick ごとの減衰幅はその場の環境によって異なる。例えば氷の上では減衰幅は小さく、相対的に少しずつ velocity が減衰していく。

これらの事柄からわかる、エンダーパールの初速について

以上の事柄より、以下のようなことがわかる。

  • ただダッシュしているだけではサーバー側の velocity は設定されず、エンダーパールの初速にも影響しない
  • ジャンプしたタイミングでエンダーパールを投げると、ジャンプ時に設定される velocity がエンダーパールの初速に影響するようになる
    • この時にダッシュしていれば、追加で定数と角度で計算した値が velocity に加算される
  • 落下時や、爆風によって打ち上げられている時など、外部から velocity に関する影響を受けている時にエンダーパールを投げると、プレイヤーに掛かっている velocity はエンダーパールの初速に影響する
  • 俊敏のポーションやソウルスピードによって事前に大きなスピードを得ていたとしてもエンダーパールの初速への影響はない!!!
    • ジャンプ時に得られる横方向の velocity は定数と角度で計算した値と、サーバー側の velocity のみが影響するが、初回ダッシュジャンプ直前の横方向の velocity は 0 であるため、俊敏のポーションやソウルスピードの影響をここに与えることができない
  • 氷の上で何度もダッシュジャンプする例のように、連続してダッシュジャンプを行いスピードを稼ぐ場合はエンダーパールの初速に影響する
    • velocity が 0 になる前に次の加算を行うことがこの種類の加速の原理であるため、サーバー側の velocity にも適切に反映される

1.21 からの差分点: movement

……と、ここまでは従来の挙動について説明してきたが、1.21 からは movement という新しい概念が追加される。

movement について

ref: net.minecraft.server.network.ServerPlayNetworkHandler#onPlayerMove

ref: net. minecraft. server. network. ServerPlayerEntity#setOnGround

おそらく、今回挙げたものに類する不自然な挙動に対してのパッチとして導入された概念*3で、実質的にはプレイヤーに対してのみ存在する。

movement は、クライアントから送信されたパケットをサーバー側が受け取り処理する際にプレイヤーが移動した距離がそのままベクトルとして格納される。重要なのは、これがサーバー側で設定されること。

これが、これまで説明した物事に対して以下のように影響を与える。

エンダーパールの初速は サーバー側のプレイヤーの movement を元に決定される。プレイヤーの movement がそのままエンダーパールの velocity に加算される。

差分としては、velocity ではなく、 movement が使われるようになったこと。これにより、ただプレイヤーがダッシュしているだけでもそのベクトルはエンダーパールの初速に加算され、より自然な振る舞いを見せるようになる。

また、そのパケットで進んだ距離がそのまま movement として扱われるということは、俊敏のポーションやソウルスピードで得られた速度もそのままエンダーパールの初速に影響を与えることとなる。こちらの方が直感的な動作と言えるだろう。

まとめ

ダッシュしている時など、クライアント側で velocity を得ていたとしても、サーバー側にその値が反映されているとは限らず、直感に反した動きを見せることがある。エンダーパールの初速も、この事象の影響を受ける一例だった。クライアント側とサーバー側の挙動の差異について理解することで、説明しづらい事象に対しても理解を深めることができる。

また、Mojangがこのような問題に対してどのように対応するのかの一例を確認することができた。今回の場合、ダッシュしているだけでもサーバー側でプレイヤーの速度が適切に反映されるようになった。その結果、エンダーパールの初速にも影響を与え、より直感的な挙動となった。

*1:実際にはここに乱数ベクトルが加算される

*2:イレギュラーはいくつかあるが、通常のブロックの上で通常の振る舞いを行っている場合はおおよそここに記載のあるものが大半に見える

*3:たぶんこの修正 MC-273007 で導入された概念 ぜんぜん違うかもしれないけど