敵の動きの計算方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
dic
記事: 657
登録日時: 13年前
住所: 宮崎県
連絡を取る:

敵の動きの計算方法

#1

投稿記事 by dic » 10年前

STG作ってます dic です。

今、敵の動きの計算方法でつまっています。
まず、0フレーム目に指定した座標で出現して、これを x0, y0 として、
指定したフレームに、指定した座標を、指定した角度で通過するという計算式がほしいのですが、
直線移動しか、私にはまったく思い浮かばず、どうしたらいいのかまったくわかりません。

使用する変数として、
速度v, 角度angle, 座標x,y, アクティブなフレーム数frame, で、できないでしょうか?

指定したフレームで、というのは、数フレーム後に 座標 xn yn を通るというのまではわかるのですが、
なにぶん、x成分とy成分が、v * cos( 3.14 / 180 * angle ) などと複雑になり
なかなか頭でイメージがつかめず、また、指定した角度で指定した座標を通過するというのも
まったくわかりません。
仮に角度0°で移動しているものが、指定した座標を通過するときは角度90°というのも
毎フレームx成分とy成分が増減しており、どのように計算していいのかわかりません。

言葉足らずの部分もあるかもしれませんが、
どうしたら、指定したフレームに、指定した座標を、指定した角度で通過するという計算式がわかるでしょうか?

hide

Re: 敵の動きの計算方法

#2

投稿記事 by hide » 10年前

初期位置 (x0,y0) に対して 移動後の座標 (x1,y1) を設定した等速直線運動の場合は
角度は一意に決まってしまうので指定した角度で通過と言うのは無理でしょう。

・点 P(x0,y0) から指定時間 T 後に点 Q(x1,y1) を通過する等速直進運動の 速度V は
V = (Q - P) / T
(vx,vy) = {(x1,y1) - (x0,y0)} / T = ((x1-x0)/T,(y1-y0)/T)

速さと角度に直すと
速さv = √(vx^2+vy^2) 角度 θ = ArcTan(vx/vy)
です。

それとも等速直線運動でない運動の式がほしい という話でしょうか

因みにですが、移動の計算ではなるべく角度という概念を使わずにベクトルで考えることをオススメしてみます。
ググるとむつかしいことばかり書かれているかもしれませんが、
初歩的な部分は角度の概念よりずっと簡単でx,yを組にして計算は別でしましょうというやり方です。
座標の概念とそのまま噛み合うので扱いやすいですよ。座標と速度ベクトルだけ持って角度を丸投げできます。

角度ということは直線でなく、曲線?
指定した角度で、というのは考えていくと微積の分野に入っていきそうなので辛いかもしれませんね。
直線と違って軌跡が無数に存在するのでもう少し絞った条件がほしいところです。
「半円の円運動で」 「cosカーブで」 とかであればそんなに苦労しないかもしれません。

アバター
usao
記事: 1889
登録日時: 11年前

Re: 敵の動きの計算方法

#3

投稿記事 by usao » 10年前

やりたいことがわからないですね.
例えば,
「(0,0)から(100,0)に移動しろ,ただし,(100,0)を通過するときの角度は90度(移動方向が真下向き)でな」
と言われたら,
どういう軌道をとれば正解なのでしょう?

これだけの条件だとパターンは無限に考えられますが,きっと
「そういう場合はこう」っていう考えというか軌跡のイメージがあると思うのです.だとしたら,
・それをいくつかの線分に分解して折れ線で指定してやるという妥協案
があります.

そんなのじゃ嫌で,もっと自動でうまいことやってほしいんだ!ということであれば
・パラメトリック曲線とかの話
になるのかもしれません.
オフィスの図形で,数点クリックしていくと曲線になるやつありますよね.
「頂点の編集」から各点での傾きも指定できますし,あんなのが欲しいのでしょうか.
(でも,ああいうのは激しく難しいと思う)

そこまでじゃなくても,「思ったような角度に近い感じになるようにいろいろと調整する作業」を頑張ってやってもいいのなら
もっと簡単に実装できそうな曲線(bezierとか)でやってしまう,というのもありかも.
オフトピック
そういや過去にこんなの↓を書いたことがありますね.
ある程度の形を折れ線で与えて,それをより細かく曲線的に補間するようなことで良ければ使えるかもしれません.
http://dixq.net/forum/viewtopic.php?f=83&t=13807

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 敵の動きの計算方法

#4

投稿記事 by softya(ソフト屋) » 10年前

イメージしている動きを図にして頂くと伝わりやすいと思います。
線と三角形の敵とか簡単な図式で構わないです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

dic
記事: 657
登録日時: 13年前
住所: 宮崎県
連絡を取る:

Re: 敵の動きの計算方法

#5

投稿記事 by dic » 10年前

みなさん回答ありがとうございます。


今、思い出しました。
やりたいことは、ベジェ曲線です。

