ページ 11

円運動する際のxy成分の算出方法

Posted: 2016年10月12日(水) 04:29
by しずく
初めまして、しずくと申します。
現在、「ゲームプログラミングの館」を参考にゲーム制作を行っておりますが、
敵の行動処理の部分で詰まっております。

ゲームの内容としては東方の2Dシューティングみたいなものです。

■本題
画面左から敵が移動してきて、一定時間後に停止。
停止してから画面中央に対して時計回りに円運動するような行動をとらせたいのですが、
移動する際のx,y成分の算出方法が分かりません(数学の部分)

自分なりに調べてみたのですが、
一定時間後の座標を求めるものが多く、
式の変形も難しかったので、質問させて頂きました。

今の段階で実装したコードは敵の座標(x,y)を直接変更しているのですが、
座標を直接更新するのではなく、速度(dx,dy)を変更して、円運動するようにしたいです。

■参考にしたサイト
等速円運動の加速度」のベクトルvのx,y成分

■補足
・座標を求める際の数値(278)は、「EnemySub.cnt」が角度として使用するまでにカウントされてしまっているので、決め打ちで調整しました。
・画面サイズは(800,600)です。

プログラムに直接関係ない数学の初歩的な部分かもしれませんが、
ご教示頂けると幸いです。

コード:

//5秒後右に向かって移動してから停止
//停止してから5秒後、中心を軸にして円運動(右回転)
void enemy_calc_pattern_006(int i)
{
	if (EnemySub[i].cnt < 120)
	{
		//真右へ移動
		EnemySub[i].dx = 2;
	}
	else if (EnemySub[i].cnt < 420)
	{
		//停止
		EnemySub[i].dx = 0;
	}
	else if(EnemySub[i].cnt == 420)
	{
		//軸との距離を求める(半径)
		EnemySub[i].range_point = 400 - EnemySub[i].x;
	}
	else
	{
		//軸との角度を求める
		EnemySub[i].angle = atan2(300 - EnemySub[i].y, 400 - EnemySub[i].x);

		//画面中央を軸にして円運動
		EnemySub[i].x = 400 + cos(PI2 / 278 * EnemySub[i].cnt) * EnemySub[i].range_point;
		EnemySub[i].y = 300 + sin(PI2 / 278 * EnemySub[i].cnt) * EnemySub[i].range_point;

		//指定した角度への移動量計算
		//EnemySub[i].dx = cos(EnemySub[i].angle) * 2;
		//EnemySub[i].dy = sin(EnemySub[i].angle) * 2;
	}
}

Re: 円運動する際のxy成分の算出方法

Posted: 2016年10月12日(水) 10:00
by usao
>今の段階で実装したコードは敵の座標(x,y)を直接変更しているのですが、
>座標を直接更新するのではなく、速度(dx,dy)を変更して、円運動するようにしたいです。

うーん,もうちょっと質問の意図を明確にされた方が,有用な情報が得られやすくなるのではないかと思います.
オフトピック
"座標" と "速度" との関係が,例えば,単純に
 座標(更新後) = 座標(更新前) + 速度
という形であるとして…
既に 座標(更新後) が求まっているのならば,"速度"を逆算することはできますよね.

とかいう不毛な答えが欲しいわけではないでしょうから.
"速度"を計算 → "座標"を更新
という流れであれば,現在の座標における円の接線方向のベクトルを速度ベクトルとして用いるようなことは割とよくやります.
(が,それを単純に(上記offtopicのような単純なオイラー法的に)やると軌跡が所望の円の外側にどんどんずれていくことになりますので用途次第で工夫します.)

Re: 円運動する際のxy成分の算出方法

Posted: 2016年10月14日(金) 23:10
by しずく
返信が遅れて申し訳ありません。
ご回答有難うございます。

>うーん,もうちょっと質問の意図を明確にされた方が,有用な情報が得られやすくなるのではないかと思います.

おっしゃる通り、offtopicのような更新後の座標から逆算するというのは求めている回答でありませんでした。
質問の意図が曖昧なものであったことをお詫びすると同時に、今後利用させて頂く際に意識して参ります。


>"速度"を計算 → "座標"を更新
>という流れであれば,現在の座標における円の接線方向のベクトルを速度ベクトルとして用いるようなことは割とよくやります.

「円の接線」というワード、完全に忘却しておりました。
せっかくヒントを頂いたのですが、すぐに実装出来そうにないので、
解決はしていないままですが、一旦返信させて頂きます。

解決され次第ここに報告に参ろうかと思います。
(が、こういう場合はいったんクローズした方が宜しいでしょうか・・・)

Re: 円運動する際のxy成分の算出方法

Posted: 2016年10月15日(土) 09:21
by usao
>円の接線

(を実際に使うのがよいのかどうかば別として)
求めること自体はそう難しくはないと思います.

円弧上のある座標における接線は,
円の中心とその座標とを通る線を引いたときに,その引いた線と垂直.

