広告
↓発売日:2018年09月22日↓
新品価格 |
今回は自機バーの表示・移動、ボールとの当たり判定をつけるトコロまでをやっていきたいと思います。
先によく使う定数に名前を付けてしまいます。
#define BALL_SIZE 5 #define BALL_SPEED 5
ボールのサイズとスピードです。
#define BAR_SPEED 5 #define BAR_WIDTH 50 #define BAR_HEIGHT 10
自機バーのスピードと横幅「BAR_WIDTH」と縦幅「BAR_HEIGHT」です。
#define FIELD_WIDTH 300 #define FIELD_HEIGHT 480 #define X_POSI 170
あとはボールが跳ね返るフィールドの横幅「FIELD_WIDTH」と縦幅「FIELD_HEIGHT」になります。
前回のように画面全体ではさすがに大きいので幅を少し狭めております。
「X_POSI」はフィールドの表示位置を調整する為のものです。
最初は画面の左端にフィールドを作り、表示する時だけこの「X_POSI」でずらして画面の中心などに表示するようにします。
フィールドを表示してそのフィールド幅に当たり判定を変更します。
フィールドの表示、そして自機バーの表示などにはシンプルに四角を表示する「DrawBox()」を使います。
DrawBox(左上x座標, 左上y座標, 右下x座標, 右下y座標, 色の指定, 塗りつぶしの指定);
こちらになります。
ではフィールドを表示する「my_draw_field()」になります。
void my_draw_field(){ DrawBox(0,0,FIELD_WIDTH,FIELD_HEIGHT,Color_White,false); }
フィールドの大きさ分四角を表示します。
そしてこれに合わせて当たり判定の範囲も変更します。
void my_collision_detection(){ if(ball_x >= FIELD_WIDTH){ ball_x = FIELD_WIDTH - BALL_SIZE; x_speed *= -1; } if(ball_x <= 0){ ball_x = BALL_SIZE; x_speed *= -1; } if(ball_y <= 0){ ball_y = BALL_SIZE; y_speed *= -1; } if(ball_y >= FIELD_HEIGHT){ ball_y = FIELD_HEIGHT - BALL_SIZE; y_speed *= -1; } }
当たり判定部分をまとめた「my_collision_detection()」になります。
フィールドの幅に合わせて当たり判定を変更しました。
ではあらためて自機バーを表示してみたいと思います。
x座標とy座標の変数「bar_x」と「bar_y」をそれぞれ用意して自機バーを表示してみます。
int Color_Red = GetColor(255, 0, 0); int bar_x = 0; int bar_y = FIELD_HEIGHT - BAR_HEIGHT; DrawBox(bar_x, bar_y, bar_x + BAR_WIDTH, bar_y + BAR_HEIGHT, Color_Red, true);
色の指定には赤色の「Color_Red」を用意しました。
初期位置は先ほど用意したフィールドの左端にしてみました。
そしてその左上の座標から常に自機バーの大きさ分ずれた座標を右下の座標にすればいいので
bar_x + BAR_WIDTH bar_y + BAR_HEIGHT
と縦幅と横幅を足した座標を指定してあげれば自機バーの表示は完了です!
では続きまして移動です。
まずはキー入力から説明します。
横に動かすためのキー入力の簡単なプログラムを作ってみます。
実行すると入力待ちの無限ループになりますので矢印キーの左か右を押してください!
#include "DxLib.h" void my_get_key(void); int key[256]; int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){ ChangeWindowMode(TRUE); DxLib_Init(); SetDrawScreen(DX_SCREEN_BACK); int Color_White = GetColor(255,255,255); while(ProcessMessage() == 0){ ClearDrawScreen(); my_get_key(); if(key[KEY_INPUT_LEFT] > 0){ DrawFormatString(0,0,Color_White, "左=%d",key[KEY_INPUT_LEFT]); } if(key[KEY_INPUT_RIGHT] > 0){ DrawFormatString(0,20,Color_White, "右=%d",key[KEY_INPUT_RIGHT]); } ScreenFlip(); } DxLib_End(); return 0; } void my_get_key(){ int i; char keys[256]; GetHitKeyStateAll(keys); for(i=0;i<256;i++){ if(keys[i] != 0){ key[i]++; } else{ key[i] = 0; } } }
キー入力を確認するためのプログラムです。
矢印キーの「左←」か「右→」を押すと反応するようになってます。
まず256種類全てのキーの入力状態を取得する関数が
char keys[256]; GetHitKeyStateAll(keys);
この「GetHitKeyStateAll()」という関数になります。
実行すると変数「keys」のキー入力があった場所に「0」以外が返されます。
そして256種類のキーは定数化されているのでこの「keys」を調べて
if(keys[KEY_INPUT_LEFT] != 0){ /*左が押された時の処理*/ }
のようにすればキーが押された時の処理ができます。
「KEY_INPUT_LEFT」・・・左
「KEY_INPUT_UP」・・・上
「KEY_INPUT_RIGHT」・・・右
「KEY_INPUT_DOWN」・・・下
「KEY_INPUT_Z」・・・Z
「KEY_INPUT_H」・・・H
「KEY_INPUT_RETURN」・・・エンター
とりあえず今回使用するキーの定数になります。
他のキーに関しては「dxlib キー リファレンス」などで検索してみてください。
広告
上記の方法でもキー入力は取得できますが、押しっぱなした時や少しだけ押された時などの微妙なニュアンスのキー入力処理ができません。
そこで少し工夫したものが最低限のプログラムにもある次の関数になります。
void my_get_key(){ int i; char keys[256]; GetHitKeyStateAll(keys); for(i=0;i<256;i++){ if(keys[i] != 0){ key[i]++; } else{ key[i] = 0; } } }
キーの入力状態をグローバルに用意した変数「int key[256]」にインクリメント、値の増加というカタチによって保存しなおします。
こうする事によってさきほどのプログラムの実行結果のようにキーを少し押せば値が少しだけ上昇し、長押せばその押した分だけ値が上昇するので微妙なキー入力のニュアンスも処理できるようになります!
実際に使う時は
if(key[KEY_INPUT_LEFT] > 0){ /*左が押された時の処理*/ }
のようにすればキーが押されれば即時に反応しますし、
if(key[KEY_INPUT_LEFT] > 100){ /*左が押された時の処理*/ }
長押しした時
if(key[KEY_INPUT_LEFT] > 10 && key[KEY_INPUT_LEFT] < 30){ /*左が押された時の処理*/ }
ちょい押しした時
のように細かく入力を処理できます。
ちなみに、この関数は「ゲームプログラミングの館」さんというサイトからほぼそのまま流用させて頂いております。
他にも本格的なゲームの作り方などがやさしく解説されておりますので興味ある方はぜひ一度覗いてみてください。
ではあらためて自機バーの移動部分を見てみましょう!
void my_move_bar(){ if (key[KEY_INPUT_RIGHT] > 0 && (bar_x + BAR_WIDTH) < FIELD_WIDTH){ bar_x += BAR_SPEED; } if (key[KEY_INPUT_LEFT] > 0 && bar_x > 0){ bar_x -= BAR_SPEED; } }
入力があった時に自機バーを移動する「my_move_bar()」になります。
入力があった時は変数「bar_x」に移動分の「BAR_SPEED」を加えて左右に移動させております。
そしてそのままではどこまでも移動してしまいますので条件を重ねる「&&」を使ってフィールドの端以内という条件を重ねます。
(bar_x + BAR_WIDTH) < FIELD_WIDTH
これでフィールドをはみださずに移動ができました!
続きましては自機バーの当たり判定です。
当たり判定用関数「my_collision_detection()」に追加するカタチになります。
最初に少し説明しましたがブロックの判定含めあくまでも最低限の当たり判定になりますのでご注意ください。
if (ball_x > bar_x && ball_x < (bar_x + BAR_WIDTH) && ball_y >= bar_y){ ball_y = bar_y - BALL_SIZE; y_speed *= -1; }
「ボールのx座標が自機バーx座標の内側にあり、ボールのy座標が自機バーy座標を超えたら」
という感じの簡単な当たり判定になります。
y座標の判定を「bar_y以上全て」と幅広くしてありますので自機バーに関しては細かい事を抜きにすればけっこう反応してくれます。
これで自機バーの表示、移動、当たり判定ができました!
それでは最後ついでにゲームオーバーまで作ってしまいます。
まずこのブロック崩しに限らずオープニング、エンディング、イベントなどなどいわゆるゲームの中の場面を分ける時は「switch」文を使います。
while (ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && my_get_key()==0){ switch(game_state){ case 0: game_state = 5; break; case 5: my_move_bar(); my_move_ball(); my_collision_detection(); my_draw_ball(); my_draw_bar(); my_draw_field(); break; case 10: my_draw_bar(); my_draw_field(); my_gameover(); break; default: break; } }
こんな感じで場面を表す変数を適当に用意して(今回の場合は「game_state」という変数になります)それによってゲームループ内を分けてしまいます。
こうすれば「case」ごとにまったく違った処理をする事できるので、他にも初期化処理、画像、音楽データなどのローディング処理などさまざまな場面分けができるというワケです。
それではゲームオーバー処理を追加します。
まずこちらを見てわかる通りメインのゲーム処理は「case 5」にまとめてあります。
ボールの移動なども簡単に関数に分けてありますのでそのへんは中間ソースを確認してみてください。
「case 0」があけてあるのはオープニングや次ステージ用初期化処理などを加えたくなった時の為にあけております。
そしてゲームオーバーはといいますと「case 10」の場所にひっそりと設置しております。
void my_gameover(){ DrawFormatString(280,240,Color_White,"GAME OVER"); }
画面中央あたりに「GAME OVER」と表示するだけのごく簡単なものになります。
それでは「case 10」に飛ばす処理を追加します。
「my_collision_detection()」内のフィールド底辺の当たり判定を変更します。
if(ball_y >= FIELD_HEIGHT){ game_state = 10; }
フィールド底辺を超えたらゲームオーバーが設置してある「case 10」へと変更しました。
ゲームオーバー!!!
ここまでの中間ソースになります。
中間ソースは変数をグローバル変数にして今回説明した以外にもそれぞれを関数にまとめております。
そして「my_draw_ball()」、「my_draw_bar()」、「my_draw_field()」でボール、自機バー、壁などを表示する時それぞれのx座標にさきほどの「X_POSI」を加える事によって画面の中心にくるように調整しております。
それでは次回はブロックを表示したいと思います。
広告
↓発売日: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日↓
新品価格 |