えいまぼJEブログ

Minecraft Java Editionに関していろいろ書きます

TNT64個長距離チャレンジの最大飛距離の求め方

経緯

BongoBongoさんより、TNT64個でなるべく飛ぶTNTキャノンを作るチャレンジが開催されました。 面白そうだと思ったので、まずは限界を見極めるため最大飛距離を求めることにしました。

最大飛距離を求める

飛距離の最大値を求める前に、 V _ xの最大値を求めます。

 V _ yの最大値

まずは V _ yを考えます。  T = 79 (爆発の1gt前)で弾頭のy座標が発射地点より1ブロック低くなるようにすると、飛距離が最長になります。このときの V _ yは定数として求められます。飛距離の公式*1から、以下のように計算します。


\begin{align}
\displaystyle y &= y _ 0 + 50 \times (N V _ y + v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T \\\
\to \displaystyle - 1 &= 50 \times (64 \times V _ y + 0.2 + 1.96) \times (1 - 0.98 ^ {79}) - 2 \times 79 \\\
\to \displaystyle V _ y &= \frac{1}{64} \left( \frac{ 2 \times 79 - 1 }{ 50 \times (1 - 0.98 ^ {79}) } - 0.2 - \displaystyle 1.96 \right) \\\
&= 0.027786... \tag{1}
\end{align}
\

 v _ {0y} = 0.2は、TNT着火時に与えられるy方向の速度です。

 X Yの関係式

 V _ yが求まったので、 X Yの関係式が作れます。


\begin{align}
\displaystyle V _ y &= \left(1 - \frac{R}{8} \right) \frac{Y}{R} \\\
\displaystyle         &= \left(\frac{1}{R} - \frac{1}{8} \right) Y \\\
\to \displaystyle  R                              &= \frac{1}{ \frac{V _ y}{Y} + \frac{1}{8} } \\\
\to \displaystyle  \sqrt{X ^ 2 + Y ^ 2} &= \frac{1}{ \frac{V _ y}{Y} + \frac{1}{8} } \\\
\to \displaystyle  X ^ 2 + Y ^ 2 &= \frac{1}{\left( \frac{V _ y}{Y} + \frac{1}{8} \right) ^ 2} \\\
\to \displaystyle  X                    &= \sqrt{ \frac{1}{\left( \frac{V _ y}{Y} + \frac{1}{8} \right) ^ 2} - Y ^ 2} \tag{2} \\\
\end{align}
\

 \displaystyle \frac{dX}{dY}を求める

式2から \displaystyle \frac{dX}{dY}を求めると、式3のようになります。


\begin{align}
\displaystyle  \frac{dX}{dY} &= \sqrt{ \frac{64}{ (8V _ y + Y) ^ 2 } - 1 }
                                                 - \frac{ 64Y }{(8V _ y + Y) ^ 3 \sqrt{ \frac{64}{ (8V _ y + Y) ^ 2 } - 1 }} \tag{3} \\\
\end{align}
\

式3に Y = 0を代入します。0なのは V _ xが最も大きくなる距離だからです。


\begin{align}
\displaystyle  \left. \frac{dX}{dY} \right | _ {Y=0}
                     &= \sqrt{ \frac{64}{ (8V _ y) ^ 2 } - 1 } \\\
\displaystyle &= \sqrt{ \frac{1}{ V _ y^ 2 } - 1 } \\\
\displaystyle &= \frac{1}{a} \tag{4} \\\
\end{align}
\

ここからは Y = 0近傍において


\begin{align}
\displaystyle  Y &= aX \tag{5} \\\
\end{align}
\

といえることを使います。

 V _ xの最大値

残りは V _ xを求めます。


\begin{align}
\displaystyle V _ x &= \left(1 - \frac{R}{8} \right) \frac{X}{R} \tag{6} \\\
\end{align}
\

式5より、 R = \sqrt{X ^ 2 + Y ^ 2} = \sqrt{X ^ 2 + a ^ 2 X ^ 2} = X \sqrt{a ^ 2 + 1} なので、式6は以下のように書き換えられます。


\begin{align}
\displaystyle V _ x &= \left(1 - \frac{R}{8} \right) \frac{X}{R} \\\
\displaystyle         &= \left(1 - \frac{X \sqrt{a ^ 2 + 1}}{8} \right) \frac{X}{X \sqrt{a ^ 2 + 1}} \\\
\displaystyle         &= \left(1 - \frac{X \sqrt{a ^ 2 + 1}}{8} \right) \frac{1}{\sqrt{a ^ 2 + 1}} \\\
\end{align}
\

 X = 0なので、


\begin{align}
\displaystyle V _ x &= \frac{1}{\sqrt{a ^ 2 + 1}} \tag{7} \\\
\end{align}
\

となります。 X = 0としたのは、 Y = 0のときと同様で V _ xが最も大きくなる距離だからです。

式7に式4を適用します。


\begin{align}
\displaystyle V _ x &= \frac{1}{\sqrt{a ^ 2 + 1}} \\\
\displaystyle          &= \sqrt{1 - V _ y ^ 2} \tag{8} \\\
\end{align}
\

式8に式1を代入します。


\begin{align}
\displaystyle V _ x &= 0.99961... \\\
\end{align}
\

以上より、 V _ xの最大値を求められました。

飛距離の最大値

あとは V _ xの最大値をx座標の飛距離の公式に代入するだけです。


\begin{align}
\displaystyle x &= 50 \times (N V _ x + v _ {0x}) \times (1 - 0.98 ^ T) \\\
\displaystyle    &= 50 \times (64 \times 0.99961 + 0) \times (1 - 0.98 ^ {80}) \\\
\displaystyle    &= 2563.3...
\end{align}
\

最大飛距離は2563.3ブロックでした。

ちなみに、 v _ {0x}TNT着火時に与えられる横方向の速度で、最大0.02、最小-0.02です。水平方向の振幅は0.02で、角度だけランダムです。

着火時の速度も考慮に入れると、最大のときは 50 \times (64 \times 0.99961 + 0.02) \times (1 - 0.98 ^ {80}) = 2564.1... ブロック、最小のときは 50 \times (64 \times 0.99961 - 0.02) \times (1 - 0.98 ^ {80}) = 2562.5... ブロック飛びます。

余談

 V _ xの導出において式2~7は省略できそうです。まず、式9, 10に初速度の式を再掲します。


\begin{align}
\displaystyle V _ x &= \left(1 - \frac{R}{8} \right) \frac{X}{R} \tag{9} \\\
\displaystyle V _ y &= \left(1 - \frac{R}{8} \right) \frac{Y}{R} \tag{10} \\\
\end{align}
\

式9, 10の2乗和を計算します。


\begin{align}
\displaystyle V _ x ^ 2 + V _ y ^ 2 &= \left(1 - \frac{R}{8} \right) ^ 2 
            \left( \frac{X}{R} ^ 2 + \frac{Y}{R} ^ 2 \right) \\\
\displaystyle &= \left(1 - \frac{R}{8} \right) ^ 2 
            \left( \frac{X ^ 2 + Y ^ 2}{R ^ 2}\right) \\\
\displaystyle &= \left(1 - \frac{R}{8} \right) ^ 2 
            \left( \frac{R ^ 2}{R ^ 2}\right) \\\
\displaystyle &= \left(1 - \frac{R}{8} \right) ^ 2 \tag{11} \\\
\end{align}
\

 X Yも0近傍なので、 R = 0といえます。このとき、式11は式12のように書き換えられます。


\begin{align}
\displaystyle V _ x ^ 2 + V _ y ^ 2 &= 1 \tag{12} \\\
\end{align}
\

式12を変形すると、式8と一致します。 これで証明できているか不安ですが、式が一致するのでありだと思います。たぶん。

TNTキャノン開発に便利なコマンド (バニラ・MOD) | Minecraft Java 1.14.4+

はじめに

今回は TNTキャノン開発に便利なコマンドを紹介します。 コマンドの動作を確認した環境はJava Edition 1.14.4~1.19.2です。 前半はバニラ、後半はCarpetというMODを使ったコマンドを紹介します。 前半と後半の間にコマンド周辺の便利な小技を紹介します。

スクリーンショットは載せていないので、辞書的に使ってください。

バニラ編

summon - TNTを召喚

TNTを召喚します:

/summon tnt ~ ~ ~ {Fuse:80,Motion:[0.0d, 1.0d, 0.0d]}

Fuseは爆発までの時間(単位:gt)です。20gtで1秒です。

速度は10以下のみ指定可能です。10を超えた値を入力すると0として解釈されます。

3つのにょろにょろはチルダです。相対座標を指定します。 例えば/summon tnt ~ ~2 ~と書けば、足元から2ブロック上にTNTが召喚されます。

絶対座標で指定する場合は数字を直接書きます:

/summon tnt 10 80.5 0 {Fuse:80,Motion:[0.0d, 1.0d, 0.0d]}

kill - 着火されたTNTを削除

着火された(=エンティティと化した)TNTのみを削除します:

/kill @e[type=tnt]

data - TNTの座標・速度を取得

1つのTNTが持っている情報をすべて表示します:

/data get entity @e[type=tnt,limit=1]

座標だけ取得したい場合:

/data get entity @e[type=tnt,limit=1] Pos

x座標のみ(1行目)、y座標のみ(2行目)、z座標のみ(3行目)取得したい場合:

/data get entity @e[type=tnt,limit=1] Pos[0]
/data get entity @e[type=tnt,limit=1] Pos[1]
/data get entity @e[type=tnt,limit=1] Pos[2]

速度だけ取得したい場合:

/data get entity @e[type=tnt,limit=1] Motion

各軸単独の成分を取得したい場合は座標と同じように[0]などを末尾につけます。

なお、/dataはコマンドブロックで実行してもログに表示されません。 コマンドブロックで座標などを取得したい場合は次で紹介する/tellrawを使います。

tellraw - TNTの座標・速度を取得

/tellrawでは複数のTNTの座標・速度を取得できます。 座標(x, y, z)を表示:

/tellraw @a {"entity":"@e[type=tnt]","nbt":"Pos"}

速度を測定するにはPosMotionに変えます:

/tellraw @a {"entity":"@e[type=tnt]","nbt":"Motion"}

x, y, z軸単独の成分を取得したい場合は/data同様[0], [1], [2]を末尾につけます:

/tellraw @a {"entity":"@e[type=tnt]","nbt":"Pos[0]"}
/tellraw @a {"entity":"@e[type=tnt]","nbt":"Pos[1]"}
/tellraw @a {"entity":"@e[type=tnt]","nbt":"Pos[2]"}

これは速度(Motion)でも同じです:

/tellraw @a {"entity":"@e[type=tnt]","nbt":"Motion[0]"}
/tellraw @a {"entity":"@e[type=tnt]","nbt":"Motion[1]"}
/tellraw @a {"entity":"@e[type=tnt]","nbt":"Motion[2]"}

TNTロッコの場合はtnttnt_minecartに変えます。 これは/summon/dataでも同様です。

便利な小技

コマンド基本操作1

/キーを押したあとキーを押すことで過去に実行したコマンドを復元できます。 で未来のものに戻れます。

コマンド基本操作2

F3+Dで表示を全て消せます。

コマンドのログの場所

C:/Users/(ユーザー名)/AppData/Roaming/.minecraft/logs/latest.logがログです。 ゲームディレクトリを変えていれば、.minecraft以上のパスを変えてください。

latest.logには起動してから書かれたログがすべて載っています。 コマンドを大量に実行してチャット欄から見られなくなったときに使えます。 メモ帳で開くことができます。

MOD編

後半はMOD導入で使えるコマンドの紹介です。 CarpetというMODを導入します。 1.14.4から対応しています。 導入の仕方は各自で調べてください。

ブロック破壊を無効化

爆発によるブロック破壊を無効にします:

/carpet explosionNoBlockDamage true

ブロック破壊を有効にします(デフォルトに戻す):

/carpet explosionNoBlockDamage false

TNTの爆発座標を測定

簡易的に表示します:

/log explosions brief

※ここで表示されるy座標は爆発座標であることに注意してください。TNT自身の座標よりも0.06125 (= 0.98 / 16)高い値が表示されます。

※1.14.4にこのコマンドはありません。1.15.2以上で使えます。1.14.4では/log tnt briefを代わりに使ってください。

詳細に表示します:

/log explosions full

fullを指定すると他のエンティティに対して与えた速度を知ることができます。

表示を停止させるとき:

/log explosions clear

同一tickで爆発したとしても順番を揃えて表示してくれます。 ちなみに、バニラの状態で/data/tellrawを使ったとしても爆発するtickの座標は取得できません。バニラでは取得できない情報を取得できる有用なコマンドです。

TNT着火時のランダムな跳ねを無効化

TNT着火時の横方向の(ランダムな)速度ベクトルをゼロにします:

/carpet tntPrimerMomentumRemoved true

デフォルトに戻します:

/carpet tntPrimerMomentumRemoved false

ブロックをすり抜ける

クリエイティブモードで飛行中、ブロックをすり抜けられるようにします。 スペクテイターモードと同じ感じです。

/carpet creativeNoClip true

入り組んだ機構の内部に入り配線を組み直す場面で活躍します。

※1.14.4にこのコマンドはありません。1.15.2以上で使えます。

デフォルトに戻します:

/carpet creativeNoClip false

tick - 時間停止

時間停止・進行を切り替えます:

/tick freeze

tick - ステップ実行

/tick freezeで時間を止めているときに実行します。 以下は20gt進めるコマンドです:

/tick step 20

tick - 時速の変更

時間の流れの早さを変えます。 以下は1秒あたり2gt進めるコマンドです:

/tick rate 2

なお、以下のコマンドも併用すると良いです:

/carpet smoothClientAnimations true

ピストンやエンティティの描画がスムーズになります。

最後に

筆者がMinecraftに復帰する際にとても役立ったので、共有しました。 みなさんのTNTキャノン製作に役立てられたら嬉しいです。

TNTの座標計算 #3 - 単位TNT推力

前回: TNTの座標計算 #2 - 複数の装薬 - えいまぼJEブログ

環境はMinecraft Java Editionです。

単位TNT推力の式

単位TNT推力1とは、装薬TNT1つで弾頭TNTが飛ぶ距離のことです。 装薬がN個あるとき、弾頭TNTの飛ぶ距離は「『単位TNT推力』×N」となります。飛ぶ距離が比例の関係にあるので、とても使いやすい考え方です。

さて、単位TNT推力は、以下の式で表されます。

\\\
u _ x = 50 \times V _ x \times (1 - 0.98 ^ T) \\\
u _ y = 50 \times V _ y \times (1 - 0.98 ^ T) \\\
u _ z = 50 \times V _ z \times (1 - 0.98 ^ T)

x, y, z軸ですべて共通です。

ただし、諸定数は図1に基づき、以下のように定義します。

図1

  •  X : 装薬と弾頭との距離のx成分
  •  Y : 装薬と弾頭との距離のy成分
  •  Z : 装薬と弾頭との距離のz成分
  •  R : 装薬と弾頭との距離  \displaystyle R = \sqrt{X ^ 2 + Y ^ 2 + Z ^ 2}
  •  \displaystyle T = 弾頭着火\rm{gt} - 装薬着火\rm{gt} + 1
  •  \displaystyle V _ {x} = \left(1 - \frac{R}{8}\right) \frac{X}{R}
  •  \displaystyle V _ {y} = \left(1 - \frac{R}{8}\right) \frac{Y}{R}
  •  \displaystyle V _ {z} = \left(1 - \frac{R}{8}\right) \frac{Z}{R}

使い方

例えばx方向の単位TNT推力が10の装薬がほしいなら、 u _ x = 10 となるように  V _ x T を調節します。

x方向の単位TNT推力  u _ x を10としたいときの、装薬と弾頭の間の距離を求めてみます。 ここでは、弾頭爆発時間までの時間を20gt ( T = 20)とします。

まず、 V _ x を以下のように求めます。

\\\
\begin{align}
V _ x &= \displaystyle \frac{u _ x}{50 \times (1 - 0.98 ^ T)} \\\
 &= \displaystyle \frac{10}{50 \times (1 - 0.98 ^ {20})} \\\
 &= 0.6017...
\end{align}

 V _ x から装薬と弾頭をどのくらいの距離にするかを求めます。 \displaystyle V _ {x} = \left(1 - \frac{R}{8}\right) \frac{X}{R} を使います。 ここでは計算の簡略化のため、 X = R とします。Minecraftでは、装薬の爆発y, z座標が弾頭のy, z座標と一致している状況になります。座標指定型TNTキャノンでは理想の状態です。

\\\
\begin{align}
V _ {x} &= \left(1 - \frac{R}{8}\right) \frac{X}{R} \\\
 &= \displaystyle 1 - \frac{X}{8}
\end{align}

式を変形します。

\\\
\begin{align}
X &= 8 \times \left(1 - V _ x\right) \\\
 &= 8 \times \left(1 - 0.6017...\right) \\\
 &= 3.1864...
\end{align}

これで、 T = 20 でx方向の単位TNT推力  u _ x を10としたいときの装薬と弾頭の距離を求めることができました。3ブロックと少し離すと良いことがわかりました。

導出

単なる式変形ですが、導出過程を載せます。

x軸

x座標は式1で表されます(前回の記事より)。

 \displaystyle x = x _ 0 + 50 \times (N V _ x + v _ {0x}) \times (1 - 0.98 ^ T) \tag{1}

これを展開し、 u _ x を当てはめます(式2)。


\begin{align}
\displaystyle x &= x _ 0 + 50 \times N V _ x \times (1 - 0.98 ^ T) + 50 \times v _ {0x} \times (1 - 0.98 ^ T) \\\
\displaystyle  &= x _ 0 + N u _ x + 50 \times v _ {0x} \times (1 - 0.98 ^ T) \tag{2}
\end{align}

定数項を  C _ x = x _ 0 + 50 \times v _ {0x} \times (1 - 0.98 ^ T) としてまとめます(式3)。

 \displaystyle x = N u _ x + C _ x \tag{3}

y軸

y軸もx軸と同様に導出します。

y座標は式4で表されます(前回の記事より)。

 \displaystyle y = y _ 0 + 50 \times (N V _ y + v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T \tag{4}

これを展開し、 u _ y を当てはめます(式5)。


\begin{align}
\displaystyle x &= y _ 0 + 50 \times N V _ y \times (1 - 0.98 ^ T) + 50 \times (v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T \\\
\displaystyle  &= y _ 0 + N u _ y + 50 \times (v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T \tag{5}
\end{align}

定数項を  C _ y = y _ 0 + 50 \times (v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T としてまとめます(式6)。

 \displaystyle x = N u _ y + C _ y \tag{6}

これで、装薬  N 個で弾頭がどのくらい飛ぶかが単位TNT推力を使って示されました。

z軸

z軸はx軸と全く同じなので省略します。

y軸特有の注意

y軸はx, z軸と違い、注意点があります。 それは、切片(オフセット)の扱いです。

弾頭を真上に打ち上げるタイプのTNTキャノンを考えます。この場合、以下の条件が成り立つので、  x = N u _ x と考えることができます ( C _ x = 0)。

  •  x _ 0 = 0 (相対座標なので)
  •  v _ {0x} = 0 (水平方向の速度はないので)

一方でy座標は絶対座標であり( y _ 0 \neq 0)、 50 \times (v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T をゼロにするのは難しいので、一般的には  C _ y 0 にできないことがわかります。 一応  v _ {0y} を調整することで  C _ y = 0 にすることも可能です。しかし、そうするよりも切片を最初から考慮していたほうが実用的かと思います。y座標は絶対座標なので。

別の注意点としては、 T を切り替えて平面軸の単位TNT推力を変更する方式2は切り替えによって  C _ y が大きく変わる点があります。 楽に作るのであれば  T を切り替えずに固定としたほうが良さそうです。 実際、y軸入力対応のTNTキャノン3では  T を切り替えませんでした( C _ y を固定として扱った)。

理論上の限界

単位TNT推力の上限値は40.068 (≒ 50×1×(1 - 0.98 ^ 80))です。 なお、この値はコマンド以外ではほぼ実現不可能です。 装薬の爆発座標と弾頭座標が限りなく近い(ただし一致ではない)状況でなければ上限値は出せません4

検証環境

Minecraft Java Edition 1.17.1-1.18.1


前回: TNTの座標計算 #2 - 複数の装薬 - えいまぼJEブログ

TNTの座標計算 #2 - 複数の装薬

前回: TNTの座標計算 #1 - 式と導出 - えいまぼJEブログ

次回: TNTの座標計算 #3 - 単位TNT推力 - えいまぼJEブログ

環境はMinecraft Java Editionです。

結論

 N 個の装薬がすべて図1のような位置で爆発したとき、弾頭の速度・位置は以下の式で表されます。

図1

\\\
\displaystyle v _ {x} = (N V _ x + v _ {0x}) \times 0.98 ^ T \\\
\displaystyle v _ {y} = (N V _ y + v _ {0y} + 1.96) \times 0.98 ^ T - 1.96 \\\
\displaystyle v _ {z} = (N V _ z + v _ {0z}) \times 0.98 ^ T \\\
\displaystyle x = x _ 0 + 50 \times (N V _ x + v _ {0x}) \times (1 - 0.98 ^ T) \\\
\displaystyle y = y _ 0 + 50 \times (N V _ y + v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T \\\
\displaystyle z = z _ 0 + 50 \times (N V _ z + v _ {0z}) \times (1 - 0.98 ^ T)

ただし、

  •  X : 装薬と弾頭との距離のx成分
  •  Y : 装薬と弾頭との距離のy成分
  •  Z : 装薬と弾頭との距離のz成分
  •  \displaystyle R = \sqrt{X ^ 2 + Y ^ 2 + Z ^ 2}
  •  \displaystyle V _ {x} = \left(1 - \frac{R}{8}\right) \frac{X}{R}
  •  \displaystyle V _ {y} = \left(1 - \frac{R}{8}\right) \frac{Y}{R}
  •  \displaystyle V _ {z} = \left(1 - \frac{R}{8}\right) \frac{Z}{R}
  •  v _ {0x} : 装薬の爆発を受ける前に持っていた速度のx成分
  •  v _ {0y} : 装薬の爆発を受ける前に持っていた速度のy成分
  •  v _ {0z} : 装薬の爆発を受ける前に持っていた速度のz成分
  •  v _ x : 速度のx成分
  •  v _ y : 速度のy成分
  •  v _ z : 速度のz成分
  •  x _ 0 : x座標の初期座標 (装薬の爆発を受ける点)
  •  y _ 0 : y座標の初期座標 (装薬の爆発を受ける点)
  •  z _ 0 : z座標の初期座標 (装薬の爆発を受ける点)
  •  x : x座標
  •  y : y座標
  •  z : z座標
  •  \displaystyle T = 弾頭着火\rm{gt} - 装薬着火\rm{gt} + 1

です。

要するに、前回の式の  V _ x V _ y V _ z の前に  N がついただけです。

※z座標・速度のz成分は図に描画していません(できません)。

装薬の爆発座標がバラバラな場合

ここではより厳密に、装薬の爆発座標がバラバラな場合を考えます。 まず、装薬の個数と位置関係、速度ベクトルを以下のように定義します。

  •  M : 装薬の個数
  •  X_k : ある装薬と弾頭との距離のx成分
  •  Y_k : ある装薬と弾頭との距離のy成分
  •  Z_k : ある装薬と弾頭との距離のz成分
  •  R_k : ある装薬と弾頭との距離
  •  V_{xk} : ある装薬が弾頭に与える速度ベクトルのx成分
  •  V_{yk} : ある装薬が弾頭に与える速度ベクトルのy成分
  •  V_{zk} : ある装薬が弾頭に与える速度ベクトルのz成分
  •  \displaystyle V _ {xk} = \left(1 - \frac{R_k}{8}\right) \frac{X_k}{R_k}
  •  \displaystyle V _ {yk} = \left(1 - \frac{R_k}{8}\right) \frac{Y_k}{R_k}
  •  \displaystyle V _ {zk} = \left(1 - \frac{R_k}{8}\right) \frac{Z_k}{R_k}

ただし、 R_k \lt 8 が成り立っているものとします。

その上で、弾頭の速度・位置は以下の式で表されます。

\\\
\displaystyle v _ {x} = \left(\sum_{k = 1} ^ M V _ {xk} + v _ {0x}\right) \times 0.98 ^ T \\\
\displaystyle v _ {y} = \left(\sum_{k = 1} ^ M V _ {yk} + v _ {0y} + 1.96\right) \times 0.98 ^ T - 1.96 \\\
\displaystyle v _ {z} = \left(\sum_{k = 1} ^ M V _ {zk} + v _ {0z}\right) \times 0.98 ^ T \\\
\displaystyle x = x _ 0 + 50 \times \left(\sum_{k = 1} ^ M V _ {xk} + v _ {0x}\right) \times (1 - 0.98 ^ T) \\\
\displaystyle y = y _ 0 + 50 \times \left(\sum_{k = 1} ^ M V _ {yk} + v _ {0y} + 1.96\right) \times (1 - 0.98 ^ T) - 2 T \\\
\displaystyle z = z _ 0 + 50 \times \left(\sum_{k = 1} ^ M V _ {zk} + v _ {0z}\right) \times (1 - 0.98 ^ T)

要するに  V _ x V _ y V _ z がシグマで合計されただけです。

8block/gtを超えない速度で2次装薬を打ち上げるキャノンはこの式を使うんではないかなと思います。 この場合は爆発座標のずれに注意してください1

検証環境

Minecraft Java Edition 1.17.1-1.18.1


前回: TNTの座標計算 #1 - 式と導出 - えいまぼJEブログ

次回: TNTの座標計算 #3 - 単位TNT推力 - えいまぼJEブログ


  1. 爆発座標のずれはこの動画の10:30あたりから解説しています

TNTの座標計算 #1 - 式と導出

次回: TNTの座標計算 #2 - 複数の装薬 - えいまぼJEブログ

環境はMinecraft Java Editionです。

結論

1つの装薬が図1のような位置で爆発したとき、弾頭の速度・位置は以下の式で表されます。

図1

\\\
\displaystyle v _ {x} = (V _ x + v _ {0x}) \times 0.98 ^ T \\\
\displaystyle v _ {y} = (V _ y + v _ {0y} + 1.96) \times 0.98 ^ T - 1.96 \\\
\displaystyle v _ {z} = (V _ z + v _ {0z}) \times 0.98 ^ T \\\
\displaystyle x = x _ 0 + 50 \times (V _ x + v _ {0x}) \times (1 - 0.98 ^ T) \\\
\displaystyle y = y _ 0 + 50 \times (V _ y + v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T \\\
\displaystyle z = z _ 0 + 50 \times (V _ z + v _ {0z}) \times (1 - 0.98 ^ T)

ただし、

  •  X : 装薬と弾頭との距離のx成分
  •  Y : 装薬と弾頭との距離のy成分
  •  Z : 装薬と弾頭との距離のz成分
  •  \displaystyle R = \sqrt{X ^ 2 + Y ^ 2 + Z ^ 2}
  •  \displaystyle V _ {x} = \left(1 - \frac{R}{8}\right) \frac{X}{R}
  •  \displaystyle V _ {y} = \left(1 - \frac{R}{8}\right) \frac{Y}{R}
  •  \displaystyle V _ {z} = \left(1 - \frac{R}{8}\right) \frac{Z}{R}
  •  v _ {0x} : 装薬の爆発を受ける前に持っていた速度のx成分
  •  v _ {0y} : 装薬の爆発を受ける前に持っていた速度のy成分
  •  v _ {0z} : 装薬の爆発を受ける前に持っていた速度のz成分
  •  v _ x : 速度のx成分
  •  v _ y : 速度のy成分
  •  v _ z : 速度のz成分
  •  x _ 0 : x座標の初期座標 (装薬の爆発を受ける点)
  •  y _ 0 : y座標の初期座標 (装薬の爆発を受ける点)
  •  z _ 0 : z座標の初期座標 (装薬の爆発を受ける点)
  •  x : x座標
  •  y : y座標
  •  z : z座標
  •  \displaystyle T = 弾頭着火\rm{gt} - 装薬着火\rm{gt} + 1

です1

※z座標・速度のz成分は図に描画していません(できません)。

導出

Minecraft内での処理

Minecraft内のTNTの座標・速度演算は図2のようになっています。

図2. Minecraft内での座標・速度演算
①はy軸専用の処理です。x, z軸は①がありません。

なお、この図はMinecraft Java Edition 1.17.1のソースコードに基づいて描かれました。ソースコードを見たい場合は逆コンパイルするか、Technical Minecraft Wikiaを覗いてみてください。Technical Minecraft Wikiaのコードは1.17.1と変わりませんでした。ですので、これより古いバージョンでも使える数式だと思われます。 統合版でも適用できるかはわかりません2

速度のx成分

まずは一番簡単な速度のx成分を導出します。図3は図2から速度のx成分の計算に必要な処理のみを抜粋したものです。

図3. 速度のx成分の処理

図3から、漸化式が導けます(式1)。

 \displaystyle v _ {n+1} = v _ n \times 0.98 \tag{1}

式1を解くと、式2のようになります。

 \displaystyle v _ {n} = v _ 0 \times 0.98 ^ n \tag{2}

式2の  v _ n v _ x に、 v _ 0 V _ x + v _ {0x} に、 n T に置き直して完成です(式3)。

 \displaystyle v _ {x} = (V _ x + v _ {0x}) \times 0.98 ^ T \tag{3}

速度のy成分

次は速度のy成分を導出します。図4は図2から速度のy成分の計算に必要な処理のみを抜粋したものです。

図4. 速度のy成分の処理

図4から、以下の漸化式が導けます(式4)。

 \displaystyle v _ {n+1} = 0.98 (v _ n - 0.04) \tag{4}

式4を特性方程式などを使って解くと、式5のようになります。

 \displaystyle v _ {n} = (v _ 0 + 1.96) 0.98 ^ n - 1.96 \tag{5}

式5の  v _ n v _ y に、 v _ 0 V _ y + v _ {0y} に、 n T に置き直して完成です(式6)。

 \displaystyle v _ {y} = (V _ y + v _ {0y} + 1.96) 0.98 ^ T - 1.96 \tag{6}

速度のz成分

速度のz成分はxと全く同じです。 x ( X) を  z ( Z) に置き換えるだけで成立します。

x座標

次は座標xを導出します。図5は図2からx座標の計算に必要な処理のみを抜粋し、式を導出しやすいよう処理の順番を変えたものです。

図5. x座標の処理

図2の時点では座標の更新(②)が最後ではないので、そのまま式を導出できません。そこで、③をループの一番最初に持っていき、その分を④で補います。

導出は、

  1. 始めは④がなかったものとして計算
  2. 最後に④を考慮

という方針です。

では、図5の②から漸化式を導きます。式7のようになります。

 \displaystyle x _ {n+1} =  x _ n + v _ {n + 1} \tag{7}

式7の  v _ {n + 1} は、意味合いとしては式2と等価ではありません。しかし、形は変わらないのでそのまま適用します(この意味はy座標を求めるときにわかります)。よって、式2を式7に適用した漸化式は式8のようになります。

 \displaystyle x _ {n+1} =  x _ n + v _ 0 \times 0.98 ^ {n + 1} \tag{8}

式8は式9のように変形できます。

 \displaystyle x _ n =  x _ 0 + \sum _ {k = 0} ^ {n - 1} v _ 0 \times 0.98 ^ {k + 1} \tag{9}

式9を等比数列の和の公式を使って解くと式10のようになります。

 \displaystyle x _ n =  x _ 0 + 50 \times 0.98 \times v _ 0  \times (1 - 0.98 ^ n) \tag{10}

ここで、④を展開します。式10で  \displaystyle v _ 0 = v _ 0 / 0.98 と置き直すと式11のようになります。

 \displaystyle x _ n =  x _ 0 + 50 \times v _ 0  \times (1 - 0.98 ^ n) \tag{11}

ここで式11の  x _ n x に、 v _ 0 V _ x + v _ {0x} に、 n T に置き直して完成です(式12)。

 \displaystyle x =  x _ 0 + 50 \times (V _ x + v _ {0x}) \times (1 - 0.98 ^ T) \tag{12}

y座標

最後は座標yを導出します。図6は式を導出しやすいよう図2の処理の順番を変えたものです。

図6. y座標の処理

ここからの導出はx座標と同様です。

図2の時点では座標の更新(②)が最後ではないので、そのまま式を導出できません。 そのため、③をループの一番最初に持っていき、その分を④で補います。

導出は、

  1. 始めは④がなかったものとして計算
  2. 最後に④を考慮

という方針です。

では、図6の②から漸化式を導きます。式13のようになります。

 \displaystyle y _ {n+1} =  y _ n + v _ {n + 1} \tag{13}

式13の  v _ {n + 1} は、意味合いとしては式5と等価ではありません。ここはx座標と違ってこのまま式5を適用できません。よって、ここで"図6の③・①における"  v _ n を求めます。漸化式は式14になります。

 \displaystyle v _ {n+1} =  0.98 v _ n - 0.04 \tag{14}

式14を解くと、式15になります。

 \displaystyle v _ {n} = (v _ 0 + 2) 0.98 ^ n - 2 \tag{15}

ここで式15を式13に適用します。漸化式は式16のようになります。

 \displaystyle y _ {n+1} =  y _ n + (v _ 0 + 2) 0.98 ^ {n + 1} - 2 \tag{16}

式16は式17のように変形できます。

 \displaystyle y _ n =  y _ 0 + \sum _ {k = 0} ^ {n - 1} (v _ 0 + 2) \times 0.98 ^ {k + 1} - \sum _ {k = 0} ^ {n - 1} 2 \tag{17}

式17を等比数列の和の公式などを使って解くと式18のようになります。

 \displaystyle y _ n =  y _ 0 + 50 \times 0.98 \times (v _ 0 + 2)  \times (1 - 0.98 ^ n) - 2 n \tag{18}

ここで、④を展開します。式18で  \displaystyle v _ 0 = v _ 0 / 0.98 と置き直すと式19のようになります。

 \displaystyle y _ n =  y _ 0 + 50 \times (v _ 0 + 1.96)  \times (1 - 0.98 ^ n) - 2 n \tag{19}

ここで式19の  y _ n y に、 v _ 0 V _ y + v _ {0y} に、 n T に置き直して完成です(式20)。

 \displaystyle y = y _ 0 + 50 \times (V _ y + v _ {0y} + 1.96) \times (1 - 0.98 ^ T) - 2 T \tag{20}

z座標

z座標はxと全く同じです。 x ( X) を  z ( Z) に置き換えるだけで成立します。

弾頭の爆発座標

この記事で導出した座標は弾頭TNT本体の座標になります。弾頭の爆発座標は y に 0.06125 (= 0.98 / 16) を加算した値になります。Carpet Modの/log explosions briefで出る値は爆発座標なので、取り違えにご注意ください(1敗)。

前々回のブログ記事について

前々回のブログは1gtあとの速度を初速度としています。 そのため、式の形が今回とは違います。 「なんかよくわからんけど2%増しにすると値が合う」と書いたのは1gtあとの速度を初速度としたためだと思われます。

検証環境

Minecraft Java Edition 1.17.1-1.18.1


次回: TNTの座標計算 #2 - 複数の装薬 - えいまぼJEブログ


  1. gt:ゲームティック。一般的には1gt = 0.05秒として差し支えありません。
  2. x, z軸は適用できる可能性があります。統合版の報告で示された係数は同じでした(0.96035 ^ 0.5 ≒ 0.98)。

Minecraftを逆コンパイル(+難読化解除)してソースコードを編集・コンパイルする方法 [Java 1.17]

注意

コンパイルしたコードは公開できません。 後ほど紹介するMCP-Rebornのライセンスで禁止されています。 その他の禁止事項についてはMCP-RebornのMCP-Licenseにあります。 これらの点を注意して逆コンパイル・編集・コンパイルをしましょう。

はじめに

Minecraftの詳しい動作を知りたいとき、ソースコードをただ見るだけよりも、ソースコードにログを吐くコードを追加して挙動を見たほうが理解しやすかったりします。そこで、ソースコードが編集されたMinecraftを作る方法を紹介します。

コンパイルコンパイル

各種ツールのダウンロード

まずは、IntelliJ IDEAMCP-Rebornをダウンロードします。

IntelliJ IDEA

以下のリンクから「ダウンロード」をクリックします。

https://www.jetbrains.com/ja-jp/idea/

f:id:rslatch:20211021124526p:plain

そうするとこのようなページに飛ぶので、Communityのほうをダウンロードします。

ダウンロードできたら、exeなら実行、zipなら展開して適当な場所に置いてください。

MCP-Reborn

以下のリンクからお好みのバージョンのzipファイル(Source codeではないほう)をダウンロードしてください。

https://github.com/Hexeption/MCP-Reborn/releases

f:id:rslatch:20211021124736p:plain

ダウンロードできたら展開して適当な場所に置いてください。

IntelliJ IDEAの操作

ダウンロードできたら、IntelliJ IDEAを開きます。規約の同意と匿名の情報送信について選択したあと、ウィンドウが出ます。そのウィンドウの「Open」をクリックします。

f:id:rslatch:20211021125039p:plain

ファイルの選択ダイアログが出るので、展開したMCP-Rebornの中にあるbuild.gradleを選択して「OK」をクリックします。

f:id:rslatch:20211021125312p:plain

そのファイルをどのように開くか聞かれるので、「Open as Project」をクリックします。

f:id:rslatch:20211021125424p:plain

更に、そのプロジェクトを信頼するか聞かれるので、「Trust Project」をクリックします。

f:id:rslatch:20211021125703p:plain

そうすると自動でプロジェクトのビルドが開始されるので、しばらく待ちます。

f:id:rslatch:20211021125817p:plain

左下に「finished」、右下に「BUILD SUCCESSFUL」などと書かれていれば、ビルドが完了しています。

f:id:rslatch:20211021130053p:plain

ビルドが完了したら、次はセットアップをします。

右端に縦書きで「🍃Gradle」と書いてある部分をクリックします。右上にメニューが出てくるので、mcp-reborn/Tasks/mcpの中のsetupをダブルクリックします。

f:id:rslatch:20211021213344p:plain

これで逆コンパイル(+難読化解除)を含めたセットアップが始まります。左下に「successful」、右下に「BUILD SUCCESSFUL」などと書かれていれば、セットアップが完了しています。

f:id:rslatch:20211021230219p:plain

ここまで来れば、あとはコンパイルしてゲームを起動するだけです。setupの上にあるrunclientをダブルクリックします。

f:id:rslatch:20211021230341p:plain

コンパイルのあと、ゲームが起動します。

f:id:rslatch:20211021230419p:plain

ゲームの起動が確認できたら、この節は終了です。

編集する

さて、次はソースコードを編集します。

今回は、TNTが爆発した瞬間のTNT座標をログファイルに出力するプログラムを挿入します。 Minecraftでは「複数のTNTが近くで同一tickで爆発するとき、先に爆発したTNTの影響を受けて、後に爆発するTNTが爆発直前に移動」します。これをソース編集によって体感します。

では、まずはエンティティのTNTの挙動を記述したファイルを探します。ここでCtrl+Shift+Nキーを押します。

f:id:rslatch:20211023010350p:plain

検索画面が出たので、「tnt」と入力します。

f:id:rslatch:20211023010401p:plain

いろいろなファイルが引っかかりますが、エンティティのTNTPrimedTnt.java*1に記述されているので、それを選択します。

PrimedTnt.javaが開かれるので、このファイルの82行目あたり*2にあるexplosion関数*3の中に以下のコードを挿入します。

System.out.println("(x, y, z) = (" + this.getX() + ", " + this.getY() + ", " + this.getZ() + ")");

場所はどこでも良いですが、とりあえず関数の最初にします。

f:id:rslatch:20211023010453p:plain

挿入したら、runclientをしてゲームを実行します。

そして、以下の動画と同じことをします。TNTを(x, z) = (0.5, 0.5)と(0.5, 2.5)に置き、同時に着火します。y座標は見ないので適当に。

爆発したので、そのログが吐かれています。

ログはrun/logs/latest.logにあります。

f:id:rslatch:20211023012039p:plain

latest.logを開くと、ファイルの末尾に座標が書き込まれているはずです。

f:id:rslatch:20211023012212p:plain
着色が違いますが内容は同じです

このように、爆発した瞬間の座標や爆発の順番がわかります。 爆発の順番は(x, z) = (0.5, 0.5)→(0.5, 2.5)のようです*4。 よく見るとz = 2.5付近で爆発するはずのTNTはz = 3.259で爆発しています。 これは先に爆発したTNTの爆発力で、後に爆発するTNTが飛んだ現象です。 これで「後に爆発するTNTが爆発直前に移動する」ことを体感できたかなと思います。 お疲れさまでした。

その他の編集のヒント

その他のファイルを編集してログを吐かせる場合も、以上と同じ手順でできます。

Tips : 関数の元を見つける

TNTの例では、explosion関数の中にthis.level.explosionという関数が書かれていました。 この関数は何をやっているのか?を追うために、便利なショートカットキーがあります。

this.level.explosionexplosionにカーソルを合わせてCtrl+Alt+Bキーを押します。 これでこの関数が実装されている位置まで飛んでくれます。

このショートカットキーを使って奥深くまで追うことができます。

日本語でコメントできるようにする

このままだと、日本語でコメントするとエラーが出ます。

これは、文字コードを指定すれば解決します。

左のメニューからbuild.gradleを開きます。 開いたファイルの末尾に以下の2行を追記します。

compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'

f:id:rslatch:20211023015801p:plain

これで日本語のコメントを打てるようになります。

(おまけ1) IntelliJ IDEAの日本語化

IntelliJ IDEAの日本語化の方法を紹介します。

左上の「File」から「Settings」をクリックします。

f:id:rslatch:20211023003433p:plain

設定ウィンドウが出ます。左側の「Plugin」をクリックし、検索欄に「japan」と入力します。そうすると日本語化プラグインが出てくるので、それを選択してインストールします。

f:id:rslatch:20211023003453p:plain

インストールができたら「Restart IDE」をクリックしてIntelliJ IDEAを再起動します。

f:id:rslatch:20211023003634p:plain

これで日本語化ができました。

f:id:rslatch:20211023003724p:plain

(おまけ2) Assetsのコピー

何もしない状態では、実行したゲームから音が鳴りません。また、日本語化もできません。 このような音や言語ファイル(Assets)が欲しい場合は、右上のGradleメニューのmcp-reborn/Tasks/mcpからcopyAssetsを実行します。

f:id:rslatch:20211023000237p:plain

実行したあと、左下に「successful」、右下に「BUILD SUCCESSFUL」などと書かれていれば、Assetsのコピーが完了しています。

このままrunclientでゲームを実行すれば、音が出てくるはずです。

(余談) yarnについて

yarnというツールがあります。 これは逆コンパイルと難読化解除までをしてくれるツールです。 編集とコンパイルはできません。 特徴としては、難読化解除後の名前がMCP-Rebornと違う点です。 MCP-Rebornよりわかりやすい命名なこともあるので、見比べると理解が早くなるかもしれません。 yarnの使い方についてはこちらを参照ください。ただし、バージョン1.17以降はJava16のパスを通す必要があり、手順が増えることに注意してください。

参考動画

*1:バージョンによって名前が異なる場合があります

*2:バージョンによって場所が異なる場合があります

*3:バージョンによって名前が異なる場合があります

*4:この順番はおそらく方角や座標に依存します

着火されたTNTの速度と座標の計算式【Minecraft JE 1.16.5】

1. はじめに

TNTキャノン開発者向けに、着火されたTNTの速度と座標の計算式を掲示します。

検証環境はMinecraft Java Edition 1.16.5になります。

TNTの速度の計算はy、r、θの円筒座標系で行われているようです。 回転成分(θ)は計算に関係ないため、高さ成分(y)と中心軸からの半径(r)のみ掲載しています。

また、この記事中のtickはゲームティックであり、1tick = 1/20秒 とします。

2. 速度

y成分

文献*1*2より、着火されたTNTの速度のy成分v_y[block/tick]は

\displaystyle v_y = (v _ {0y} + 1.96)  \times 0.98 ^ {20t - 1} - 1.96

となります。 ただし、v _ {0y}はy方向の初速度[block/tick]、tは時間[秒]です。

なお、この記事での初速度は外部からTNTの爆発などで推進力を得た瞬間のフレームの速度としています。ただし、時間tは推進力を得るフレームの1tick前のフレームを0として計算します。

r成分

文献*3より、着火されたTNTの速度の半径成分v_r[block/tick]は

\displaystyle v_r = v _ {0r} \times 0.98 ^ {20t - 1}

となります。 ただし、v _ {0r}はr方向の初速度[block/tick]、tは時間[秒]です。

原文のツイートは統合版でしたが、Java版でもそのままの式が当てはまりました。ただし、レッドストーンティックから秒に変換する関係で累乗の中の数は変わっています*4

3. 座標

毎フレームのTNTの座標を階差数列とみなして*5、その一般項を求めることで座標の計算式を導出します。後述しますが、積分で導出すると値が小さくなるため、数列での解決になりました。

y成分

着火されたTNTのy座標 y [block]は

\displaystyle y = y _ 0 + 50 \times (v _ {0y} + 1.96) \times (1 - 0.98 ^ {20t}) - 39.2 \times t

となります。 ただし、v _ {0y}はy方向の初速度[block/tick]、tは時間[秒]、y _ 0は着火されたTNTが推進力を得るフレームの1tick前のフレームのy座標です。 補足すると、初速度はその1tick後、つまり推進力を得た瞬間のフレームの速度となります。

r成分

着火されたTNTの半径方向 r [block]は

\displaystyle r = 51 \times v _ {0r}\times (1 - 0.98 ^ {20t})

となります。 ただし、v _ {0r}はr方向の初速度[block/tick]、tは時間[秒]です。

4. 実際に計算する

適当なTNTキャノンで打った弾頭の速度、座標(弾道)を計算式で算出し、実際の値と比較していきます。

速度

横軸を時間として、速度のy成分・r成分をそれぞれグラフに表します。

f:id:rslatch:20210608172831p:plain
速度のy成分のグラフ

f:id:rslatch:20210608172902p:plain
速度のr成分のグラフ

青い線がMinecraft内で観測したデータで、オレンジの線が計算式で算出した値です。

グラフを見ると、y成分・r成分ともに観測値と計算値がよく一致しています。 なお、時刻0の部分は推進力を受ける直前のため、値は一致しません。

座標

横軸を時間として、座標のy成分・r成分をそれぞれグラフに表します。

f:id:rslatch:20210608173002p:plain
座標のy成分(y座標)のグラフ

f:id:rslatch:20210608173025p:plain
座標のr成分のグラフ

速度と同様、y成分・r成分ともに観測値と計算値がよく一致しています。

弾道

せっかくなので、弾道を出してみます。 横軸をr、縦軸をyにしてプロットします。

f:id:rslatch:20210608173055p:plain
TNTの弾道

同様に、観測値と計算値がよく一致しています。 空気抵抗の影響で後半の放物線が潰れているのが見てわかると思います。

5. あとがき

最近は全方位対応型のTNTキャノンを作っていて、弾道がわかると便利だと思い、計算式を探していました。 見つかった・導出した計算式が本当に正しいかを確かめるため、毎フレームのTNTの座標・速度を測定しました。この測定にはCarpet Modを使用しました。時間を止めたり、指定したティック数だけステップ的に時間を進められたりできるので検証が非常に楽でした。

この記事がTNTキャノン開発の一助になることを願います。

6. 付録: 導出のためのメモ

ここからは完全におまけで、座標の計算式導出のためのメモです。興味のある方のみご覧ください。

積分は値が小さい

まず、最初は座標の導出のために数列ではなく愚直に積分をしていました。 積分をすると、y成分・r成分は以下のようになるはずです。

y成分:

\displaystyle y = \frac{v _ {0y} + 1.96}{0.98 \ln(0.98)} \times (0.98 ^ {20t} - 1) - 39.2 t + y _ 0

r成分:

\displaystyle r = \frac{v _ {0r}}{0.98 \ln(0.98)} (0.98 ^ {20t} - 1)

しかし、この計算式だと本来より値が小さく出ます。

それに、離散的な値を連続であるとして計算しているのでどこかで誤差が出るはずです(出ました)。

そういうわけで、積分で導出した式は使えないということがわかりました。

階差数列で解く

積分はダメでした。マインクラフト防衛部さんのページ*6に載っている数列の一般項を求めることにします。とりあえず簡単なr成分の方を解いていくこととして、以下のような式を立てられるはずです。


\displaystyle R _ {n+1} = R _ n + f(n) \\\
\displaystyle R _ 0 = 0 \\\
\displaystyle f(n) = v _ {0r} \times 0.98 ^ n \\\
\displaystyle n = 20t

 R _ n nティックのフレームの rとしました。

これを解くために、公式*7を使います。以下のような公式でした。


\displaystyle R _ n = R _ 0 + \sum _ {k=0} ^ {n - 1} f(k)

この公式の右辺の2項目は等比数列の和ですので、解けます。等比数列の和の公式*8を使って、


\displaystyle R _ n = R _ 0 + v _ {0r} \times \frac{1 - 0.98 ^ n}{0.02}

となります。また、 R _ 0 = 0であり、  n = 20tなので、


\displaystyle r = v _ {0r} \times \frac{1 - 0.98 ^ {20t}}{0.02}

となります。なお、勝手に r = R _ nとしましたがこれでも大丈夫です。

少し整形すると以下のようになり、はじめに紹介した計算式に近づきました。


\displaystyle r = 50 \times v _ {0r} \times (1 - 0.98 ^ {20t})

なお、y成分も同じようにして解くことができます。詳しい解説は省略します。

r成分の謎の1.02倍

しかしながら、このようにして数列の一般項を求めても、r成分だけはなぜか観測値よりも小さく出ます。 速度の観測値を数値積分していっても 50 \times v _ {0r} \times (1 - 0.98 ^ {20t})と同じ値が出るため、私の導出に間違いはないようです。おそらく、Minecraft内で速度を座標に変換するときに変な処理がされていると思われます。

このとき、積分で解いたときに少し値が小さく出たことを思い出し、定数倍すれば観測値と合うのでは?と考えました。適当に定数倍をしていき、1.02倍するとバッチリ合いました(50 × 1.02 = 51)。一応他の射撃パターンでも合うか確認しましたが、どれも合致しました。多分これが一番正確だと思います。また、1.02は2-0.98であり、一応関係ありそうな値です。とりあえずこれでFAとします。

あまり気持ちの良い解決方法ではないので、なにか理論的な導出方法があれば教えていただきたいです。

*1:日本語: https://minecraftjapan.miraheze.org/wiki/%E3%82%A8%E3%83%B3%E3%83%86%E3%82%A3%E3%83%86%E3%82%A3

*2:英語Wiki: https://minecraft.fandom.com/wiki/Entity

*3:たいぼんさんツイート: https://twitter.com/taibon852/status/1065551923351216130

*4:元のツイートの値は0.96035でしたが、それを0.5乗すると0.98に極めて近い値になります。切りの良い数字なので、そのまま0.98としました。

*5:マインクラフト防衛部: http://blog.livedoor.jp/minecraft_boueibu/archives/1059975020.html

*6:ちなみにこのページにはy成分しか載っていませんが、基本的な考え方はr成分にも適用できます。

*7:漸化式と一般項(階差形): https://www.geisya.or.jp/~mwm48961/electro/recurr_series1.htm

*8:等比数列の和: https://www.kwansei.ac.jp/hs/z90010/sugakua/suuretu/touhisum/touhisum.htm