着弾点の予想と目標距離まで飛ばすための発射角の計算

;
;	着弾予測
;
;弾道、飛距離、の予想。弾丸の発射が行えます。
;

#include "hspmath.as"	; asinで使用

;	定数
#const BULLET_FRAME  50
groundTop = 400.0	; 地面の高さ
gravity = -1.0		; 重力
velocity = 20.0		; 初速


;	初期値
bulletPosX = 50.0	; 弾丸座標
bulletPosY = 0.0	; 弾丸座標
firingAngle = 45.0 * M_PI / 180.0	; 発射角度[rad]
setHikyori = 200.0	;設定飛距離(この飛距離を出すための発射角度を算出します。)

flgShot = 0			; ショットフラグ
vx = 0.0
vy = 0.0

ddim courseOfBullet, BULLET_FRAME, 2	; 弾道座標データ


;-----------------------------------------------------------
;
;	メインループ
;
*main
	redraw 1 : await 16 : redraw 0 : color 255, 255, 255 : boxf : color 0,0,0 : pos 0,0
	
	stick key, 1|2|4|8|128
	
	; --------------
	;	終了
	; --------------
	if key&128 : end
	
	; --------------
	;	発射角度
	; --------------
	if key&2 : firingAngle += 1.0 * M_PI / 180.0
	if key&8 : firingAngle -= 1.0 * M_PI / 180.0

	; --------------
	;	発射
	; --------------
	if key&16 : flgShot = 1

	; --------------
	;	物理計算
	; --------------
	
	;	重力
	if int(bulletPosY) > 0 {
		vy += gravity
	} else {
		vx = 0.0
		vy = 0.0
	}
	
	;	ショット
	if flgShot = 1 {
		;	初速
		vx = velocity * cos(firingAngle)
		vy = velocity * sin(firingAngle) + gravity / 2.0
			;微調整のため適当に1/2しています。最初だけ1/2倍しないと計算が大きくずれるようです。
			;射出時は地面に付いているのでってことで、詳細な考察は省略。
		flgShot = 0
	}
	
	;	予備計算
	px = bulletPosX
	py = bulletPosY
	px += vx
	py += vy
	
	;	地面
	if py < 0.0 {
		;予測地点が地面にめり込む場合
		bulletPosX += vx * bulletPosY/-vy
		bulletPosY = 0.0
	} else {
		bulletPosX += vx
		bulletPosY += vy
	}

	;	弾道計算
	gosub *CalcDandou

	;	飛距離予測計算
	gosub *CalcHikyori

	; --------------
	;	描画
	; --------------
	line 0, groundTop, ginfo_winx, groundTop
	gosub *PlotDandou
	gosub *PlotBullet
	pos 0, 0 : color 0,0,0
	mes "現在の座標:" + bulletPosX + ", " + bulletPosY
	mes "予想飛距離:" + yosouHikyoriX 
	mes "予想座標  :" + (bulletPosX + yosouHikyoriX) + ", 0.0"
	mes "予想角度(距離 " + setHikyori + "):" + rad2deg(yosouKaku) + " [deg] or " + (90.0-rad2deg(yosouKaku)) + " [deg]"



	goto *main


;-----------------------------------------------------------
;
;	弾道軌道描画
;
*PlotDandou
	; --------------
	;	弾道軌道
	; --------------
	color 200, 200, 200
	x  = courseOfBullet(0, 0)
	y  = courseOfBullet(0, 1) * -1 + groundTop
	pos x, y
	repeat BULLET_FRAME-1, 1
		x = courseOfBullet(cnt, 0)
		y = courseOfBullet(cnt, 1) * -1 + groundTop
		line x, y
	loop
	return


;-----------------------------------------------------------
;
;	弾丸描画
;
*PlotBullet	
	; --------------
	;	発射角度
	; --------------
	x = bulletPosX
	y = -bulletPosY + groundTop
	r = 30.0
	dx =  r * cos(firingAngle) + x
	dy = -r * sin(firingAngle) + y
	color 0,0,255
	line x, y, dx, dy
	mes strf("%.2f", (firingAngle*180.0/M_PI) ) + " [deg]"
	mes "Up:↑/Down:↓"

	; --------------
	;	弾丸
	; --------------
	x = bulletPosX
	y = -bulletPosY + groundTop
	r = 5
	color 0,0,0
	circle x-r, y-r,  x+r, y+r
	return


;-----------------------------------------------------------
;
;	弾道予想計算
;
*CalcDandou
	cvx = velocity * cos(firingAngle)
	cvy = velocity * sin(firingAngle) + gravity/2.0
	;0フレーム目
	courseOfBullet(0, 0) = bulletPosX
	courseOfBullet(0, 1) = bulletPosY
	;1フレーム目
	repeat BULLET_FRAME-1, 1
		courseOfBullet(cnt, 0) = courseOfBullet(cnt-1, 0) + cvx
		courseOfBullet(cnt, 1) = courseOfBullet(cnt-1, 1) + cvy + gravity * (cnt-1)
	loop
	return


;-----------------------------------------------------------
;
;	飛距離予想計算
;
*CalcHikyori
	;重力加速度:g
	;t秒後の移動距離:x,y
	;ただし上向きを正とする
	;したがって重力加速度gは g<0 である。
	;着弾点は射出点との高低差0とする。
	;
	;時刻tのときの距離座標x,yは次式のようになる。
	;
	;	x = vx0 * t
	;	y = 1/2*g*t^2 + vy0 * t
	;
	;着弾点と射出点は高低差0なので y = 0 のときの t を求める
	;(なお高低差0以外ならy=高低差として計算すれば良い。)
	;
	;	0 = 1/2*g*t^2 + vy0 * t
	;	  = t(1/2*g*t + vy0)
	;
	;	(1/2*g*t + vy0) = 0
	;	1/2*g*t = -vy0
	;	t = -vy0 * 2/g
	;
	;y = 0となるtは、
	;	t = 0
	;	t = -vy0 * 2/g
	;t = 0は射出時なので、t = -vy0 * 2/g となる。
	;このときxは
	;
	;	x = vx0 * t
	;	  = vx0 * -vy0 * 2/g
	;
	;射出角をrとしたとき
	;	vx0 = v0 * cos(r)
	;	vy0 = v0 * sin(r)
	;
	;したがってxは次のようになる。
	;
	;	x = -v0^2 * cos(r) * sin(r) * 2/g
	;
	;xがそのまま距離なので
	
	yosouHikyoriX = -1.0 * velocity * velocity * cos(firingAngle) * sin(firingAngle) * 2.0 / gravity

	;
	;xを任意の値に決めたい場合のrを求める。
	;	x = -v0^2 * cos(r) * sin(r) * 2/g
	;	cos(r) * sin(r) = x / (-v0^2) * g/2
	;
	;ここで加法定理
	;	sin(x+y) = sin(x) * cos(y) + cos(x) * sin(y)
	;	y=xの場合
	;	sin(2*x) = 2 * sin(x) * cos(x)
	;	1/2 * sin(2*x) = sin(x) * cos(x)
	;
	;したがって
	;	cos(r) * sin(r) = x / (-v0^2) * g/2
	;	1/2 * sin(2*r) = x / (-v0^2) * g/2
	;	sin(2*r) = x / (-v0^2) * g
	;	2 * r = asin( x / (-v0^2) * g )
	;	r = 1/2 * asin( x / (-v0^2) * g )
	;となり角度を求めることができる。rは複数でるがどれを用いても良い。
	;
	
	yosouKaku = asin( setHikyori / (-velocity * velocity) * gravity ) / 2.0


	return