※公開:2013.2.1 {{outline}} !!! 経緯 私はゲーム製作者コミュニティ「Kawaz」で活動している[^1]わけなのだが、正直なところ、ゲームプログラミングらしいゲームプログラミングは殆どやっていなかった。 もちろん、全くしていないといえば嘘になるのだが、 * [Snake|http://sinryow.s54.xrea.com/snake/snake.html](1998年作): 画像の表示をWindowsフォーム(Windows版)やHTML(ブラウザ版)に頼っており、UI周りであまり凝ったことをする必要がなかった * [かずぴったん|http://www.kawaz.org/projects/kazupittan/](2010年作): 表示する画面の更新は限定されたケースでのみ行えばよかったので、裏方の処理があまり面倒でなかった * [街マッチ|http://www.kawaz.org/projects/machi/](2011年作): 自分は主にメニュー画面担当で、ゲームの複雑な部分はほぼ触ってなかった ということもあり、「'''グラフィックを多用し、時間経過とともにどんどん動くゲームを作れるのか'''」という不安があったのである。 ただ、GlobalGameJam2013で実際にそういうプログラムを書くことになり、実際にやってみたところ、「やれば何とかなるじゃないか」と気づいたのである。その際に気づいたことを書いておく。 !!! 環境とか * [DXライブラリ|http://homepage2.nifty.com/natupaji/DxLib/]利用(言語はC++) * プログラマー3人で作業分担 * 作ったもの:[イチの、みち|http://www.kawaz.org/projects/ichi-no-michi/] !!! 1. データの表現方法大事 描画するものを適切に表現するデータ構造を考える必要がある。 例えば2次元のマップであれば、単なる2次元配列を作っておき、それを描画時に一つずつ読み込んで描画してやればよいのだが {{image "http://cdn-ak.f.st-hatena.com/images/fotolife/m/maraigue/20130201/20130201030632.png?1359658180","イチの、みち"}} 下のような複雑な構造だと、「各地点の座標はいくつなのか」「現在地から次にどこへ移れるのか」というのをいちいち指定しないとならない。 {{image "http://cdn-ak.f.st-hatena.com/images/fotolife/m/maraigue/20130201/20130201034225.png?1359658177","イチの、みち"}} これがRubyやPythonやJavaScriptなら、以下のような感じで連想配列リテラルを使えばよいのだが stages = { :stage1 => {:x => 100, :y => 300, :next => :stage2, :stage3, :stage4}, :stage2 => {:x => 300, :y => 100, :next => :stage5, :stage6, :stage7}, :stage3 => {:x => 300, :y => 300, :next => :stage5, :stage6, :stage7}, :stage4 => {:x => 300, :y => 500, :next => :stage5, :stage6, :stage7}, : : } C++ではあいにくそういうことができないので、普通の2次元配列にマクロを組み合わせて、こんなコードを書いていた。 #define BIT(n) (1 << (n)) #define XPOS(stg) ((stg)[0]) #define YPOS(stg) ((stg)[1]) #define NEXT_STAGES(stg) ((stg)[2]) const int stages[10][3] = { {100, 300, BIT(2)|BIT(3)|BIT(4)}, {300, 100, BIT(5)|BIT(6)|BIT(7)}, {300, 300, BIT(5)|BIT(6)|BIT(7)}, {300, 500, BIT(5)|BIT(6)|BIT(7)}, : : } Rubyのコードに比べると、無理やり情報を配列に押し込めているため汚く見えるのだが、マクロでその辺をある程度吸収している。これなら、各ステージが持つべきデータ(=配列の要素数)が増えても、マクロの書き換えで対応できる[^2]。 !!! 2. 継承とかテンプレートをうまく使う 班のプログラマーの一人・やまださきさんが「各ゲームはupdateメソッド(ゲームの状態の更新)とdrawメソッド(実際の描画)を持つことにしましょう」と話していたのだが、この仕様が私が各ゲームを統合する処理を書いたときにかなり役立った。updateメソッドとdrawメソッドを持つ純粋仮想クラス(インターフェイス)Sceneを作っておき、Sceneを継承している何らかのクラスを受け取って処理させれば、それによってそれぞれのゲームの処理が行われるので。 class Scene{ public: virtual void init(GameResult * result) = 0; virtual GameResult * update(void) = 0; virtual void draw(void) = 0; }; あと、上記で述べたステージ情報の例では、実際には二つ用意して使い分ける必要があったので、クラス内に直接stagesを定義するわけにいかなかった。これは継承だとうまく行かなかったため、テンプレートを使って対処した。 // ステージ選択画面を表示するためのクラス template class SceneSelectStage : public Scene { ... }; // メインのステージ選択画面の定義 class StageDefMap { public: static const int STAGE_NUMBER = 11; static const int STAGE[STAGE_NUMBER][5]; }; const int StageDefMap::STAGE[STAGE_NUMBER][5] = { ... }; // チュートリアル選択画面の定義 class StageDefTutorial { public: static const int STAGE_NUMBER = 4; static const int STAGE[STAGE_NUMBER][5]; }; const int StageDefTutorial::STAGE[STAGE_NUMBER][5] = { ... }; // メインのステージ選択画面、チュートリアル選択画面はそれぞれこのように生成 SceneSelectStage stage_select_main; SceneSelectStage stage_select_tutorial; !!! 3. 台本を書くのは大変 例えば「ゲームをクリアしたら、ゲームクリアというメッセージを5秒間出して次のステージに移る」という処理はよくあるものだが、こういった「通常のゲーム進行とは異なる処理」を実装するために、コードがフラグ変数だらけになっていた。これはどうにかしたいと思いつつ解消できなかったのだが、何かよい方法はあるのだろうか。 あと今回はチュートリアルを見られるようにしたのだが、これは完全に台本を書くレベルで所作を細かく(0秒から3秒はキャラを動かす、3秒から6秒はカーソルを動かす、など)プログラムとして書いていた。これは本当に大変だったので、何かいい方法がないか考えたい。 !!! 4. DXライブラリに助けられた もちろん、DXライブラリそのものが多機能ということもあるのだが、それ以上に[DXライブラリのリファレンス|http://homepage2.nifty.com/natupaji/DxLib/dxfunc.html]に助けられたと感じている。 このリファレンスページでは、DXライブラリでできることが1ページにまとめて載っているため、「●●がしたい」ということを見つけやすいのである。内容が多すぎてまとまりがないように見えるかもしれないが、初めて利用する人には非常にありがたい構成になっている。 !!! 感じたこと 結局ゲームのグラフィック部分を作っていて気づいたのは、描画周りは楽できるところはあまりなくて、基本的には泥臭い作業になるということ。もちろん、画像の読み込み・表示とかはライブラリがやってくれるので楽できるのだが、どんなものを表示すればよいのかとかは「データ構造を決めて」「それらのデータを更新するルールを決めて」ということを細々とコードに落としてやるしかない。 あと、ゲームプログラミング自体は不慣れだったけど、データ構造の設計が慣れていたのはプラスに働いたなと感じた。 今後は苦手意識は持たずにやっていけそうです。 * [^1]: もっとも、2012年は幽霊部員化してましたが。 * [^2]: 実際、当初は各ステージは3要素(X座標、Y座標、次に進めるステージ)しか持っている必要がなかったのだが、のちに2要素追加が入った(ステージのレベル、ステージの色)。この際、データの並びとマクロを少し変えるだけで実際に対応できた。