ドキュメントを書き始めたら直したい箇所が大量に出てきた件

我ながらよくまあ3日でここまでドキュメント書かせる程の量書いたなぁと思いながら、ドキュメント書いてたんですけど。
ドキュメントを書くってのは、言うなら仕様の自己レビューになるわけですよ。


人に説明するってのは、覚えるのの3倍だっけ?の知識がいるとか言われてると思うんですけども、まあ説明書くので色々考えたり、色々調べたりするわけで。
そうするとどーも現状のjgame.jsがいまいち気にいらなくなってきたので、今の内にどんどん直しちまおうという流れになってきた。


いくつかピックアップして書いてみる。

Rendererの問題

jgame.jsでは描画するクラスとしてRendererってのがいるんだけど、こいつがコアのGameクラスと密結合していてちっとも切り離せない。


Labelクラスの説明を書いていた時に、ベクターの描画だからエンジンによっては遅くなるよーってな話を一応書いてて、「例えばこういう解決策があります」って話も合わせて書いていたわけだ。
その解決策の一つにはまあ当然ビットマップフォントがあって、今ビットマップフォントサポートしてないからSpriteで頑張って作ってねとかの話も書くんだけど、アイデアとしてはLabelを裏のバッファに書いてビットマップフォント用のSprite自動生成しちまえばいいじゃんとかも出てくる。


でもRendererがくっつきまくってるから、Labelを裏のバッファに書いておくってのがすげー面倒くさい。
だからRendererの描画部分をいったん切り離して、単体描画を担当するRendererと、Rendererを継承してGameとくっついてるGameRendererと、裏のバッファに書いてくれるBufferedRendererを作ろうぜと。


BufferedRendererは、適当に裏画面に書いて、その裏画面から自由にSpriteを作れるって形。
まだ作ってないけど、これがあれば今マップを作るTileにcreateSpriteってのを用意して、マップからはSprite作れるようにしてたんだけど、こんなカスタム処理もいらなくなっていいじゃないかと。
いいならやろうと。

Rendererの転送方法

どうもOperaのちらつきが気になって仕方ないので、せっかくRenderer直すからと調べてみたんだけど、一般的にはフリップ方式でやるらしいと。


今、なんつーんだっけこれ、名前思い出せないけど、裏のバッファから表のバッファにピクセル転送するやり方でやってるんだけど、普通のゲームってこの転送をしないんだよね。フリップ方式ってのを使う*1
フリップってのはこういうやり方。
裏に書く→裏のものを表に出す(今まで表だったものは裏に行く)→裏に書く→裏のものを表に出す(表だったもの以下略)


裏に書く→表に裏の内容をコピーする→裏に書く→表に裏の内容をコピーする
に、比べて、フリップの方が転送という工程がない分処理量が少ないわけだ。


で、試してみたんですよ、フリップ。
CANVAS2枚重ねて、z-indexいじって表示順ぐるぐる変えるってやり方。
でもOperaはやっぱり遅い。


多分だけど、裏のcanvasが非表示になっているってわかってない。だからOperaが二回描画して結局意味ない。
だからといってOperaにわかってもらうためにdisplayやvisibilityいじると当然ちらつきが発生するので、結論としてフリップは遅いってこったな。


じゃあって事でGameRendererに転送モード設けて、表画面に直接書くってのをやってみた。
詳細端折るけど、3パターンの中で今のところこれが最強。
ちらつくって情報見たからわざわざ裏画面持ったのに、表のCANVASに直書きしちゃうのがぱっと見一番速いし一番綺麗なんだけど、なんでだろうね。


で、とはいえ環境によって直書きだとちらつく可能性は十分にあるので、フリップモード、転送モード、直書きモードの三つを作ったんだけど、今GameがRendererを勝手に作るのでRendererの属性をいじれない。
せっかくサポートしても台無しじゃんということで、この辺の作りも見直そうぜと。


ゲームの時間管理

言い訳じゃないけどっていうか言い訳だけど、俺も長い事フレームベースの開発しかしてこなかったから慣れてないんですよ、時間ベースの管理。
ということで、今回のメインループはこんな感じなんだけどさ。

function that() {
	while (time < (beforeTime+16)) {
		update(16);
		beforeTime += 16;
	}
	render();
	window.requestAnimationFrame(that);
}
window.requestAnimationFrame(that);


これだと、時間によって起動するupdateでゲームの処理を進めて、常時レンダリングしてくれる上に、enterFrameっぽい処理をupdate内に書いてけばいいからまあ楽なんだけど。
やっぱだせぇよなぁと。

function that() {
	update(time - beforeTime);
	beforeTime = time;
	render();
	window.requestAnimationFrame(that);
}
window.requestAnimationFrame(that);

こうじゃね?と。
これすると、基本的にupdate内の処理が「1ピクセルずつ移動」って形では書けなくて、必ず「1秒で10ピクセル移動する、今回は17msec分の移動なので0.05882ピクセルの移動だ」とかになる。
常に小数の計算になっちゃうわけだ。


てか一昔前だと小数計算も随分遅かったから、なるべく整数で計算出来るようにしないといけない気がするんだけど、まあその辺の詳しい事は昔っからゲーム業界にいたわけじゃない俺にはようわからん。
わからんけど、時間ベースでやるなら、本来こうだよねというのはなんとなくわかる。


なら直そうぜと。
旧来のupdateも、flashに慣れている人間からすると使いやすいはずだから、オプションで発火出来るようにはしようかと思うけど、ベースのフレームワークでは使わないように修正する。
多分小数座標になればマップが妙に滲んだりすると思うけど、その辺はちゃんと処理しましょうということで。

とまあ色々直すと

今度はこれまで書いたドキュメントも直さなくちゃいけなくなんだよなぁ、と。
くっそーと思いつつ、BULLETの開発機直る予感もないし、時間はまだあるはずだから、今の内にやっちまおうとは思う。


まあ、自己レビューが足りてませんでしたということで、ドキュメントもこれまで書いた分は検証のためでしたと思えば別に無駄じゃないし、いいということにしよう。

次回目標

まあ、ドキュメント書く過程で色々わかってきたので、ドキュメントも書けて修正も出来ていいですねというバージョンを、今週中にver 0.2として登録したいなってとこだな。


さすがにいったんレビューした後はもうこんな根本からいじるくそでかいアップデートは無くせると思うんだけど、強いて言えばその次はModule対応した辺りがでかそう。
個人的にはbox2dをサポートするタイミングでModule対応したいんだけど、box2d使った(簡単に出来る)ゲームのアイデアも特にないから、まだ結構先になりそう。


フレームワークの修正で無駄に時間とられて、不思議のすごろくの戦闘シーンがあんまり進んでないけど、あっちも能力値の自動生成やキャラ配置辺りまではやっていて、後は自動で戦わせるだけ。
戦闘の計算式とかをぼちぼち考えてるので、もしかしたらver 0.2のリリースと同時に戦闘シーンのデモもいけるかもって感じ。


大体40キャラくらいがマックスになるのでゴチャキャラっぽくもなるから、パフォーマンス検証としてはうってつけだし、間に合うといいけどな。
まあニートの特権なので、焦らず騒がずぼちぼち進めるさ。

*1:10年くらい前の知識だが。