プロジェクト演習Ⅱ インタラクティブゲーム制作 イントロダクション2 第11回 プログラミングサプリ グラフィック素材編
今日の内容 2次元画像の扱い方 3次元モデルデータの扱い方 関数(メソッド)の扱い方 画像を読み込んで表示 文字表示 関数の作り方・使い方 値の渡し方・受け取り方
今週のプロジェクト 授業資料ページからダウンロードします 今週のFKUT更新情報 落としたZipファイルを解凍して、 出てきたフォルダを好きなところに配置 今週のFKUT更新情報 ゲームパッド対応を強化 fk_Sceneクラスのオブジェクトを使い分けることで画面切り替えが容易に
やっぱり2次元もいいよね! 画像素材との付き合い方
画像データとは 言うまでもないですが、 色のついた点の集まり FKで扱える画像形式 ビットマップ(BMP) PNG JPEG(JPG) きれいだがでかい PNG きれいでコンパクト 透過処理もできる JPEG(JPG) きちゃないけど超コンパクト
表示するには fkut_SpriteModel 詳しくはお品書きを読んでね 色々お節介焼きな便利変数です 画像の表示を絶望的に簡単にできます FKUT/SpriteModel.h を参照 できることが書いてあります それに加えてModel系の命令も使えるよ
基本手順 fkut_SpriteModel型の変数を作る 表示させたい画像を読み込む ウィンドウにエントリーする fkut_SpriteModel spr; 表示させたい画像を読み込む spr.readPNG(“hoehoe.png”); readBMP/PNG/JPGを形式に合わせて使う ウィンドウにエントリーする window.entry(spr); 先にカメラの設定を済ませておくこと カメラモデルを変更したら再エントリー
位置やサイズの調整 glMoveTo()やglTranslate()などが利用可能 画像の中心点がどこにくるかを指定 ただし、有効なのはX,Y座標値のみ 座標の軸の取り方にも注意(次スライド参照) setPositionLT()で、画像の左上がどこにくるか基準での位置指定が可能 表示サイズは基本的に画像自体のサイズが そのまま適用される 変更したい場合はsetSpriteSize()を使う
座標系の注意 ウィンドウが800x600の場合 (-400,300) (0,0) (400,-300)
文字表示の手順 文字データ(フォント)を読み込む 表示させたい文字をdrawText()で指定 数値の表示についてはサンプル参照 initFont()を使う Vista,7の場合は「メイリオ」をおすすめ C:/Windows/fonts/Meiryo.ttc 表示させたい文字をdrawText()で指定 drawText()を呼ぶたびに文字が付け足される 改行したい場合は”\n”と書く 後で書き直すこともできる 再度drawText()を呼んだり、clearText()で消したり 数値の表示についてはサンプル参照 色の変更や細かい装飾はWeb上の資料を参照
3次元空間中に表示したい場合 SpriteModelは画面上に貼り付ける専門の変数なので、別の変数を用意する サンプル中に「背景に画像を敷く」 コードがあるので、それを参考に エリアコメントを解除してみよう
単なる手順の話でしかないですが 形状モデルデータの扱い方
FKにおける「形状」と「モデル」 ポリゴンがどんな形をしているのか、 画像がどう張り込まれているのか、を FKでは「形状」と呼ぶ 球、ブロック、モデリングデータは「形状」 「形状」が乗っかった台座に相当して、 位置や向きを保持するのが「モデル」 「形状」を「モデル」にセットして使う SphereModelやBlockModelはこれらを ミックスさせていたもの
MQOやXを単純に読む場合 ifsに対してテクスチャ画像を読み込む ifsに対して形状データを読み込む fk_IFSTextureクラスのオブジェクトを用意 ifsとする ifsに対してテクスチャ画像を読み込む 画像形式に応じてreadXXX関数を使う ifsに対して形状データを読み込む readMQO()かreadD3DX()を使う ModelのオブジェクトにsetShape(&ifs) さらにモデルにマテリアルを設定し、 ウィンドウにエントリーしてようやく表示
モーションを手軽に付けた モデルを扱いたい場合 FK Performerを使ってみる MQO形式のデータに対応 作ったモデルとモーションを fkut_Performerクラスで扱う 形状とモデルとモーションをセットにした クラス 詳細はサンプル参照
当たり判定は球かブロックで近似 MQOやXを読み込んだ場合でも、 FK Performerを利用する場合でも、 当たり判定用にBlockModelかSphereModelを用意しておく create()はするが、entry()はしない 形状をセットしたモデルを親子関係にして、移動処理などは当たり判定モデルをベースに行う
使えてる人、そうでない人いるでしょうが 関数を使ってスマートに書こう
関数とは 以下の3つの特徴を持ちます。 いくつかの処理を1つのカタマリにしておき、 必要に応じて呼び出せる。呼び出した処理が終わったら元の場所に戻ってくる。 呼び出す際に必要な値を引き渡せる。 要らなければ渡さなくてもいい。 これを利用すると大幅に処理が効率化できる。 戻ってくる際に計算結果を返すことができる。要らなければ返さなくてもいい。
このコードを見て、 無駄だと思わないかい? // se0のキー操作(a)による再生 if(window.getKeyStatus('a') == FKUT_SW_DOWN) { se0_play = true; se0.seek(0.0); } if(se0_play == true) { se0_play = se0.play(); // se1のキー操作(s)による再生 if(window.getKeyStatus('s') == FKUT_SW_DOWN) { se1_play = true; se1.seek(0.0); if(se1_play == true) { se1_play = se1.play();
赤くしたところが違うだけで、 処理の流れや構造は一緒だ // se0のキー操作(a)による再生 if(window.getKeyStatus('a') == FKUT_SW_DOWN) { se0_play = true; se0.seek(0.0); } if(se0_play == true) { se0_play = se0.play(); // se1のキー操作(s)による再生 if(window.getKeyStatus('s') == FKUT_SW_DOWN) { se1_play = true; se1.seek(0.0); if(se1_play == true) { se1_play = se1.play();
こういう感じで 差し替え効くように書けると素敵 // key, flag, seの所は状況に応じて差し替えたい if(window.getKeyStatus(key) == FKUT_SW_DOWN) { flag = true; se.seek(0.0); } if(flag == true) { flag = se.play(); playSoundByKey()関数 判定するキーの種類、鳴らしたいSEの変数、再生フラグ変数を引数として渡している
お品書きと本体 関数を作る時の 返値の種類 関数名(引数リスト); の部分をこの授業ではお品書きと呼ぶ お品書きはヘッダー(.h)に書く 関数を作る時の 返値の種類 関数名(引数リスト); の部分をこの授業ではお品書きと呼ぶ 正確には「プロトタイプ宣言」と呼ぶ お品書きはヘッダー(.h)に書く 本体を書く.cppファイルと、その関数を 利用したい.cppファイルでインクルードする 本体はC++ファイル(.cpp)に書く
インクルードとは 使いたい関数(やその他もろもろ)のヘッダーファイル(お品書き)を取り込む命令 FKUTの機能もインクルードすることで 使えるようになっている 動作としては、ファイルに書かれている内容をその場にコピペするのと大差ない なので、プロトタイプ宣言を直書きしても、ヘッダーをインクルードしても動作は一緒
ゲームでよく使う関数の作り方 返値は要らない場合が多い 引数は「参照渡し」にした方がよい 座標や数値の計算をしたい場合は返値を返すように作るとよい int, doubleの他に、bool, fk_Vectorなどが返値としてよく用いられる 引数は「参照渡し」にした方がよい 引数で受け取る変数名の前に「&」を付ける 値を参照するためだけに渡すものは型名の前に「const」を付ける 付けないと’a’やFK_ENTERなどの定数が渡せない fkut系の変数は必須 fk系も基本的に参照渡しの方が問題が起きない
その他注意点 配列を渡したい場合の引数リスト SpecialKeyのコード 普通のキーコード 文字列を渡したい int *iArray や fk_Model *mArray など *で渡した先で、[0]や[i]で要素を参照できる SpecialKeyのコード const fk_SpecialKey &spKey 普通のキーコード const char &key 文字列を渡したい string &text (関数内でいじらないならconst string &text)
より詳しい人のために 構造体やクラスの利用は推奨します グローバル変数の利用は極力避けるべし ただし、ある程度独学で頑張れるチームのみ 分かってる人だけが使うのではなく、チーム全体で出来るだけ理解レベルを統一して開発にあたりましょう グローバル変数の利用は極力避けるべし トラブルのもとです ゲームを通じて必要な変数はmain関数に作り、面倒でも引数でしっかり渡すように
値渡しと参照渡しの違い 値渡し(通常)の関数 参照渡しの関数 void func(int iA) { iA *= 2; } // 以下main内だとする int iValue = 10; func(iValue); // ここでのiValueの値は? void func(int &iA) { iA *= 2; } // 以下main内だとする int iValue = 10; func(iValue); // ここでのiValueの値は?
違いのまとめ 値渡しだと、引数として「同じ値がコピーされる」ので、関数側でいじってもコピー元には影響がない 参照渡しだと、引数として「その変数の箱自体が渡される」ので、関数側でいじった結果が反映される 多くの場合はこっちの挙動の方がうれしい だが、変数ではなく値を直書きしているものは受け取れない キーの種類が参照渡しじゃないのはそのため 引数を意図せずにいじって混乱することもある