Skip to content

第03回 入力と移動 / キー入力でキャラクターを動かす

前回: 第02回 C++入門 / ゲーム業界での立ち位置 / 描画プログラム制作 / 次回: 第04回 2Dシューティングゲーム入門 1 / 構造体と衝突判定の基本

前回の振り返り

DXライブラリで文字と画像を表示しました

  • C++ の変数と型の基本を確認しました
  • DXライブラリ のゲームループが 消す → 描く → 表示する の繰り返しで動くことを確認しました
  • DrawString で文字を表示し、DrawFormatString で値も表示できることを確認しました
  • LoadGraphDrawGraph で画像を表示しました
  • 今回は、表示したものをキー入力で動かします

今回の目的

画像をキー入力で動かせる状態にする

  • キー入力を取得する方法を理解する
  • 座標を更新すると見た目が動く理由を理解する
  • 画像をキー入力に応じて動かす流れを組み立てる
  • 自分でキャラクター操作を試せる状態を目指す

今回の授業内容

入力と移動 / キー入力でキャラクターを動かす

  • キー入力をプログラムで取得する方法を学ぶ
  • 座標の値を変えると見た目がどう変わるかを理解する
  • キー入力に応じて xy を更新する
  • 前回表示できた画像を、今回は座標更新で動かす流れを理解する
  • 自分でキャラクターを操作できる状態まで組み立てる

入力と移動

ゲームの基本はなにより 表示画像を「動かすこと」

いよいよ ゲーム制作本番

座標の復習

位置を数値で管理する

  • 二次元の画面上の場所は xy の 2つの値で表します
  • x を増やすと右へ、減らすと左へ動きます
  • y を増やすと下へ、減らすと上へ動きます
  • 座標の値を毎フレーム少しずつ変えることで 表示を動かすことができます

ゲームループとは

毎フレーム繰り返す処理

  1. ユーザーの入力を処理し
  2. ゲームの状態を更新し
  3. 画面に描画する
  • この一連の流れを繰り返すのが ゲームループです
cpp
while (ProcessMessage() == 0 &&
    CheckHitKey(KEY_INPUT_ESCAPE) == 0)
{
    ClearDrawScreen(); // 画面を消す

    // ここに様々な描画処理を書く

    ScreenFlip(); // 描いた内容を画面に反映する
}

数値を変えると動いて見える

描画位置を毎フレーム少しずつ変える

cpp
int playerX = 320;
int playerY = 240;

playerX += 4;
  • playerX を 1回だけ +4 すると、 画像は右に 4 ピクセル移動します
  • この更新をゲームループの中で繰り返せば画像が右に動いて見えます
  • ループの中で座標を都度更新することで 動きを作るのがゲームの描画の基本です

キー入力を調べる

CheckHitKey を使う

cpp
// 左キーが押されている間、playerX を減らす
if (CheckHitKey(KEY_INPUT_LEFT))
{
    playerX -= 4;
}
  • CheckHitKey(キー名) は、そのキーが押されているかを調べます
  • 戻り値が 0 以外なら「押されている」と判断できます
  • たとえば左キーが押されている間だけ playerX を減らせば、 キーを押している間だけプレイヤーが左に動きます
  • 右キーでプレイヤーを右に動かす方法を考えてみましょう

キーを指定して入力をチェック

矢印キーを使う場合

定数対応するキー
KEY_INPUT_LEFT左矢印キー
KEY_INPUT_RIGHT右矢印キー
KEY_INPUT_UP上矢印キー
KEY_INPUT_DOWN下矢印キー
KEY_INPUT_ESCAPEEsc キー
  • 他にもWASDなどあらゆるキーを指定できます 今回は矢印キーと Esc キーを使用します
  • 余裕があれば好みのキーを指定してみましょう

ゲームループの中で入力を使う

