※公開:2013.2.1
私はゲーム製作者コミュニティ「Kawaz」で活動している[^1]わけなのだが、正直なところ、ゲームプログラミングらしいゲームプログラミングは殆どやっていなかった。 もちろん、全くしていないといえば嘘になるのだが、
ということもあり、「グラフィックを多用し、時間経過とともにどんどん動くゲームを作れるのか」という不安があったのである。
ただ、GlobalGameJam2013で実際にそういうプログラムを書くことになり、実際にやってみたところ、「やれば何とかなるじゃないか」と気づいたのである。その際に気づいたことを書いておく。
描画するものを適切に表現するデータ構造を考える必要がある。
例えば2次元のマップであれば、単なる2次元配列を作っておき、それを描画時に一つずつ読み込んで描画してやればよいのだが
下のような複雑な構造だと、「各地点の座標はいくつなのか」「現在地から次にどこへ移れるのか」というのをいちいち指定しないとならない。
これが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]。
班のプログラマーの一人・やまださきさんが「各ゲームは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 StageDef> 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<StageDefMap> stage_select_main; SceneSelectStage<StageDefTutorial> stage_select_tutorial;
例えば「ゲームをクリアしたら、ゲームクリアというメッセージを5秒間出して次のステージに移る」という処理はよくあるものだが、こういった「通常のゲーム進行とは異なる処理」を実装するために、コードがフラグ変数だらけになっていた。これはどうにかしたいと思いつつ解消できなかったのだが、何かよい方法はあるのだろうか。
あと今回はチュートリアルを見られるようにしたのだが、これは完全に台本を書くレベルで所作を細かく(0秒から3秒はキャラを動かす、3秒から6秒はカーソルを動かす、など)プログラムとして書いていた。これは本当に大変だったので、何かいい方法がないか考えたい。
もちろん、DXライブラリそのものが多機能ということもあるのだが、それ以上にDXライブラリのリファレンスに助けられたと感じている。
このリファレンスページでは、DXライブラリでできることが1ページにまとめて載っているため、「●●がしたい」ということを見つけやすいのである。内容が多すぎてまとまりがないように見えるかもしれないが、初めて利用する人には非常にありがたい構成になっている。
結局ゲームのグラフィック部分を作っていて気づいたのは、描画周りは楽できるところはあまりなくて、基本的には泥臭い作業になるということ。もちろん、画像の読み込み・表示とかはライブラリがやってくれるので楽できるのだが、どんなものを表示すればよいのかとかは「データ構造を決めて」「それらのデータを更新するルールを決めて」ということを細々とコードに落としてやるしかない。
あと、ゲームプログラミング自体は不慣れだったけど、データ構造の設計が慣れていたのはプラスに働いたなと感じた。
今後は苦手意識は持たずにやっていけそうです。