ボールが飛ぶ距離を先に計算してだしたかった(妥協あり)

角度 θ 、速度 v で投げ上げた場合を考える。




参考より、空気抵抗をkとして、Unityでの空気抵抗は以下のように計算されているらしいので、

v=v(1kdt);

※ 引用元のfはfloatなので省略、k が空気抵抗(UnityでのDrag)


調査した結果、Unity上で dt は「Project Settings」「Time」「Fixed Timestep」であった。
物理演算は「Fixed Timestep」ごとに行われ、n 回かかるものとする。
速度は、前の速度から 1kdt をかけた等比数列なので、速度の一般項 vxn
vxn=vx0(1kdt)n
そして位置x1を計測したところ、Unity上ではvx0ではなく、vx1から使われていた。
x0=0
x1=vx1dt+x0
x2=vx2dt+x1
よって位置の一般項 xn (n1)は、
xn=vxndt+xn1=vxndt+vxn1dt+vxn2dt=(vxn+vxn1+vxn2+vx1)dt=dti=1nvxi
初項 a=v1、公比 r=1kdt等比数列の和より、
xn=vx1dt1(1kdt)n1(1kdt)=vx1dt1(1kdt)n11+kdt=vx1dt1(1kdt)nkdt(x)=vx11(1kdt)nk

速度vyを計測したところ、Unity上での式は以下のようになっていた。
vy=(vy+gdt)(1kdt);
vxと違い、一般項が複雑になるので求めると、
vy2=(vy1+gdt)(1kdt)=vy1(1kdt)+gdt(1kdt)=(vy0+gdt)(1kdt)(1kdt)+gdt(1kdt)=(vy0+gdt)(1kdt)2+gdt(1kdt)=vy0(1kdt)2+gdt(1kdt)2+gdt(1kdt)=vy0(1kdt)2+gdt((1kdt)2+(1kdt)+)=vy0(1kdt)2+gdti=12(1kdt)i
の部分は初項 a=1kdt、公比 r=1kdt等比数列の和より、
vy2=vy0(1kdt)2+gdt(1kdt)1(1kdt)21(1kdt)=vy0(1kdt)2+gdt(1kdt)1(1kdt)211+kdt=vy0(1kdt)2+gdt(1kdt)1(1kdt)2kdt=vy0(1kdt)2+g(1kdt)1(1kdt)2k=vy0(1kdt)2+g(1kdt)kg(1kdt)(1kdt)2k=(vy0g(1kdt)k)(1kdt)2+g(1kdt)k
よって速度の一般項 vyn は、
vyn=(vy0g(1kdt)k)(1kdt)n+g(1kdt)k
位置の一般項yn (n1)も、 xnと同様に dti=1nvi なので、
yn=dti=1nvyi=dti=1n((vy0g(1kdt)k)(1kdt)n+g(1kdt)k)=dt(i=1n(vy0g(1kdt)k)(1kdt)n+i=1ng(1kdt)k)
初項 a=(vy0g(1kdt)k)(1kdt)、公比 r=1kdt の等比数列の和より、
yn=dt((vy0g(1kdt)k)(1kdt)1(1kdt)n1(1kdt)+ng(1kdt)k)yn=dt((vy0g(1kdt)k)(1kdt)1(1kdt)nkdt+ng(1kdt)k)yn=(vy0g(1kdt)k)(1kdt)1(1kdt)nk+ngdt(1kdt)k

ここから距離を求めたいので、y00 から、 y=0 となるx を求める。
※Unityで確認したところ、1kdt=0の場合は空気抵抗が釣り合ってしまい、ボールが動かなかったので、1kdt0とする
0=(vy0g(1kdt)k)(1kdt)1(1kdt)nk+ngdt(1kdt)k+y00=(vy0g(1kdt)k)1(1kdt)nk+ngdtk+y01kdt
(x)より、xnvx1=1(1kdt)nk なので、
0=(vy0g(1kdt)k)xnvx1+ngdtk+y01kdt
(x)より、
xn=vx11(1kdt)nkkxnvx1=1(1kdt)nkxnvx11=(1kdt)n1kxnvx1=(1kdt)nlog(1kdt)(1kxnvx1)=nn=log(1kdt)(1kxnvx1)
よって、
0=(vy0g(1kdt)k)xnvx1+gdtklog(1kdt)(1kxnvx1)+y01kdt
vx1=vx0(1kdt) より、
0=(vy0g(1kdt)k)xnvx0(1kdt)+gdtklog(1kdt)(1kxnvx0(1kdt))+y01kdt0=(vy0g(1kdt)k)kxngdtvx0(1kdt)+log(1kdt)(1kxnvx0(1kdt))+ky0gdt(1kdt)(1kdt)0=(1kdt)((vy0g(1kdt)k)kxngdtvx0(1kdt)+log(1kdt)(1kxnvx0(1kdt))+ky0gdt(1kdt))1=(1kdt)log(1kdt)(1kxnvx0(1kdt))(1kdt)((vy0g(1kdt)k)kxngdtvx0(1kdt)+ky0gdt(1kdt))1=(1kxnvx0(1kdt))(1kdt)((vy0g(1kdt)k)kxngdtvx0(1kdt)+ky0gdt(1kdt))0=1+(1kxnvx0(1kdt))(1kdt)((vy0g(1kdt)k)kxngdtvx0(1kdt)+ky0gdt(1kdt))
これを解けば飛距離xn をだせるが、どうやらこの式は解けないようなので、近似値を求めることにした。 既存のライブラリ で対処したかったが、式のグラフの形が想定されていないのか、v0が400ほどですでに解がだせなかったので、 禁断の自作実装で対処した。

飛距離の最低値は当然0であり、最大値は(x)n=で求められるので、
xmax=vx11(1kdt)k
1kdt<1より、
xmax=vx111kxmax=vx110kxmax=vx1kxmax=vx0(1kdt)k

飛距離の計算を実現するクラスは以下の通り。
 ※2025/3/6追記 lowerBoundがupperBound/2だと解が含まれない場合があったので、0に修正

残念ながら飛距離はUnity上でも正確に測れないためか、それとも浮動小数点の計算誤差なのか、値が大きくなると精度の範囲を超えた誤差がでる。(以下の図は精度0.1を指定している)

 

しかし、これ以上精度をよくする方法が思いつかないため、ここで妥協する。

最大値がかなり飛距離に近い値なので、これを上手く活用して計算できそうだが、不明。

コメント