Skip to content
第01回 ウィンドウの作成と図形の描画

3 / 3 座標の指定と図形の描画 ​

このステップの目的

ウィンドウ内に四角形を描画し、画面座標系(x座標・y座標)の仕組みを理解します。描画位置や色、大きさの指定を変更しながら、コードの数値と表示結果の対応関係を確認します。

描画先の準備と背景色の設定 ​

ウィンドウの表示を維持できるようになったため、次は画面上に図形を描く「描画処理」を追加します。

まず、初期化エラーを確認する if 文の直後に、次の2行を追加します。これらはメインループ(while)の前に配置するため、起動時に一度だけ実行されます。

cpp
    if (DxLib_Init() == -1)
        return -1;

    SetDrawScreen(DX_SCREEN_BACK);
    SetBackgroundColor(245, 248, 252);

裏画面(バックバッファ)の指定 ​

SetDrawScreen は、これ以降に呼び出す描画命令をどこに描き込むかを指定する関数です。引数に DX_SCREEN_BACK を渡すと、「裏画面(バックバッファ)」が描画先として設定されます。

コンピュータの画面描画において、ディスプレイに現在表示されている画面(表画面)に直接図形を描き込んでいくと、描画途中の状態が見えてしまい、画面の激しいちらつき(フリッカー)が発生します。

これを防ぐため、普段は見えないメモリ上の領域(裏画面)に1コマ分の絵を完全に描き上げてから、一瞬で表画面へ転送して表示を切り替えます。この仕組みを ダブルバッファリング(double buffering) と呼びます。

背景色の指定 ​

SetBackgroundColor は、画面を消去するときに塗りつぶす背景色を設定します。

引数には、光の三原色である赤(R)・緑(G)・青(B)の強さを、それぞれ 0 〜 255 の範囲で指定します。ここでは (245, 248, 252) を渡し、白に近い明るいブルーグレーを背景色として設定しています。

1フレームの描画サイクル(消去・描画・反映) ​

メインループへの描画処理の追加 ​

続いて、毎フレームの描画処理をメインループの中に記述します。main.cpp の while ブロック内で、WaitTimer(16); の直前に次の3行を追加してください。既存の待機処理は最後に残します。

cpp
        ClearDrawScreen();
        DrawBox(160, 120, 208, 168, GetColor(38, 113, 191), TRUE);
        ScreenFlip();
        WaitTimer(16);

消去から画面反映までの処理順序 ​

この4行は、ゲームプログラミングにおける基本的な「1フレーム(1コマ)の描画サイクル」を形成しています。

  1. ClearDrawScreen()(画面消去)
    裏画面に残っている前フレームの描画内容を消去し、SetBackgroundColor で指定した背景色で一面を塗りつぶします。
  2. DrawBox(...)(図形描画)
    真っ新になった裏画面の指定座標に四角形を描きます。
  3. ScreenFlip()(画面の反映)
    描き終えた裏画面の内容を表画面へ転送し、ウィンドウの表示を更新します。
  4. WaitTimer(16)(待機)
    約16ミリ秒待機し、ループが待機なしに繰り返されることを抑えます。1周には処理時間もかかるため、更新間隔が常に一定になるわけではありません。

現在は毎フレーム同じ位置に四角形を描き続けているため、画面上は静止しているように見えます。今後の回では、この描画座標を毎フレーム少しずつ変化させることで、アニメーションや移動を実現します。

画面座標系と引数の読み方 ​

原点と座標の増加方向 ​

画面上の位置は、横方向(x軸)と縦方向(y軸)の数値を組み合わせた画面座標で指定します。

ウィンドウの描画領域の左上端が、基準位置である原点 (0, 0) です。右へ進むほどxの値が大きくなり、下へ進むほどyの値が大きくなります。

数学の座標平面との違い

学校の数学で扱う一般的な座標平面では上へ進むほどyの値が増加しますが、この教材の画面座標では下へ進むほどyが増加します。図形を上へ動かしたいときは、yの値を小さくします。

四角形の位置・大きさ・色の指定 ​

