Skip to content
第04回 キャラクターの追加

3 / 3 パラメーターの変更と動作の分離 ​

このステップの目的

2体のキャラクターの位置や移動速度を調整し、どの変数がどの挙動を決定しているかを特定します。値や描画順序を一つずつ変え、もう一方のキャラクターへの影響も確認します。

パラメーターの特定と独立性の検証 ​

現在、青いロボットはキーボード入力に応じて、オレンジ色のロボットは変数 speed に従って移動しています。動く要因は異なりますが、どちらも「変数の値を更新し、その変数を引数として渡して描画する」という根本的な仕組みは同一です。

変更したい動作と変数の対応 ​

目的の挙動を変更するために、どの変数を操作すべきかを整理しましょう。

実現したい変更内容操作対象の変数変更する値の例
青いロボットの初期位置を変更するx, yx = 200; y = 200;
オレンジ色のロボットの表示高度を変更するwalkerYwalkerY = 150;
オレンジ色のロボットの移動速度を速くするspeed の絶対値speed = 4;
オレンジ色のロボットを最初から左向きに進めるspeed の符号speed = -2;

もう一方のキャラクターへの影響の確認 ​

上記の表から項目を選び、1つずつ数値を書き換えて実行してみましょう。1つの変更を確認したら元の値に戻してから次を試すと、どの変数がどの動作に影響したかを正確に比較できます。

重要な確認点は、「目的のキャラクターの挙動だけが変化し、もう一方のキャラクターには意図しない影響が出ていないこと」です。例えばオレンジ色のロボットの速度を 4 に変更した際、青いロボットのキー移動速度まで変わってしまっていないかを確かめることで、変数が正しく分離されていることが確認できます。

応用課題:縦方向(上下)への往復運動の実装 ​

横方向の往復運動を理解できたら、応用課題として「オレンジ色のロボットを上下方向に自動往復させる」ロジックへ改造してみましょう。

加算対象と境界値の変更 ​

上下往復へ改造するには、次の3つの要素を walkerX から walkerY へ切り替える必要があります。

  1. 毎フレームの加算: walkerY = walkerY + speed;
  2. 境界の判定と補正:
    • 上端:if (walkerY < 0) { walkerY = 0; speed = -speed; }
    • 下端:画面の高さ(480px)と画像の高さ(48px)から、if (walkerY > 480 - 48) { walkerY = 480 - 48; speed = -speed; }
  3. 初期値の調整: 画面中央付近(例: walkerX = 320; walkerY = 100;)から開始する

単に加算対象を walkerY に変えるだけでなく、判定対象や補正代入先、画面端の最大許容値(480 - 48 = 432)もすべて縦方向(y)に合わせて変更しなければならない点に注意してください。

上下往復の確認と元の状態への復帰 ​

実行して、オレンジ色のロボットが上下の画面端で反転することと、青いロボットを引き続き操作できることを確認してください。

確認後は、加算対象と境界判定・補正を walkerX に戻し、右端の上限を 640 - 48 に戻します。初期値も walkerX = 320、walkerY = 300、speed = 2 に戻し、次回の実習の開始状態をそろえます。

描画順序の変更実験とプログラム構造の総括 ​

重なり順の入れ替え ​

青いロボットをキー操作で動かし、往復するオレンジ色のロボットと重なる位置へ移動させてみましょう。

現在のコードでは、

cpp
        DrawGraph(x, y, image, TRUE);       // 1. 青ロボットを描画
        DrawGraph(walkerX, walkerY, walker, TRUE); // 2. 橙ロボットを描画

という順序で呼び出しているため、常にオレンジ色のロボットが手前(前面)に描画されます。

ここで、この2行の順序を入れ替えてビルド・実行してみてください。

cpp
        DrawGraph(walkerX, walkerY, walker, TRUE); // 1. 橙ロボットを描画
        DrawGraph(x, y, image, TRUE);       // 2. 青ロボットを描画

重なった際に、今度は青いロボットが手前に表示されることが確認できます。座標計算の順序に関わらず、「後から描画されたものが手前に映る」という2Dグラフィックスの基本規則を実感してください。

確認後は、青いロボット、オレンジ色のロボットの順に描く元の並びへ戻してください。次回は、この表示と動作を維持しながらコードを関数へ分けます。

プログラム全体の構造把握 ​

コード全体を見渡し、各セクションが果たしている役割を再確認しましょう。

図を準備しています…

このように、ゲームプログラムは「初期化」「メインループ(入力・更新・描画・待機)」「終了処理」という明確な骨格の上に成り立っています。

チェックリスト
  • パラメーターの変更が特定のキャラクターのみに反映されることを確認した
  • 横往復と上下往復における計算式・境界値の違い(幅640と高さ480)を説明できる
  • 描画関数の記述順序が画面上の重ね合わせ順(前後関係)を決定する理由を説明できる

参考実装 ​

ここまでの手順を終えた 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);

    int image = LoadGraph(L"Assets/Images/Robot.png");
    if (image == -1)
    {
        MessageBoxW(nullptr, L"Assets/Images/Robot.png を読み込めません。", L"画像の確認", MB_OK);
        DxLib_End();
        return -1;
    }

    int x = 160;
    int y = 120;

    int walker = LoadGraph(L"Assets/Images/Walker.png");
    if (walker == -1)
    {
        MessageBoxW(nullptr, L"Assets/Images/Walker.png を読み込めません。", L"画像の確認", MB_OK);
        DeleteGraph(image);
        DxLib_End();
        return -1;
    }

    int walkerX = 320;
    int walkerY = 300;
    int speed = 2;

    while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
    {
        if (CheckHitKey(KEY_INPUT_LEFT) != 0)
            x = x - 3;
        if (CheckHitKey(KEY_INPUT_RIGHT) != 0)
            x = x + 3;
        if (CheckHitKey(KEY_INPUT_UP) != 0)
            y = y - 3;
        if (CheckHitKey(KEY_INPUT_DOWN) != 0)
            y = y + 3;

        if (x < 0)
            x = 0;
        if (x > 640 - 48)
            x = 640 - 48;
        if (y < 0)
            y = 0;
        if (y > 480 - 48)
            y = 480 - 48;

        walkerX = walkerX + speed;
        if (walkerX > 640 - 48)
        {
            walkerX = 640 - 48;
            speed = -speed;
        }
        if (walkerX < 0)
        {
            walkerX = 0;
            speed = -speed;
        }

        ClearDrawScreen();
        DrawGraph(x, y, image, TRUE);
        DrawGraph(walkerX, walkerY, walker, TRUE);
        ScreenFlip();
        WaitTimer(16);
    }

    DeleteGraph(walker);
    DeleteGraph(image);
    DxLib_End();
    return 0;
}