入力更新と描画を毎フレーム繰り返す

  1. キー入力を調べて playerXplayerY を更新する
  2. ClearDrawScreen() で前のフレームを消す
  3. 新しい位置に描画して ScreenFlip() で表示する
  • 入力、更新、描画を毎フレーム繰り返すのが、移動処理の基本形です
  • 次のスライドで、この流れをコード全体の中で確認します

実習1: 円をキー入力で動かす

座標を変えて描画する

  • 次のコードを入力し、矢印キーで 円が動くことを確認してください
  • 動いたら playerXplayerY、 移動量の値を変えて結果を比べます
  • 今回表示されているのは円ですが、 画像を動かす場合も基本は同じです
cpp
// キー入力で円を動かすサンプル
#include "DxLib.h"

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    int playerX = 320;
    int playerY = 240;

    ChangeWindowMode(TRUE);
    if (DxLib_Init() == -1) return -1;
    SetDrawScreen(DX_SCREEN_BACK);

    while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
    {
        if (CheckHitKey(KEY_INPUT_LEFT))  playerX -= 4;
        if (CheckHitKey(KEY_INPUT_RIGHT)) playerX += 4;
        if (CheckHitKey(KEY_INPUT_UP))    playerY -= 4;
        if (CheckHitKey(KEY_INPUT_DOWN))  playerY += 4;

        ClearDrawScreen();
        DrawCircle(playerX, playerY, 20, GetColor(255, 255, 255), TRUE);
        ScreenFlip();
    }

    DxLib_End();
    return 0;
}

コードの解説

座標を変えて描き直す流れで動きを作る

  1. playerXplayerY に初期位置を設定する
  2. キー入力に応じて、その値を増減する
  3. 画面を消す
  4. 新しい位置に円を描く
  5. 画面を更新する
  • 難しく見えても「値を変えて描き直している」だけです ですがここに衝突判定が入ればゲームになります 基本的かつ重要な流れなので、しっかり理解しておきましょう

補足: SetDrawScreen とは

裏画面に描いてから表示する

cpp
// 描画モード設定、裏画面に描くようにする
SetDrawScreen(DX_SCREEN_BACK);
  • 描画中の情報を表示してしまうと画面のちらつきの原因になります
  • 裏画面を用意して、一旦描き終えてから表示を切り替えることで 画面のちらつきを防止できます これをダブルバッファリングと呼びます
  • DXライブラリでは ScreenFlip() で 裏画面と表画面を入れ替えます

座標を表示して動作を確認する

デバッグ用の表示を追加する

cpp
DrawFormatString(10, 10,
    GetColor(255, 255, 0),
    L"X=%d  Y=%d", playerX, playerY);
  • 意図した表示にならないときは、 座標の問題か、描画の問題かを切り分けて考えるのが大事です
  • ここで変数の現在の値を画面に表示することができれば、 動作がおかしい場合も原因を探しやすくなります
  • 描画の見た目だけで判断せず、 値が想定通りかを確認しましょう

円を画像に置き換える

新しく変わるのは描画する対象だけ

cpp
// ループの前: 前回と同じように画像を読み込む
int playerHandle = LoadGraph(L"Images/Player.png");

// ループの中: 円の代わりに画像を描く
DrawGraph(playerX, playerY, playerHandle, TRUE);
  • 変わるのは「何を描くか」だけです
  • 今回は円ではなく画像を描いていますが 入力処理と座標更新の考え方は円を動かしたときと同じです

画像を動かすサンプルコード

1/2

cpp
#include "DxLib.h"

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    ChangeWindowMode(TRUE);

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

    // 描画モード設定
    SetDrawScreen(DX_SCREEN_BACK);

    // 画像を読み込む
    int playerHandle = LoadGraph(L"Images/Player.png");

    // プレイヤーの座標を管理する変数
    int playerX = 320;
    int playerY = 240;

    // 画像の大きさは途中で変えないので定数にする
    const int playerWidth = 32;
    const int playerHeight = 32;

2/2

