Skip to content

第12回 最終アレンジ・デバッグと完成

前回: 第11回 操作感の設計と改善

今回の授業内容

  • 第08〜11回で変更した敵AI、状態遷移、操作感を通して確認する。
  • 自分が変更したい項目を1つ選び、最終アレンジを加える。
  • 現象、再現手順、期待する結果を分けて、不具合の原因を絞る。
  • F1のデバッグ表示を使い、変更した値とゲーム画面を対応させる。
  • Debugビルドで完成状態を確認したあと、必要に応じてReleaseビルドを作る。
  • 自分のゲームを最初から結果画面まで遊び、時間があればほかの受講者にも遊んでもらう。

完成版の配布コード

第11回までに追加したアニメーション、複数敵、状態管理、音響、操作支援をまとめた完成版です。
自分のコードが動かなくなった場合は、完成版の同じ関数と比較して復帰できます。

第12回のコード一式をダウンロード

配布ファイル一覧
ファイル内容
Source/ImageManager.h画像識別子と画像管理クラス
Source/ImageManager.cpp画像の一括読み込み、サイズ取得、解放
Source/Game.hゲーム全体の宣言
Source/Game.cppゲーム全体の実装
Source/Stage.hステージの宣言
Source/Stage.cppCSVマップ、画像、スクロール
Source/Common.h共通の型と関数
Source/main.cppDXライブラリの初期化
Source/SoundManager.h音声IDと音響管理
Source/SoundManager.cppBGMと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回の配布コードを一度起動します。
この動作を、変更後に戻れる基準にします。

  1. タイトル画面からプレイを開始する。
  2. 左右移動、短いジャンプ、高いジャンプを試す。
  3. 敵へ近づき、巡回から追跡へ変わることを確認する。
  4. 敵を上から踏み、横から触れた場合との違いを確認する。
  5. ゲームクリアまたはゲームオーバーへ進む。
  6. リトライとタイトル復帰を確認する。

この時点で動かない場合は、最終アレンジを始めず、配布されたGame.hGame.cppへ戻します。

最終アレンジを1つ選ぶ

第08〜11回で扱った変更から、1つを選びます。
複数の場所を一度に変更すると元へ戻しにくいため、最初は1項目だけ変更します。

アレンジする場所変更するもの
敵の物理enemyGravityenemyMaxFallSpeedゆっくり落下する敵
敵AI待機時間、速度、追跡距離近づくと急に追う敵
タイトル文字列、座標、色自分のゲーム名を表示
操作感加速、摩擦、重力軽快またはゆったりした移動
ジャンプ支援コヨーテタイム、入力バッファ入力を受け付ける時間を変更
ステージCSVのタイルと敵配置敵や足場の位置を変更

変更前のファイルを残したい場合は、Visual Studio上でファイル名を変えず、別のフォルダへコピーしておきます。
コードが動かなくなった場合は、配布コードの同じ関数と見比べ、変更した行だけを戻します。

現象、再現手順、期待を分ける

原因が分からないまま別の値を変更すると、何によって動きが変わったのか判断できません。
まず、同じ操作で問題を繰り返せる形にします。

text
現象:
足場の端を離れた直後にジャンプできない。

再現手順:
1. 開始位置から右へ移動する。
2. 足場の右端を歩いて離れる。
3. 離れた直後にZを押す。

期待:
コヨーテタイムが残っていればジャンプする。

文章として提出する必要はありません。
どの操作で、何が起きて、どうなってほしいかを自分で区別できれば十分です。

デバッグ表示から原因を絞る

F1を押すと、ゲーム中の値と当たり判定を表示できます。

表示確認できること
x, yプレイヤーのワールド座標
vx, vy移動方向と速度
onGround接地状態
game, playerゲーム状態とプレイヤーのアニメーション状態
coyote, bufferジャンプ入力猶予の残り時間
frame表示中のアニメーションフレーム
赤い矩形ゲームルールに使う当たり判定
黄色い矩形画像から切り出している範囲

画像の範囲と当たり判定は、目的が違うため完全に一致する必要はありません。
プレイヤーの足元や中心が、意図した位置にそろっているかを確認します。

1か所だけ変更して再確認する

  1. 問題を同じ操作で再現する。
  2. F1で関係する値を確認する。
  3. 配布コードと自分のコードを比較する。
  4. 関係する値または処理を1か所だけ修正する。
  5. 同じ操作でもう一度確認する。
  6. 関連する別の場面も確認する。
変更した処理追加で確認する場面
横移動左右の壁、方向転換、キーを離したあとの停止
縦移動上昇、頂点、着地、天井
入力猶予通常ジャンプ、足場端、着地直前、押しっぱなし
敵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()を残します。
debugVisiblefalseなら、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構成で最終確認します。

  1. ビルド > ソリューションのリビルドを実行する。
  2. 出力ウィンドウで、失敗したプロジェクトが0であることを確認する。
  3. Visual Studioからゲームを起動する。
  4. 最初からゲームクリアまたはゲームオーバーまで遊ぶ。
  5. リトライとタイトル復帰を確認する。

