ページ 11

FPSが60にならない・・

Posted: 2009年6月09日(火) 19:23
by チルチル
龍神録のコードを参考にゲームプログラミングの館のwait_fanc関数の中身を変更してみたんですが
FPSが60になりません、どこか間違っているでしょうか?
環境はDXライブラリです
#include "DxLib.h"
int Key[256];
int GetHitKeyStateAll_2(int GetHitKeyStateAll_InputKey[/url]){
    char GetHitKeyStateAll_Key[256];
    GetHitKeyStateAll( GetHitKeyStateAll_Key );
    for(int i=0;i<256;i++){
        if(GetHitKeyStateAll_Key==1) GetHitKeyStateAll_InputKey++;
        else                            GetHitKeyStateAll_InputKey=0;
    }
    return 0;
}
int count=0;
void wait_fanc(){
	int term;
	static int t=0;
	if( count%60==1 )t=GetNowCount();
	if( count%60==60 )term=1000+t-GetNowCount();
	else term=(1000.0/60)*(count%60)+t-GetNowCount();
	if(term>0)Sleep(term);
}
void fps(){
    int i;
    static int t=0,ave=0,f[60];
    f[count%60]=GetNowCount()-t;
    t=GetNowCount();
    if(count%60==59){
        ave=0;
        for(i=0;i<60;i++)
            ave+=f;
        ave/=60;
    }
    if(ave!=0){
        DrawFormatString(0, 0,GetColor(255,255,255),"%.1fFPS",1000.0/(double)ave);
        DrawFormatString(0,20,GetColor(255,255,255),"%dms"  ,ave);
    }
    return;
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
ChangeWindowMode(TRUE);SetOutApplicationLogValidFlag( FALSE );
if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化
while(ProcessMessage()==0 && ClearDrawScreen()==0 && GetHitKeyStateAll_2(Key)==0 && Key[KEY_INPUT_ESCAPE]==0){
          //↑メッセージ処理          ↑画面をクリア           ↑入力状態を保存       ↑ESCが押されていない
fps();
count++;
ScreenFlip();
wait_fanc();
}
DxLib_End();
return 0;
}

Re:FPSが60にならない・・

Posted: 2009年6月10日(水) 00:20
by チルチル
関数を呼ぶ場所の関係上カウンタが一周目で既に1になっているのが紛らわしかったりしますけどね・・

Re:FPSが60にならない・・

Posted: 2009年6月10日(水) 07:45
by sizuma
走り読みですから、処理の内容は確かめてませんが・・・・

wait_fanc()の
>if( count%60==60 )term=1000+t-GetNowCount();
が到達不可なコードになりますよね?
1から呼び出されるなら
>if( count%60==0 )term=1000+t-GetNowCount();
でいけるんじゃないですか?

どちらにしろ、僕の脆弱マシンじゃぁたぶんFPSは60にならないですが(笑

Re:FPSが60にならない・・

Posted: 2009年6月10日(水) 17:45
by チルチル
あ~そういえばそうですね・・
とりあえず修正しました
しかしまだ60にならないですね・・
#include "DxLib.h"

int Key[256];

int GetHitKeyStateAll_2(int GetHitKeyStateAll_InputKey[/url]){

    char GetHitKeyStateAll_Key[256];

    GetHitKeyStateAll( GetHitKeyStateAll_Key );

    for(int i=0;i<256;i++){

        if(GetHitKeyStateAll_Key==1) GetHitKeyStateAll_InputKey++;

        else                            GetHitKeyStateAll_InputKey=0;

    }

    return 0;

}

int count=0;

void wait_fanc(){

	int term;

	static int t=0;

	if( count%60==1 )t=GetNowCount();

	if( count%60==0 )term=1000+t-GetNowCount();

	else term=(1000.0/60)*(count%60)+t-GetNowCount();

	if(term>0)Sleep(term);

}

void fps(){

    int i;

    static int t=0,ave=0,f[60];

    f[count%60]=GetNowCount()-t;

    t=GetNowCount();

    if(count%60==59){

        ave=0;

        for(i=0;i<60;i++)

            ave+=f;

        ave/=60;

    }

    if(ave!=0){

        DrawFormatString(0, 0,GetColor(255,255,255),"%.1fFPS",1000.0/(double)ave);

        DrawFormatString(0,20,GetColor(255,255,255),"%dms"  ,ave);

    }

    return;

}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){

ChangeWindowMode(TRUE);SetOutApplicationLogValidFlag( FALSE );

if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化

while(ProcessMessage()==0 && ClearDrawScreen()==0 && GetHitKeyStateAll_2(Key)==0 && Key[KEY_INPUT_ESCAPE]==0){

          //↑メッセージ処理          ↑画面をクリア           ↑入力状態を保存       ↑ESCが押されていない

fps();

count++;

ScreenFlip();

wait_fanc();

}

DxLib_End();

return 0;

}

Re:FPSが60にならない・・

Posted: 2009年6月10日(水) 22:35
by チルチル
しかし管理人さんのコードに比べて短すぎますかね・・

Re:FPSが60にならない・・

Posted: 2009年6月11日(木) 10:20
by Libra
t=GetNowCount();
を1フレームで呼び出す回数が多すぎるのが原因です。

また、wait_funcの

if( count%60==1 )t=GetNowCount();
if( count%60==0 )term=1000+t-GetNowCount();
else term=(1000.0/60)*(count%60)+t-GetNowCount();

でやっている事の意図が不明です。

Re:FPSが60にならない・・

Posted: 2009年6月11日(木) 10:58
by ねこ
ひとまず
「60フレーム周期で手前に遅れてるフレームがあったら今回遅れていなくてもスリープさせず補正していく」
処理を実装したい、という認識でOKですかね?