cpp
    // ゲームループ
    while (ProcessMessage() == 0 &&
        CheckHitKey(KEY_INPUT_ESCAPE) == 0)
    {
        // キー入力で座標を更新する
        if (CheckHitKey(KEY_INPUT_LEFT))  playerX -= 4;
        if (CheckHitKey(KEY_INPUT_RIGHT)) playerX += 4;
        if (CheckHitKey(KEY_INPUT_UP))    playerY -= 4;
        if (CheckHitKey(KEY_INPUT_DOWN))  playerY += 4;

        ClearDrawScreen();
        DrawGraph(playerX, playerY, playerHandle, TRUE);
        DrawFormatString(10, 10, GetColor(255, 255, 0),
            L"X=%d  Y=%d", playerX, playerY);
        ScreenFlip();
    }

    DxLib_End();
    return 0;
}

動かす速さを調整する

移動量も変数にして調整する

cpp
int moveSpeed = 4;

if (CheckHitKey(KEY_INPUT_LEFT))
{
    playerX -= moveSpeed;
}
  • moveSpeedが 1 だとゆっくり、4 だと普通、 816 だと速く大きく動きます
  • こうした数値の調整がゲーム制作において極めて重要です 好みの操作感になるように、いろいろな値を試してみましょう

変えない値は定数にする

画像の大きさなど、途中で変えない値は定数に

cpp
const int playerWidth = 32;
const int playerHeight = 32;
  • const は constant(定数)の略で、 値を変えないことを宣言するためのキーワードです
  • 変更する必要のない値を定数にすれば、 誤って値を変えてしまうミスを防げます
  • また、同じような定数の定義を近い場所にまとめると、 どの数値を調整すればよいか分かりやすくなります

画面の外に出ないようにする

値の範囲を制限する

cpp
if (playerX < 0) playerX = 0;
if (playerY < 0) playerY = 0;

if (playerX > 640 - playerWidth)
    playerX = 640 - playerWidth;
if (playerY > 480 - playerHeight)
    playerY = 480 - playerHeight;
  • 画像サイズを踏まえて移動できる範囲を決めれば キャラクターが画面の外にはみ出さないようにできます
  • 右端は 画面の幅 - 画像の幅、 下端は 画面の高さ - 画像の高さ で求めます
  • 画像サイズを変えた場合も、playerWidthplayerHeight を 実際の大きさに合わせれば同じ考え方で制限できます
  • 「画面の外へ行きすぎたら戻す」値の制限は ゲーム制作ではよく行う処理の一つです

実習2: 動きを整える

画像を動かしながら調整する

  1. 解説した画像版のコードをもとに、自分の環境で画像を動かす
  2. moveSpeed を変数にして、動作速度を調整できるようにする
  3. 画面端で止まる処理を追加する
  4. DrawFormatString で座標を確認する
  • 意図した通りに動かない場合は、前回の画像表示ができているかと、 今回追加した入力処理を分けて確認してください
  • 余裕があれば Shift キーで速く動くなどの追加も試します

よくあるつまずき

動かないときの確認ポイント

  • CheckHitKey のキー定数を間違えていないか
  • playerXplayerY をゲームループの外で宣言しているか
  • ClearDrawScreen()ScreenFlip() を忘れていないか
  • 前回の画像表示コードがそのまま動く状態になっているか
  • DrawFormatString で座標が変化しているか

今回のまとめ

今日のポイント

  • ゲームの移動は、座標の値を更新して描き直すことで表現します
  • CheckHitKey を使うと、押されているキーに応じて処理を変えられます
  • DrawFormatString を使うと、見た目だけでなく数値でも状態を確認できます
  • 画面外に出ないようにするなど、値を制限する処理も重要です
  • 次回は 2Dシューティングの形に近づけながら ゲームの基本構造を組み立てていきます

おつかれさまでした!

次回予告 第04回 2Dシューティング入門 1

テンションあげて いきましょう