« 固定小数点数ライブラリを更新(20110102a) | トップページ | VirtualBoxでJavaプロセスがハングアップする件の対応策 »

2011年2月 7日 (月)

ビデオゲームプログラムのワークフロー

特別なことではないが、わたしはずいぶん前からメインループを切り離し、サブルーチンをコールバックする構造でプログラムを作っている。

内部状態を1フレーム分進行させるUPDATEルーチンと呼び出された時点の内部状態で画面描画するRENDERルーチンが基本的に1フレームに1回ずつ呼び出されて動く構造だ。

ふと、いつからこんなふうに作っていたのかと思い返してみたら、20年ほど昔、ハードウェアで実装されたスプライト機能を搭載したパソコンで必要に迫られたものだったと思い出した。

数少ないスプライトに動的にキャラクタを割り付けたり、テアリングを避けるために垂直同期の帰線期間内に設定しなければならなかったから描画処理をまとめる必要があった。

その後、世の中はPC/AT互換機一色に。フレームバッファ方式になってさほど描画処理をまとめることに神経質になる必要が無くなった。ところがポリゴンの登場で再び描画処理をまとめる必要が出てきた。

パソコン以外では、マルチスレッドが使えない協調型マルチタスク環境とか、描画イベントのコールバック以外で描画用のコンテキストを取得できない環境とか、制限の厳しいプラットフォームが現役だが、この構造はあらゆるプラットフォームに容易に対応できる。

ビデオゲームプログラミングを始めて間もないひとの中には、1フレームごとに呼び出し元に戻るプログラミングを受け入れ難いというひとが大勢いるようだが、複数のキャラクターを並行して動かすときには、同様の入れ子の構造のコードを書くことになる。

けっきょく入れ子のレベルがひとつ増えるだけの違いなのだから、メイン『ループ』などという先入観を植え付けられる前にコールバックを受け入れてしまえば良いと思うのだ。

|

« 固定小数点数ライブラリを更新(20110102a) | トップページ | VirtualBoxでJavaプロセスがハングアップする件の対応策 »

日記・コラム・つぶやき」カテゴリの記事

コメント

はじめまして。
以前、C言語何でも質問掲示板でお世話になったものです。

この記事の内容に興味があるのですが、実際にどんなコードになるのかがわかりません・・。
無限ループ内で1フレームずつ描画する処理は何となく想像できるのですが、
それをコールバック関数を使って置き換えることができるのでしょうか・・?

もしできましたら参考URL等でもなにか追加情報を頂けると嬉しいです。。

私はゲームプログラミングをやっているわけではありませんが、
画像処理や画面描画の高速化などには興味があり、
Win32APIやGDIで遊んでいる程度のレベルです。

投稿: たろ | 2011年4月16日 (土) 午後 07時05分

コメントありがとうございます。
Javaのソースコードですが、以前書いた記事があります。

【JavaSE】ゲーム用フレームワークの実装
http://isle.cocolog-nifty.com/blog/2010/06/javase-0-52b5.html

この記事に書いてあるフレームワークは無限ループになっていますが、肝は、コールする側とコールされる側を切り離して考えましょう、ということです。
コールする側はプラットフォームに応じてできるだけ高い精度でコールするように実装します。
コールされる側は仕様どおりに呼び出されることを信じてプログラミングすることになります。

呼び出す側がイベントドリブンなプラットフォームでループを構成していないかそうでないかとか、ゲームの中身をコーディングしているときにそんなことを気にする必要はないわけです。

呼び出しの仕組みをあらかじめ決めておくことで呼び出し側を容易に差し替えることができるようになります。
JavaSEカテゴリの記事にあるサンプルプログラムはこのフレームワークの高機能版で動いていて、あとからフレームワークだけを改良して更新したこともあります。

投稿: ISLe | 2011年4月16日 (土) 午後 11時20分

お返事ありがとうございます!

Javaのソースコード読ませてもらいました。
なんとなくわかったような・・ただ、私には少し高度な話だったかもしれません・・。
C++やJavaが苦手で、abstractクラスとか使ったことがなく・・。

元々、こちら↓のサイトを読んで、Win32APIでゲームやアニメーションを作る場合は
こんな感じのループ構造になるのかーと思っていました。
http://eternalwindows.jp/graphics/bitmap/bitmap20.html

まず基礎的な話で恐縮ですが、ゲームやアニメーションが動作する処理の流れとしては、

