広告
↓発売日:2018年09月22日↓
新品価格 |
今回は敵を表示させてみます。
敵の構造体から見てみましょう。
#define DISP_ENEMY_MAX 5 struct ENEMY{ int serial_num; int appear_point; double first_x, first_y; double x, y; double draw_x, draw_y; double m_x,m_y; double a_x,a_y; double f_x,f_y; int init_flag; int move_flag; double angle; int move_type; int shot_type; int pattern; double var[5]; int gamecount_point; int power; double range; int item_flag; }; struct ENEMY enemy[DISP_ENEMY_MAX];
プレイヤーの時と少し違い敵はプログラムによって動かすのでその為の情報が少し増えます。
わかりやすいトコロから見てみましょう。
double first_x, first_y; double x, y; double draw_x, draw_y;
「first_x,first_y」は敵の出現する座標の初期位置になります。
あとはプレイヤーの時と同じく計算座標「x,y」と表示する時の座標「draw_x,draw_y」になります。
int serial_num;
いちよう識別番号的なものになります。
ここでつけた番号が敵表示の時にテキスト表示されます。
int appear_point;
敵が出現するポイントになります。
「gamecount」がこのポイントに到達すると敵が現れます。
int init_flag; int move_flag; int move_type; int gamecount_point;
順番が前後して申し訳ないですが、プレイヤーショットの時と同じく「init_flag」が初期化用のフラグ、「move_flag」が「敵の入れ物」が使用中かどうかのフラグ、「move_type」が敵の動き方、「gamecount_point」が基準となるその時のgamecountを記録するものになります。
int shot_type;
こちらはその敵の持つショットの種類、
int power; double range; int item_flag;
続けて敵の体力と当たり判定の範囲とアイテムの有無のフラグ、
double angle; int pattern; double var[5]; double m_x,m_y; double a_x,a_y; double f_x,f_y;
残りのものは実際動かす時に必要な変数になります。
ざっと説明しましたが、これがいわゆる「敵の入れ物」になります。
この構造体の数が「同時に画面に表示できる敵の最大数」になります。
最初はとりあえず
#define DISP_ENEMY_MAX 5 struct ENEMY enemy[DISP_ENEMY_MAX];
5体分だけ用意しました。
初期化します。
void my_init_enemy(){ for (int i = 0; i < DISP_ENEMY_MAX; i++){ enemy[i].serial_num = 0; enemy[i].appear_point = 0; enemy[i].first_x = 0; enemy[i].first_y = 0; enemy[i].x = 0; enemy[i].y = 0; enemy[i].draw_x = 0; enemy[i].draw_y = 0; enemy[i].m_x = 0; enemy[i].m_y = 0; enemy[i].a_x = 0; enemy[i].a_y = 0; enemy[i].f_x = 0; enemy[i].f_y = 0; enemy[i].init_flag = 0; enemy[i].move_flag = 0; enemy[i].angle = 0; enemy[i].move_type = 0; enemy[i].shot_type = 0; enemy[i].pattern = 0; enemy[i].gamecount_point = 0; enemy[i].power = 0; enemy[i].range = 0; enemy[i].item_flag = 0; for(int j = 0;j < 5;j++){ enemy[i].var[j] = 0; } } }
全て「0」で初期化しているだけです。
当たり判定範囲の「range」もあとで敵のデータから読み込んでいくのでここでは「0」にしております。
敵のデータは別の場所にまとめておいて、ここから先ほどの「敵の入れ物」である構造体にデータを読み込んでいきます。
0,100,-180,260,0,0,1,10,0,
1体分の敵のデータになります。
左から順に「serial_no」「appear_point」「first_x」「first_y」「move_type」「shot_type」「power」「range」「item_flag」となっております。
#define STAGE_ENEMY_MAX 10 #define ENEMY_DATA_MAX 9 int ary_enemy_data[STAGE_ENEMY_MAX * ENEMY_DATA_MAX] = { 0,100,-180,260,0,0,1,10,0, 1,300,-140,260,0,0,1,10,0, 2,500,-100,260,0,0,1,10,0, 3,700,-60,260,0,0,1,10,0, 4,900,-20,260,0,0,1,10,0, 5,1100,20,260,0,0,1,10,0, 6,1300,60,260,0,0,1,10,0, 7,1500,100,260,0,0,1,10,0, 8,1700,140,260,0,0,1,10,0, 9,1900,180,260,0,0,1,10,0 };
とりあえず10体分の敵のデータを用意しました。
1体9個のデータ「#define ENEMY_DATA_MAX 9」が10体分「#define STAGE_ENEMY_MAX 10」という事で計90個のデータになりますね。
この配列を毎回調べて「gamecount」が敵の出現ポイントである「appear_point」と一致した敵がいればその敵のデータを読み込んでいきます。
敵を表示する流れを見てみましょう。
1・敵データから「appear_point」が「gamecount」と一致する敵データを調べる
2・一致した敵データがあれば現在未使用の「敵の入れ物」を調べて使用中に
3・その時の敵の動き方やショットの種類など他の情報を登録
4・その「敵の動き方」に応じて出現・計算・表示
5・終わったら再び「敵の入れ物」を未使用に
最初に敵のデータからデータを読み出すトコロ以外はプレイヤーショットの時とよく似ていますね。
このように最初の手順が少し異なるだけで基本は同じような考え方で他の敵のショット、アイテム、爆発エフェクトなども作っていきます。
では敵データ「ary_enemy_data」を調べて「敵の入れ物」にデータを読み出すまでを見てみたいと思います。
void my_set_enemy(){ for (int i = 0; i < STAGE_ENEMY_MAX; i++){ if (ary_enemy_data[(i * ENEMY_DATA_MAX) + 1] == gamecount && gamecount > 0){ for (int j = 0; j < DISP_ENEMY_MAX; j++){ if (enemy[j].move_flag == 0){ enemy[j].move_flag = 1; enemy[j].serial_num = ary_enemy_data[i * ENEMY_DATA_MAX]; break; } } } } }
「ary_enemy_data」からデータを読み出す「my_set_enemy()」になります。
少し長いので省略しております。
一つ一つ見ていきましょう。
まずはさきほどの敵データを全て調べます。
for (int i = 0; i < STAGE_ENEMY_MAX; i++)
ここで要素数全てを調べるワケではなくあくまでも敵の合計数、この場合10体分だけ調べているトコロに注意です。
そして次の「if」文を少し工夫する事によって敵の出現するポイントである「appear_point」だけを調べていきます。
if (ary_enemy_data[(i * ENEMY_DATA_MAX) + 1] == gamecount)
まずそれぞれの敵データの先頭には「i * ENEMY_DATA_MAX」でアクセスできます。
あとはその次の要素が「appear_point」になるのでそこに「+1」すればたどり着く事ができるというワケです。
その後の「&& gamecount > 0」という部分は「gamecount」が「0」以上の場合はという事ですね。
これは今後ステージを増やした際ステージごとの敵の出現数が異なる時、「appear_point」を「0」に合わせる事によって敵の出現を防止する為の処理です。
ここまでくればあとはプレイヤーショットの時と同じように未使用の「敵の入れ物」を調べて使用中にしてそこにデータを登録するだけです。
for (int j = 0; j < DISP_ENEMY_MAX; j++){ if (enemy[j].move_flag == 0){ enemy[j].move_flag = 1; enemy[j].serial_num = ary_enemy_data[i * ENEMY_DATA_MAX]; enemy[j].appear_point = ary_enemy_data[(i * ENEMY_DATA_MAX) + 1]; enemy[j].first_x = ary_enemy_data[(i * ENEMY_DATA_MAX) + 2]; enemy[j].first_y = ary_enemy_data[(i * ENEMY_DATA_MAX) + 3]; enemy[j].move_type = ary_enemy_data[(i * ENEMY_DATA_MAX) + 4]; /*省略*/ break; } }
それぞれのデータも「i * ENEMY_DATA_MAX」に「+1」、「+2」と足してずらしていけばたどり着く事ができます。
これでデータの読み出し完了です。
気付いた方いるかもしれませんが、ここで「enemy[j].appear_point」に登録しなおした「appear_point」は実はその後は使う事はありません。
なんとなくプログラムの見た目上入れただけです。
敵の動き方を計算する「my_move_enemy()」です。
少し長いので分けて説明します。
まずは全ての敵の入れ物を調べてそれが使用中かどうか調べます。
for (int i = 0; i < DISP_ENEMY_MAX; i++){ if (enemy[i].move_flag == 1){ } }
ここで使用中「enemy[i].move_flag == 1」の状態のものがあった場合、その敵は動いていなきゃいけないので動かします。
「switch」文で動き方を振り分けます。
switch (enemy[i].move_type){ case 0: break; default: break; }
今回は「case 0」上から現れて中間までまっすぐ下に降りてきて再び戻っていくような動きを作っていきます。
初期化部分と移動部分に分かれます。
初期化部分を見てみます。
if (enemy[i].init_flag == 0){ enemy[i].x = enemy[i].first_x; enemy[i].y = 260; enemy[i].gamecount_point = gamecount; enemy[i].init_flag = 1; } else{ /*移動部分*/ }
初期化用のフラグ「enemy[i].init_flag == 0」の時は初期化されていないので初期化をします。
初期x座標は先ほどの「ary_enemy_data」に入れた初期x座標がそのまま入ります。
初期y座標は上から現れるので少し隠れるぐらいの「260」からにします。
あとは基準となる「gamecount」を記録して初期化完了です。
では移動部分を見てみましょう。
先ほどの「else」部分ですね。
else{ if (gamecount < enemy[i].gamecount_point + 500){ if (gamecount < enemy[i].gamecount_point + 220){ enemy[i].y--; } if (gamecount > enemy[i].gamecount_point + 260){ enemy[i].y++; } } else{ enemy[i].move_flag = 0; enemy[i].init_flag = 0; } }
「gamecount_point」を基準に最初は「220」カウント下降して少し経過してから再び上昇しているだけですね。
そして「gamecount_point」から「500」カウント経過した時点で消滅します。
座標を中央に移動します。
for (int i = 0; i < DISP_ENEMY_MAX; i++){ if (enemy[i].move_flag == 1 && enemy[i].init_flag == 1){ enemy[i].draw_x = enemy[i].x + 220; enemy[i].draw_y = ((-1) * enemy[i].y) + 240; } }
「my_to_center()」にこちらを追加して
void my_draw_enemy(){ for (int i = 0; i < DISP_ENEMY_MAX; i++){ if (enemy[i].move_flag == 1 && enemy[i].init_flag == 1){ DrawFormatString(enemy[i].draw_x, enemy[i].draw_y, Color_Red, "e[%d]", enemy[i].serial_num); } } }
表示します!
敵が出現しました!
ここまでの中間ソースになります。
次回は敵のショットを作ります。
広告
↓発売日:2016年02月29日↓
12歳からはじめる ゼロからのC言語 ゲームプログラミング教室 新品価格 |
↓発売日:2018年06月22日↓
新品価格 |
↓発売日:2018年03月09日↓
新品価格 |
↓発売日:2017年06月14日↓
新品価格 |
↓発売日:2018年05月21日↓
新品価格 |
↓発売日:2017年12月07日↓
新品価格 |
↓発売日:2017年02月08日↓
新・明解C言語で学ぶアルゴリズムとデータ構造 (明解シリーズ) 新品価格 |
↓発売日:2017年09月26日↓
新品価格 |