まずcountが1の時に
「if( count%60==1 )t=GetNowCount();」
でtに現在時間が入り

if( count%60==0 )term=1000+t-GetNowCount();
else term=(1000.0/60)*(count%60)+t-GetNowCount();
↑のelseで「t-GetNowCount()」はほぼ0になります。
つまりcountが1の時強制的に16ミリ秒スリープさせる処理になってるんですね。
このフレームでの処理が0.6秒以内に終わらなければ計算上FPS60にならない気がします。

Re:FPSが60にならない・・

Posted: 2009年6月11日(木) 18:38
by チルチル
>>「60フレーム周期で手前に遅れてるフレームがあったら今回遅れていなくてもスリープさせず補正していく」

そんな感じです
void wait_fanc(){
	--count;
	int term=GetNowCount();
	static int t=0;
	if( count%60==0 )t=term,term=0;
	else if( count%60==59 )term=1000+t-term;
	else term=(1000.0/60)*(count%60)+t-term;
	if(term>0)Sleep(term);
	++count;
}
とりあえずcountを内部補正してます
最初の時の待ち時間は思い付かなかったので
待たないで次のフレームで2回分待ってます

Re:FPSが60にならない・・

Posted: 2009年6月11日(木) 19:03
by ねこ
初回はDxLib_Initの後とかに「term = GetNowCount();」としておくのが良いかと思います。
その際termはグローバル変数にしておきます。

<「60フレーム周期で手前に遅れてるフレームがあったら今回遅れていなくてもスリープさせず補正していく」
ただこの処理、意図的にcount%60==1の時に過負荷をかければ分かると思いますが
凄くガクンガクン動く結果になりますよ。特殊な意図があってしているなら良いと思いますが
普通のゲームにはお勧めできません。

Re:FPSが60にならない・・

Posted: 2009年6月11日(木) 20:13
by チルチル
まあ確かにガクガクしますよね・・
しかしまだ60になる気配がないです・・

Re:FPSが60にならない・・

Posted: 2009年6月11日(木) 23:06
by チルチル
う~ん何でかな?

Re:FPSが60にならない・・

Posted: 2009年6月11日(木) 23:46
by Libra
答えはNo:34863の書き込みでねこさんが既に出してます。

Re:FPSが60にならない・・

Posted: 2009年6月12日(金) 00:35
by 御津凪
折角なので私が使っている方法を適当にDXライブラリで書いてみました。
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
    ChangeWindowMode(TRUE);

    SetWaitVSyncFlag(TRUE);

    if( DxLib_Init() == -1 ) return -1;

    DWORD beginTick = GetTickCount();
    DWORD nowTick;
    LONGLONG prevTick;
    LONGLONG tickOne = (1000LL << 32) / 60;
    LONGLONG tick;
    DWORD loop;
    prevTick = nowTick = 0;

    DWORD fps = 0,fps_show = 0; // FPS : 1秒あたりのフレーム描画回数
    DWORD mps = 0,mps_show = 0; // MPS : 1秒あたりのループ回数
    DWORD tickSec = 1000;

    while(ProcessMessage() == 0){
        ClearDrawScreen();
    begin:
        nowTick = GetTickCount() - beginTick;
        tick = ((LONGLONG)nowTick << 32) - prevTick;
        if(tick < tickOne){
            Sleep((tickOne - tick) >> 32);
            goto begin;
        }
        while(tickSec <= nowTick){
            fps_show = fps;
            fps = 0;
            mps_show = mps;
            mps = 0;
            tickSec += 1000;
        }
        ++fps;

        for(loop = (DWORD)(tick / tickOne); loop > 0; --loop, prevTick += tickOne){
            ++mps;
            // ここに毎回行う処理を入れる
            DrawFormatString(32, 32, GetColor( 255 , 255 , 255 ), "FPS: %2d, MPS: %2d.", fps_show, mps_show);
        }

        if(CheckHitKey(KEY_INPUT_ESCAPE) != 0) break;

        ScreenFlip();
    }

    DxLib_End();
    return 0;
}
この方法だと、 FPS は 60 を維持できなくても、
ループ回数(MPS)は必ず平均 60 回になるようになっています。

ねこさんの方法もそうですが、この方法はフレームスキップする処理です。
ガクガクするのは、補正した時の処理状態は画面に反映しないため、
見ている側は動作が重くなったとき、表示が飛び飛びに見えます。

(ちなみに、上のコードは for 文を外せばフレームスキップしないようになっています)

Re:FPSが60にならない・・

Posted: 2009年6月12日(金) 20:04
by チルチル
>>初回はDxLib_Initの後とかに「term = GetNowCount();」としておくのが良いかと思います

これの事でしょうか?
なんだか初回がうまく動くようになるだけで
それ以降の処理には影響しないように見えますが・・

御津凪さんの方法も良さそうですが
ゲームプログラミングの館の構造でかなり作ってしまったので
導入は無理そうです・・

Re:FPSが60にならない・・

Posted: 2009年6月12日(金) 20:40
by ねこ
VC2008 Expressならデバッグの方法は分かるでしょうか?
Debugでビルドしてブレイクしたい行でF9を押すと処理を中断します。

FPSが60未満の場合、というIF文を作ってその中でブレイクをかけます。
その時の平均値、または別途毎回のterm値を保持する配列等を作って置いて
60FPS未満の時の配列の中身を調べます。
1回じゃ分からないなら何回か実行して不自然なSleepがかかっている個所を探します。
これで原因を追究して下さい。

提示した以外に処理があるならなおのこと環境は違いますし、自身で解決するしかないと思います。

Re:FPSが60にならない・・

Posted: 2009年6月12日(金) 20:59
by チルチル
う~んそのようですね

とりあえずこの問題は保留にしておきます

どうもありがとうございました