今回の場合だと,接線の方向を向いたベクトルUは,以下の手順で作れるかと.
(1)ベクトルV = 現在の敵の座標 - 円運動の中心
(2)ベクトルU = Vを90度回転(2次元だろうから,Vのx成分とy成分を入れ替えて,さらにどちらか一方を-1倍する)
(3)用途に応じてUの長さを調整とか

Re: 円運動する際のxy成分の算出方法

Posted: 2016年10月22日(土) 22:35
by fm
スクリプトをテキストファイルで良いので用意するか、もしくは、2ドットか1ドット単位での移動量を上下左右キーで動くカーソルで移動する座標を
スペースキーで決定してバイナリファイルを編集できるエディターを実行ファイル内か、もしくは別のプログラムで用意すると良いと思います。

前者なら拡張子が.txtもしくはお気に入りのテキストエディタを使いつつ、拡張子を隠蔽したりする
なら編集しやすいし、一々コンパイルする必要がないので便利です。
ただし、文字列を1バイトずつあるいは2バイトずつ読み込む必要があるので、最低限、字句解析が出来ないと難しいですが、
最近の書籍はそういう情報も豊富なので、参考にするのは良いと思います。

後者なら、一々コンパイルするのが手間ですが、一度作ってしまえばどんな動きでも編集可能ですし、初代グラディウスのような敵の動きも
編集可能です。

Re: 円運動する際のxy成分の算出方法

Posted: 2016年10月30日(日) 01:52
by しずく
一応実装はしてみたのですが、オイラー法的?な手法な為か、
円の外側にドンドンずれていくことになりました。
(ずばり予想された通りの挙動になりまた。。。)

なぜこの様な挙動になるのでしょう。。
XY成分が少数だから誤差が積まれているという事でしょうか。

いくつか解決策を模索してみたのですが、
ちょっと難しすぎました。

■参考サイト
ぞうさんの何でもノート
deq notes

>今回の場合だと,接線の方向を向いたベクトルUは,以下の手順で作れるかと.
>(1)ベクトルV = 現在の敵の座標 - 円運動の中心
>(2)ベクトルU = Vを90度回転(2次元だろうから,Vのx成分とy成分を入れ替えて,さらにどちらか一方を-1倍する)
>(3)用途に応じてUの長さを調整とか

こちらにでてくる(3)というのは、
決め打ちで適当に数値を決めて調節しても宜しい箇所でしょうか。


>スクリプトをテキストファイルで良いので用意するか、もしくは、2ドットか1ドット単位での移動量を上下左右キーで動くカーソルで移動する座標を
>スペースキーで決定してバイナリファイルを編集できるエディターを実行ファイル内か、もしくは別のプログラムで用意すると良いと思います。

こちらは、すみません。ちょっと仰ってる意味が分からないですね。
スクリプトをテキストファイルとで用意する→円運動するようなスクリプトを記述する?(まず円運動が出来ておりません!)
2ドットか1ドット単位での移動量(ry → 移動パターンを生成するツールを作成する?(まず円運動が(ry)
という解釈をしたのですが、合ってますでしょうか。

コード:

//5秒後右に向かって移動してから停止
//停止してから5秒後、中心を軸にして円運動(右回転)
void enemy_calc_pattern_006(int i)
{
	if (EnemySub[i].cnt < 120)
	{
		//真右へ移動
		EnemySub[i].dx = 2;
	}
	else if (EnemySub[i].cnt < 420)
	{
		//停止
		EnemySub[i].dx = 0;
	}
	else if(EnemySub[i].cnt == 420)
	{
		//EnemySub[i].range_point = 400 - EnemySub[i].x;
	}
	else
	{
		//中心との角度を求める
		EnemySub[i].angle = atan2(300 - EnemySub[i].y, 400 - EnemySub[i].x);

		//指定した角度への移動量計算
		EnemySub[i].dx = -sin(EnemySub[i].angle) * 2;
		EnemySub[i].dy = cos(EnemySub[i].angle) * 2;
	}
}

Re: 円運動する際のxy成分の算出方法

Posted: 2016年10月31日(月) 09:47
by usao
>なぜこの様な挙動になるのでしょう。。

円と接線の絵を描いてみればわかりますよ.
「円弧上のある点Pにおける接線」というのは,すなわち,その直線は,「点P以外では円と接してない」わけですから,
Pから接線の方向に動けばその先はもう円弧の上ではないわけです.

オイラー法の誤差がでかくて困る場合には
修正オイラー法とかルンゲクッタ法とかいうのを使うと大分マシになったような気がます.
(それらの内容は忘れたけど)


>こちらにでてくる(3)というのは、
>決め打ちで適当に数値を決めて調節しても宜しい箇所でしょうか。

方向が決まればあと必要なのは移動速度的なところなので
ここは必要な長さにすれば良いかと思います.