Appearance
第12回 最終アレンジ・デバッグと完成
前回: 第11回 操作感の設計と改善
今回の授業内容
- 第08〜11回で変更した敵AI、状態遷移、操作感を通して確認する。
- 自分が変更したい項目を1つ選び、最終アレンジを加える。
- 現象、再現手順、期待する結果を分けて、不具合の原因を絞る。
- F1のデバッグ表示を使い、変更した値とゲーム画面を対応させる。
- Debugビルドで完成状態を確認したあと、必要に応じてReleaseビルドを作る。
- 自分のゲームを最初から結果画面まで遊び、時間があればほかの受講者にも遊んでもらう。
完成版の配布コード
第11回までに追加したアニメーション、複数敵、状態管理、音響、操作支援をまとめた完成版です。
自分のコードが動かなくなった場合は、完成版の同じ関数と比較して復帰できます。
配布ファイル一覧
| ファイル | 内容 |
|---|---|
| Source/ImageManager.h | 画像識別子と画像管理クラス |
| Source/ImageManager.cpp | 画像の一括読み込み、サイズ取得、解放 |
| Source/Game.h | ゲーム全体の宣言 |
| Source/Game.cpp | ゲーム全体の実装 |
| Source/Stage.h | ステージの宣言 |
| Source/Stage.cpp | CSVマップ、画像、スクロール |
| Source/Common.h | 共通の型と関数 |
| Source/main.cpp | DXライブラリの初期化 |
| Source/SoundManager.h | 音声IDと音響管理 |
| Source/SoundManager.cpp | BGMとSEの再生・解放 |
| Assets/Maps/Stage01.csv | 完成版マップ |
| Assets/Images/Player.png | プレイヤー画像 |
| Assets/Images/Enemy.png | 敵キャラクター画像 |
| Assets/Images/Tile.png | 足場タイル画像 |
| Assets/Images/Goal.png | ゴール画像 |
| Assets/Sounds/PlayingBgm.wav | プレイ中BGM |
| Assets/Sounds/TitleBgm.wav | タイトルBGM |
| Assets/Sounds/Jump.wav | ジャンプSE |
| Assets/Sounds/Land.wav | 着地SE |
| Assets/Sounds/GameOver.wav | ゲームオーバーSE |
| Assets/Sounds/Goal.wav | ゴールSE |
最初に完成版を起動する
編集を始める前に、第12回の配布コードを一度起動します。
この動作を、変更後に戻れる基準にします。
- タイトル画面からプレイを開始する。
- 左右移動、短いジャンプ、高いジャンプを試す。
- 敵へ近づき、巡回から追跡へ変わることを確認する。
- 敵を上から踏み、横から触れた場合との違いを確認する。
- ゲームクリアまたはゲームオーバーへ進む。
- リトライとタイトル復帰を確認する。
この時点で動かない場合は、最終アレンジを始めず、配布されたGame.hとGame.cppへ戻します。
最終アレンジを1つ選ぶ
第08〜11回で扱った変更から、1つを選びます。
複数の場所を一度に変更すると元へ戻しにくいため、最初は1項目だけ変更します。
| アレンジする場所 | 変更するもの | 例 |
|---|---|---|
| 敵の物理 | enemyGravity、enemyMaxFallSpeed | ゆっくり落下する敵 |
| 敵AI | 待機時間、速度、追跡距離 | 近づくと急に追う敵 |
| タイトル | 文字列、座標、色 | 自分のゲーム名を表示 |
| 操作感 | 加速、摩擦、重力 | 軽快またはゆったりした移動 |
| ジャンプ支援 | コヨーテタイム、入力バッファ | 入力を受け付ける時間を変更 |
| ステージ | CSVのタイルと敵配置 | 敵や足場の位置を変更 |
変更前のファイルを残したい場合は、Visual Studio上でファイル名を変えず、別のフォルダへコピーしておきます。
コードが動かなくなった場合は、配布コードの同じ関数と見比べ、変更した行だけを戻します。
現象、再現手順、期待を分ける
原因が分からないまま別の値を変更すると、何によって動きが変わったのか判断できません。
まず、同じ操作で問題を繰り返せる形にします。
text
現象:
足場の端を離れた直後にジャンプできない。
再現手順:
1. 開始位置から右へ移動する。
2. 足場の右端を歩いて離れる。
3. 離れた直後にZを押す。
期待:
コヨーテタイムが残っていればジャンプする。文章として提出する必要はありません。
どの操作で、何が起きて、どうなってほしいかを自分で区別できれば十分です。
デバッグ表示から原因を絞る
F1を押すと、ゲーム中の値と当たり判定を表示できます。
| 表示 | 確認できること |
|---|---|
x, y | プレイヤーのワールド座標 |
vx, vy | 移動方向と速度 |
onGround | 接地状態 |
game, player | ゲーム状態とプレイヤーのアニメーション状態 |
coyote, buffer | ジャンプ入力猶予の残り時間 |
frame | 表示中のアニメーションフレーム |
| 赤い矩形 | ゲームルールに使う当たり判定 |
| 黄色い矩形 | 画像から切り出している範囲 |
画像の範囲と当たり判定は、目的が違うため完全に一致する必要はありません。
プレイヤーの足元や中心が、意図した位置にそろっているかを確認します。
1か所だけ変更して再確認する
- 問題を同じ操作で再現する。
- F1で関係する値を確認する。
- 配布コードと自分のコードを比較する。
- 関係する値または処理を1か所だけ修正する。
- 同じ操作でもう一度確認する。
- 関連する別の場面も確認する。
| 変更した処理 | 追加で確認する場面 |
|---|---|
| 横移動 | 左右の壁、方向転換、キーを離したあとの停止 |
| 縦移動 | 上昇、頂点、着地、天井 |
| 入力猶予 | 通常ジャンプ、足場端、着地直前、押しっぱなし |
| 敵AI | 待機、巡回、追跡、踏みつけ、落下 |
| カメラ | 開始地点、マップ中央、右端 |
| 状態遷移 | 開始、クリア、失敗、リトライ、タイトル復帰 |
意図的に値を崩して戻す
動かない原因を探す練習として、次のうち1つだけを試します。
確認が終わったら標準値へ戻します。
| 変更 | 起こること | 戻す値 |
|---|---|---|
enemyGravity = 0.0f | 敵が穴へ出ても落下しない | 0.55f |
enemyChaseStartDistance = 300、終了を100 | 距離によって追跡と巡回が切り替わり続ける | 開始180、終了260 |
coyoteFrameLimit = 60 | 足場から離れて長時間ジャンプできる | 6 |
jumpCutMultiplier = 0.0f | キーを離した瞬間に上昇速度がなくなる | 0.5f |
目的は壊れたままにすることではありません。
どの値がどの動きへつながっているかを確認し、自分で標準状態へ戻します。
最初から結果画面まで通して遊ぶ
最終アレンジが動いたら、途中の場面だけでなく、ゲーム全体を順番に確認します。
- エラーなく起動してタイトル画面が表示される。
- タイトルBGMが鳴り、SPACEでプレイを開始できる。
- 左右移動、短いジャンプ、高いジャンプができる。
- 敵が待機、巡回、追跡を切り替える。
- 敵を上から踏める。
- 敵へ横から触れるとゲームオーバーになる。
- ゴールへ触れるとゲームクリアになる。
- Rでプレイヤーと敵が初期位置へ戻る。
- Tでタイトル画面へ戻る。
- 画像、CSV、音声がすべて読み込まれている。
途中で問題が見つかった場合は、最初に失敗した項目から直します。
その後ろの項目を先に直そうとすると、原因となる処理がまだ実行されていない場合があります。
デバッグ表示は残して初期状態を非表示にする
デバッグ表示は、完成後にも修正へ利用できます。
機能を削除せず、起動時だけ非表示にします。
Game.hcpp
bool debugVisible = false;
bool previousF1 = false;DrawPlaying()ではDrawDebug()を残します。debugVisibleがfalseなら、DrawDebug()の先頭で何も描画せずに戻ります。
Game.cppcpp
void Game::DrawPlaying() const
{
stage.Draw(cameraX, images);
DrawEnemies();
DrawPlayer();
DrawHud();
DrawDebug();
}動作確認中はF1で表示し、通常のプレイ画面を見たいときはもう一度F1を押します。
リトライとタイトル復帰を最後に確認する
StartPlay()は、プレイヤーと敵を初期化してからPlayingへ変更します。
Game.cppcpp
void Game::StartPlay()
{
ResetPlay();
ChangeGameState(Playing);
}
void Game::ResetPlay()
{
ResetPlayer();
ResetEnemies();
cameraX = 0;
}結果画面では、リトライとタイトル復帰を別の操作として処理します。
Game.cppcpp
void Game::UpdateResult()
{
if (CheckHitKey(KEY_INPUT_R))
{
StartPlay();
return;
}
if (CheckHitKey(KEY_INPUT_T))
{
ChangeGameState(Title);
}
}gameStateへ直接代入せず、ChangeGameState()を使います。
これにより、画面の状態とBGMが同じタイミングで切り替わります。
Debugビルドで完成状態を確認する
Releaseビルドへ切り替える前に、普段使っているDebug構成で最終確認します。
ビルド > ソリューションのリビルドを実行する。出力ウィンドウで、失敗したプロジェクトが0であることを確認する。- Visual Studioからゲームを起動する。
- 最初からゲームクリアまたはゲームオーバーまで遊ぶ。
- リトライとタイトル復帰を確認する。
ビルドエラーがある状態で、以前作った古い.exeを完成版として使わないでください。
ソースコードを変更したあとは、もう一度ビルドします。
必要に応じてReleaseビルドを作る
Visual Studioを使わずに自分のゲームを起動したい場合や、教室内でほかの人に遊んでもらう場合はReleaseビルドを作ります。
| 構成 | 主な用途 |
|---|---|
Debug | コードを変更しながら動作を確認する |
Release | 完成したゲームを単体で起動する |
- Visual Studio上部の
ソリューション構成をReleaseへ変更する。 ソリューション プラットフォームを授業で使用しているx64またはWin32へ合わせる。ビルド > ソリューションのリビルドを実行する。出力ウィンドウで、失敗したプロジェクトが0であることを確認する。- 出力された
.exeの更新日時を確認する。
Debugではビルドできても、Release側にDXライブラリの設定がない場合はエラーになります。
その場合は独自に設定を増やさず、教員が用意したプロジェクト設定とプラットフォームを確認します。
実行ファイルとAssetsを同じ場所へそろえる
ゲームの.exeだけでは、画像、CSV、音声を読み込めません。
第03回で設定したビルド後イベントによって、出力先へAssetsがコピーされていることを確認します。
ビルド後イベントbat
robocopy "$(ProjectDir)Assets" "$(OutDir)Assets" /MIR
if %ERRORLEVEL% LEQ 7 exit /b 0
exit /b %ERRORLEVEL%単体で起動するフォルダには、少なくとも次のファイルが必要です。
- MyPlatformer
- MyPlatformer.exe
- Assets
- Images
- Maps
- Sounds
- Visual Studioを閉じずに、出力先の
.exeを直接起動する。 - タイトル画面が表示されることを確認する。
- 画像、ステージ、BGM、SEが読み込まれることを確認する。
外部素材を使ったゲームを教室外へ公開する場合は、素材の利用条件を確認し、必要なクレジットを別途用意します。
この授業では、ファイルの提出や公開を必須にはしません。
時間があればほかの人に遊んでもらう
ゲームを完成させたら、別の受講者に操作してもらえます。
発表資料や説明文を作る必要はありません。
最初に、左右移動、ジャンプ、クリア条件だけを短く伝えます。
遊んだ人が迷った場合は、タイトル画面の操作説明やステージ配置を変更する手掛かりになります。
自分では操作しやすくても、別の人には速度が速すぎたり、追跡範囲が広すぎたりすることがあります。
時間があれば数値を1つだけ調整し、もう一度遊んでもらいます。
今回の完成条件
- 第08回の敵の物理挙動と落下無効化が動く。
- 第09回の敵AIが待機、巡回、追跡、やられを切り替える。
- 第10回のタイトル、プレイ、クリア、ゲームオーバーを行き来できる。
- 第11回の可変ジャンプ、コヨーテタイム、入力バッファが動く。
- 自分で選んだ項目を1つ以上変更できる。
- 変更後も最初から結果画面まで遊べる。
- リトライするとプレイヤーとすべての敵が初期状態へ戻る。
- F1でデバッグ表示を表示・非表示にできる。
- 必要な場合は、Releaseの
.exeから画像、CSV、音声を読み込んで起動できる。
よくある問題
自分が変更した場所が分からなくなった
配布された第12回のGame.hとGame.cppを横に開きます。
自分のファイルと見比べ、異なる行を1つずつ確認します。
すべてを最初から入力し直す必要はありません。
変更した関数または定数だけを配布コードへ戻します。
Debugでは動くがReleaseではビルドできない
Release側のプラットフォームが、授業で使用しているDXライブラリの設定と一致しているか確認します。x64とWin32を自己判断で切り替えず、教員が指定した構成を使用します。
.exeを直接起動すると画像や音が出ない
.exeと同じ場所にAssets/Images、Assets/Maps、Assets/Soundsがあるか確認します。
ビルド後イベントが失敗している場合は、Visual Studioの出力ウィンドウにrobocopyの結果が表示されます。
最終アレンジ後にリトライできない
StartPlay()からResetPlay()とChangeGameState(Playing)を呼んでいるか確認します。ResetPlay()ではResetPlayer()、ResetEnemies()、カメラ位置の初期化を行います。
全12回で作ったもの
- ゲームループの入力、更新、判定、描画を確認した。
- 左右移動、ジャンプ、重力、タイル衝突を実装した。
- 画像範囲と当たり判定を分け、足元を基準に描画した。
- 画像の読み込みとサイズを
ImageManagerへまとめた。 - CSVからタイルマップと複数の敵を配置した。
- スプライトシートからプレイヤーのアニメーションを表示した。
- BGMとSEを、ゲーム中の出来事に合わせて再生した。
- 敵へ重力、地形衝突、落下無効化を追加した。
- 敵の待機、巡回、追跡、やられを状態として切り替えた。
- タイトル、プレイ、クリア、ゲームオーバーの状態変更を整理した。
- 可変ジャンプ、コヨーテタイム、入力バッファを追加した。
- デバッグ表示を使い、数値と画面上の動きを対応させた。
- 配布コードの数値、文字列、条件、CSVを変更し、自分なりのゲームへ調整した。
まとめ
- 第08〜11回で追加した機能を、最初から結果画面まで通して確認しました。
- 最終アレンジを1つ選び、配布コードを自分なりに変更しました。
- 現象、再現手順、期待する結果を分けて、不具合の場所を絞りました。
- デバッグ表示と配布コードを使い、動かない状態から復帰しました。
- Debugビルドで完成状態を確認し、必要に応じてReleaseビルドを作りました。
- 実行ファイルと
Assetsを同じ場所へそろえ、単体で起動できる形を確認しました。 - 全12回で作ったコードを、今後の自由制作で変更できる土台として完成させました。