44 章
さてさて、これだけ処理が増えてくると、段々と不具合が出てきた時、どこが原因か突き止めにくくなって来ましたね。
また、どの辺に無駄があるのかも解りにくくなってきたと思います。
そこで、メインループから呼んでいる自作関数がそれぞれどれ位処理に時間がかかっているか表示させてやりましょう。
そうすると、仮に無駄な処理が大量にあったり、
どこかおかしなプログラムがまざっていれば、特定の箇所だけ処理時間が長くなっているはずですから
その辺を見直してやればいい事がわかります。
特に間違いではないにしても、やたら処理に時間のかかる関数があればどうにか効率化出来ないか
検討してみる必要があるでしょうし、こうして各関数の処理時間を表示させる事は結構役立ちます。
実装の考え方としては、
時間測定関数();
自作関数();
時間測定関数();
このように、自作関数を時間測定関数で挟んでやることで、処理時間がわかりますね。
この理論で実装してみましょう。
また、今回から文字の描画にフォントデータを使ってみようと思います。
フォントハンドルを格納するためのfontという変数を用意し、ロード関数でフォントデータを作っておきましょう。
---- 新規にcheck_time.cpp を作成し、そこに以下を追加 ---- #include "../include/GV.h" //名前の最大文字数 #define STR_MAX 64 //調べられる関数の最大数 #define FUNC_MAX 30 //今何個目かをカウントする int func_count; //前回測定した時間を保存する LONGLONG lt; //測定データ格納用変数 typedef struct{ int tm; char str[STR_MAX]; }func_tm_t; func_tm_t func_tm[FUNC_MAX]; //stという名前で受け取ったデータを登録する。flagが1ならリセット void enter_func_tm(char st[], int flag){ int i; LONGLONG nowtm; if(func_count>=FUNC_MAX){ printfDx("func_countの値%dが異常です\n",func_count); return ; } nowtm=GetNowHiPerformanceCount() ; if(nowtm-lt<INT_MAX){//intの扱える範囲なら func_tm[func_count].tm=(int)(nowtm-lt);//処理時間格納 memcpy(func_tm[func_count].str,st,STR_MAX-1);//文字列コピー func_tm[func_count].str[STR_MAX-1]=0;//終端記号入れる } else//扱えなかったら func_tm[func_count].tm=-1;//エラー lt=nowtm ;//前回記録した時刻として保存 if(flag==1){//リセット for(i=func_count+1;i<FUNC_MAX;i++) func_tm[i].str[0]=0; func_count=0; } else func_count++; } //データを描画 void draw_func_tm(int x, int y){ int i; unsigned int total=0; for(i=0;i<FUNC_MAX;i++){ if(func_tm[i].str[0]==0)break; DrawFormatStringToHandle(x,y+14*i,color[0],font[0],"%05.2f:%s",func_tm[i].tm/1000.0,func_tm[i].str ); total+=func_tm[i].tm; } DrawFormatStringToHandle(x,y+14*i,color[0],font[0],"合計:%05.2f",total/1000.0); }
今回は厳密な時間を測定する為に、100万分の1秒(マイクロ秒)まで測定出来るGetNowHiPerformanceCount()という関数を使ってみました。
100万分の1秒まで測定出来るということは、1000秒たったら10億マイクロ秒になってしまいますから、
すぐにint型の上限を超えてしまいます。なので、返り値はLONGLONG型だと言う事に注意しておいて下さい。
しかし、1つの関数が処理をする時間が数千秒以上なんてことはありえないので、
時間を格納する変数はint型で用意しました。
また、このenter_func_tmは文字列を引数にします。
引数にした文字列と計測した時間をセットで後から表示させます。
その為名前と測定時間をセットにしたfunc_tm_t型を用意しました。
関数の呼び出しがどうなっているのか、他の点と一緒に見て行きましょう。
あ、それから前章で紹介したdraw_fps関数は描画関数の中に移動しましたので、以下紹介するとおり確認しておいて下さい。
---- GV.h に以下を追加 ---- //その他の変数 GLOBAL int font[20]; ---- function.h に以下を追加 ---- //check_time.cpp GLOBAL void enter_func_tm(char st[], int flag = 0); GLOBAL void draw_func_tm(int x, int y); ---- load.cpp の load() に以下を追加 ---- font[0] = CreateFontToHandle( "HGPゴシックE" , 15 , 2 , DX_FONTTYPE_ANTIALIASING_EDGE); ---- graph.cpp の先頭に 以下を追加 ---- extern void draw_fps(int,int); extern void draw_func_tm(int,int); ---- graph.cpp の graph_main() 関数の以下を変更 ---- void graph_main(){ if(bright_set.brt!=255)SetDrawBright(bright_set.brt,bright_set.brt,bright_set.brt); graph_back_main();//背景描画メイン graph_effect(0);//敵が死ぬエフェクト if(bright_set.brt!=255)SetDrawBright(255,255,255); graph_effect(4);//喰らいボムのエフェクト if(bright_set.brt!=255)SetDrawBright(bright_set.brt,bright_set.brt,bright_set.brt); graph_child(); graph_item();//アイテム描画 graph_boss(); graph_enemy();//敵の描画 graph_cshot();//自機ショットの描画 if(bright_set.brt!=255)SetDrawBright(255,255,255); graph_ch();//自機の描画 if(bright_set.brt!=255)SetDrawBright(bright_set.brt,bright_set.brt,bright_set.brt); graph_lazer();//レーザーの描画 graph_bullet();//弾の描画 if(bright_set.brt!=255)SetDrawBright(255,255,255); graph_effect(1);//ボムのエフェクト graph_effect(2);//ボム線のエフェクト graph_effect(3);//ボムキャラのエフェクト graph_stage_title();//タイトルの表示 graph_board();//ボードの描画 graph_flash();//フラッシュ描画 graph_develop(); draw_fps(0,465);//fps描画 draw_func_tm(450,250);//処理時間描画 } ---- main.cpp のメイン関数の以下注釈の通り変更 ---- ---- 変更したのはenter_func_tm関数の追加 ---- int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){ ChangeWindowMode(TRUE);//ウィンドウモード if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初期化と裏画面化 while(ProcessLoop()==0){//メインループ music_ini(); switch(func_state){ case 0://初回のみ入る処理 load(); //データロード first_ini();//初回の初期化 func_state=99; break; case 99://STGを始める前に行う初期化 ini(); load_story(); func_state=100; break; case 100://通常処理 enter_func_tm("最初"); calc_ch(); enter_func_tm("キャラ計算"); ch_move(); enter_func_tm("キャラ移動"); cshot_main(); enter_func_tm("自機ショットメイン"); enemy_main(); enter_func_tm("敵処理メイン"); boss_shot_main();enter_func_tm("ボスショットメイン"); shot_main(); enter_func_tm("ショットメイン"); out_main(); enter_func_tm("当たり判定"); effect_main(); enter_func_tm("エフェクトメイン"); calc_main(); enter_func_tm("計算メイン"); graph_main(); enter_func_tm("描画メイン"); if(boss.flag==0) stage_count++; break; default: printfDx("不明なfunc_state\n"); break; } music_play(); enter_func_tm("音楽再生"); fps_wait(); enter_func_tm("待機した時間",1); if(CheckStateKey(KEY_INPUT_ESCAPE)==1)break;//エスケープが入力されたらブレイク ScreenFlip();//裏画面反映 count++; } DxLib_End();//DXライブラリ終了処理 return 0; }
「最初」と「待機した時間」はきにしなくていいです。
FPSをあわせる為に待機した時間と、垂直同期にあわせて待機した時間ですから、多くなって当然です。
パソコンのスペックにもよりますが、各自作関数の処理時間はなるべく1ms以内にした方がいいでしょう。
全ての合計が16.6msを超えると「処理落ちした」という状態になりますから、
そうならないように作っていきましょう。
実行結果
- Remical Soft -