メインゲームループが制御されずに実行されても問題はありませんか?


19

私のゲームループがシステムの許す限り高速で実行されたときに、害が生じる可能性があるかどうか疑問に思っていましたか?

現在、ナノ秒単位で経過時間を測定することで、問題なく事前定義された速度でゲームロジックとレンダリングロジックを実行するループがあります。実際、私がループ内で行うロジックは、毎秒一定量の呼び出しに合わせてクロックされます。

ループ自体は、私のマシンでは1秒間に約1,170万のループに達するのと同じくらい高速で実行されます。

ループ(単純な擬似コード):

while(!isGameOver){

 if(canPollInputs){
    pollInputs()
 }

 while(canStepLogic){
    stepLogic()
 }

 if(canRender){
    render()
 }
}

私の質問は、基本的に、その単純なループが、制御された速度で実行されていない場合、システムに害を及ぼすことができるかどうかです。

編集:つまり、ロジックは1秒に30回(30 tps)で実行され、レンダラーは60 fpsで実行され、入力を1秒に100回ポーリングしています。 。ただし、ループ自体は調整されません。

編集:Thread.sleep()たとえば、メインループを1秒あたり250ループまでスロットルすると、削減につながりますが、ループは目的の250ではなく、1秒あたり約570ループで実行されます(デスクトップマシンにいるときにコードを追加します。)

編集:ここで、物事を明確にするために動作するJavaゲームループに行きます。また、自由に使用できますが、あなたのものではありません;)

private void gameLoop() {

    // Time that must elapse before a new run
    double timePerPoll = 1000000000l / targetPPS;
    double timePerTick = 1000000000l / targetTPS;
    double timePerFrame = 1000000000l / targetFPS;
    int maxFrameSkip = (int) ( (1000000000l / MINIMUM_FPS) / timePerTick);

    int achievedPPS = 0;
    int achievedFPS = 0;
    int achievedTPS = 0;

    long timer = TimeUtils.getMillis();

    int loops = 0;

    int achievedLoops = 0;

    long currTime = 0l;
    long loopTime = 0l;

    long accumulatorPPS = 0l;
    long accumulatorTPS = 0l;
    long accumulatorFPS = 0l;

    long lastTime = TimeUtils.getNano();

    while(!isRequestedToStop) {
        currTime = TimeUtils.getNano();
        loopTime = currTime - lastTime;
        lastTime = currTime;

        loops = 0;

        accumulatorPPS += loopTime;
        accumulatorTPS += loopTime;
        accumulatorFPS += loopTime;

        if(accumulatorPPS >= timePerPoll) {
            pollInputs();
            playerLogic();
            achievedPPS++;
            accumulatorPPS -= timePerPoll;
        }

        while(accumulatorTPS >= timePerTick && loops < maxFrameSkip) {
            tick();
            achievedTPS++;
            accumulatorTPS -= timePerTick;
            loops++;
        }

        // Max 1 render per loop so player movement stays fluent
        if(accumulatorFPS >= timePerFrame) {
            render();
            achievedFPS++;
            accumulatorFPS -= timePerFrame;
        }

        if(TimeUtils.getDeltaMillis(timer) > 1000) {
            timer += 1000;
            logger.debug(achievedTPS + " TPS, " + achievedFPS + " FPS, "
                    + achievedPPS + " Polls, " + achievedLoops + " Loops");
            achievedTPS = 0;
            achievedFPS = 0;
            achievedLoops = 0;
        }

        achievedLoops++;
    }
}

ご覧のとおり、各ループで実行されるコードはほとんどありませんが、リアルタイムの経過時間に基づいて常に特定の選択が行われます。問題は、その「ワーカーループ」と、それがシステムにどのように影響するかということです。


2
必須の「タイムステップの修正」リンク:gafferongames.com/game-physics/fix-your-timestepをお読みください。また、Thread.Sleep()を使用してタイムステップを確実に調整することはできません。これは、要求された時間が経過するとOSがアプリケーションに制御を戻すことを保証しないためです。(それは異なる場合があります)。
ロイT. 14

ええ、それは擬似コードの問題です。gafferが書いていることのほとんどを行いました(現在、ループ構造を補完するデルタ実装に取り​​組んでいます)。Thread.sleep()を使用できない場合、標準Javaで他に何が利用できますか?
dot_Sp0T 14

