Appearance
第04回 マップデータの外部化(CSV読込)とスクロール
前回: 第03回 画像表示の導入とエラー確認 / 次回: 第05回 物理パラメーター調整と操作感の改善
前回の振り返り
画像描画と読み込みエラー確認を導入しました
LoadGraphで画像を読み込みました- 画像ハンドルが
-1の場合は、エラーを表示して初期化失敗にしました - プレイヤー、タイル、ゴールの見た目を画像に置き換えました
- 必須画像が揃っている状態で、移動や当たり判定が同じように動きました
- 今回は、ステージの形をコード外のCSVから読み込み、横スクロール表示も導入します
今回の目的
コードを書き換えずにマップを変更できるようにする
- CSVファイルから地形を読み込む
0、1、2の値をステージへ反映する- 読み込み失敗時はエラーを表示して停止する
- CSVを書き換えて地形とゴール位置を変更する
cameraXを導入して、ワールド座標を画面座標へ変換する
今回の授業内容
マップデータの外部化(CSV読込)とスクロール
- CSVマップの仕様
fstream、string、stringstreamの使い方Stage::LoadFromCsvの実装- 読み込み失敗時のエラー処理
- CSV編集による地形変更
- 読み込み確認とエラー時の切り分け
- ワールド座標と画面座標の分離
- カメラ追従と端クランプの基本
マップを外へ出す
コードを直さず ステージを作ります
データを変えれば 地形が変わる
CSV仕様
授業では最小ルールに固定する
- カンマ区切りのテキストファイルとして扱う
0: 空白1: 固形床2: ゴール- 行数は
mapHeight、列数はmapWidthを上限にする - ゴール
2が1つも無い場合は読み込み失敗として扱う
CSVファイル例
Assets/Maps/stage01.csv
- 1行が横方向のタイル列です
- 上から順に
y = 0、y = 1として読みます 2はゴール位置として扱い、床にはしません
csv
0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,2
0,0,0,0,0,0,0,1,1,1
0,0,0,0,1,1,0,0,0,0
1,1,1,1,1,1,1,1,1,1Stage.hの変更
CSV読込用の関数を追加する
InitializeにCSVパスを渡せるようにしますLoadFromCsvは成功したらtrue、失敗したらfalseを返します- 失敗時はエラーを表示し、初期化を止めます
cpp
class Stage
{
public:
bool Initialize(const char* csvPath);
bool LoadFromCsv(const char* csvPath);
bool IsLoadedFromCsv() const;
private:
bool LoadImages();
void ClearMap();
void SetGoalTile(int tileX, int tileY);
int tiles[mapHeight][mapWidth] = {};
Rect goal = { 0, 0, tileSize, tileSize };
bool loadedFromCsv = false;
};Initializeの変更
読めなければ初期化失敗にする
LoadFromCsvの戻り値をloadedFromCsvに保存します- 読み込みに失敗した場合は、エラーを表示して
falseを返します - CSVはステージそのものなので、読めないままゲームを開始しません
cpp
bool Stage::Initialize(const char* csvPath)
{
if (!LoadImages()) return false;
loadedFromCsv = LoadFromCsv(csvPath);
if (!loadedFromCsv)
{
MessageBoxW(nullptr,
L"Assets/Maps/stage01.csv を読み込めません。",
L"Map Load Error", MB_OK);
return false;
}
return true;
}
bool Stage::IsLoadedFromCsv() const
{
return loadedFromCsv;
}CSV読込コード(1/2)
ファイルを開いて1行ずつ読む
<fstream>はファイル読み込みに使います<sstream>は1行をカンマ区切りに分解するために使います- ファイルが開けない場合は
falseを返します
cpp
#include <fstream>
#include <sstream>
#include <string>
bool Stage::LoadFromCsv(const char* csvPath)
{
std::ifstream file(csvPath);
if (!file.is_open()) return false;
ClearMap();
bool foundGoal = false;
std::string line;
int y = 0;
while (y < mapHeight && std::getline(file, line))
{
std::stringstream lineStream(line);
std::string cell;
int x = 0;
// 次のスライドの処理でcellを数値に変換する
y++;
}
return foundGoal;
}CSV読込コード(2/2)
値をタイルとゴールへ反映する
std::getline(lineStream, cell, ',')でカンマ区切りの値を取り出しますstd::stoiで文字列を数値に変換します- 変換できない値は
0として扱います
cpp
while (x < mapWidth && std::getline(lineStream, cell, ','))
{
int value = 0;
try { value = std::stoi(cell); }
catch (...) { value = 0; }
if (value == 1)
{
tiles[y][x] = 1;
}
else if (value == 2)
{
tiles[y][x] = 0;
if (!foundGoal)
{
SetGoalTile(x, y);
foundGoal = true;
}
}
x++;
}ClearMapとSetGoalTile
読み込み前に初期化する
ClearMapは、すべてのタイルを空白に戻しますSetGoalTileは、タイル座標をピクセル座標へ変換します- CSV読込前に初期化しておくと、前のデータが残りません
cpp
void Stage::ClearMap()
{
for (int y = 0; y < mapHeight; y++)
{
for (int x = 0; x < mapWidth; x++)
{
tiles[y][x] = 0;
}
}
goal = { (mapWidth - 2) * tileSize,
(mapHeight - 3) * tileSize, tileSize, tileSize };
}
void Stage::SetGoalTile(int tileX, int tileY)
{
goal.x = tileX * tileSize;
goal.y = tileY * tileSize;
goal.width = tileSize;
goal.height = tileSize;
}読み込み失敗を通知する
必須データが無い状態で進めない
- CSVはステージ本体なので、読めない場合はエラーにします
- エラー文には、足りないファイル名を含めます
- エラーを確認したら、ファイル配置とパスを直して再実行します
cpp
if (!loadedFromCsv)
{
MessageBoxW(nullptr,
L"Assets/Maps/stage01.csv を読み込めません。",
L"Map Load Error", MB_OK);
return false;
}Game側からCSVを指定する
初期化時にパスを渡す
- CSVファイルの場所を
Stageに渡します - 完成サンプルでは
Assets/Maps/stage01.csvを使います - パスが違う場合は、エラーを表示してゲームを開始しません
cpp
bool Game::Initialize()
{
if (!LoadImages()) return false;
if (!stage.Initialize("Assets/Maps/stage01.csv")) return false;
ResetPlay();
gameState = Title;
return true;
}コードの解説
データを読み、ゲーム内の構造へ変換する
- CSVファイルを開く
- 1行ずつ文字列として読む
- カンマで分割する
- 文字列を数値へ変換する
1は床としてtilesに入れる2はゴール位置としてgoalに反映する- 失敗時はエラーを表示して初期化を止める
実習1: CSVを読み込む
まずは短い検証マップで確認する
Assets/Maps/stage01.csvを作成するStage::LoadFromCsvを実装するStage::Initialize("Assets/Maps/stage01.csv")を呼ぶ- CSVの
1の位置を変えて床が変わるか確認する - CSVの
2の位置を変えてゴールが変わるか確認する
実習2: エラー時の動作を確認する
失敗時に止まることを確認する
- CSVファイル名を一時的に変える
- パスを間違えた状態で起動する
- エラー表示でゲームが開始されないことを確認する
- デバッグ表示や画面表示で、CSV読込成功かどうかを確認する
- 確認後、正しいファイル名に戻す
よくあるつまずき
CSVはテキストとして扱う
- Excelで保存した形式がCSVではない
- カンマではなく全角カンマが入っている
- 行数や列数が想定と大きく違う
- 実行フォルダから見た
Assets/Maps/stage01.csvが存在しない - ゴール値
2が入っておらず、読み込み成功扱いにならない
今日の最低到達
コード編集なしでマップを変える
- CSVから床タイルを読み込める
- CSVからゴール位置を読み込める
- CSV変更がゲーム画面に反映される
- CSVが読めない場合はエラーを表示して停止できる
0、1、2の意味を説明できる
今回のまとめ
マップをCSVファイルから読み込みました
- ステージの形をコード外のデータとして扱いました
fstreamとstringstreamでCSVを読みました- 読込失敗時にエラーを表示して停止する処理を入れました
- CSV編集だけで、地形とゴール位置を変更できるようにしました
- 次回は、操作感を数値で調整します