フレームスキップの導入を検討する
概要
何でもっと早く気付かなかったんだ。理想と腕前がちぐはぐなオイラのゲームはもっそい重い。
FPSが落ちればそれだけ、もっさり感が増してしまうのは当然のこと。ならばフレームスキップを導入し、速度の統一化を図ればいいんじゃないか。
フレームスキップとは
このインチキ臭い資料館を読んでいる人はフレームスキップという言葉の意味ぐらいはわかっているだろうが、一応念のために書いておくと、描写速度が足りない場合は描写をスキップし、速度の改善を図るものだ。
パラパラ漫画の真ん中の部分を適宜引っこ抜く形になるため、滑らかさは落ちるが一定の速度は保てる。
利点と問題点
利点
- オイラの重たいゲームがとりあえず理想のスピードで動く
- ベースがもっさりなのに処理落ちしたら目も当てられない
- 通信の効率がうp!
- 多分
問題点
今回オイラが実装するフレームスキップは、描写処理そのものをすっ飛ばす処理である。従って、描写処理の結果を利用する命令がある場合うまくいかなくなる可能性が高い。
あと、元がもっさりなので微妙だが、フレームスキップをするので当然カクカクする。
実装の方針
重要な点をまとめる。
- E3Dpresentを呼ばない限り画面は維持される
- パーティクルは描写命令を呼ばないと時間が進まず、移動も消失もしない
- 当たり判定に使う情報は、E3Dchkinviewによって更新される
- 自前でスリープを取る
- E3DwaitbyFPSを使わないため、FPSを手動で計算する必要がある
さて、重要なのはレッドマン。フレームスキップを行ったばやい、パーティクル命令も当然呼び出されない。という事は該当フレームで消えるはずだったパーティクルは消えず、次に描写命令が来た時に消える事になる。
どうしようかと悩んだが解決方法がわからんので、おちゃっこ様に聞くことにした。パーティクル描写命令を呼ばずに、パーティクルの時間が経過するような関数があれば、この問題はズバッと参上、ズバッと解決する。卑しくもこのような命令を追加するよう、おちゃっこラボの掲示板に要望を提出してしまった。
ライブラリに頼るのは何か趣旨が違うような気がするし、おちゃっこ様がこれを実装してくれるかどうかわからんので、別の手法も考えてた。以下が、嘆願書がスルーされた時の方針だ。
- E3DBigenScene EndSceneを必ず呼ぶ
- フレームスキップする場合、モデルとスプライトとビルボードは一切描写しない
- ただし当たり判定を行う必要があるため、E3Dchkinviewは必ず呼ぶ
- パーティクル描写命令は必ず呼ぶ
- フレームスキップをする場合にE3Dpresentを呼ばない
メインループでパーティクル描写命令を呼び出す回数は変わらない。よって以上で、パーティクルの寿命などは変えず、フレームをスキップさせることが出来る。パーティクルの描写命令はスキップしていないので、インチキフレームスキップとでも名づけることにする。
実装
実装する手順は割りと簡単だ。要点だけをまとめて連ねておく。HSPの速度に依存しない外部のカウンタ、ぶっちゃけた話Windowsのタイマーを取得する事が重要。
- 一定の間隔で、基準となるカウンタの値(A)を更新する
- 描写処理をよしなに行う
- 描写終了後のカウンタの値(B)を取得する
- B-Aより、描写にかかった時間(C)が求まる
- Cの値が、自分が想定するFPSより劣っているならばフレームスキップフラグをON
TIPS
自前でスリープ処理
オイラの適当な説明が聞きたいお兄様達のために一応要点を解説する。といっても、それほどややこしいことではない。
- ループの一番初めでAにd3timerを使い時間を記録
- ~なんか処理をする~
- ループの終わりにBに再び時間を記録
- B-Aにより、この1フレーム消費するのにかかった時間が求まる。
- この時間が自分が想定する速度より速いなら、以下を実行する
repeat Bを更新 if B-A<(1000ミリ秒/想定FPS):{ await 1、上記モジュールのSleep等のウェイトを実行 } else:{ break } loop
- 1へ戻る
ウェイト処理はこんな風に簡単。
あえて描写する優しさ
例えば、猛烈に遅いPCが此処にあるとする。極端に遅いマシンの場合、一番最初の描写処理が終わった後、フレームスキップのフラグがONになったあとずっとフラグが起ちっぱなしで、画面がいつまでたっても更新されないという事態になりかねない。
また早いPCでも、2秒ほどウルトラエフェクトで重い、などといった場合、やっぱり画面の更新が完全に止まってしまう可能性がある。
こんな時は、1秒に一回とか、適当な頻度で、必ず画面を描写してやろう。速度は悪くなるが、それも一瞬だ。