トップ 差分 一覧 ソース 検索 ヘルプ RSS ログイン

Kawazポータルから移行/「ゲームプログラミングらしいゲームプログラミング」をやって

※公開:2013.2.1

経緯

私はゲーム製作者コミュニティ「Kawaz」で活動している[^1]わけなのだが、正直なところ、ゲームプログラミングらしいゲームプログラミングは殆どやっていなかった。 もちろん、全くしていないといえば嘘になるのだが、

  • Snake(1998年作): 画像の表示をWindowsフォーム(Windows版)やHTML(ブラウザ版)に頼っており、UI周りであまり凝ったことをする必要がなかった
  • かずぴったん(2010年作): 表示する画面の更新は限定されたケースでのみ行えばよかったので、裏方の処理があまり面倒でなかった
  • 街マッチ(2011年作): 自分は主にメニュー画面担当で、ゲームの複雑な部分はほぼ触ってなかった

ということもあり、「グラフィックを多用し、時間経過とともにどんどん動くゲームを作れるのか」という不安があったのである。

ただ、GlobalGameJam2013で実際にそういうプログラムを書くことになり、実際にやってみたところ、「やれば何とかなるじゃないか」と気づいたのである。その際に気づいたことを書いておく。

環境とか

1. データの表現方法大事

描画するものを適切に表現するデータ構造を考える必要がある。

例えば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]。

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;

3. 台本を書くのは大変

例えば「ゲームをクリアしたら、ゲームクリアというメッセージを5秒間出して次のステージに移る」という処理はよくあるものだが、こういった「通常のゲーム進行とは異なる処理」を実装するために、コードがフラグ変数だらけになっていた。これはどうにかしたいと思いつつ解消できなかったのだが、何かよい方法はあるのだろうか。

あと今回はチュートリアルを見られるようにしたのだが、これは完全に台本を書くレベルで所作を細かく(0秒から3秒はキャラを動かす、3秒から6秒はカーソルを動かす、など)プログラムとして書いていた。これは本当に大変だったので、何かいい方法がないか考えたい。

4. DXライブラリに助けられた

もちろん、DXライブラリそのものが多機能ということもあるのだが、それ以上にDXライブラリのリファレンスに助けられたと感じている。

このリファレンスページでは、DXライブラリでできることが1ページにまとめて載っているため、「●●がしたい」ということを見つけやすいのである。内容が多すぎてまとまりがないように見えるかもしれないが、初めて利用する人には非常にありがたい構成になっている。

感じたこと

結局ゲームのグラフィック部分を作っていて気づいたのは、描画周りは楽できるところはあまりなくて、基本的には泥臭い作業になるということ。もちろん、画像の読み込み・表示とかはライブラリがやってくれるので楽できるのだが、どんなものを表示すればよいのかとかは「データ構造を決めて」「それらのデータを更新するルールを決めて」ということを細々とコードに落としてやるしかない。

あと、ゲームプログラミング自体は不慣れだったけど、データ構造の設計が慣れていたのはプラスに働いたなと感じた。

今後は苦手意識は持たずにやっていけそうです。

  • [^1]: もっとも、2012年は幽霊部員化してましたが。
  • [^2]: 実際、当初は各ステージは3要素(X座標、Y座標、次に進めるステージ)しか持っている必要がなかったのだが、のちに2要素追加が入った(ステージのレベル、ステージの色)。この際、データの並びとマクロを少し変えるだけで実際に対応できた。