敵のパターンを実装する方法について
敵のパターンを実装する方法について
シューティングの基礎としては結構できあがったのですが
敵の種類を増やすことになり、敵のパターンをswitch-case文でソースコードで書いているのですが
最低24種類作りたいのですが、結構な量になり後々のゲームバランスなどを
考慮し、保守的な観点から見ると、修正に時間がかかり、デバックにも時間がかかりすぎることが
予想できますし、今やっているところなのですが、10種類も作ったら覚えきれず把握しきれません
そこで、敵の動きを数値化したいと考えているのですが
数値化できるものとして、始点、終点、速度、加速度、角度を考え付きました
しかし、これでは単純な動きしか実現できず、もうすこし複雑な動きを実現したいと考えていますが
どうすればよいのか分かりません
ベジェ曲線などを私が勉強し、理解していれば簡単なのかもしれませんが
中学生レベルで、直線、円を描く以外に敵の動きを実現する方法はどういった方法があるのでしょうか?
参考程度までに聞かせていただきたいです。
敵の種類を増やすことになり、敵のパターンをswitch-case文でソースコードで書いているのですが
最低24種類作りたいのですが、結構な量になり後々のゲームバランスなどを
考慮し、保守的な観点から見ると、修正に時間がかかり、デバックにも時間がかかりすぎることが
予想できますし、今やっているところなのですが、10種類も作ったら覚えきれず把握しきれません
そこで、敵の動きを数値化したいと考えているのですが
数値化できるものとして、始点、終点、速度、加速度、角度を考え付きました
しかし、これでは単純な動きしか実現できず、もうすこし複雑な動きを実現したいと考えていますが
どうすればよいのか分かりません
ベジェ曲線などを私が勉強し、理解していれば簡単なのかもしれませんが
中学生レベルで、直線、円を描く以外に敵の動きを実現する方法はどういった方法があるのでしょうか?
参考程度までに聞かせていただきたいです。
Re:敵のパターンを実装する方法について
私なりの考えですが、
たとえば、敵の動作を関数単位で用意するようにし、下記のような定義をおこないます。
EnemyUpdate 関数を毎フレーム実行すれば、登録された敵を移動させることが出来ます。
これなら難しい軌道計算を行なわなくても、複雑な軌道をとることが出来ます。
また、相対位置からの移動を行なうので、登録時のパラメータを変えれば軌道を調整できます。
(これは応用すれば弾のパターンにも使えます)
長くなりましたが、こんな方法はどうでしょうか?
# やっつけで書いたのでおかしいところがあるかもしれません。
たとえば、敵の動作を関数単位で用意するようにし、下記のような定義をおこないます。
typedef struct _SEnemy; typedef void (*EnemyMove)( struct _SEnemy *enemy ); typedef struct _SEnemy { EnemyMove move; // 移動関数 int counter; // カウンタ float x,y; // 敵の位置 float angle; // 移動角度 float speed; // 移動速度 } SEnemy;そして、下記のような移動関数等を定義します。
// 移動計算 void EnemyUpdatePos( SEnemy *enemy ){ enemy->x += sinf(enemy->angle) * enemy->speed; enemy->y += cosf(enemy->angle) * enemy->speed; } // カウントチェック。 // 指定値を超えたら次のフェイズへ void EnemyCountCheck( int n ){ if(enemy->counter > n){ ++enemy->phase; enemy->counter = 0; } } // 敵移動パターン 001 void EnemyMove001( SEnemy *enemy ){ ++enemy->counter; switch(enemy->phase){ case 0: // 30f 直線移動 EnemyCountCheck(30); break; case 1: // 30f カーブ enemy->angle += M_PI_2 / 30; EnemyCountCheck(30); break; case 2: // 30f 逆カーブ enemy->angle -= M_PI_2 / 30; EnemyCountCheck(30); break; case 3: // 以降加速しながら直線移動 enemy->speed += 0.01f; } // 移動計算 EnemyUpdatePos(enemy); }そして、以下のような関数で処理を行います。
// 敵移動更新 void EnemyUpdate( SEnemy *enemy, int count ){ int i; for(i = 0;i < count; ++i){ if(enemy.move) enemymove(&enemy); } }
EnemyUpdate 関数を毎フレーム実行すれば、登録された敵を移動させることが出来ます。
これなら難しい軌道計算を行なわなくても、複雑な軌道をとることが出来ます。
また、相対位置からの移動を行なうので、登録時のパラメータを変えれば軌道を調整できます。
(これは応用すれば弾のパターンにも使えます)
長くなりましたが、こんな方法はどうでしょうか?
# やっつけで書いたのでおかしいところがあるかもしれません。
Re:敵のパターンを実装する方法について
私のシューティングは今のところ敵に
x座標、y座標、移動方向(度数)、移動方向の角度変化量(度数)、移動速度、移動速度の変化量、グラフィックサイズ、グラフィック回転度、グラフィック回転度変化量、当たり判定サイズ、撃つ弾の初速、発射規準角度、発射基準角度変化量、
円形射撃か扇射撃かのフラグ、扇射撃時の間隔、way数、カウンター、制御用カウンター、HP、行動タイプ、
発射した弾の行動タイプ、攻撃回数カウンター、攻撃回数最大値、発射間隔、ロックオンフラグetc…
…とまぁものすごい数のステータスを与えることができるようにしてますww
このものすごい数のステータスはコース、難易度別に分けたファイルにswitch-case文とif文で敵機ひとつひとつ書いてますw後で苦労しそうな気もしますが自分はこれで…
どうも説明するのが難しいです^^;
あまり参考にならないですが図のcase8の敵を出現させると右下のようになります
上から出てきてゆっくり停止、一定時間後にレーザー射撃~です
弾を針型にするのは弾の移動を書いたファイルにまとめてますw
x座標、y座標、移動方向(度数)、移動方向の角度変化量(度数)、移動速度、移動速度の変化量、グラフィックサイズ、グラフィック回転度、グラフィック回転度変化量、当たり判定サイズ、撃つ弾の初速、発射規準角度、発射基準角度変化量、
円形射撃か扇射撃かのフラグ、扇射撃時の間隔、way数、カウンター、制御用カウンター、HP、行動タイプ、
発射した弾の行動タイプ、攻撃回数カウンター、攻撃回数最大値、発射間隔、ロックオンフラグetc…
…とまぁものすごい数のステータスを与えることができるようにしてますww
このものすごい数のステータスはコース、難易度別に分けたファイルにswitch-case文とif文で敵機ひとつひとつ書いてますw後で苦労しそうな気もしますが自分はこれで…
どうも説明するのが難しいです^^;
あまり参考にならないですが図のcase8の敵を出現させると右下のようになります
上から出てきてゆっくり停止、一定時間後にレーザー射撃~です
弾を針型にするのは弾の移動を書いたファイルにまとめてますw
Re:敵のパターンを実装する方法について
龍神録の館の12章のように私は敵の移動制御をしています。
switch文よりも、ファイルからとってきた敵の移動制御番号を関数ポインタの配列番号に入れてやると1行で済みますよ。
あと、複雑な軌跡の描き方ですが、角度の変更と、スピードの変更だけでかなり色んな動きが出来ますよ。
龍神録の弾幕の制御はほとんど角度の変更とスピードの変更だけで実装しています。
例えばどんな軌跡が描きたいのかを教えてもらえたら何かアドバイス出来るかもしれません。
switch文よりも、ファイルからとってきた敵の移動制御番号を関数ポインタの配列番号に入れてやると1行で済みますよ。
あと、複雑な軌跡の描き方ですが、角度の変更と、スピードの変更だけでかなり色んな動きが出来ますよ。
龍神録の弾幕の制御はほとんど角度の変更とスピードの変更だけで実装しています。
例えばどんな軌跡が描きたいのかを教えてもらえたら何かアドバイス出来るかもしれません。
Re:敵のパターンを実装する方法について
>御津凪さん回答ありがとうございます
時間軸を追加してさらに枝分かれさせるというとこでしょうか?
時間も敵の情報を保持する構造体に入れているのですがそういった使い方は思いつきませんでした
ありがとうございます
>めいらるさん回答ありがとうございます
そうですね
私も構造体にほとんどの情報を入れれるようにしているのですが
やはりswitch-case文が多くなりますね
>管理人さん
回答ありがとうございます
switch-case文を使わないでファイルからということでスクリプト形式にするのでしょうか?
前にやったことがあるのですが、スクリプトにするのに必要となる情報の洗い出しに苦心し、
また、スクリプト解析などの作成にも苦労したわりにはあまり多くの動きを実現できませんでしたので
switch-case文でソースコード上で管理するようになったのです
どんな動きというと抽象的になってしまうのですが
敵に情報を与えたら自動で画面内を動くようにしたいのです
どんな動きでも実現できたらいいと思っているのですが、多分無理だと考えているので
制約をともなう動き、x座標、y座標、進む角度、速度、加速度、時間で実現できる動きになると
考えています
それを踏まえた上での動きとなってくるので(私の勉強不足ですが)
単調になってしまうのですし、複雑な動きをしようと思ったらソースの量が一気に増え
また管理も大変になるのでバランスが難しいといったところです
例を言うとゲーム「どどんぱち」にでてくるようなプレイヤーの前までにやってきて
弾をばらまき、画面外に逃げるというような動きを実現したいです
時間軸を追加してさらに枝分かれさせるというとこでしょうか?
時間も敵の情報を保持する構造体に入れているのですがそういった使い方は思いつきませんでした
ありがとうございます
>めいらるさん回答ありがとうございます
そうですね
私も構造体にほとんどの情報を入れれるようにしているのですが
やはりswitch-case文が多くなりますね
>管理人さん
回答ありがとうございます
switch-case文を使わないでファイルからということでスクリプト形式にするのでしょうか?
前にやったことがあるのですが、スクリプトにするのに必要となる情報の洗い出しに苦心し、
また、スクリプト解析などの作成にも苦労したわりにはあまり多くの動きを実現できませんでしたので
switch-case文でソースコード上で管理するようになったのです
どんな動きというと抽象的になってしまうのですが
敵に情報を与えたら自動で画面内を動くようにしたいのです
どんな動きでも実現できたらいいと思っているのですが、多分無理だと考えているので
制約をともなう動き、x座標、y座標、進む角度、速度、加速度、時間で実現できる動きになると
考えています
それを踏まえた上での動きとなってくるので(私の勉強不足ですが)
単調になってしまうのですし、複雑な動きをしようと思ったらソースの量が一気に増え
また管理も大変になるのでバランスが難しいといったところです
例を言うとゲーム「どどんぱち」にでてくるようなプレイヤーの前までにやってきて
弾をばらまき、画面外に逃げるというような動きを実現したいです
Re:敵のパターンを実装する方法について
具体的に図に示すような軌道をえがく運動(移動)をさせたいのです
私の作成した基本物理エンジンでは細かく設定する必要があるので
幾何学的なアプローチを用いて解決できないかと考えています
なにはともあれ switch-case文が多くなっても大丈夫なように
大きめのモニターを購入予定です
私の作成した基本物理エンジンでは細かく設定する必要があるので
幾何学的なアプローチを用いて解決できないかと考えています
なにはともあれ switch-case文が多くなっても大丈夫なように
大きめのモニターを購入予定です
Re:敵のパターンを実装する方法について
> dicさん
1と2の軌道、それぞれの関数は決まっていますか?
カウント(時間)を媒介変数とし、定義域を分けて関数形を決めるとよいでしょう。
また、物理エンジンという特性を生かして、物理現象に置き換えてみてもいいですね。
速度比例抵抗とかクーロン力を設定できるなら、運動方程式から関数が求まります。
幾何学的な方程式が求まればいいのでしょうか?
1と2の軌道、それぞれの関数は決まっていますか?
カウント(時間)を媒介変数とし、定義域を分けて関数形を決めるとよいでしょう。
また、物理エンジンという特性を生かして、物理現象に置き換えてみてもいいですね。
速度比例抵抗とかクーロン力を設定できるなら、運動方程式から関数が求まります。
幾何学的な方程式が求まればいいのでしょうか?
Re:敵のパターンを実装する方法について
>SCIさん回答ありがとうございます
幾何学的な方程式は分かっても私の勉強不足により、ソースコードで表現することができないので
これは、今の現状では switch-case文で作ることにしようかと考えています
幾何学的な方程式は分かっても私の勉強不足により、ソースコードで表現することができないので
これは、今の現状では switch-case文で作ることにしようかと考えています
Re:敵のパターンを実装する方法について
物理エンジンってなんですか?
後、この軌跡は固定ですか?
私もSCIさんの意見に賛成で、カウントを媒介変数として定義域にわけ、関数で表現すればいいと思います。
・○~○の時は速さアップ
・○~○の時は速さ角度1°ずつ加算
・○~○の時は減速しながら角度減算
みたいな感じです。
後、この軌跡は固定ですか?
私もSCIさんの意見に賛成で、カウントを媒介変数として定義域にわけ、関数で表現すればいいと思います。
・○~○の時は速さアップ
・○~○の時は速さ角度1°ずつ加算
・○~○の時は減速しながら角度減算
みたいな感じです。
Re:敵のパターンを実装する方法について
>管理人さん 回答ありがとうございます
物理エンジンについてですが私が勝手に作った言葉ですね
具体的に言うと 速度、角度、加速度、を設定し1フレームごとに物理エンジン(関数)を呼び出すと
物理エンジン(関数内で定義された規則どおりに)したがって計算されるというものです
私の説明不足でした すいません
軌跡についてですが固定の場合もあれば変動の場合もあります
固定の敵と、変動の敵、それぞれを組み合わせて敵のそれぞれ動かしたいと考えています
なるほど
固定の場合については 時間軸で制御を行うということですね
しかし、変動的つまり 非固定的なプレイヤーの位置の前までやってきて
弾をばらまいて そのまま画面の外にでるという動き
変動的に変わるプレイヤーの位置の前にくるという処理方法がしっくりきません
プレイヤーの座標をグローバル変数で宣言し、どこからでも参照できるようにしているのですが
これを汎用的に、
敵の動き1を実現する関数 move1
敵の動き2を実現する関数 move2
...
敵の動きXを実現する関数 moveX
とどの関数からも処理できる形にする方法が分からないのです
期待する動きとしては 指定したフレーム数でプレイヤーの前の位置 (x,y) に移動するように
したいのですが、逆算ができない状況です
物理エンジンについてですが私が勝手に作った言葉ですね
具体的に言うと 速度、角度、加速度、を設定し1フレームごとに物理エンジン(関数)を呼び出すと
物理エンジン(関数内で定義された規則どおりに)したがって計算されるというものです
私の説明不足でした すいません
軌跡についてですが固定の場合もあれば変動の場合もあります
固定の敵と、変動の敵、それぞれを組み合わせて敵のそれぞれ動かしたいと考えています
なるほど
固定の場合については 時間軸で制御を行うということですね
しかし、変動的つまり 非固定的なプレイヤーの位置の前までやってきて
弾をばらまいて そのまま画面の外にでるという動き
変動的に変わるプレイヤーの位置の前にくるという処理方法がしっくりきません
プレイヤーの座標をグローバル変数で宣言し、どこからでも参照できるようにしているのですが
これを汎用的に、
敵の動き1を実現する関数 move1
敵の動き2を実現する関数 move2
...
敵の動きXを実現する関数 moveX
とどの関数からも処理できる形にする方法が分からないのです
期待する動きとしては 指定したフレーム数でプレイヤーの前の位置 (x,y) に移動するように
したいのですが、逆算ができない状況です
Re:敵のパターンを実装する方法について
> 期待する動きとしては 指定したフレーム数でプレイヤーの前の位置 (x,y) に移動する
であれば、
自分(敵)と計算された座標との間を、指定したフレーム数で
線形補間なり、 sin, cos 関数で移動位置を計算するといいと思います。
この場合、計算時のプレイヤーの位置を計算するので、常にプレイヤーの前に来るわけではないです。
毎フレーム位置を計算しておけば、常にプレイヤーの前に張り付きに来ます。
その場合、プレイヤーが瞬間移動・高速移動をすると違和感はでてしまいますが。
(ちなみに距離を0にして、弾の軌道に毎フレーム計算するように使うと、回避不可のホーミング弾になります^^)
であれば、
// 敵と自機との直線上で、自機の前の座標を計算する float CalcPlayerBeforePos( float enemyX, // 敵のX位置 float enemyY, // 敵のX位置 float playerX, // 自機のX位置 float playerY, // 自機のX位置 float length, // 自機と計算位置との距離 float* posX, // X位置を取得するポインタ float* posY) // Y位置を取得するポインタ { float dir = atan2f(enemyY - playerY, enemyX - playerX); // 自機から敵への方向角度を得る *posX = playerX + sinf(dir) * length; // 自機の前の座標を計算 *posY = playerY + cosf(dir) * length; // 自機の前の座標を計算 return dir; // 角度(自機から敵への方向角度)を返す }で、プレイヤーの前の位置を計算し、
自分(敵)と計算された座標との間を、指定したフレーム数で
線形補間なり、 sin, cos 関数で移動位置を計算するといいと思います。
この場合、計算時のプレイヤーの位置を計算するので、常にプレイヤーの前に来るわけではないです。
毎フレーム位置を計算しておけば、常にプレイヤーの前に張り付きに来ます。
その場合、プレイヤーが瞬間移動・高速移動をすると違和感はでてしまいますが。
(ちなみに距離を0にして、弾の軌道に毎フレーム計算するように使うと、回避不可のホーミング弾になります^^)
Re:敵のパターンを実装する方法について
どういう処理も美しい軌跡を描こうとすると難しくなったりします。
さしあたり4種類考えられると思います。
・敵が現れる瞬間、目標地点を設定し、等速直線でその地点へ移動する
・敵が現れる瞬間、目標地点を設定し、加速、減速してその地点へ移動する
・プレイヤーが動き回るごとに常に軌道修正し、等速で常にホーミングする
・プレイヤーが動き回るごとに常に軌道修正し、美しくホーミングする
上が簡単で、下に行くほど難しいと思います。
一番上は作れますか?
敵の座標と自機の座標から角度を出すにはatan2を使うと簡単です。
2番目は入門レベルの物理の知識があれば出来ると思います。
移動すべき合計距離は
v0を初速度、aを加速度、t1を加速する時間、t2を等速移動する時間とすると

※14:20訂正
このグラフのt2はt1+t2のこと、t3はt1+t2+t1のことです。訂正しますm(_ _)m
v1 = v0+a*t1
v2 = v1
v3 = v2+(-a)*t1
これらのtの積分の和になります。積分すると距離になるので
x1 = v0*t1 + 0.5*a*t1*t1(ピンクの部分)
x2 = v1*t2(青の部分)
x3 = v2*t1 - 0.5*a*t1*t1(緑の部分)
これをv0=0としわかりやすくすると
x = a*t1*t1 + v1*t2
となります。
こうしてaを導き出し、それにあった距離を計算してそちらに飛ばしてやったらよいでしょう。
さしあたり4種類考えられると思います。
・敵が現れる瞬間、目標地点を設定し、等速直線でその地点へ移動する
・敵が現れる瞬間、目標地点を設定し、加速、減速してその地点へ移動する
・プレイヤーが動き回るごとに常に軌道修正し、等速で常にホーミングする
・プレイヤーが動き回るごとに常に軌道修正し、美しくホーミングする
上が簡単で、下に行くほど難しいと思います。
一番上は作れますか?
敵の座標と自機の座標から角度を出すにはatan2を使うと簡単です。
2番目は入門レベルの物理の知識があれば出来ると思います。
移動すべき合計距離は
v0を初速度、aを加速度、t1を加速する時間、t2を等速移動する時間とすると

※14:20訂正
このグラフのt2はt1+t2のこと、t3はt1+t2+t1のことです。訂正しますm(_ _)m
v1 = v0+a*t1
v2 = v1
v3 = v2+(-a)*t1
これらのtの積分の和になります。積分すると距離になるので
x1 = v0*t1 + 0.5*a*t1*t1(ピンクの部分)
x2 = v1*t2(青の部分)
x3 = v2*t1 - 0.5*a*t1*t1(緑の部分)
これをv0=0としわかりやすくすると
x = a*t1*t1 + v1*t2
となります。
こうしてaを導き出し、それにあった距離を計算してそちらに飛ばしてやったらよいでしょう。
Re:敵のパターンを実装する方法について
>御津凪さん回答ありがとうございます
なるほど
プレイヤーの前の位置の座標を計算し、そこに進むようにすればいいのですね
細かい計算までありがとうございます
>管理人さん回答ありがとうございます
1敵が現れる瞬間、目標地点を設定し、等速直線でその地点へ移動する
2敵が現れる瞬間、目標地点を設定し、加速、減速してその地点へ移動する
3プレイヤーが動き回るごとに常に軌道修正し、等速で常にホーミングする
4プレイヤーが動き回るごとに常に軌道修正し、美しくホーミングする
4パターンあるんですね
難易度のもっとも高い4番を目指してました
1,2はなんとかできそうです
3,4はちょっと時間がかかりそうです
もう一度チャレンジしてみようかと思います
回答ありがとうございました
なるほど
プレイヤーの前の位置の座標を計算し、そこに進むようにすればいいのですね
細かい計算までありがとうございます
>管理人さん回答ありがとうございます
1敵が現れる瞬間、目標地点を設定し、等速直線でその地点へ移動する
2敵が現れる瞬間、目標地点を設定し、加速、減速してその地点へ移動する
3プレイヤーが動き回るごとに常に軌道修正し、等速で常にホーミングする
4プレイヤーが動き回るごとに常に軌道修正し、美しくホーミングする
4パターンあるんですね
難易度のもっとも高い4番を目指してました
1,2はなんとかできそうです
3,4はちょっと時間がかかりそうです
もう一度チャレンジしてみようかと思います
回答ありがとうございました