Appearance
第01回 プレイヤーの表示と操作
4 / 4 入力とプレイヤーの移動
このステップの目的
入力から移動方向を作り、速度と1フレーム分の時間を使ってプレイヤーの位置を更新します。最後に、移動範囲を確認し、移動速度を調整します。
入力とアクション
これからキーボードでの入力とプレイヤーの移動操作を対応づけていきます。
この回の必須内容は、入力方向を作り、速度とdeltaTimeから位置を更新することです。 usingやstatic_cast<void>の補足は、最初の実装が動いた後に読む発展内容として扱います。
たとえば、左方向への移動に←キーとAキーを割り当てるとします。キーごとにif文を書いて判定すると、同じ処理が重複してコードが冗長になります。
キーコードを移動処理へ直接書き込むのも問題です。操作方法を変更するたびに複数箇所を修正する必要があり、更新漏れやキーの割り当ての衝突が起こりやすくなります。
冗長で望ましくない例cpp
if (CheckHitKey(KEY_INPUT_LEFT) || CheckHitKey(KEY_INPUT_A))
direction.x -= 1.0f;
if (CheckHitKey(KEY_INPUT_RIGHT) || CheckHitKey(KEY_INPUT_D))
direction.x += 1.0f;
if (CheckHitKey(KEY_INPUT_UP) || CheckHitKey(KEY_INPUT_W))
direction.y -= 1.0f;
if (CheckHitKey(KEY_INPUT_DOWN) || CheckHitKey(KEY_INPUT_S))
direction.y += 1.0f;ここでは左方向への移動というアクションと、それに対応するキーのマップを設けることで、入力を簡素に扱うシューティング専用のInputクラスを用意しています。 Inputはシューティング内の操作と物理キーの対応表を持ち、キー入力をアクションに変換するクラスです。複数作品で再利用する入力ライブラリへの抽出は、第13回以降に両作品を比較してから扱います。
Input.cppcpp
// ゲーム内の操作と物理キーの対応表
constexpr Binding Bindings[] =
{
{ Action::MoveLeft, KEY_INPUT_LEFT },
{ Action::MoveLeft, KEY_INPUT_A },
{ Action::MoveRight, KEY_INPUT_RIGHT },
{ Action::MoveRight, KEY_INPUT_D },
...ゲーム内の操作を直接キーと対応させるのは、通常あまり望ましくありません。 間に1ステップ挟んでゲーム内のアクションとして扱う方が柔軟性が高くなります。 この概念は、例えば格闘ゲームのコマンド入力などに発展させることができます。
Inputクラスの実装については、後の回で詳しく扱います。
余談:using の利用
Player.cppには、入力操作を表す型の別名が用意されています。
Player.cppcpp
using Action = Input::Action;Input::Actionは入力とゲームで求められるアクションを対応させる定義ですが、そのままInput::Action::MoveLeft等と書くと長くなってしまいます。 ここではusingキーワードを使ってInput::ActionをActionと書けるようにエイリアスを指定しています。
using キーワード
C++のusingは、名前空間や型などへのアクセスを指定するためのキーワードです。 同じキーワードに複数の機能が与えられており混乱しやすいキーワードですが、重要な機能はごく一部です。 今回の制作では踏み込みません。
入力から移動方向を求める
配布プロジェクトでは、Player::Update()の先頭で移動方向を扱うdirectionを定義しています。
Player.cppcpp
void Player::Update(const Input& input, float deltaTime)
{
Vector2 direction;
...Vector2のxとyには初期値0.0fが定義されています。
Player::Update()にある次の2行を、後に示す4つのif文へ置き換えます。
置き換える範囲cpp
// 第01回: 入力から移動方向を求める
static_cast<void>(input);static_cast<void> の意味
入力処理が未実装の間、コンパイラはinputが使用されていないという警告を発生させます。static_cast<void>(input)は、inputをvoidにキャストしてコンパイラの警告を防ぐために書かれています。
機能は一切ありませんので、置き換え時に削除してください。
Player.cppcpp
if (input.IsDown(Action::MoveLeft))
direction.x -= 1.0f;
if (input.IsDown(Action::MoveRight))
direction.x += 1.0f;
if (input.IsDown(Action::MoveUp))
direction.y -= 1.0f;
if (input.IsDown(Action::MoveDown))
direction.y += 1.0f;画面座標ではXの正の方向が右、Yの正の方向が下です。 よって左と上は-1.0f、右と下は+1.0fを加えます。
INFO
左と右、上と下といった反対方向を同時に押すと、同じ成分へ-1.0fと1.0fが加わり0になります。 深い意味のある実装ではありませんが、左右と上下のどちらかの軸が優先されることを回避できます。
斜め移動と速度
例えば右と下を同時に押すと、方向は{ 1, 1 }になります。 このベクトルの長さは
配布コードでは、方向の長さを1以内に調整する処理が実装済みです。 この処理を正規化と呼びます。
INFO
正規化の処理はゲームでの方向制御や速度計算の際にたびたび必要になります。
プレイヤーを狙う敵弾を実装する回で詳しく扱います。
位置を更新する
同じ関数にある次の2行を、後に示す位置更新へ置き換えます。
置き換える範囲cpp
// 第01回: 方向、速度、1フレーム分の時間から位置を更新する
static_cast<void>(deltaTime);Player.cppcpp
position.x += direction.x * tuning::player::MoveSpeed * deltaTime;
position.y += direction.y * tuning::player::MoveSpeed * deltaTime;MoveSpeedはGameTuning.hに1秒あたりの移動ピクセル量として定義されています。deltaTimeは1フレーム分の1/60秒です。
60FPS動作で MoveSpeed が 240 の場合、1フレームの移動距離はおよそ4ピクセルになります。
text
240ピクセル / 秒 × 1/60秒 = 4ピクセル
プレイヤーの移動
ここまで実装できたら実行し、プレイヤーが上下左右と斜めへ移動できることを確認してください。
画面外にはみ出さないようにする処理は実装済みです。 同じ方向を押し続けると画面端で止まります。
動作確認
次の順に確認します。
- 左右上下の各端へ移動する
- 同じ方向を押し続けても一定の位置で止まることを確認する
- 左上と右下でも一定の位置で止まることを確認する
- 斜め移動が縦横より速く感じないことを確認する
- Escapeキーで終了できることを確認する
斜め移動の調整と画面内の移動制限は、ここまでの実装で済んでいます。 今回は動作を確認できれば十分です。
実装済みの画面内制限
ここではプレイヤーの命中範囲全体が画面外にはみ出さないよう移動範囲を制限しています。
命中範囲の形と座標を使った制限の計算は、第04回「プレイヤーの弾と敵の命中判定」で扱います。
値を変更して違いを見る
GameTuning.hのMoveSpeedを変更すれば、プレイヤーの移動速度を調整できます。
GameTuning.hcpp
// プレイヤーの速度 単位はピクセル/秒
inline constexpr float MoveSpeed = 240.0f;例えば120.0fへ変更して実行し、移動速度が半分になることを確認しましょう。
第01回の完成確認
ここまでで以下が実装できていることを確認してください。
チェックリスト
- プレイヤー画像が画面下部に表示される
- 矢印キーとWASDの両方で移動できる
- 斜め移動でも縦横と同じ速度で移動する
- 画面外にはみ出さないよう移動範囲が制限されている
- 移動速度を変更し、調整できる
お疲れさまでした
次回、第02回ではプレイヤーの弾の発射処理を実装します。