Spellus met afzonderlijke timer voor rendering en gamelogica

Ik wil de spellogica en de weergave in twee verschillende loops scheiden, omdat ik niet wil dat de fps de spelsnelheid regelt. Ik probeerde dit te bereiken door een CADisplayLink te maken voor de rendering en een NSTimer voor de spellogica. Maar toen gebeurde er iets vreemds:

Soms (1 van de 15 applicatie-runs) draait het spel op een zeer lage fps (ongeveer 5-10), maar de rest van de tijd is het volledig soepel. Als ik de NSTimer van de spellogica verwijder en de twee lussen combineer, is de fps constant hoog, maar het is duidelijk geen acceptabele oplossing. Dus het lijkt erop dat soms de twee timers elkaar 'uitstellen' of iets dergelijks, maar ik begrijp de interne werking van runloops niet helemaal.

Zo maak ik de timer en de displaylink:

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;

Kun je me vertellen wat het fps-probleem veroorzaakt en hoe het te repareren?

Of kunt u andere oplossingen aanbevelen om de rendering en de logicakringloop te scheiden?

6
Het is niet de bedoeling dat je twee loops maakt om de spelsnelheid en fps te verwijderen ...
toegevoegd de auteur thedaian, de bron
Hoe kan ik het dan met één lus doen?
toegevoegd de auteur mikabast, de bron

3 antwoord

Je kunt dit met één lus doen met je gameTimer of met de CADisplayLink door de verstreken tijd sinds de laatste lus te meten en deze te gebruiken om je spellogica te vergroten.

Zo..

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];
}

Dat is meestal de manier waarop ik met dit soort dingen omga. Ik vind het meestal leuk om update-functies te bouwen in mijn game-objecten. Dus het bijwerken van het schip zou er eigenlijk zo uit zien:

[ship update:timePassed_mc];
3
toegevoegd
Bedankt voor de suggestie. Het inspireerde mijn uiteindelijke oplossing (zie hieronder).
toegevoegd de auteur mikabast, de bron

Dus uiteindelijk gebruikte ik wat Andrew Zimmer suggereerde, met een paar kleine aanpassingen, omdat ik mijn game-objecten samen in gelijke intervallen bijwerk.

Dus ik gebruik slechts één lus, degene die werd gelanceerd door de CADisplayLink. Hier is de definitieve code:

- (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
toegevoegd
Blij dat dit voor u werkt! Houd er rekening mee dat de prestaties van de updateGame-functie (zoals botsingsdetectie tussen veel objecten) mogelijk aanzienlijk minder goed werken. Proost!
toegevoegd de auteur Andrew Zimmer, de bron

Wees je ervan bewust dat NSTimer je geen betrouwbare timing zal geven. Het is dichtbij, maar niet gegarandeerd, en als je een gamelus uitvoert, krijg je af en toe een afgebroken frame.

Je zou je game logica in een thread kunnen doen, en slapen tot het volgende frame (dit zal niet een constante hoeveelheid tijd zijn).

0
toegevoegd
NSTimer is niet echt betrouwbaar, maar vanuit een gebruikersperspectief maakt 56 updates per seconde of 64 updates per seconde niet echt een verschil. Maar als ik de game-objecten alleen bijwerk als ik teken, kan deze op 20 seconden of 20 seconden naar 20 of 30 updates op langzamere apparaten vallen, wanneer het tweemaal zo snel op snellere apparaten draait.
toegevoegd de auteur mikabast, de bron