プロジェクト演習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(); 配列を作りかえる時には必ず↑の 処理を呼んでおくべし。 でないと以前に作っていた要素が まるっとメモリリークする。 ここまでの要素で出来るはず • 後はどこまで理解できていて、 組み合わせられるかの問題 • やってみよう!
© Copyright 2024 ExpyDoc