Skip to content

第02回 ゲームループと描画

前回: 第01回 環境確認とプロジェクト構造 / 次回: 第03回 入力と移動

今回の授業内容

  • ゲームループが更新と描画を毎フレーム繰り返す仕組みを理解する
  • 画面の座標系(原点と軸の向き)を確認する
  • プレイヤーの座標をGameクラスに追加する
  • Update()Draw()の役割を分けて実装する
  • 座標や色を変更し、画面への影響を確認する

今回の開始状態

第01回終了時点のプロジェクトから始めます
画面にはDX Library Projectという文字だけが表示されています

今回の終了時点では、画面下部にプレイヤーの仮図形を表示します
まだ入力では動きません

変更するファイル

ファイル変更内容
Game.hプレイヤー座標をメンバ変数として追加する
Game.cpp初期化、更新、描画処理を追加する

ゲームループの役割

ゲームは、状態を更新し、画面を描く処理を1秒間に60回程度繰り返しながら動き続けます
この繰り返し処理をゲームループと呼びます
開始用プロジェクトでは、すでに次の流れが用意されています

cpp
while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
{
    Update();
    Draw();
    ScreenFlip();
}

ProcessMessage()はWindowsからウィンドウへ送られる操作(移動、フォーカス切り替えなど)を処理する関数で、閉じるボタンが押されると0以外を返します
CheckHitKey(KEY_INPUT_ESCAPE)はEscキーが押されているかを調べ、押されていれば0以外を返します
どちらかが真になった時点でループを抜け、ゲームが終了します

ループの中身は毎フレーム同じ順序で実行されます

図を準備しています…

Update()はゲーム内の値を変える処理です
座標、速度、HP、スコアなど、時間とともに変化する値はすべてここで更新します
Draw()は現在の値を画面に表示する処理です
値を変えるのはUpdate()だけに限定し、Draw()では値を変えません
この役割分担が崩れると、「描画するたびに値が変わってしまう」といった、原因を追いにくい不具合につながります
Draw()constにしているのも、この関数がメンバ変数を変更しないことをコンパイラに保証させるためです

画面の座標系

DXライブラリの画面座標は、左上を原点(0, 0)とし、右へ進むとx座標が増え、下へ進むとy座標が増えます
数学のグラフとは上下が逆になる点に注意します

text
(0,0) ────────► x



  y

screenWidthは640、screenHeightは480なので、画面右下は(640, 480)に近い座標になります
この座標系は今後すべての描画・移動・当たり判定の基準になります

プレイヤー座標を追加する

まず、Game.hにプレイヤーの中心座標を追加します

cpp
private:
    void Update();
    void Draw() const;

    int playerX = 0;
    int playerY = 0;

次に、Initialize()で初期位置を決めます
画面サイズを直接数値で書かず、screenWidthscreenHeightから計算します

cpp
bool Game::Initialize()
{
    playerX = screenWidth / 2;
    playerY = screenHeight - 60;
    return true;
}

数値を直接書かない理由は、画面サイズを変更したときに初期位置がずれないようにするためです
screenWidth / 2と書いておけば、Common.hscreenWidthを変えるだけで、プレイヤーは常に画面の水平中央に配置されます

プレイヤーを描画する

Draw()で、文字表示の代わりにプレイヤーの仮図形を描きます
最初は画像ではなく、三角形で構いません

cpp
void Game::Draw() const
{
    ClearDrawScreen();

    DrawTriangle(
        playerX,
        playerY - 18,
        playerX - 16,
        playerY + 14,
        playerX + 16,
        playerY + 14,
        cyan,
        TRUE
    );
}

ClearDrawScreen()は裏画面を消去する関数です
これを呼ばずに毎フレーム図形を描き足すと、前のフレームの絵が消えずに残り続けます
DrawTriangleの引数は、3つの頂点座標、色、塗りつぶすかどうか(TRUE)の順です
頂点をplayerY - 18のように中心座標からの相対位置で指定しているのは、playerXplayerYを変えるだけで三角形全体が同じ形のまま移動できるようにするためです

この段階では、プレイヤーは動きません
座標を変えると表示位置が変わることを確認します

動作確認

  1. ビルドして画面下部に三角形が表示されることを確認する
  2. playerXの初期値を変えて、左右の表示位置が変わることを確認する
  3. playerYの初期値を変えて、上下の表示位置が変わることを確認する
  4. cyangreenyellowへ変えて、色が変わることを確認する

よくある問題

  • 何も表示されない場合は、Draw()MainLoop()から呼ばれているか確認する
  • 図形が画面外に出る場合は、playerXplayerYの値と、画面の座標系(下がy+方向)を確認する
  • screenWidthが使えない場合は、Game.cppCommon.hをインクルードしているか確認する
  • 図形が前のフレームの絵と重なって濃く見える場合は、ClearDrawScreen()が呼ばれているか確認する

今回の完成条件

  • Gameクラスにプレイヤー座標を追加できる
  • Initialize()で初期位置を設定できる
  • Draw()でプレイヤーの仮図形を表示できる
  • Update()Draw()の役割の違いを説明できる
  • 画面座標の原点と軸の向きを説明できる

まとめ

今回は、ゲームループ上にプレイヤーの仮表示を追加しました
次回はキー入力を読み取り、プレイヤーを上下左右に動かします