1
通常、Thread.Sleep(0)でビジーループを使用し、最善の結果を期待します。ガファーは、これについていくつかの記事でたくさん語っています。
ロイT.

それは、基本的にはthread.sleep(0)なしで、そしてもともとこの質問を引き起こしたものです。システムのビジーループ(繰り返しロジックが少しでも抑制されているためビジーではありません)ループに害はありませんか?
dot_Sp0T

しばらく前、バグが原因で特定の条件下でStarcraft IIがこれを行っていました。最終的にいくつかのGPUが文字通り溶けてしまいました。そのため、制御されていないゲームループが害を及ぼす可能性が非常に高くなります。
メイソンウィーラー14

回答:


35

これにより、1つのCPUコアが常に100%で実行されます。これは通常、システムに及ぼすことはありません。CPUは、数時間にわたって100%で実行されるように設計されています。しかし、モバイルデバイスでは、バッテリーがすぐに消耗し、デバイスが熱くなるため、店舗の評価で星が出るほどコストがかかります。デスクトップコンピューターではこれは問題になりませんが、ユーザーの電力をより多く消費し、CPUファンを高速で回転させます。これにより、ノイズが発生したり、他のプロセスで使用されるCPUサイクルが浪費されたりします。これらは重大な欠陥ではありませんが、それでもスタイルが悪いため、可能な限り回避する必要があります。

ゲームロジックループが内部でどのように機能するかについては何も言わなかったが、デルタ時間アプローチを使用している場合(実行する各計算には最後の呼び出しから時間がかかる)、非常に小さなデルタ-時間値。これは、あらゆる種類の奇妙な動作を引き起こす可能性のある浮動小数点の不正確さの問題に遭遇する可能性があることを意味します。また、システムタイマーの解像度は制限されることが多いため、ロジックループが速すぎると、デルタtの値がゼロになり、ゼロによる除算が発生してクラッシュする可能性があります。

この問題を軽減するには、フレームレート(グラフィックとロジック)を、人間の目が知覚できる最大値に制限する必要があります。それはどの程度議論されており、アニメーションの種類と表示されるディスプレイの種類によって異なりますが、推定は40 FPSから120 FPSの範囲です。つまり、各ループの最小実行時間を20ms〜8msに設定する必要があります。ループの反復がより速く終了する場合、CPUスレッドsleepに残りのタイムスパンを許可します。


フィリップの説明をありがとう。実際には、特定のfpsとtpsで実行しているという事実を参照し、1秒間に特定の回数だけポーリングを実行しました。しかしそれをより明確にしようとします。
dot_Sp0T 14

@ dot_Sp0T質問の編集後、最初の段落と最後の文のみがケースに関連します。ただし、回答の2番目と3番目の段落は、他の人にとって役立つ可能性があるため、保持したいと思います。
フィリップ14

最初の段落が完全によく答えているので、おそらくあなたの答えも受け入れます。どうにかして視覚的にそれを残りの回答と区別してください。
dot_Sp0T

パッチが適用されていないCivilization 2をデスクトップで再生すると、問題があることがわかります。少なくとも、私は。肩をすくめる
corsiKa 14

1
バッテリーとファンの考慮事項に加えて、CPU使用量の増加は、不快な過剰な熱を発生させる可能性があり(ラップトップ、特にrMBPなど)、PCがシャットダウンすることさえあります(特に、古いPCのほこりっぽいクーラー)。すべてのコアを100%実行すると、これらの要因はexします。
NPSF3000 14

2

CPUサイクルを無駄にしています。つまり、ノートブック、タブレット、電話のバッテリー時間が短くなり、電気代が高くなり、機械から発生する熱が多くなり、ファンの騒音が大きくなります。また、他の重要なシステムプロセス(ウィンドウサーバーがぎくしゃくする可能性があります)からのサイクルを食べている可能性があり、ゲームプレイに影響を与える可能性があります。今日のプリエンプティブマルチタスクシステムの一部のシステムスケジューラーは、あまりにも多くのサイクルを使用するアプリケーションにペナルティを科します。

また、多くの筋金入りのゲーマーは、ファンの仕様の端にあるカスタムPCをゼロから構築します。この場合、暑い日とゲームが発生する熱により、マシンで安全性がトリガーされるか(せいぜい)、部品が過熱し、 (最悪の場合)死にます。したがって、それがあなたのターゲットオーディエンスであるなら、あなたは常に「トップ」に少しのスペースを残していることを確実にしたいかもしれません。

