これはあまり理解しなくてもいいかもしれません。
今回は1秒間に60フレームの描画になるようにします。ただし、60FPSよりも早くならないようにする内容です。しかもこの方法、かなりアバウトです(1/1000秒単位なので見て分かるほどではありませんが)。精度の高いFPS管理をしたいのなら別の方法をとりましょう。
fps.hについて
struct SFpsControl{ int Index,One; // 添え字、1フレームあたりの時間 double Fps; // Fps int RemTime[60]; // 60回の描画にどれだけ時間がかかったか int PreCount; // 1フレーム前の時間を記憶 void FirstInit(); void Process(); };
Indexはメンバ変数RmTime配列の添え字に使用する変数です。
Oneは1フレームを描画するのに平均何ミリ秒かかったかを記憶する変数です。
Fpsは1秒間に何フレーム描画したかを記憶する変数です。
RemTimeは1回の描画に何ミリ秒経過したかを60回分記憶する配列です。
PreCountはこの関数が前回呼び出された時間を記憶する変数です。
Oneは1フレームを描画するのに平均何ミリ秒かかったかを記憶する変数です。
Fpsは1秒間に何フレーム描画したかを記憶する変数です。
RemTimeは1回の描画に何ミリ秒経過したかを60回分記憶する配列です。
PreCountはこの関数が前回呼び出された時間を記憶する変数です。
Process関数について
// フレームコントロール void SFpsControl::Process(){ int Difference = GetNowCount() - PreCount; PreCount = GetNowCount(); RemTime[Index] += Difference; Index++; if( Index == 60 ){ // 60回ループしてきたなら Index = 0; // 添え字初期化 Fps = 0; // 初期化 for(int i=0 ; i<60 ; i++) // 60回でどれだけの時間が経過したか Fps += RemTime[i]; One = (int)(Fps/60); // 平均で1フレームに何ミリ秒かかったか if( Fps != 0 ) // 0で割らないための処理 Fps = ( ( 60/Fps )*1000 ); // Fpsをもとめる memset(RemTime,0,sizeof(RemTime)); } int WaitCount = 16 - Difference ; if( WaitCount > 0 ){ // 早くここにきたら RemTime[Index]+=WaitCount; Sleep( WaitCount ); // 待つ } DrawFormatString(0, 0,White,"%3.1f",Fps); DrawFormatString(0,20,White,"%d",One); }
この関数が呼び出されたらまず、前回呼び出されたときの時間と今回呼び出された時間の差を求めます。この差が小さければ小さいほどすばやく処理をしてきたということになります。そのため、特別な処理をしないとどんどん画面が更新されてしまいます(かなり嘘ついてます)。
次に、今回呼び出された時間を記憶しておきます。これが次に呼び出されたときの前回の呼び出された時間になります。
先に行った時間の差を配列の要素に足します。代入ではありません。これは後述の内容によるためです。
添え字をインクリメントします。次の要素に格納するためのです。
次に、今回呼び出された時間を記憶しておきます。これが次に呼び出されたときの前回の呼び出された時間になります。
先に行った時間の差を配列の要素に足します。代入ではありません。これは後述の内容によるためです。
添え字をインクリメントします。次の要素に格納するためのです。
もし添え字が60になったら、分岐内容を処理します。これは60回分の平均をとって数値を求めているためです。
添え字とFps変数を初期化します。
Fps変数にRemTimeの全要素を足します。この時点でFps変数には60回描画するのにかかった合計時間が記憶されます。
One変数にFps変数を60で割った値を小数点以下を切り捨てて代入します。1回あたりの平均描画時間が記憶されます。
Fps変数が0以外なら、FPSを求めます。0で割るとエラーが発生するためです。
最後にRemTime配列の全要素を0でクリアします。1つ前のfor文でやってもよかったのですが、気分です。
添え字とFps変数を初期化します。
Fps変数にRemTimeの全要素を足します。この時点でFps変数には60回描画するのにかかった合計時間が記憶されます。
One変数にFps変数を60で割った値を小数点以下を切り捨てて代入します。1回あたりの平均描画時間が記憶されます。
Fps変数が0以外なら、FPSを求めます。0で割るとエラーが発生するためです。
最後にRemTime配列の全要素を0でクリアします。1つ前のfor文でやってもよかったのですが、気分です。
ここで待つべき時間を計算します。計算してみると1秒間に60回描画するには16.66..ミリ秒ごとに描画すればよいです。そのため、Difference変数が16より小さいとき、待てばいいということになります(この16.66..の小数点以下の切捨てがアバウトな原因の1つ)。
ここで、待つ時間をRemTime配列に足してやります。
Speep関数を用いて処理をとめます。Speep関数は引数の値だけ処理をとめるというものです。引数の単位はミリ秒になります。
ここで、待つ時間をRemTime配列に足してやります。
Speep関数を用いて処理をとめます。Speep関数は引数の値だけ処理をとめるというものです。引数の単位はミリ秒になります。
最後にFPSと1フレームあたりの平均描画時間を描画して関数終了です。
FirstInit関数について
void SFpsControl::FirstInit(){ memset(this,0,sizeof(SFpsControl)); }
全メンバ変数を0で初期化します。
こんな考え方があるのかー程度にとどめておいてください。すっごいアバウトです。