2012-01-02 5 views
1

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

Некоторое время, когда мои fps поднимаются до некоторого числа смешных.

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

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

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

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

И где я подходила к 'Tick'?

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

    } 

} 
+0

Другие темы или даже процессы на вашем компьютере может влияют на время этого потока. Что еще происходит в вашем приложении? – user1118321

+0

Только метод перемещает мяч. – Necro

ответ

1

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

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

Вы найдете у вас будет гораздо более гладкой Frame Rate затем.

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

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

EDIT: Я также заметил, что вы создаете новый пул и сливаете каждый раз, когда обновление попадает , в моем опыте пулы авторефератов должны быть размещены на более высоких уровнях таких приложений appDelegate. Но я бы не принял его ниже, чем создание уровня (создание)/release (утечка), и продвижение этого дальше также поможет в производительности.

+0

Но как я равномерно увеличиваю фиксированное время в потоке? это будет как можно быстрее. Я не могу видеть, как будет увеличиваться время в нем. Разве это не сразу перейдет в тысячи? – Necro

+0

Вы не равномерно увеличиваете его, увеличиваете его на разницу во времени с конца предыдущего игрового времени (потребовалось для завершения). Затем, после того, как вы сделали свое обновление (даже если вы ничего не сделали), подсчитайте, сколько времени прошло, и следующее обновление будет увеличено на это. Это приведет к значительно более плавной частоте кадров – CStreel

+0

Вы уверены, что перемещение пула не вызовет больше лаг, потому что количество объектов в бассейне? iv видно, что люди заставляют даже смывать пул и создавать новые в цикле более одного раза. – Necro

1

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

Очевидно, что если ваш код не может закончить через 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(); 
} 
+0

, когда речь идет о играх, использование фиксированного timestep является широко распространенным методом. Это позволяет делать вещи, такие как физика и события времени в играх, более детерминированными. Это также хорошая практика, так как позволяет вам разбить свою логику игры из вашей физики и/или логики связи, что само по себе еще больше повышает производительность и способность поддерживать код – CStreel

+0

@CStreel: Я полностью согласен. Однако вы не можете (легко) гарантировать фиксированную частоту обновления видео, поэтому я предлагаю отключить физику и логику игры от обновления дисплея. –

0

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

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

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