最後に、ゲームプレイの問題があり、遅いマシンのマシンよりも速いマシンのプレーヤーを優先している可能性があります。特定の頻度でゲームループの上限を設定し、ゲームプレイに関係のない側面(忠実度の高いグラフィックス、サラウンドサウンドエフェクトなど)だけに追加のサイクルを使用すると、ゲームがより公平になります。


逆に、情熱的にマシンを構築する人々は、水冷さえも良いファンを使用すると言います。HTPCまたはミニITXの場合のみ、その部分に制限があります。さもなければ、貧しいけれども熱狂的な人々は箱の換気装置を使用します。100%でも十分で、暑いけど大丈夫です。
v.oddou 14年

私は経験から繰り返しています。Star Trek Onlineを見ると、実際にはGUIに個別の設定があり、メインループにsleep()を挿入して特定のマシンが過熱しないようにするため、NeverwinterのメーカーとSTOはそれを追加する必要性を認識しました。その情熱!=スキルに留意してください。多くの熟練した情熱的ないじくり手がいますが、まだ学んでいる初心者や他の人もいます。まともなドロップアウト率で、統計は彼らが大多数であると言います。
ウリウィットネス14年

これらのコメントのおかげで、私はあなたの答えを読み直しに来ました。有効なポイントをリストしますが、悲しいことに、高速のマシンと低速のマシンの論争で問題を実際に解決しません。ロジック・ループ(と呼ばれるgameloopあなたの第三段落の)がキャップされます。あなたのマシンがひどくラメでない限り、何より高速ロジック-質問は、単に入力を引くことがダメならば、ロジックを計算するか、フレームがレンダリングされるべきではないを通じてすべての実行を再評価バックボーンループをキャップについてだった
dot_Sp0T

1

不安定な動きやゲームプレイはないはずです

ゲームロジックを実装する方法は2つあります-リアルタイムに結び付けられるか、「ターン」/処理ステップの数に結び付けられます。ゲーム内の何かが左に動いている場合、stepLogic()が50回ではなく100回呼び出された場合、動きは異なりますか?

コードがどこでも経過時間を明示的に処理し、正しく処理する場合、問題ないかもしれません。しかし、コードに「ステップ」の数に依存するものがある場合、望ましくない副作用が発生します。

まず、絶えず動いているものの速度は(プロセッサの使用状況に応じて)予想外に変化し、これは非常に迷惑なコントロールになります-ゲームが突然スピードアップまたはスローダウンした場合、正確なパンチやジャンプを行うことはできません。「けいれん」がまったくないゲームであっても、物事がスムーズに動いていないと、面倒で不安になります。

次に、コンピューターの速度に応じてキャラクターの能力に問題が発生する可能性があります。典型的な例は、最大ジャンプ範囲が意図せずfpsに依存していた古いQuakeの問題です。

これらの問題は、コードをfpsに依存しないようにした場合でも発生することがあり、丸めエラーの蓄積により、このようなバグが発生することがよくあります。


1
私はあなたの答えが質問に関連していないので、私は不安な動き/ゲームプレイについてではなく、制御されていないループ(ロジックやレンダリングなどを制御しない)がシステムにできる害について尋ねているので
dot_Sp0T 14

1

唯一の潜在的な害は電力消費量であるため、モバイルデバイスでこれを実行しないでください。逆に、かなりの数の組み込みシステムは、物事が起こるのを待って一生をループで過ごします。

以前は、ゲームフレームを可能な限り高速に単純にレンダリングするために完全に正常でしたが、場合によっては、異なるCPU速度のゲームプレイを補正するタイマーさえありませんでした。BullfrogのレースゲームであるHi-Octaneは、これの特に悪い犠牲者であり、これはCiv2について言及しているポスターが言及しているものだと思います。

Windowsシステムなどを使用している場合は、少なくともすべてのパスで入力をポーリングして、迅速な入力応答を確保することをお勧めします。


タイマーの問題ではなく、私が言いたいクロノメーターの問題、またはパフォーマンスカウンターの問題です。ある時点でリアルタイムを取得する必要があり、フリーループは完全に機能します。ただし、タイマーは(定期的な中断の意味で)一般的な使用でループ速度を調整しようとしています。これは良いとは思わない。swap/ present/ flip関数を使用してvsync信号で待機し、ゲームループを調整することをお勧めします。
v.oddou 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.