# 第22回開始用プロジェクト

第21回の標準到達状態です。第22回では、敵の重力、地形衝突、巡回状態を追加します。
シューティングゲームで学んだC++17、名前空間、クラス、Manager、Tuning、RAIIの構成を前提に、プラットフォーマー固有の実装を独立してまとめています。

## 含まれる機能

- `DxGame`と`Platformer`のソース分離
- `Vector2`と`Rect`による座標・矩形管理
- CSVからの可変サイズのタイルマップ、開始位置、ゴール、敵配置の読み込みと行・列付きエラー診断
- `StageCell`と定義表によるCSV値、複数種類の地形画像・地形判定の管理
- ワールド座標とカメラによる横スクロール
- 加速度、摩擦、重力、ジャンプ
- 可変ジャンプ、コヨーテタイム、ジャンプ入力バッファ
- 横方向・縦方向の地形衝突とめり込み解消
- プレイヤーの待機、走行、上昇、落下アニメーション
- `PlayerEvents`を介したBGM、ジャンプSE、着地SE
- 画像・音声IDと定義表の件数・重複・登録内容の検証
- CSVから複数の敵を`EnemyManager`へ配置して描画
- F1によるプレイヤーの表示矩形・命中矩形、ゴールの表示矩形・判定矩形、状態のデバッグ表示
- 固定60Hzのゲームループと秒単位の速度・加速度

## 第21回で導入するジャンプ操作の改善

第18回の基本ジャンプに、操作の意図を受け取りやすくする3つの処理を追加します。実装は`Player::UpdateInput`へまとめ、調整値は`Tuning/GameTuning.h`から変更します。

<!-- markdownlint-disable MD013 -->

| 処理 | 動作 | 主なコード |
| --- | --- | --- |
| 可変ジャンプ | 上昇中にZキーを離すと上向き速度を弱め、短く押すと低く、長く押すと高く跳べるようにする | `JumpCutMultiplier`と`Input::IsReleased` |
| コヨーテタイム | 足場から離れた直後も6フレームだけジャンプを受け付ける | `coyoteFrames_`と`CoyoteFrameLimit` |
| ジャンプ入力バッファ | 着地直前に押したジャンプを6フレームだけ記憶し、接地した時点で実行する | `jumpBufferFrames_`と`JumpBufferFrameLimit` |

<!-- markdownlint-enable MD013 -->

ジャンプを開始できる条件は「コヨーテタイムが残っている」かつ「入力バッファが残っている」です。ジャンプ開始後は両方を0へ戻すため、1回の入力で連続ジャンプしません。固定60Hzでは6フレームが約0.1秒に相当します。

確認時は、足場の端から少し遅れてZを押す、着地する少し前にZを押す、Zを短く押す・長く押す、の3点を比較します。成功・失敗だけでなく、入力を受け付ける時間を`GameTuning.h`で調整できることも確認します。

## 複数種類の地形に最初から対応する理由

第15回でStageを導入する時点から、CSVの数値を処理中で直接比較せず、すべて`StageCell`の名前を通して扱います。

```cpp
enum class StageCell : int
{
    Empty = 0,
    // 1～9: 進行・ギミック
    Goal = 1,
    PlayerSpawn = 2,
    // 10～19: 通常地形・障害物
    Brick = 10,
    Moss = 11,
    // 90～99: 敵の配置
    EnemySpawn = 90
};
```

`StageCell::Goal`のように書けば、数値だけの場合と違ってセルの役割をコードから読み取れます。`: int`と各整数値を明示するのは、値が配布済みのCSVとの対応を表すためです。同じ用途を近くへ追加できるよう、進行・ギミック、通常地形、トラップ、敵配置の値域をあらかじめ分けます。並び順を変えても意味がずれないよう、追加時に既存値を変更しません。

値域はCSVを整理するための規約であり、処理を決める条件ではありません。`value >= 90`のような判定は使わず、列挙子の名前と定義表から役割を判断します。読み込んだ整数は`TryParseStageCell()`で検証し、未定義の値を誤って受け入れないようにします。

地形セルはさらに`tileDefinitions`の対応表を通して画像と地形判定へ接続します。このスナップショットでは`StageCell::Brick`をレンガ、`StageCell::Moss`を苔石として読み込み、どちらも地形判定を持ちます。

`Goal`と`PlayerSpawn`は各CSVに1つずつ置きます。開始座標を`GameTuning.h`へ固定せずCSVから取得するため、ステージの配置を変えてもコード側の座標を修正する必要がありません。

制作途中で地形画像を増やすことはよくあります。最初から定義表を使えば、新しい種類を追加するときに描画処理や衝突処理へ条件分岐を増やす必要がありません。画像ファイルのピクセル数は、ワールド上の表示サイズである`stage::TileSize`とは独立しています。実際に新しい種類を登録する演習は、ゲーム本編が完成した第24回で行います。

## この段階に含めない機能

敵の重力・巡回・追跡・撃破状態、プレイヤーと敵の相互作用、タイトル画面、ゲームクリア、ゲームオーバー、リトライは第22回以降で扱います。

## 操作

| キー | 操作 |
| --- | --- |
| 左右矢印 | 移動 |
| Z | ジャンプ |
| F1 | デバッグ表示の切り替え |
| Esc | 終了 |

## ファイル構成

```text
プロジェクトルート/
  PlatformerGame2D.sln
  PlatformerGame2D.vcxproj
  DxLib.props
  Source/
    main.cpp
    Config/
      ApplicationConfig.h
    DxGame/
      Vector2.h
      Rect.h
    Platformer/
      Camera.h/.cpp
      Enemy.h/.cpp
      EnemyManager.h/.cpp
      Game.h/.cpp
      ImageManager.h/.cpp
      Input.h/.cpp
      Player.h/.cpp
      SoundManager.h/.cpp
      Stage.h/.cpp
      Tuning/
        GameTuning.h
  Assets/
    Images/
    Sounds/
    Maps/
```

## 責務

- `Game`はゲームループ、更新順、描画順を調整します。
- `Stage`はCSV、タイル、ゴール、敵配置を管理します。
- `Player`は入力、速度、ジャンプ、重力、地形衝突、アニメーションを管理し、音を直接再生せず`PlayerEvents`を返します。
- `Enemy`は配置された敵1体を表し、`EnemyManager`は敵の集合を所有します。敵の行動は次回以降に追加します。
- `Camera`はワールド座標を画面座標へ変換します。
- `ImageManager`と`SoundManager`はDXライブラリのリソースハンドルを所有します。
- `DxGame`には作品の意味を持たない値型だけを置き、ゲーム固有処理は`Platformer`へ置きます。

## Visual Studio 2026で実行する

1. Visual Studio 2026の「C++によるデスクトップ開発」とWindows版DXライブラリを用意します。
1. Visual Studioを起動する前に、DXライブラリを展開したフォルダーを環境変数`DXLIB_ROOT`へ設定します。
1. `PlatformerGame2D.sln`を開き、`Debug`・`x64`でビルドします。
1. ビルド後に生成された`x64\Debug\PlatformerGame2D.exe`を起動します。

DXライブラリの配置が異なる場合は、`DxLib.props`が参照する`DXLIB_VC_DIR`へ`DxLib.h`とライブラリのあるフォルダーを指定してください。

Debug・Releaseのビルド後には、プロジェクト直下の`Assets`が出力フォルダーへ自動コピーされます。編集する素材はプロジェクト直下の`Assets`だけにしてください。
