プロジェクト演習 インタラクティブゲーム制作

プロジェクト演習III,V
<インタラクティブ・ゲーム制作>
プログラミングコース
第5回
ファイル読み込みとクラスの応用
~マップデータを作ろう~
今日の内容
• 具体的な目的を掲げて、それを達成する
ために必要な材料の使い方、組み合わせ
方を学ぶ
• ファイルからマップデータを読み込んで、
それに従ってプログラムで地形を生成!
– ファイル入出力
– vector配列
– newとdeleteの実践
ファイル入出力の重要性
• もうパラメータとかをプログラム中で
ゴリゴリ代入するのはまっぴらだ!
– 「ハードコーディング」と言われて、
現場では忌み嫌われる行為
– ちょっとマップを手直しするのにもビルドが
必要=Visual Studioが必要=マップも自分で
作らなくてはいけなくなる=あばばばばばb
読み書きのターゲットは
• とりあえずテキストファイル
– 特にCSVやタブ区切りテキストは扱えると
超便利
• マップデータ、キャラクタパラメータ、
シナリオデータ、セーブデータなどなど
• 「テキストファイルとか見られたら
恥ずかしいし…」とか言うのは後だ!
とりあえず使うもの
• C++の標準機能だけでやってみよう
– 読み込みは「ifstream」
– 書き込みは「ofstream」
• インクルードは以下の通り
– #include <iostream>
– #include <fstream>
– 両方必要なので気をつける
• 必要に応じてusing namespace std;も
書き込みは絶望的に簡単だ!
• インスタンス生成時
にファイル名指定
• is_open()で開けてい
るかチェック
• <<で書き込むデータ
を繋げて流し込む
• 最後はclose()
ofstream out_file(“ファイル名”);
if(out_file.is_open() == false) エラー処理;
out_file << “書き込みたい文字” << endl;
// int値やdouble値も<<で繋いで書ける
// もちろんstring型やchar型もOK
out_file << x << “,” << y << endl;
//書き込み終わったらcloseして終了
out_file.close();
読み込みも
ただ読み込むだけなら簡単だ!
• インスタンス生成時
にファイル名指定
• is_open()で開けて
いるかチェック
• getlineで1行ずつ
取り出し、必要に
応じて処理する
• 最後はclose()
ifstream
string
vector<string>
in_file(“ファイル名”);
lineStr;
readBuffer;
if(in_file.is_open() == false) エラー処理;
// 1行ずつwhileループで読み出す
while(getline(in_file, lineStr) == true) {
// lineStrに1行分入る
// とりあえず配列にしまうならこう
readBuffer.push_back(lineStr);
}
in_file.close();
結局何が面倒って
• 読み込んだ後の文字列処理なんです
• C++のstringクラスはJavaに比べると
機能が正直貧弱です
• 最低限のものは揃っているので、うまく
素材を組み合わせて関数を作ろう
– サンプルあります
• データを取り出しやすいファイル構造を
作ろう
区切り取り出し関数実装例
vector<string> fk_StrSplit(string argStr, string argToken)
{
vector<string>
retStrArray;
string::size_type curPos = 0, nextPos = 0;
while(nextPos != string::npos) {
nextPos = argStr.find(argToken, curPos);
retStrArray.push_back(argStr.substr(curPos, nextPoscurPos));
curPos = nextPos+argToken.size();
}
return retStrArray;
}
区切って取り出す
• fk_StrSplit()関数
– JavaのStringクラスのsplit()のようなもの
– 正規表現は使えないけどカンマ区切り、
タブ区切り程度なら対応可能
• カンマ区切り、タブ区切りはExcelでの
編集が容易なので、出来ておいて損は
ない
– マップやシナリオのエディタをExcelで
代用しているソフトハウスもあります
整数値・実数値変換
• Cの標準関数をなんだかんだで使う
– atoi()がInteger.parseInt()にあたる
– atof()がDouble.parseDouble()にあたる
• ただしstring型の変数は直接引数に
渡せないので、c_str()関数を使う
– atoi(anyStr.c_str())のようにする
• 実数値は誤差に厳しいものだと変換時に
値がズレることがあるので注意
ファイル形式の作り方のコツ
• FK Performerのモーションデータなどを
見てみるとよい
• 「ファイルヘッダ」は必ず付ける
– ファイルの先頭でファイルの種類が
識別できるようにする
• 階層構造を持つデータを扱う場合
– [BEGIN]~[END]で囲っておくと、
データを読み込むべき範囲が
分かりやすくなるので処理しやすい
Worldクラスを
ファイル読み込み対応にしてみる
• buildModel[6]というメンバ変数に注目
– 配列だけど、6個固定になってしまっている
– ここが状況に応じて変動したら幸せなんじゃ
なかろうか?
• makeBuild()という関数に注目
– init()内で何度もコールして建物を生成
– この処理をファイルから読み込んだ内容に
置き換えられたら幸せなんじゃなかろうか?
データ形式を仮決めしよう
• 次のようなテキストファイルを作ること
にする
– 最初の行に[MY MAP DATA]と表記する
– 次の行に建物の個数を記述する
– 3行目以降から、
1行で建物1個分を表すことにする
• X座標,Z座標,高さ,色指定(とりあえず英語で)
データの例
[MY MAP DATA]
6
-250.0, 100.0, 5.0,Red
-150.0, 400.0, 2.0,DimYellow
50.0, 250.0, 4.0,Blue
300.0, 200.0, 3.0,Gray
250.0, -250.0, 0.5,Green
-50.0, -350.0, 6.0,Orange
個数に応じて配列を作りなおす
// 配列の中身の準備
• ただの配列から、
for(int i = 0; i < num; ++i) {
vector<fk_Model *>
vArray.push_back(new
fk_Model());
に変更
}
• ポインタの配列なので、
用意する時にnewし、 // こう書いても同じ意味
片付ける時はdeleteが vArray.resize(num);
for(int i = 0; i < num; ++i) {
必要!
vArray[i] = new fk_Model();
– intやdoubleや
fk_Vectorの場合は
ここまで面倒ではない
}
お片付けもセットで
• よく使うvector配列の
メンバ関数
– resize()
• 配列のサイズ変更
– size()
• 今のサイズを得る
– push_back()
• 配列のお尻に1部屋足す
– back()
• 配列のお尻を参照する
– at()
• [i]のかわりに.at(i)とする
– clear()
• resize(0)とほぼ一緒
// お片付けのパターン
int len = vArray.size();
for(int i = 0; i < len; ++i) {
delete vArray[i];
}
vArray.clear();
配列を作りかえる時には必ず↑の
処理を呼んでおくべし。
でないと以前に作っていた要素が
まるっとメモリリークする。
ここまでの要素で出来るはず
• 後はどこまで理解できていて、
組み合わせられるかの問題
• やってみよう!