Oct.24.1999
Oct.31.1999 加筆修正
スプライトの概念と実装について。
前回作成したダブルバッファリングの機構にスプライトの概念を
実装してみる実験。同時に X-Server などの動作速度
(ベンチマークっぽいもの)を計測し、どれぐらいの処理が現実的かを
見極める。
今回も GTK+ のヴィジェットにあまり頼ってませんので、GTK+ の Tips では なかったりするんですけども。(^^;
一応サンプルではメニューバーとステータスバーを使っているので、 そのあたりは参考になるかもしれません。けれども、結局は Glade で 作って吐き出しただけのものです。(毎度のことですね)
実際に書き込む drawable や、statusbar widgetをトップウィンドウ
から得るために、各widgetに名前をつけ、それを RRGetParts なる
関数で抜き出しています。この RRGetParts は自分で作っていながらも
なかなか有用であるとご満悦の関数です。(^^
実際ソース内でも結構使われています。
ゲームマシンといえばスプライトというくらいにメジャーになった機能
ではありますがそれを Linux や X-Window で実装するにはどうすれば
良いのでしょうか。
スプライトというのは優先順位が付いた小さな絵(セル)が順番に重なって
表示されると言ったものを指します。2Dゲームではこれら小さな一枚一枚が
動いて画面を構成します。ここでは小さなパーツを組み合わせるといった
点に注視します。
それを実装した概念図を以下に示します。
その形式(ビットマップフレームバッファ型)においてスプライト間の
表示優先順位というのは、どういった順番で描いていったかによります。
つまり、描いた順に重なっていくわけです。
スプライトのバッファを持って、一番奥になるスプライトから順番に
描き重ねていく、これがスプライトの概念です。
今回のサンプルプログラムではそのスプライトの管理および重ね合わせ
といった部分をインプリメントしていません。大体どういった形になって
いるかだけを捕らえてください。
sprite を表示するのには、X-Window の管理する window に書き込まなくては
なりません。実際に window に書き込むのはフレームバッファ管理のお仕事
ですので、その上のライブラリが気にすることが無いように実装します。
ここでスプライト表示というのはスプライトバッファからフレームバッファへと
転送する部分を指します。もっと具体的に言うと Pixmap から Pixmap への
転送ということです。この Pixmap 、X-Window が管理しているけど表示されない
ためオンメモリバッファと呼ばれることもあります。
Pixmap から Pixmap へと転送するさい GC(GraphicContext) によって
その挙動を制限することができます。といっても実際問題はマスク領域を
設定してそのマスク内だけを転送するといった作業に用いるわけですが。
言うところの抜き色を設定する、といったようなものです。残念ながらこの
表示マスク、Bitmapで指定(1bit mask)でしか無いのですが、スプライトを
実装するに当たっては十分使えるものであります。逆に言ったらこれがないと
どうすれバインダーといったところなのですが。
このあたりについての詳しいことは SOFTBANK から出ている 「GTK+入門〜基礎から始めるXプログラミング」ISBN4-7973-0871-0 にて解説されていますのでここでは省略します :-P
ていうかサンプルソース見てって感じ。
(追記)
翔泳社から出た
「GTK+/GDKによるLinuxアプリケーション開発」ISBN4-88135-775-1
にもそのあたりが詳しく説明されています。
(詳しすぎてくどいきらいもあり (^^;)
ここでは一般的概念ではなくサンプル内のrr_screen.c においてどのように
スプライトを実装しているかを説明します。
以後の説明はサンプルプログラムを覗きながら読んでください。
「おねアクセサリー集」とかを作った経験からインターフェースの実装とか
細部が変更になっているのですが、取り敢えずそのへんは省きます。
当初設計時は、スプライト用の Pixmap も rr_screen に管理させようと
思っていたのですが、色々と考えて行くとそこらは独立していたほうが良い
だろうということになり rr_screen ではフレームバッファに描画する部分
のみのインプリメントを行うということになりました。
スプライトを表示するにはスプライトバッファのポインタ、表示位置、
表示サイズ、スプライトバッファ内での位置、を与えてやる必要があるので
これを RRSprite という structure にまとめました。RRScreenObjPut
には RRSprite のポインタを渡します。
struct _RRSprite { gboolean sw; /* 表示スイッチ */ GdkPixmap *objmap; /* 表示するObjのマップ */ GdkBitmap *objmask; /* Objの抜き */ gint x, y; /* スクリーンへの表示位置 */ gint w, h; /* 表示したいObjのサイズ */ gint ox, oy; /* objmap内でのテクスチャー座標 */ gint offset_x, offset_y; /* オブジェ中心の座標オフセット */ };
スプライトバッファの管理、優先順位のマネージメントに関しては rr_screen でなくもう一つ上のレベルで行うことにしたのですが今回の サンプルでは必要なかったので実装していません。また、この部分は タイトルによっては簡素化できたり別形態での実装とある可能性も あるので今回は特に言及しないでおきます。
話は戻りますが、現在インプリメントされているのはスプライトバッファの どの部分からどのサイズをフレームバッファのどこに転送するかといった 部分のみです。優先順位を「誰がマネジメントするか」といった部分を 置いておけば、とりあえずは問題がないと思います。(^^;
今回のプログラムにて秒単位でのフレーム表示枚数を計測するまでは
気が付かなかったのですが、gtk_timeout_add にて指定した時間ってのは
厳密でないのですね。いや、書き方が悪いですね、正確には
gtk_timeout_add にて指定した時間ってのがミッションクリティカルな
割り込み時間でなく、gtk_main 内での処理無し時間がそれくらい経過
したらコールされるといったイメージの様です。(このへんソース覗いて
いないので言及できませんが)
例えば n msec (ミリ秒)の間隔で hogehoge() という関数をコールするように
gtk_timeout_add に設定したとすると、毎回きっかり n msec 毎に
コールされるのではなく (gtk_main()が諸処理を行うのに要する時間+n) msec
毎にコールされる感じ。
だから今回のサンプルで 15Frame/Sec に設定しても実際の動作を計測すると
11〜12Frame/Sec ほどの実行値しか現れないのですね。どんなに処理を軽く
してもその数値。もちろん、X Server がついてこなかったり、プロセスが
手一杯で遅くなるってのは別の問題ですが、そうでなくてもだいぶ落ちる
数値を出しています。
そんな状況ではアクションゲームを作ったりするのは難しいなあとか
思ってしまいます。もちろん、pthread を用いてもうちょっとタイミング
クリティカルに近づける方法もあると思いますけど、そこまでする必要が
あるかどうかというのはアプリケーションによると思いますね。
あと、gtk関数内で納めているとポータビリティー(移植性)が向上すると
いうのもありますし。(結構重要か?)
取りあえず私が作ろうとしているものに関しては gtk_timeout_add レベルで
問題ないというかその範囲で作ればよいので、現状維持といった感じです。
今回のサンプルを見て「やっぱり遅いなあ」と思われたでしょうか、
それとも「意外と動いているじゃん」と思われたでしょうか。
先のスプライトの解説で、オフラインスクリーンである Pixmap を持つ
のと同時にスプライトも Pixmap に持つという部分がありました。
このあたりは X-Window の仕組みに関係します。
いまさら私が解説するほどの事でもないのですが、X-Window(X11)は
サーバー・クライアント型の Window System です。X Server が画面に
ウィンドウやその中身を表示し、X crient がそのサーバーに
描画リクエストをリクエストして画面に表示を行う。ですから、
X-Window ではネットワーク越しに表示するサーバーを選べたり、
クライアントが別のマシンにいても大丈夫なわけです。
なかなかに美しいシステムなのですが、クライアントがサーバーに
リクエストをするという形式は大きなオーバーヘッドを生みます。
特にたくさんのデータを扱うときとか、ビットマップの様な大きなデータ
を扱うといった場面に顕著に現れます。例えば、ネットワーク越しの
クライアントがGIMPを実行したならば?識者ならわかると思いますが、
GIMPはクライアントメモリー上でイメージ操作を行い表示はその度に
X Server へとビットマップを送りつけます。ネットワーク環境にも
よりますが、10Base Ether とかの上でやると帯域の関係で、GIMPの
表示が目に見えるくらいの遅さになります (^^;
ですから、今回のサンプルは画像データを最初のうちにすべてサーバーに
送りつけ(Pixmapとして持ち)、あとは Pixmap 間の画像操作のみにして
いるわけです。クライアント側でフレームバッファは持っていません。
例えばの話、今回のサンプルを LAN越しに実行したりしてもあんまり
実行速度は変わらず、X-Server の速度のみに依存します。さすがに
ネットワークの帯域があまりにも細くなっていたら実行値はさがるで
しょうけれども。
配布条件はソース/データ共に GPL に準ず。
厳重注意!!
起動したあと色々な数値を設定して動作速度を見ることができますが、
フレームレートとかあまり高くしすぎるとマシンが付いてこなくて
非常に重くなりますので注意してください。場合によっては終了するのが
困難なほど重くなりますので。