タスク処理の設計
"タスク"というとコンピュータ業界ではとても抽象的で高度な処理と考えがちですが、ここでいうタスクとは「ゲーム上、実行する最も単純な処理」と位置付けます。
では、この実行とは何かというと、これはゲーム上で実行されるものであれば何でも良く、よくあるシューティングゲームでいえば自機となるシップ、アクションゲームでいえば主人公となるキャラクター、また画面上に表示される敵、敵の弾が"実行"されて動いています。
また、これらは画面上で目に見えたりしていますが、あまり意識しないもの等もタスクとして考えます。例えばフェードイン/アウトの処理、これも徐々に暗く(明るく)なっていく一つの「実行」となります。また、シューティングゲーム等によくある、「数秒後(数フレーム後)、もしくはある場所まで来たら、ボスを表示させる」というような処理も、ある意味「実行」の単位として扱うことができます。
ようは変化のあるもの('=, タスク)位と考えます。完成されたゲームをお手本によく分析してみると、変化のあるもの全てがタスクとして分解できると思います。
さて、ではどのようにタスクを実装するかというと、まずは基底クラスをタスク(実行単位)として表現し、ここから各実行すべきもの(自機等)を派生させる形となります。
雰囲気でいうと以下のような感じです。
単純なコーディング例(Java)
// 基底タスク // コンストラクタ // タスクの初期化 // タスク実行 // 例えば自機クラス(Taskクラスからの派生) |
これら実行すべきタスクとその派生クラスを設計したら、次はこれらをフレームワークとして実行する処理を検討します。
実行する処理は配列や、C++, Java, C# などのどの言語にも存在するコンテナを用いて集合管理し、この集合に登録されたタスクを一つずつ実行するという方法を実現すると、シンプルな実行処理を実現することができます。
具体的に説明すると、「集合管理された、基底クラス(タスククラス)から派生した実行処理クラス(自機等のクラス)の実行メソッドをポリモーフィズムにより動的に実行させる。」ということになります。
よくある面クリアタイプのゲームや、シューティングゲームを設計するとしたら、この方法が一般的かと思います。
この管理処理をメインループで呼び出せば、タスクの実行を行うことができます。
単純なコーディング例
// タスク管理 // 登録されているタスクの実行を行います // もしも消滅状態の場合はタスクを抹消します // タスクを実行します(ここでポリモーフィズムによる派生先のタスクが実行されます) |
まずは単純な説明はここまでとして、将来はこの基底クラス(基底タスク)からどのように、クラス派生を行うべきかを説明したいと思います。
メインループでのウェイト
前回の日記(1/4)でメインループの事を述べましたが、ループにて繰り返しを行う際に、ウェイト処理を入れてやる必要があります。
このウェイト処理というのは、ゲームに対するCPUのパワーが余れば余るほど必要になる処理で、これを行わないとゲームのスピードがとんでもなく速くなってしまう現象が発生します。
最近では当てはまらなくなってきましたが、少し前まではTVのブラウン管の映像表示(走査)速度と同じ、60fps(1秒間に60コマ = 60フレーム)で、タイミングを取るというのが当たり前という感じでゲームのプログラミングを行うというのが定石で、間に合わなければ30fpsに低下…というような処理をメインループに入れるということを行っていることが一般的のようです。
私もJavaで2Dゲームを開発したときは、同様なことを行い、
long m_nowTime; while(true) { // 入力処理、タスク、描画、サウンド等の処理 // 処理した時間を計算して、ウエイトが必要か判断します |
のようなコーディングを行いました。ここの「m_framerate = 17」というのが、1秒間のうちの60コマのおおよその1コマ秒となり、1÷60=0.016666… = 約0.017秒 = 17ミリ秒となります。
(Javaのスレッドを停止させるThread.sleepメソッドの単位はミリ秒となります)
ただし、このようなコーディングの場合は、処理落ちが発生した場合は全体がスローモーションとなり、フレームのスキップなどが行えないので、フレームスキップを行う場合には、描画処理を数フレーム行わない等の高度なウェイトを更に検討しなければなりません。
XNAの場合は非常にインテリジェンスなフレームワークを実装しており、メインループの実装も不要、フレームレートもMicrosoft.Xna.Framework.Gameインスタンス内のIsFixedTimeStep、TargetElapsedTimeにて、可変/固定フレームレート、フレームレートのタイミングを設定することができます。
(殆ど何も考えずに、メインループの実装が行われている&フレームスキップの実装が容易となります)
プログラミングを行う時にまず最初に検討すること
フレームワークと言うとものすごく抽象的ですが、大まかに言ってしまうと以下の物となる感じです。
1.メインループ
2.入力情報の保持
3.タスク(集合)処理
4.描画処理
5.サウンド処理(これは「3.」の処理の中に埋めこむケースが多いです)
これらの内「2.」「4.」「5.」の処理がプラットフォームや言語によって、かなり依存しやすい部分になるので、どのように実装すべきか検討します。
(これらの検討は日記として述べるのにはボリュームがありすぎるので、別の機会で各々少しずつ述べていこうと思います)
Java や C# など(というよりは大概のプログラム言語)は、必ずエントリポイントとなる、メイン関数から起動されるので、そこから「1.」のメインループ処理に入り、このメインループ内で「2.」~「5.」の処理を行うといった感じになります。
(私の場合ですが)

開発環境
【2Dのアクションゲーム開発の時】
言語:Java(j2sdk1.5)
プラットフォーム:Windows 2000, XP
コンパイラ:javac, ant(1.6.x)
コンソール:cmd, cygwin(2.51)
グラフィックソフト:Windowsペイント, GIMP
ステージ作成:OpenOffice
サウンド(SE):Windowsサウンドレコーダー, SoundEngine Free
サウンド(BGM):ソング頼太
【3Dのアクションゲーム開発の時】
言語:C# + XNA
プラットフォーム:Windows XP
コンパイラ:Visial C# 2005 Express Edition, XNA Game Studio 1.0
グラフィックソフト:Windowsペイント, GIMP
モデリングソフト:Metasequoia(これはフリーの方がメイン)
モーションソフト:RokDeBone2(これは現時点ではまだ導入していません)
サウンド(SE):Windowsサウンドレコーダー, SoundEngine Free(これは現時点ではまだ導入していません)
サウンド(BGM):ソング頼太(これは現時点ではまだ導入していません)
このように列挙してみると、全てフリーウェアで開発が行える感じがします。
お金や人をかければ質の良いソフトウェアが使えて、ある程度作業が楽になり、質の良いものができるかもしれませんが、所詮は趣味と割り切って開発を行えば、費用はほとんどかからないということがよく分ります。
年々質の良いフリーウェアが出きてきて、こんなにも開発がしやすいなんていい時代です。
ゲームプログラミングをするにあたり
特にゲームの場合は、タスク(リアルタイム)プログラミング、グラフィックプログラミング、入力制御プログラミング、サウンドプログラミング等、割とプログラミング技術が異ったものの複合で構築されるため、全てを独り(もしくは少数)で作成する場合にはそれなりのスキルが必要になると思います。
こう考えてしまうと、かなりモチベーションが下ってしまう訳ですが、それではマズイので、この難易度の高い開発を趣味で行うにあたり、私の場合は「この開発を行うことにより、何か別なこと(例えばビジネスアプリ開発)などで、応用できるような形で習得が行えるようにしてモチベーションを上げる」努力をしています。
2Dのアクションゲームを作成した際は、「とりあえず仕事で使用したことのないJava言語」を。
3Dのアクションゲームを作成する際には、「とりあず仕事で使用したのことのないC#言語」を。
といった感じで動機付けをしました。
また、「この開発するプログラムは、とりあえず世界で一つだけのもの。幾ら似ているものがあろうとも、とりあえず世界で一つだけのもの = それはホンのちょっぴりカッコイイ!」と考えながら開発を行うようにしています。
趣味のプログラミングというのは、少数で行うほど「人に言われて開発するものではない」ので、こういった職人(芸術家)気質な自分の世界を作ることや、損して特取れみたいな、ニンジン的要素があると継続して開発ができると思います。