4

ゲーム ループだけで CPU 使用率の 50% を使用しています。レンダリング作業はまだ行っていません。私はここで何をしているのですか?

        while(true)
        {
            if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
            {
                    if(msg.message == WM_QUIT || 
                           msg.message == WM_CLOSE || 
                           msg.message == WM_DESTROY)
                            break;

                    TranslateMessage(&msg);
                    DispatchMessage(&msg);                   
            }
            else
            {
                    //Run game code, break out of loop when the game is over

            }
        }
4

12 に答える 12

8

古典的なビジー/待機ループ。CPU は忙しくメッセージをチェックしています (そして無限に再チェックしています)。メッセージをブロックする方法で待機する必要があります。または、ゲーム スレッドを定期的にウェイクアップするタイマーを使用して、その作業を実行できるようにする必要があります。その後、ゲーム スレッドは次に起動されるまで消えてしまいます。

于 2010-03-02T13:08:51.447 に答える
5

ビジー待機ループが作成されました。おそらく 1 つのコアを 100% 使用しているため、デュアル コアの 50% を使用しています。

読み取り時に (別のスレッドで) ブロックする方法、必要に応じて I/O 呼び出しをブロックしてフォールアウトする方法、またはスレッドで何か他の有用なことを行う方法を見つける必要があります。それぞれの戦略には長所と短所があります。個別のスレッドには、mutex などの同期された通信方法が必要です。I/O から抜け出すということは、メッセージがないときに、このスレッドで他に役立つことが何も起こらないことを意味します。ループ内で別の処理を行うと、処理がゴツゴツする可能性があります (「別の処理」は、メッセージが少ないほど多く処理されます。メッセージが多いほど処理が少なくなります)。

于 2010-03-02T13:09:04.183 に答える
3

これは、アクションゲームの標準的なゲーム ループであり、オブジェクトの位置/ゲーム ワールドを更新する必要があります。 あなたがボードゲームを作っているなら、より良い選択でしょう。 それは本当にあなたが作っているゲームに依存します。
GetMessage

于 2010-03-02T13:40:10.373 に答える
2

処理するメッセージがなく、実行するゲーム コードがない場合は、CPU を放棄する必要があります。これを行う 1 つの方法は、メッセージがキューに表示される、時間切れが切れるのMsgWaitForMultipleObjectsを待つことです。

このようなもの

   DWORD g_msNextGameCall;
   DWORD g_msGameTickTime = 1000/75;

   while (true)
      {
      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
         {
         if (WM_QUIT == msg.message)
            break;

         TranslateMessage(&msg);
         DispatchMessage(&msg);  
         }
      else
         {
         DWORD ms = GetTickCount();
         DWORD msNext = g_msNextGameCall;
         LONG  lWait = 0;
         DWORD dwRet = WAIT_TIMEOUT;

         if (ms < msNext)
            lWait = min((LONG)g_msGameTickTime, (LONG)(msNext - ms));

         if (lWait <= 1)
            {
            g_msNextGameCall = ms + g_msGameTickTime;
            DoGameStuff();
            }
         else
            {
            if (WAIT_TIMEOUT == MsgWaitForMultipleObjects (0, NULL, FALSE, lWait, QS_ALLEVENTS))
               {
               g_msNextGameCall = GetTickCount() + g_msGameTickTime;
               DoGameStuff();
               }
            }
         }
      }
于 2010-03-04T00:26:43.020 に答える
1

非常に単純なゲームを作成していて、すべてのレンダリングと計算をメイン ループで行う場合、while ループの実行速度を制御する必要があります。そうしないと、ゲームはプロセッサごとに大きく異なる速度で実行されます。その副作用として、while ループが何もせずに CPU を消費していないことも確認しています。

于 2010-03-02T13:13:44.313 に答える
1

この動作は想定されていると思います。あなたのゲーム コードが何もしないときはいつでも、アプリケーションは猛烈にメッセージ キューをチェックしますPeekMessage。これは連続ループなので、1 つのコア全体を使用します。

ブロックにロジックを追加するelse{...}と、1 つのコアで 100% の使用率が維持されますが、ゲーム ロジックの実行に時間が費やされます。未使用の CPU サイクルのみがPeekMessage呼び出しで使用されますが、現在は 100% のサイクルが使用されています。未使用。

ゲームがフルスクリーンで実行されている場合、ゲームが表示されているときに CPU が最大になるのはよくあることです。しかし、おそらくGetMessageの代わりに使用することを検討する必要がありPeekMessageます。

通常、ゲームは通常のアプリとまったく同じようには機能しないことに注意してください。通常のアプリは通常、何かを行うように指示するメッセージを受信しない限り、何もしません。ゲームは通常、1 秒あたりできるだけ多くのフレームをレンダリングする必要があるため、常に何かを実行します。ただし、すべての CPU を盗むことは、ウィンドウ モードでは少し貪欲です。

于 2010-03-02T13:33:07.333 に答える
1

私は同じ問題を抱えていて、ここで答えを得ました: Game Loops

私のプログラムでは、上記の記事「最大 FPS で一定の​​ゲーム速度」の最後のループを使用しました。

于 2011-01-14T15:49:04.510 に答える
1

これは、PeekMessage 関数が WM_PAINT メッセージを削除しないため、常に TRUE が返されるためです。MSDN は次のように述べています。