DrawBox 関数の引数は、次のように四角形の「左上端」と「右下端」の座標を指定する構成になっています。

cpp
DrawBox(左上のx, 左上のy, 右下のx, 右下のy, 色の値, 塗りつぶしフラグ);

コードの記述 DrawBox(160, 120, 208, 168, ...) を確認すると、

  • 左上座標:(160, 120)
  • 右下座標:(208, 168)

を指定しています。四角形の幅と高さは、右下座標から左上座標を引くことで求められます。

  • 幅:208 - 160 = 48 ピクセル
  • 高さ:168 - 120 = 48 ピクセル

つまり、この命令は「位置 (160, 120) に 48×48 ピクセルの正方形を描く」ことを意味します。

第5引数の GetColor(38, 113, 191) は、赤・緑・青の各成分(0〜255)からDXライブラリ用の色データを作成して返す関数です。第6引数の TRUE は四角形の内側を塗りつぶす指定です(枠線のみ描画する場合は FALSE を指定します)。

座標値の変更実験 ​

数値を書き換えてビルド・実行し、表示結果がどのように変化するか観察してみましょう。

変更内容予想される変化観察のポイント
左上xを 260、右下xを 308 に変更大きさを保ったまま右へ平行移動両方のxに +100 されたため幅(48px)は変化しない
右下xだけ 308 に変更(左上xは 160 のまま)四角形の横幅が広がる幅が 308 - 160 = 148 px に広がる
色を GetColor(220, 80, 60) に変更青色から鮮やかな赤系統に変化R(赤)の比率が高まったため

1つの変更を試して結果を確認したら、コードをいったん元の値に戻し、次の変更を試してください。1箇所ずつ確認することで、どの数値がどの表示結果に対応しているかが明確になります。

描画順序の検証実験 ​

ここまでで、座標や色の指定と表示結果の関係を確認しました。続いて、命令の実行順序が表示に与える影響を調べ、最後に位置と大きさを自分で指定してみます。

消去を描画の後へ移す実験 ​

命令を実行する「順序」の影響を実験してみましょう。

ClearDrawScreen(); の行を、DrawBox(...) の後、ScreenFlip(); の直前へと移動させて実行してみてください。

cpp
        DrawBox(160, 120, 208, 168, GetColor(38, 113, 191), TRUE);
        ClearDrawScreen(); // 描画の直後に消去してみる
        ScreenFlip();
        WaitTimer(16);

実行すると、四角形が全く表示されず、背景色だけが映るはずです。四角形を描いた直後に画面全体を消去し、その何もない状態を表画面に転送しているためです。

コンピュータは書かれた命令を厳密に上から順に実行します。「消去 → 描画 → 画面反映」という順序を守る必要があることを確認できたら、コードを正しい順序へ戻してください。

課題:右下寄りへの描画 ​

見本の数値を変更し、ウィンドウの右下寄りの位置(例えば画面サイズ 640×480 の範囲内で、x=400〜500、y=300〜400 あたり)に任意の大きさの四角形を表示させてみましょう。

チェックリスト
  • 画面座標系におけるx軸・y軸の増加方向(右方向が+x、下方向が+y)を説明できる
  • 座標の平行移動(位置の変更)と、座標差の変更(大きさの変更)を区別して数値を指定できる
  • 「消去 → 描画 → 反映」の実行順序の重要性を理解している

参考実装 ​

ここまでの手順を終えた main.cpp のコード全体です。

main.cpp の参考実装
cpp
#include <DxLib.h>

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    ChangeWindowMode(TRUE);
    SetGraphMode(640, 480, 32);
    SetMainWindowText(L"C++ Basics");
    if (DxLib_Init() == -1)
        return -1;

    SetDrawScreen(DX_SCREEN_BACK);
    SetBackgroundColor(245, 248, 252);

    while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
    {
        ClearDrawScreen();
        DrawBox(160, 120, 208, 168, GetColor(38, 113, 191), TRUE);
        ScreenFlip();
        WaitTimer(16);
    }

    DxLib_End();
    return 0;
}