レンダリングとゲームロジック用の独立したタイマーを備えたゲームループ

ゲームロジックとレンダリングを2つのループに分けたいと思っています。なぜなら、fpsでゲームの速度を制御したくないからです。レンダリング用のCADisplayLinkとゲームロジック用のNSTimerを作成してこれを達成しようとしました。しかし、奇妙なことが起こった:

場合によっては(15回のアプリケーション実行のうち1回)、ゲームは非常に低いfps(約5-10)で実行されますが、それ以外の時間は完全にスムーズです。ゲームロジックのNSTimerを削除して2つのループを結合すると、fpsは一貫して高くなりますが、明らかに許容できる解決策ではありません。だから、時には2人のタイマーが「お互いに遅れている」というようなことがあるように見えますが、私はランルーループの内部作業を完全には理解していません。

タイマーとディスプレイリンクを作成する方法は次のとおりです。

NSTimer *gameTimer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:1.0/60.0 target:self selector:@selector(gameTimerFired:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:gameTimer forMode:NSDefaultRunLoopMode];
[gameTimer release];

CADisplayLink *aDisplayLink = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(drawFrame)];
[aDisplayLink setFrameInterval:animationFrameInterval];
[aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
self.displayLink = aDisplayLink;

fpsの問題の原因と解決方法を教えてください。

または、レンダリングとゲームのロジックループを分離するための他のソリューションをお勧めしますか?

6
あなたはゲーム速度とfpsを切り離すために2つのループを作成するはずではありません...
追加された 著者 thedaian,
次に、1つのループでどのように行うことができますか?
追加された 著者 mikabast,

3 答え

これは、gameTimerまたはCADisplayLinkのいずれかを使用して、最後のループから経過した時間を測定し、それを使用してゲームロジックを強化することによって、1つのループで実行できます。

そう..

NSDate *oldTime = [[NSDate date] retain];

-(void)updateGame {
    NSDate *curTime = [NSDate date];
    NSTimeInterval timePassed_ms = [curTime timeIntervalSinceDate:oldTime] * 1000.0;

    [oldTime release];
    oldTime = curTime;
    [oldTime retain];

    //use the timePassed_ms to augment your game logic.  IE: Moving a ship
    [ship moveLength:ship.velocity * timePassed_ms/1000.0];
}

これは通常、私がこの種のものを扱う方法です。私は通常、自分のゲームオブジェクトに更新機能を組み込むのが好きです。したがって、船の更新は実際には次のようになります。

[ship update:timePassed_mc];
3
追加された
提案していただきありがとうございます。それは私の最終的な解決策に影響を与えました(下記参照)。
追加された 著者 mikabast,

だから私はゲームのオブジェクトを等間隔で一緒に更新するので、いくつかの小さな修正を加えてAndrew Zimmerが提案したものを使い終えました。

ですから、私はCADisplayLinkによって起動されたループを1つだけ使用しています。ここに最終コードがあります:

- (void)drawFrame
{
    if (!isGameTimerPaused)
    {
        NSDate *newDate = [NSDate date];
        timeSinceLastUpdate += [newDate timeIntervalSinceDate:lastUpdateDate];

        while (timeSinceLastUpdate > 1.0/60.0) 
        {
            [self updateGame];//UPDATE GAME OBJECTS
            timeSinceLastUpdate -= 1.0/60.0;
        }

        [lastUpdateDate release];
        lastUpdateDate = [newDate retain];
    }


   //DRAWING CODE HERE
    (...)
    //
}
1
追加された
これはあなたのために働いてうれしい! updateGame関数で多くの処理(多くのオブジェクト間の衝突検出など)を行っていると、パフォーマンスが低下することがあります。乾杯!
追加された 著者 Andrew Zimmer,

NSTimerはあなたに信頼できるタイミングを与えるものではないことに注意してください。それは近いものの、保証はされていません。ゲームループを実行すると、時折落ち込んだフレームが得られます。

ゲームロジックをスレッドで実行し、次のフレームまでスリープ状態にすることができます(これは一定時間ではありません)。

0
追加された
NSTimerは本当に信頼できるものではありませんが、ユーザーエクスペリエンスの観点から、1秒あたり56回の更新または1秒あたり64回の更新は実際には違いはありません。しかし、私が描くときだけゲームオブジェクトを更新すれば、遅いデバイスでは秒あたり20または30のアップデートに落ちる可能性があります。
追加された 著者 mikabast,