230 likes | 341 Views
インタラクティブ・ゲーム制作 < プログラミングコース>. 第 8 回 ファイル 入出力と アルゴリズム. 今日の内容. やるや る言いつつできなかった ファイル入出力 本質で は ない けど見た目もこだわる 石 のアニメーション アルゴリズムをガチで考える 置ける 場所とひっくり返し判定. こ、今度こそ!. ファイル入出力. ファイル 入出力の重要性. セーブ/ロードをしたいなら必須処理 ソース 中 にパラメータを直書きするのは 「 ハードコーディング」と言われて、 現場では忌み嫌われる 行為
E N D
インタラクティブ・ゲーム制作<プログラミングコース>インタラクティブ・ゲーム制作<プログラミングコース> 第8回 ファイル入出力とアルゴリズム
今日の内容 • やるやる言いつつできなかった • ファイル入出力 • 本質ではないけど見た目もこだわる • 石のアニメーション • アルゴリズムをガチで考える • 置ける場所とひっくり返し判定
こ、今度こそ! ファイル入出力
ファイル入出力の重要性 • セーブ/ロードをしたいなら必須処理 • ソース中にパラメータを直書きするのは「ハードコーディング」と言われて、現場では忌み嫌われる行為 • ちょっとマップを手直しするのにもビルドが必要=Visual Studioが必要=マップも自分で作らなくてはいけなくなる=あばばばばばb
読み書きのターゲットは • とりあえずテキストファイル • 特にCSVやタブ区切りテキストは扱えると超便利 • マップデータ、キャラクタパラメータ、シナリオデータ、セーブデータなどなど • 「テキストファイルとか見られたら 恥ずかしいし…」とか言うのは後だ!
とりあえず使うもの • C++の標準機能だけでやってみよう • 読み込みは「ifstream」クラス • 書き込みは「ofstream」クラス • インクルードは以下の通り • #include <iostream> • #include <fstream> • 両方必要なので気をつける • 必要に応じてusing namespace std;も
書き込みは絶望的に簡単だ! • インスタンス生成時にファイル名指定 • is_open()で開けているかチェック • <<で書き込むデータを繋げて流し込む • 最後はclose() ofstreamout_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() ifstreamin_file(“ファイル名”); string lineStr; vector<string> 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クラスは基本的な機能しかないので、ちょっと高度なことをやろうとすると、自前で頑張るか、boostなどに頼ることになります。 • 最低限の処理を使えるようになり、データを取り出しやすいファイル構造を作ろう
区切り取り出し関数実装例 vector<string> fk_StrSplit(string argStr, string argToken) { vector<string> retStrArray; string::size_typecurPos = 0, nextPos = 0; while(nextPos != string::npos) { nextPos = argStr.find(argToken, curPos); retStrArray.push_back(argStr.substr(curPos, nextPos-curPos)); curPos = nextPos+argToken.size(); } return retStrArray; }
区切って取り出す • fk_StrSplit()関数 • 第1引数の文字列を、第2引数の文字で区切り、バラした結果を返す • vector<string>型で受け取る • FKUT/Misc.hで定義してあるので、利用する場合はこれをインクルードする
整数値・実数値変換 • Cの標準関数をなんだかんだで使う • atoi()が整数変換にあたる • atof()が実数変換にあたる • ただしstring型の変数は直接引数に渡せないので、c_str()関数を使う • atoi(anyStr.c_str())のようにする • 実数値は誤差に厳しいものだと変換時に値がズレることがあるので注意
データ形式を仮決めしよう • 次のようなテキストファイルを作ることにする • 最初の行に[MY BOARD DATA]と表記する • 次の行に石の個数を記述する • 3行目以降から、1行で石1個分を表すことにする • X座標, Z座標, 色指定(とりあえず英語で)
データの例 [BOARD DATA] 4 -5.0, -5.0, Black 5.0, -5.0, White 5.0, 5.0, Black -5.0, 5.0, White
細かいところだけど重要な 石のアニメーションで使っているテクニック
ポイントになるもの • update()関数の利用 • 毎フレーム呼び出して動きを更新する • アニメーションの進行状態が分かるようにメンバ変数を持たせておく • 三角関数おいしい! • 一番好きな関数です! • 時間経過に応じて0~1の値を得るのに便利
動きを付ける時によくやるやり方 • 線形補間 P = (1 - t)*(startPos) + t*(endPos) • 半分半分また半分 P = 0.5*(nowPos) + 0.5*(endPos) • 色々な数式や関数の特徴を覚えておくと、色んな場面で役立ちます!
本日のメインディッシュ 置ける場所とひっくり返し判定
置ける場所の定義(1) • 置くことでひっくり返せること • そのためには、置く場所の周囲8マスに相手の石が置かれているのが前提条件になる • 初手で黒が置けるのは、前提条件のみだと右の赤いセル
あるセルにおける周囲の情報を得るには • cellInfo[x][y]の周囲8セル • [x-1][y-1], [x][y-1], [x+1][y-1] • [x-1][y], [x+1][y] • [x-1][y+1], [x][y+1], [x+1][y+1] • 範囲外(0未満、8以上)の場合は対象外 • これをこのままコーディングするとださいので、座標のペアを配列にしまってループで処理できるとクール
置ける場所の定義(2) • 相手の石があった方向の先に、自分の石があること • 端に到達した場合はその時点でNG • 隣接する相手の石が複数存在した場合は、それぞれの方向で判定 • 初手の場合青いセルが最終的にOKになる
実装方針 • 位置と方向を与えて、その方向を調べる関数を作る • 先ほどリストアップした隣接した相手の石リストを関数に流し込んでいく • あとは実演!