Создание игрового цикла NSThread - PullRequest
1 голос
/ 02 января 2012

Я пытаюсь создать игровой цикл NSThread, у меня есть возможность получить 57 FPS.

Иногда мои фпс доходят до некоторого числа насмешек.

Я не понимаю, как это происходит.

Я проверяю, сколько времени прошло с последнего цикла, и если это было быстрым, спи нить столько времени.

Это не всегда происходит, иногда оно избегает проверки скорости if и делает цикл быстрым.

Любые комментарии будут означать много.

Также, где я нахожусь под «Тик»?

   - (void)gameLoop{
   //gameIsRunning is set to TRUE in viewDidLoad
   while (gameIsRunnning){
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    //Get Current date
    NSDate *curTime = [NSDate date];

    //Time since last loop and vurrent date;
    NSTimeInterval timePassed_ms = [curTime timeIntervalSinceDate:old_date];// * 1000.0;

    NSLog(@"***************");

    //Cout the time interval
    NSLog(@"Loop Time %f",timePassed_ms);

    //Check if the loop was to fast and sleep for long enough to make up for about 60 FPS
    if (timePassed_ms < 1.0/60) {
        double timeToSleep = timePassed_ms - (1.0/60);
        timeToSleep = timeToSleep*-1;
        NSLog(@"Sleep For %f",timeToSleep);
        [NSThread sleepForTimeInterval:timeToSleep];
    }

    //This new date is to try and check if after waiting the loop is taking the correct duration
    NSDate *newDate = [NSDate date];
    NSTimeInterval timePassed_after = [newDate timeIntervalSinceDate:curTime];// * 1000.0;

    //Make an fps out of this new time interval after wait
    double FPS = (1.0/timePassed_after);
    NSLog(@"FPS %f",FPS);
    NSLog(@"Adjusted Time %f",timePassed_after);

    NSLog(@"***************");

    //Reset olddate for next loop
    old_date = curTime;

    //Apparently this will capture touches and button events
    while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002, TRUE) == kCFRunLoopRunHandledSource);

    //A test on moving a ball to see how smooth it will be
    [self performSelectorOnMainThread:@selector(moveBall) withObject:nil waitUntilDone:NO];

   [pool drain];

  } 

}

Ответы [ 4 ]

1 голос
/ 04 января 2013
- (void) gameLoop 
{

    NSAutoreleasePool* loopPool = [NSAutoreleasePool new];

    int loopCnt = 0;

    while ( isRunning ) {

    while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002f, TRUE) == kCFRunLoopRunHandledSource);

        [self draw];

        select(0, 0, 0, 0, &tm);

        if ( loopCnt > 20000 ) {      // 20000
            loopCnt = 0;
            [loopPool release];
            loopPool = [NSAutoreleasePool new];
        }
        ++loopCnt;

        while(CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.002f, TRUE) == kCFRunLoopRunHandledSource);

        }   

    [loopPool release];
}
1 голос
/ 02 января 2012

Я рекомендую перейти на CADisplayLink API ( документы ).Он создает таймер, который автоматически срабатывает так же часто, как обновляется дисплей, без необходимости выяснять, как долго спать.Это решит проблему доставки событий «обновления» в ваш код, но не решит все ваши проблемы.

Очевидно, что если ваш код не может завершиться за 1/60 секунды, вы не получите 60кадров в секунду.Убедитесь, что ваша игровая логика и физика не привязаны к частоте обновления видео.Некоторые люди не согласны, является ли CADisplayLink правильным поступком.Тем не менее, согласованная альтернатива заключается в обновлении настолько быстро, насколько позволяет аппаратное обеспечение.

В прошлом году я переключил цикл рендеринга в игрушечной игре, для которой мне пришлось использовать ссылку на дисплей (Mac, а не iOS).Я заметил значительное улучшение в том, как «гладко» чувствуется игра.Ваши результаты могут отличаться.

Вот один из способов сделать это (полупсевдокод, упрощенный):

- (void)update:(CADisplayLink *)link {
    now = gettime();
    while (gametime < now) {
        // Physics always updated at rate of 1/delta
        advanceframe();
        gametime += delta;
    }
    draw();
}
1 голос
/ 02 января 2012

Вы не должны полагаться на спящий поток, потому что вы никогда не можете быть уверены, что это займет столько же времени.

Таким образом, вместо того, чтобы усыплять поток, ничего не делайте с ним, вообще ничего (кроме, конечно, увеличения вашего фиксированного временного шага)

Тогда вы обнаружите, что частота кадров будет намного более плавной.

Также как примечание, не используйте FPS в качестве индикатора производительности. Используйте количество времени, которое потребовалось для завершения одного обновления.

Если вы стремитесь при скорости 60 кадров в секунду, время обработки цели должно быть 0,01666 * секунд. На самом деле вы должны иметь возможность увеличить время обработки до 0.02555 *, что составляет 40 кадров в секунду, и в игре не должно быть заметного падения производительности

РЕДАКТИРОВАТЬ: я также заметил, что вы создаете новый пул и сливать каждый раз при обновлении, по моему опыту пулы авто-релиза должны быть размещены на более высоких уровнях, таких как appDelegate. Но я бы не стал считать его ниже уровня создания (создания) / выпуска (истощения), а его дальнейшее повышение также поможет с производительностью.

0 голосов
/ 02 января 2012

timeIntervalSinceDate: возвращает интервал в секундах, а не в миллисекундах.

(Я хотел написать это в небольшом комментарии, а не в реальном ответе, но не мог понять, как это сделать ...)

...