通常、PeekMessage 関数はキューから WM_PAINT メッセージを削除しません。WM_PAINT メッセージは、処理されるまでキューに残ります。ただし、WM_PAINT メッセージに NULL 更新領域がある場合、PeekMessage はそれをキューから削除します。

于 2011-11-26T09:23:06.803 に答える
0

標準のwin32アプリのメインループとしては見えません...これは次のようになります

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
 {
   MSG msg;
   while(GetMessage(&msg, NULL, 0, 0) > 0)
   {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
   }
   return msg.wParam;
 }

while(true)ループ内にいるため、ユーザーイベント(マウスとキーボード)でさえメッセージキューに正しくディスパッチされない可能性があります

win32でゲームアプリを開発することが目標の場合は、Directxを確認することをお勧めします。

于 2010-03-02T13:22:03.163 に答える
0

あなたのelseブロックで、これを追加してみてください:

sleep(0);

これにより、スレッドが CPU を解放し、ビジー待機ループが中断されます。実際のゲーム コードを実行するには、tvanfosson が提案するようにタイマーを使用して、別のゲーム スレッドを起動します。

于 2010-03-02T13:13:51.793 に答える
0

質問は古いですが、私の証言が新しい読者に役立つと思います。

ここで、Delphi (VCL なし) を使用し、Win7 32 ビット、Core Duo を実行する純粋な Win32 アプリケーションで、同じ問題 (アプリのアイドル状態で CPU の 50%) に直面していました。

メッセージ ループ内で Sleep(0)、Sleep(1) などを試しましたが、CPU 使用率が 0% (アプリのアイドル状態) に低下するものはありませんでした。最終的に、すべてのループ サイクルではなく、PeekMessage() が False を返す場合にのみ、同じ Sleep() を使用することで成功しました。これで、アプリケーションがアイドル状態のときに CPU 使用率が 0% になりました。

単純化されたコードでは、それが私が今やっていることです:

AppIsDone := False;  // turned on after wm_Close (not shown below)

repeat
  if not PeekMessage(...) then
  begin
    Sleep(1);
    Continue;  {repeat}
  end;

  if GetMessage(...) then
  begin
    TranslateMessage(...);
    DispatchMessage(...);
  end;
until AppIsDone;
于 2016-08-09T20:30:33.863 に答える
0

ゲームは 1 つのコアで可能な限り高速に実行されます。これは、あなたがしたことによっては正常です。

より多くの電力を消費したい (したがって 100% に到達する) か、電力を少なくしたい (そして、貧しいユーザーのエネルギー料金を節約したい...) かはわかりません。

より多くの電力を必要とする場合は、何らかの方法でスレッド化を使用して、両方のコアを使用する必要があります。私はスレッディングにはあまり興味がないので、説明しようともしませんが、それはまったく私の分野ではありません。

LESS電力を使用したい場合は、2つのオプションもあります...

1 つは "yield" で、一部のゲーム ライブラリ API は (Allegro など) と呼んでいます。これは、FPS カウンターを作成し、十分な時間フレームごとに CPU に制御を戻すことで構成されます。たとえば、ゲームを 120 FPS で実行したい場合、60 で実行したい場合、フレームの計算にかかる時間とほぼ同じ時間 (約 8,3333... ms) を彼に与えることができます。

もう 1 つは、ループの代わりに、イベント ベースの方法でゲームをコーディングすることです。その形式では、コードを「Update」という名前の関数内に配置します。この関数は、最後の呼び出しからかかった時間を引数として受け入れます (これは非常に重要です...)。この「更新」は、アプリ全体 (より古典的)、または独自の各オブジェクト (「OnEnterFrame」としてこれを使用する Flash の発明後に人気があり、Unity もこれを使用し、他のエンジン) のいずれかです。そして、割り込みをスローしてその Update を呼び出すコードを作成します。通常は、定期的に実行されるタイマーです (60FPS の場合は 16.6.... ms、30FPS の場合は 33.33333... ms)。

明らかに、他にもたくさんの方法がありますが、小さな本には十分な情報なので、すべてを説明することはしません...

私は自分自身でさまざまなアプローチを使用してきましたが、CPU をフル ブラスト (スレッド化せずに) 使用するのが好きで、電源が利用可能になるにつれて、システムを悪用する効果をますます使用します。しかし、より単純なゲームでは、通常、すべてを計算するのにかかった時間をカウントする最も簡単な方法である「生成」するループを作成し、16.6 ミリ秒からそれを引き、スリープ関数 (その時間の OS に制御を与える) を呼び出します。結果... 同様に、フレームの計算に 3 ミリ秒かかった場合、sleep(16-3) を呼び出します。そして、いくつかのエンジンは「イベントスタイル」で作業することを強制します (つまり、入力はキーボード、マウス、およびジョイスティックの割り込みから来て、タイマーが割り込みをかけて「Update(step)」を呼び出します)、私はそれが嫌いですが、学ぶ必要がありました。 ..

最後に、「step」変数 (Update 引数) は、通常、ゲーム ロジックの数学で使用されます。一定速度... (明らかに、使用される操作は、「ステップ」が表すものによって異なります)。

于 2010-05-09T22:30:02.520 に答える