しかし、hideさんやusaoさんのおっしゃる通り、指定した座標を指定した角度で通過するという条件は無限に
あることに言われて気づきました。
それだと、答えが無数にあり式が見つかりませんね。

さらにパラメトリック曲線で検索してみたところ、ベジェ曲線よりもよさそうなものを見つけました。
パラメトリック曲線の Ferguson / Coons曲線 というものです。(したのリンク先です。)
http://markun.cs.shinshu-u.ac.jp/learn/ ... ndex4.html

この方法だと、初期位置と初期速度、最終位置と最終速度の情報でいいので
私のほしい式が求められそうです。
点を3つ以上にしたら、式をさらに追加して・・・と
と、求められるでしょうか?
リンク先の式の一番下の行列は、わかりませんが、
三次方程式なら解けるのでいけるのではないかと思っています。
(やってみます)


イメージしていた軌道を画像にしました。
左の1.がイメージしていた軌道ですが、たしかにおふたりのおっしゃるとおり、軌道がいくつも
存在します。それが右の2です。
添付ファイル
無題.png
無題.png (5.99 KiB) 閲覧数: 4789 回

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 敵の動きの計算方法

#6

投稿記事 by softya(ソフト屋) » 10年前

ここにサンプルコードがあります。
「Maverick Project」
http://maverickproj.web.fc2.com/pg91.html

> 点を3つ以上にしたら、式をさらに追加して・・・と

それは、うまくいかないかもしれません。実験してみてください。
狙ったを点を通らせるならスプライン補完ですが、Javaのサンプル・コードです。
「離散した点を補間してグラフを描画する方法:CodeZine」
http://codezine.jp/article/detail/163
スプライン補完だと角度は狙ったとおりにはいきません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
usao
記事: 1889
登録日時: 11年前

Re: 敵の動きの計算方法

#7

投稿記事 by usao » 10年前

>点を3つ以上にしたら、式をさらに追加して・・・

X0,V0とX1,V1を与えて1区間の曲線を求めたら,次の区間の曲線を
前の区間の終点データX1,V1と 次の点のデータX2,V2を用いて求めるようにすれば
連続したように見えるのではないでしょうか.
ただし,連続した曲線軌跡として実際に動かしてみると,点X1を境にして唐突に加減速があるように
みえたりするかもしれません.

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 敵の動きの計算方法

#8

投稿記事 by softya(ソフト屋) » 10年前

そういえば、加速度的な動きなら、2次関数じゃないと気持ち悪い気がするんですが、どうなんでしょう。
動きを見てみないと何とも言えませんけど。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
usao
記事: 1889
登録日時: 11年前

Re: 敵の動きの計算方法

#9

投稿記事 by usao » 10年前

オフトピック
ベジェやスプラインみたいな曲線を補間に使うにしても
点間の「勝手に補間された部分」の形状が想定していた形状とはなかなか合致しない→どうする?っていう問題はあるわけで,
制御点の位置を変えてみたり区間内に制御点を増やしたりだとか,そういう調整は必要で,
そのへん結構難しいというかやっかいかなと思います.

それと,この手のやつは,曲線を描画するだけならいいのですが
移動軌跡として使う場合には,媒介変数 t を単純に(各区間で同じ刻み幅で)扱うと,
曲線上を進む速度が場所場所で変わることになるので,そこらへんの調整もわりと面倒というか
自然に見せる方法自体が問題となると思います.
(tの刻みを決めるために曲線の長さを簡単に求められればいいですが,解析的に求めるのは難しいとか)

…なので,質問者様がこういったものを使う方向で軌跡制御を実現できた場合,
そういった事柄をどのように解決したのか?といったこと(方法とかあるいはコツ(?)みたいなの)も示してもらえたなら
ためになりますね.

FUNK
記事: 25
登録日時: 11年前

Re: 敵の動きの計算方法

#10

投稿記事 by FUNK » 10年前

役に立つかわかりませんが、
過去に私が製作したゲーム内で書いた
ベジエ曲線の軌道を進むときの各フレームの位置( px, py )を算出する関数
がありますので貼っておきます。
もし少しでも何かの役に立てられたら幸いです。

コード:

// 二次ベジエ曲線
int Bezier2( float *px, float *py, float x1, float y1, float x2, float y2, int x3, int y3, int const_time, int time )
{
	// x1,y1:始点 x2,y2:終点 x3,y3:方向点
	// count_time:全体の時間 time:現在の時間(フレーム単位)

	if( time >= const_time )
		return FALSE;

	float b = (float)time / const_time;
	float a = 1 - b;

	*px = ( a * a * x1 ) + ( 2 * a * b * x3 ) + ( b * b * x2 );
    *py = ( a * a * y1 ) + ( 2 * a * b * y3 ) + ( b * b * y2 );

	return TRUE;
}