1.無限ループでFPS制御をしつつ、
2.フレームの変化(キャラクターの移動など)を計算して
3.描画する、の繰り返し

というのは基本、と思っていて問題ないでしょうか。その上で、

1.の部分は、抽象クラスにして独立させる。
2.と3.の処理は、継承した実体クラス(?)の方で定義する。

そうすると、プラットフォームが変わった場合でも改造が楽チン!という感じの話でしょうか。

ただ、このJavaのソースが「コールバック」になる事、「1フレームごとに呼び出し元に戻る」といった点はよくわかりません・・。Javaをよくわかっていないためだと思いますが・・。

もし純粋Cで作る場合だと、関数ポインタで似たような構造にはできるんでしょうか・・。
ただCでそこまでしてどれだけ意味があるのか・・。

すみませんただの感想文になってしまいました・・。
こんなポイントがあるというのを覚えておきたいと思います。
ありがとうございました!

投稿: たろ | 2011年4月17日 (日) 午後 11時41分

FPS制御は無限ループではなくタイマーイベントを使うかもしれません。
いろんな実装があるので依存しないようにします。

こちらのJavaのサンプルプログラムでは、2.の処理をframeUpdate、3.の処理をframeRenderというメソッド(関数)に書くことに決めてフレームワークを作りました。
これはリンクされたページのMove関数とRender関数にあたります。
しかし「この関数定義の中にゲーム本体のコードを追加してください」というふうにはなりません。
C言語風に言うと関数のプロトタイプを用意しているだけです。

ゲーム本体を実装するときにframeUpdate関数の本体まるまる作成するわけです。
作成したframeUpdate関数はフレームワークから自動的に呼び出されるので「コールバック」になります。「1フレームごとに呼び出し元に戻る」ルールでフレームワークを設計しているので守らないと正しく動作しません。

コールバックとかabstractメソッドに関してはUPDATE-RENDER構造の構成要素ではなくて、ライブラリとして独立させてUPDATE-RENDER構造を強制するために利用している仕組みです。

クラスを使うより拡張性が落ちますが、C言語では、ゲーム本体のコードを追加する関数定義を、まるまる別のソースファイルに書くようにすれば良いです。
ソースファイルを中身を書き換えずに使い回せるようにすることが大事です。
それはつまりコンパイル済みのオブジェクトファイルを使い回せるのでライブラリとなります。

ライブラリの作り方をきちんと解説している本やサイトは見ないですね。
大量にコードを使い回す状況に直面しないとありがたみが分からないですし、呼ぶほうと呼ばれるほうの両方を作らないとリンクエラーになるので、一例を示しても「ただソースファイルを分けて何の意味があるの?」ってことになってしまうんですよね。

投稿: ISLe | 2011年4月18日 (月) 午前 04時13分

解説ありがとうございます!

なるほど、abstractクラス・メソッドを使った実装は、あくまでJavaにおける手段であり、肝心なのは「FPS制御処理とゲーム本体処理を分離(カプセル化?)する」という考え方ですね。

コールバック=関数ポインタを渡すという発想になっていましたが、このabstractメソッドのようなのもコールバックと言うんですね。親クラスのメソッドを子で再定義するオーバーライド?ライト?ロード?に似ていて、違いがよくわかりませんが、このあたりはオブジェクト指向をちゃんと勉強しろという話ですね・・。

C言語での考え方、参考になりました。「オブジェクトファイルの再コンパイルが不要で使いまわせるものは、すなわちライブラリといえる」という考え方。これまでライブラリというと、.libや.dllのことを指すものだと思っていましたが、なるほど・・。でもC言語だと「単に行数が多くなったからソースファイルを別にした」状態と同じになってしまいますね・・カプセル化の目的があることを別に覚えておかないと。そこらへんがJavaに比べると強制力が弱いのか・・。

また、確かに小規模なプログラムではそれほど有り難さを実感できなさそうではあります。私は単独開発だし、そんなに巨大なも作れないし、プロジェクト全体ビルドもすぐ終わるし。仕事で作るもの、何人かで分担して開発する場合はこういう初期設計が重要になってくるのかな。でも小さくてもきれいに構造化されたソースを書きたいです。

よい勉強になりました。ありがとうございました!

投稿: たろ | 2011年4月18日 (月) 午後 11時06分

コメントを書く



(ウェブ上には掲載しません)




« 固定小数点数ライブラリを更新(20110102a) | トップページ | VirtualBoxでJavaプロセスがハングアップする件の対応策 »