Appearance
第03回 入力と移動 / キー入力でキャラクターを動かす
前回: 第02回 C++入門 / ゲーム業界での立ち位置 / 描画プログラム制作 / 次回: 第04回 2Dシューティングゲーム入門 1 / 構造体と衝突判定の基本
前回の振り返り
DXライブラリで文字と画像を表示しました
C++の変数と型の基本を確認しましたDXライブラリのゲームループが 消す → 描く → 表示する の繰り返しで動くことを確認しましたDrawStringで文字を表示し、DrawFormatStringで値も表示できることを確認しましたLoadGraphとDrawGraphで画像を表示しました- 今回は、表示したものをキー入力で動かします
今回の目的
画像をキー入力で動かせる状態にする
- キー入力を取得する方法を理解する
- 座標を更新すると見た目が動く理由を理解する
- 画像をキー入力に応じて動かす流れを組み立てる
- 自分でキャラクター操作を試せる状態を目指す
今回の授業内容
入力と移動 / キー入力でキャラクターを動かす
- キー入力をプログラムで取得する方法を学ぶ
- 座標の値を変えると見た目がどう変わるかを理解する
- キー入力に応じて
xとyを更新する - 前回表示できた画像を、今回は座標更新で動かす流れを理解する
- 自分でキャラクターを操作できる状態まで組み立てる
入力と移動
ゲームの基本はなにより 表示画像を「動かすこと」
いよいよ ゲーム制作本番
座標の復習
位置を数値で管理する
- 二次元の画面上の場所は
xとyの 2つの値で表します xを増やすと右へ、減らすと左へ動きますyを増やすと下へ、減らすと上へ動きます- 座標の値を毎フレーム少しずつ変えることで 表示を動かすことができます
ゲームループとは
毎フレーム繰り返す処理
- ユーザーの入力を処理し
- ゲームの状態を更新し
- 画面に描画する
- この一連の流れを繰り返すのが ゲームループです
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_ESCAPE | Esc キー |
- 他にもWASDなどあらゆるキーを指定できます 今回は矢印キーと Esc キーを使用します
- 余裕があれば好みのキーを指定してみましょう
ゲームループの中で入力を使う
入力更新と描画を毎フレーム繰り返す
- キー入力を調べて
playerXとplayerYを更新する ClearDrawScreen()で前のフレームを消す- 新しい位置に描画して
ScreenFlip()で表示する
- 入力、更新、描画を毎フレーム繰り返すのが、移動処理の基本形です
- 次のスライドで、この流れをコード全体の中で確認します
実習1: 円をキー入力で動かす
座標を変えて描画する
- 次のコードを入力し、矢印キーで 円が動くことを確認してください
- 動いたら
playerX、playerY、 移動量の値を変えて結果を比べます - 今回表示されているのは円ですが、 画像を動かす場合も基本は同じです
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;
}コードの解説
座標を変えて描き直す流れで動きを作る
playerXとplayerYに初期位置を設定する- キー入力に応じて、その値を増減する
- 画面を消す
- 新しい位置に円を描く
- 画面を更新する
- 難しく見えても「値を変えて描き直している」だけです ですがここに衝突判定が入ればゲームになります 基本的かつ重要な流れなので、しっかり理解しておきましょう
補足: 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だと普通、8や16だと速く大きく動きます - こうした数値の調整がゲーム制作において極めて重要です 好みの操作感になるように、いろいろな値を試してみましょう
変えない値は定数にする
画像の大きさなど、途中で変えない値は定数に
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;- 画像サイズを踏まえて移動できる範囲を決めれば キャラクターが画面の外にはみ出さないようにできます
- 右端は
画面の幅 - 画像の幅、 下端は画面の高さ - 画像の高さで求めます - 画像サイズを変えた場合も、
playerWidthとplayerHeightを 実際の大きさに合わせれば同じ考え方で制限できます - 「画面の外へ行きすぎたら戻す」値の制限は ゲーム制作ではよく行う処理の一つです
実習2: 動きを整える
画像を動かしながら調整する
- 解説した画像版のコードをもとに、自分の環境で画像を動かす
moveSpeedを変数にして、動作速度を調整できるようにする- 画面端で止まる処理を追加する
DrawFormatStringで座標を確認する
- 意図した通りに動かない場合は、前回の画像表示ができているかと、 今回追加した入力処理を分けて確認してください
- 余裕があれば
Shiftキーで速く動くなどの追加も試します
よくあるつまずき
動かないときの確認ポイント
CheckHitKeyのキー定数を間違えていないかplayerXとplayerYをゲームループの外で宣言しているかClearDrawScreen()とScreenFlip()を忘れていないか- 前回の画像表示コードがそのまま動く状態になっているか
DrawFormatStringで座標が変化しているか
今回のまとめ
今日のポイント
- ゲームの移動は、座標の値を更新して描き直すことで表現します
CheckHitKeyを使うと、押されているキーに応じて処理を変えられますDrawFormatStringを使うと、見た目だけでなく数値でも状態を確認できます- 画面外に出ないようにするなど、値を制限する処理も重要です
- 次回は 2Dシューティングの形に近づけながら ゲームの基本構造を組み立てていきます