ゲームは いつぞやのダルイやつ です。 ええ分かってますとも。こういうのは動画でお見せするべきですね。いいかげん動画のアップの仕方覚えようと思います。それにしてもスマホの画面を撮んのって難しいな。どう撮っても反射するし。
移植っても9割方は Android NDK 版と共通ソースだし、どっちも OpenGLES ハードって事もあって、1週間かからず移植できましたね。よかったよかった。
でも動いたはいいものの、なんでか何もしなくとも処理落ちするような状態だったもんで、その原因をつきとめるのに今日半日かかりましたよ。せっかくだから原因でも書いときます。
ゲームの画面を更新する場合、普通は描画中の画面をユーザーに見せないように、2枚の画面を用意して交互に切り替える事が多いのですが、iOS の場合はアプリが用意する画面は1枚だけでよくて、その画面に描画後、最終的に presentRenderbuffer というメソッドを使って、 「これ表示してよ!」 と iOS 様にお願いします。
今回の処理落ちがいったい何の負荷なのか調べたところ、どう考えてもこの presentRenderbuffer だけで 100% とか 200% とか負荷食ってるんですね。
元々参考にした OpenGL ES テンプレートではそんな事はないようだし、常に遅いならともかく、起動直後は別に遅くなくて、途中でホーム画面に戻ったり画面を回転すると突然遅くなるので、どうも改造しているうちになんか余計な事をしてしまったみたい。
で、いろいろ削っていく事半日、原因が判明。
iPhone は縦画面から横画面に切り替わる時、ちゃんと 「くるっ」 とアニメーションしますよね。あの動きはOSが自動的にやってて、90度回転しつつ、縦長から横長に引き伸ばされるのですが、レンダリング済みのゲーム画面を勝手に引き伸ばされても困る訳ですよ。
そこで、回転するのは構わないからアスペクト比は維持してくれと、ビューのプロパティに以下を追加したんですね。
// 画面サイズは変えず、回転だけするようにする設定 view.contentMode = UIViewContentModeCenter; // なぜか背景色が白で、ものすごく見た目が悪いので、黒にする view.backgroundColor = [UIColor blackColor];
ということで、本日の教訓。
安定動作する前に細かい事とか気にしない!
ちなみに、今回処理落ちしてた presentRenderbuffer メソッド。ググると予測変換で slow と出るくらい、謎の負荷で悩んでる外人さんが何人もいました。ちゃんと読んでませんが、なんかいろんな要因に引っ張られて遅くなるらしく、この先も悩まされそうだなぁ。
おまけ。負荷測定のしかた
ついでだから、iOS での負荷測定のしかたも書いておきますか。まず、測定したい場所の直前と直後のタイミングで、できる限り正確な時間を取得して、その差分を取れば、その処理にかかった時間は分かりますよね。
あとは、1フレームの時間が分かっていれば、 (処理時間) / (1フレームの時間) を計算すれば、1フレームの中でどれだけ時間を食ってるかの比率が分かります。これが 100% を超えたら処理落ちですね。
例えば、CADisplayLink でフレーム管理をしているなら、インターバルが1なら1フレームは 1/60秒なので、その時間で割ればOKです。
処理時間を測定するためのクラスを作るとしたらこんな感じ?
#import <sys/time.h> class CProcTime { public: // 処理開始時間を保持 void Begin() { gettimeofday( &begT, null ); } // 処理にかかった時間(秒)を計算 double End() { struct timeval endT; gettimeofday( &endT, null ); double begSec = begT.tv_sec + begT.tv_usec * 1e-6; double endSec = endT.tv_sec + endT.tv_usec * 1e-6; return endSec - begSec; } private: struct timeval begT; };
// 使い方 CProcTime procTime; // 計測開始 procTime.Begin(); // ---------------------------- // この間の処理時間が測定される // ---------------------------- // 計測終了。Begin からの経過時間を算出 double sec = procTime.End(); // 60fps であれば、1/60 で割れば1フレームに対する比率が分かる double rate = sec / (1.0/60.0);
ナノ秒ともなると int とか float じゃ全然精度が足らないので注意。