Appearance
第11回 C++実践 独自ゲームの制作 2 / 基本動作の実装
前回: 第10回 C++実践 独自ゲームの制作 1 / 企画とクラス設計 / 次回: 第12回 C++実践 独自ゲームの制作 3 / 拡張とデバッグ
前回の振り返り
企画と実装順を決めました
- 最小完成形を決めました
- 操作、目的、終了条件を書き出しました
- 登場するオブジェクトと持つ情報を洗い出しました
- 今回は、企画を動くコードへ変えていきます
今回の目的
遊べる土台を作る
- プレイヤーを表示して操作できるようにする
- 相手や障害物を表示して更新する
- 当たり判定またはスコア条件を入れる
Playing、GameOver、GameClearの流れを作る
今回の授業内容
C++実践 独自ゲームの制作 2 / 基本動作の実装
- 第10回で決めた実装順に沿って作る
- 画面に出るものを1つずつ増やす
- 毎フレームの更新と描画を分けて書く
- 判定結果を状態につなげる
- 完成度より、最後まで動く流れを優先する
基本動作の実装
とにかく動く形へ
完成の入口を 作ります
作業の進め方
1つ追加したら動かす
プレイヤーを出したら実行する
入力を入れたら実行する
敵を出したら実行する
判定を入れたら実行する
まとめて書いてから実行すると、どこで壊れたか追いにくくなります
基本のゲームループ
更新してから描画する
cpp
while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
{
// 更新
ClearDrawScreen();
// 描画
ScreenFlip();
}- 更新では、位置や状態を変えます
- 描画では、現在の状態を画面に出します
- 更新と描画を混ぜすぎないようにします
状態を先に用意する
終了条件を入れやすくする
cpp
enum GameState
{
Playing,
GameOver,
GameClear
};
GameState gameState = Playing;- 独自制作でも、第06回と同じ考え方を使えます
Playingの間だけゲームを更新します- 結果が出たら表示を切り替えます
プレイヤーを用意する
操作できるものを最初に作る
cpp
GameObject player = { 320, 400, 32, 32, true };
int playerSpeed = 4;- 最初は画像でなく四角形でも構いません
- 位置、大きさ、速度があれば操作の確認ができます
- 見た目は、動く土台ができてから差し替えられます
プレイヤーを動かす
入力で位置を変える
cpp
if (gameState == Playing)
{
if (CheckHitKey(KEY_INPUT_LEFT)) player.x -= playerSpeed;
if (CheckHitKey(KEY_INPUT_RIGHT)) player.x += playerSpeed;
if (CheckHitKey(KEY_INPUT_UP)) player.y -= playerSpeed;
if (CheckHitKey(KEY_INPUT_DOWN)) player.y += playerSpeed;
}- 操作できることを優先します
- 画面外へ出ない制限は、この後で追加しても構いません
相手や障害物を用意する
ゲームの目的に関係するものを出す
cpp
const int obstacleMax = 5;
GameObject obstacles[obstacleMax] = {};
int obstacleSpeed = 3;- 複数出すものは配列で扱えます
isActiveを使うと、出ているものと空いている場所を分けられます- 弾やエフェクトと同じ考え方です
障害物を更新する
使用中のものだけ動かす
cpp
for (int i = 0; i < obstacleMax; i++)
{
if (obstacles[i].isActive)
{
obstacles[i].y += obstacleSpeed;
if (obstacles[i].y > 480)
obstacles[i].isActive = false;
}
}- 画面外へ出たら再利用できる状態に戻します
- 落下、横移動、追尾など、動きの種類は企画に合わせます
当たり判定を入れる
判定結果を状態につなげる
cpp
for (int i = 0; i < obstacleMax; i++)
{
if (IsHit(player, obstacles[i]))
{
gameState = GameOver;
}
}- 当たったかどうかだけで終わらせず、結果へつなげます
- クリア条件がある場合は、同じように
GameClearへ切り替えます
スコアやタイマーを入れる
数値で達成条件を作る
cpp
int score = 0;
if (gameState == Playing)
{
score++;
if (score >= 1800)
gameState = GameClear;
}- 60FPSなら、
1800フレームはおよそ30秒です - 正確な時間処理は後で調整できます
- 最初は条件が分かりやすいことを優先します
結果を表示する
状態に応じて文字を出す
cpp
if (gameState == GameOver)
{
DrawString(260, 220, L"GAME OVER", GetColor(255, 255, 255));
}
else if (gameState == GameClear)
{
DrawString(260, 220, L"GAME CLEAR", GetColor(255, 255, 255));
}- 結果が表示されると、ゲームの区切りが分かりやすくなります
- リトライ処理は、基本形ができた後で追加します
実習1: プレイヤーを操作する
画面に出して動かす
- プレイヤー用のデータを用意する
- 矢印キーで位置を変える
- プレイヤーを描画する
- 画面外へ出ないように調整する
実習2: 相手を追加する
敵や障害物を出す
- 敵や障害物のデータを用意する
- 画面に表示する
- 毎フレーム動かす
- 画面外へ出たら再利用できる状態にする
実習3: 結果までつなげる
ゲームとして終わる状態を作る
- 当たり判定またはスコア条件を入れる
GameOverまたはGameClearに切り替える- 結果の文字を表示する
- 途中まででも、遊べる流れを残す
よくあるつまずき
基本動作が安定しないときの確認ポイント
- 1つ追加するごとに実行しているか
- 更新処理が
Playingの中にあるか - 描画処理が毎フレーム呼ばれているか
- 配列の範囲外にアクセスしていないか
- 結果が出たあとも更新が続いていないか
今回のまとめ
今日のポイント
- 独自制作では、動く土台を先に作ることが重要です
- 更新、判定、描画を分けると、問題を追いやすくなります
GameStateを使うと、ゲームの結果までつなげやすくなります- 次回は拡張とデバッグを行い、遊びやすさを上げます