ビルドエラーがある状態で、以前作った古い.exeを完成版として使わないでください。
ソースコードを変更したあとは、もう一度ビルドします。

必要に応じてReleaseビルドを作る

Visual Studioを使わずに自分のゲームを起動したい場合や、教室内でほかの人に遊んでもらう場合はReleaseビルドを作ります。

構成主な用途
Debugコードを変更しながら動作を確認する
Release完成したゲームを単体で起動する
  1. Visual Studio上部のソリューション構成Releaseへ変更する。
  2. ソリューション プラットフォームを授業で使用しているx64またはWin32へ合わせる。
  3. ビルド > ソリューションのリビルドを実行する。
  4. 出力ウィンドウで、失敗したプロジェクトが0であることを確認する。
  5. 出力された.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
  1. Visual Studioを閉じずに、出力先の.exeを直接起動する。
  2. タイトル画面が表示されることを確認する。
  3. 画像、ステージ、BGM、SEが読み込まれることを確認する。

外部素材を使ったゲームを教室外へ公開する場合は、素材の利用条件を確認し、必要なクレジットを別途用意します。
この授業では、ファイルの提出や公開を必須にはしません。

時間があればほかの人に遊んでもらう

ゲームを完成させたら、別の受講者に操作してもらえます。
発表資料や説明文を作る必要はありません。

最初に、左右移動、ジャンプ、クリア条件だけを短く伝えます。
遊んだ人が迷った場合は、タイトル画面の操作説明やステージ配置を変更する手掛かりになります。

自分では操作しやすくても、別の人には速度が速すぎたり、追跡範囲が広すぎたりすることがあります。
時間があれば数値を1つだけ調整し、もう一度遊んでもらいます。

今回の完成条件

  • 第08回の敵の物理挙動と落下無効化が動く。
  • 第09回の敵AIが待機、巡回、追跡、やられを切り替える。
  • 第10回のタイトル、プレイ、クリア、ゲームオーバーを行き来できる。
  • 第11回の可変ジャンプ、コヨーテタイム、入力バッファが動く。
  • 自分で選んだ項目を1つ以上変更できる。
  • 変更後も最初から結果画面まで遊べる。
  • リトライするとプレイヤーとすべての敵が初期状態へ戻る。
  • F1でデバッグ表示を表示・非表示にできる。
  • 必要な場合は、Releaseの.exeから画像、CSV、音声を読み込んで起動できる。

よくある問題

自分が変更した場所が分からなくなった

配布された第12回のGame.hGame.cppを横に開きます。
自分のファイルと見比べ、異なる行を1つずつ確認します。

すべてを最初から入力し直す必要はありません。
変更した関数または定数だけを配布コードへ戻します。

Debugでは動くがReleaseではビルドできない

Release側のプラットフォームが、授業で使用しているDXライブラリの設定と一致しているか確認します。
x64Win32を自己判断で切り替えず、教員が指定した構成を使用します。

.exeを直接起動すると画像や音が出ない

.exeと同じ場所にAssets/ImagesAssets/MapsAssets/Soundsがあるか確認します。
ビルド後イベントが失敗している場合は、Visual Studioの出力ウィンドウにrobocopyの結果が表示されます。

最終アレンジ後にリトライできない

StartPlay()からResetPlay()ChangeGameState(Playing)を呼んでいるか確認します。
ResetPlay()ではResetPlayer()ResetEnemies()、カメラ位置の初期化を行います。

全12回で作ったもの

  • ゲームループの入力、更新、判定、描画を確認した。
  • 左右移動、ジャンプ、重力、タイル衝突を実装した。
  • 画像範囲と当たり判定を分け、足元を基準に描画した。
  • 画像の読み込みとサイズをImageManagerへまとめた。
  • CSVからタイルマップと複数の敵を配置した。
  • スプライトシートからプレイヤーのアニメーションを表示した。
  • BGMとSEを、ゲーム中の出来事に合わせて再生した。
  • 敵へ重力、地形衝突、落下無効化を追加した。
  • 敵の待機、巡回、追跡、やられを状態として切り替えた。
  • タイトル、プレイ、クリア、ゲームオーバーの状態変更を整理した。
  • 可変ジャンプ、コヨーテタイム、入力バッファを追加した。
  • デバッグ表示を使い、数値と画面上の動きを対応させた。
  • 配布コードの数値、文字列、条件、CSVを変更し、自分なりのゲームへ調整した。

まとめ

  • 第08〜11回で追加した機能を、最初から結果画面まで通して確認しました。
  • 最終アレンジを1つ選び、配布コードを自分なりに変更しました。
  • 現象、再現手順、期待する結果を分けて、不具合の場所を絞りました。
  • デバッグ表示と配布コードを使い、動かない状態から復帰しました。
  • Debugビルドで完成状態を確認し、必要に応じてReleaseビルドを作りました。
  • 実行ファイルとAssetsを同じ場所へそろえ、単体で起動できる形を確認しました。
  • 全12回で作ったコードを、今後の自由制作で変更できる土台として完成させました。