はじめに
この記事は、私の投稿している「C言語の復習がてら、ゲームに応用してみる」のPart3である。最初から見たいという方はこちらから
ゲーム制作の基礎知識について
今まではコードの話だったが、それ以前にゲームを製作するにはゲームがどのようなつくりになっているのか。その仕組みをざっくり知っておく必要がある。
座標の概念
ゲームは画面の座標で様々なオブジェクトの動きを操作する。座標がわからないという人は一度座標について勉強してみることをお勧めする。さわりだけなので難しくはないと思う。
それでは、以下で図解しよう。
今回使用するDxLibでは座標は左上を(0,0)として横軸をx、縦軸をyとして画面を構成している。
プレイヤーも敵も座標によって位置を決定されているのだ。
では、自分の操作するプレイヤーを右に移動させたいときはどうすればいいだろうか。勘のいいひとはもうお気づきかもしれないが、→のキーが押された際にプレイヤーのx座標に数値をプラスしてあげればいいのだ!
ではコードで見てみよう。
int player_x, player_y;//プレイヤーの座標
int movex;//プレイヤーの移動量
/*~~~省略~~~*/
//switch文を利用して、キーごとの処理を変える
//CheckHitKeyAll()はすべてのキーの状態を調べるDxlibの固有関数
switch (CheckHitKeyAll()) {
case KEY_INPUT_RIGHT://右キーだった場合
movex = 3;
break;
//この後にもキー操作別の処理を書く
}
player_x += movex;//座標に加算。
switch文を利用し、座標をキー操作によって変更することができる。if文でもいいが、今回のように並列したことを何個も書く際にはswitch文が適しているし、見やすいだろう。
座標におけるプレイヤーの移動は様々なゲームにおいて使われていると言える。実際、私がこの考え方を知れた時、かなり感動したことを覚えている。なんといってもゲームの仕組みに初めて触れた気がしたからだ。
いや、座標移動させただけじゃダメやねん
座標を移動させたからこれで、プレイヤーは動くと思うかもしれないが、これだけでは実は動かない。
「どういうこと...」と思うかもしれない。
座標を変えたり、何かしら画面上で変化を加えたい際には一度現在の画面上をクリアしてから描画しなければならない。DxLibの場合、描画を裏画面(自由に描ける場所に)描画してから、それを表画面(私たちの見ている画面)に描画する。実際にコードを見ていこう。
//描画先を裏画面に設定
SetDrawScreen(DX_SCREEN_BACK);
/*~~~~プレイヤー移動処理~~~*/
//プレイヤーを描画する
DrawGraph(player_x, player_y, player_img, true);
//裏画面から表画面に反転
ScreenFlip();
こうすることでうまく移動させることができる。
ちなみに"SetDrawScreen(DX_SCREEN_BACK);"を忘れると画面をクリアしなかった際の世界がのぞける。興味のある人は一度やってみてほしい。
同じような敵を描画したいけど...めんどくさくない?
続いて、プレイヤーの描画と座標移動を駆使して、敵の描画と座標移動を考えてみたい。
しかし、ここで問題なのは敵の数についてである。こんな数の敵、数値の設定だけで腱鞘炎待ったなしだ。
ちょっとやってみよう。
int enemy1_x = 0,enemy1_y = 0;
int enemy2_x = 10,enemy2_y = 0;
int enemy3_x = 20,enemy3_y = 0;
int enemy4_x = 30;//まだまだつづく...
//もうむりぃ....
//誰だよ!こんなことやってみようとか言ったやつは!
くそコードである。
すでに見てわかる。くそコードだ。
コードを打っている最中の私は完全に精神がやられている。当たり前だ。
そもそも、こんなコードでゲームを動かしても何もうれしくないし、ましてや座標を変えなきゃいけないとなったときに全ての敵の座標を変えるのはえげつない。
ではどうするか。ここで使えるのはfor文である。
似ているようなものを大量に生産したいならばfor文がとても便利だ。
//敵の数(行,列)
#define ENEMY_NUM_I 3
#define ENEMY_NUM_J 20
//敵の大きさ
#defien OBJECT_SIZE 32
//敵の座標
int enemy_x[ENEMY_NUM_I][ENEMY_NUM_J],enemy_y[ENEMY_NUM_J]
//for文で敵の座標設定
//enemy_i=0から始まり,enemy_iが<ENEMY_NUM_Iの間はループする。ループの終わりにenemy_iに+1する。*1
for (int enemy_i = 0; enemy_i < ENEMY_NUM_I; enemy_i++) {
for (int enemy_j = 0; enemy_j < ENEMY_NUM_J; enemy_j++) {
//エネミーのx座標 = エネミーの実座標(+α) + 微調整用
enemy_x[enemy_i][enemy_j] = enemy_j * (OBJECT_SIZE + 20) + 80 ;
//エネミーのy座標 = ウィンドウサイズ - 敵全体の縦幅(+α) - 微調整用 + 実際の座標値(+α)
enemy_y[enemy_i][enemy_j] = WINDOW_HEIGHT - ENEMY_NUM_J * (OBJECT_SIZE - 3) - 20 + (OBJECT_SIZE + 3) * enemy_i;
enemy_flag[enemy_i][enemy_j] = 1;
}
}
こんな感じですべての敵の分を座標設定できる。
詳しく解説していこう。
- #define は定数(変化しない数)を決めることができる。今回の敵の行列や敵一体の大きさにはあとから変更を加える必要がないため定義をこうしている
- そして、for文がどのような動きをするかは*1に書いたとおりである。
- 今回はfor文の中にfor文を書くという入れ子の構造になっている。どういう動きかプログラムを追いかながら考えてみると、最初は[0][0]の敵の座標、次は[0][1],[0][2],[0][3]...となり、やがて1行目の設定が終わると、2行目の設定に入り、[1][0],[1][1],[1][2]...といった具合だ。
こうすることですべての敵の設定を簡単にできることがわかるだろうか。
くそコードを書いたせいで手が火を噴いている。
疲れた。
今回のように数で攻めるということは、初心者としては最も素早くコードを完成させるという点では最も有効打かもしれない。だが、直す際の手間がものすごいことはもうお分かりだろう。またforやwhile文を使って書けるようにしておいたほうが簡単に、素早くできるようになっていく。ぜひコードを簡単に、素早く書けるように工夫をしてみてほしい。
ということでまた次回会いましょう。