// 三次ベジエ曲線
int Bezier3( float *px, float *py, float x1, float y1, float x2, float y2, int x3, int y3, int x4, int y4, int const_time, int time )
{
	// x1,y1:始点 x2,y2:終点 x3,y3:方向点1 x4,y4:方向点2
	// count_time:全体の時間 time:現在の時間(フレーム単位)

	if( time >= const_time )
		return FALSE;

	float b = (float)time / const_time;
	float a = 1 - b;

	*px = ( a * a * a * x1 ) + ( 3 * a * a * b * x3 ) + ( 3 * a * b * b * x4 ) + ( b * b * b * x2 );
	*py = ( a * a * a * y1 ) + ( 3 * a * a * b * y3 ) + ( 3 * a * b * b * y4 ) + ( b * b * b * y2 );

	return TRUE;
}
【追記】
関数のイメージ図を描きましたので添付します。
関数内の「全体の時間」は始点から終点まで移動するのに掛かる時間、
「現在の時間」は取得したい時間です。
例えば始点から終点まで20F掛かるなら「全体の時間=20」とし、
その時の5Fの時の位置を取得したいのなら「現在の時間=5」にします。
添付ファイル
Bezier.png
Bezier.png (18.11 KiB) 閲覧数: 4699 回
Boogaloo is funk feelin'.

dic
記事: 657
登録日時: 13年前
住所: 宮崎県
連絡を取る:

Re: 敵の動きの計算方法

#11

投稿記事 by dic » 10年前

>>softyaさん
リンク先を読ませていただきました。
下に私が作ったソースを貼りますが、初期位置、終点位置をx0, x1 とかいてあり、
y0, y1 の値がないので、y 座標をどのように求めていいのかがわかりませんでした。

スプライン補間ですが、私にはとうてい解けそうにないです。

>>usaoさん
おっしゃる通りのことをやりたかったのですが、同じくy座標の求め方がわからずに、止まりました。


>>FUNKさん
ソースコード付きで教えていただきありがとうございます。
たいへんわかりやすいです。
今回のFerguson / Coons曲線がわからなかったら、FUNKさんのソースコードを参考にさせてもらってもよろしいでしょうか?


以下、http://markun.cs.shinshu-u.ac.jp/learn/ ... ndex4.html
に書いてある式をプログラムにしたのですが、y座標についてかかれていないので、どうやったら
現在位置がわかるのかがわかりません。
どうやったらy座標は求められるのでしょうか?
もしくは、今の私の力では無理でしょうか?
dyにいちおうもとめているのですが、おそらく間違っていると思います。

もしかして、速度 v0, v1 というのは
v0 の成分を x成分と y成分にわけてベクトルとして考えるのでしょうか?
それでもわからないですが・・・

コード:

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
    //ウィンドゥモード変更と初期化と裏画面設定
    ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen(DX_SCREEN_BACK);
 
    while(ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0)
	{
		double	x0, x1, y0, y1, v0, v1;
		static	double	t = 0.001;

		x0 = 100;
		y0 = 100;
		v0 = 4;

		x1 = 400;
		y1 = 400;
		v1 = 8;

		double	dx, dy;
		double	a, b, c, d;
		a = ( 2*x0 - 2*x1 + v0 + v1 );
		b = ( -3*x0 + 3*x1 - 2*v0 - v1 );
		c = v0;
		d = x0;
		dx = ( a * t * t * t ) + ( b * t * t ) + ( c * t ) + ( d );
		dy = ( a * dx * dx * dx ) + ( b * dx * dx ) + ( c * dx ) + ( d );

		DrawCircle( (int)dx, (int)dy, 8, GetColor(255,255,255), true );
		t += 0.001;

		DrawFormatString( 0, 0, GetColor(255,255,255), "x:%d y:%d t:%f", (int)dx, (int)dy, t );
		DrawFormatString( 0, 20, GetColor(255,255,255), "a:%f b:%f c:%f d:%f", a, b, c, d );

		if( CheckHitKey( KEY_INPUT_ESCAPE ) )
			break;
    }

	DxLib_End();    // DXライブラリ終了処理

	return 0;
}

アバター
usao
記事: 1889
登録日時: 11年前

Re: 敵の動きの計算方法

#12

投稿記事 by usao » 10年前

いや,x0もv0もベクトルでしょう,そこは.

※ベクトル表記でx0とか書かれているやつは,その記号一つで x0=(x,y) を表しています.
 vの方も同様に,v0=(vx,vy)のように2つの座標成分をまとめて書いているだけです.
 実際の計算時は,↓に書いたようにそれぞれの成分ごとに独立して行えばいいでしょう.

「ベクトル」の意味がわからないのであれば,とりあえず
各制御点のデータが,(2次元ならば)(x,y, vx,vy) の4個あるのだと思えばいいでしょう.
で,
x座標の計算については そのうちの(x, vx)だけを,
y座標の計算については 残りの(y,vy)だけを用いて
それぞれ計算すればいいです.

閉鎖

“C言語何でも質問掲示板” へ戻る