Рендеринг петли в игре

Мне нужно организовать цикл рендеринга в моем приложении определенным образом (есть причины).

Допустим, у меня есть

Sprite *sprite = [[Sprite alloc] initWithContentsOfFile:@"sprite.png"]; // some sprite

while (true) {
    [Graphics update];
    sprite.x = (sprite.x + 1) % 1024 // moving sprite by one pixel each frame
    [Input update];
    [self update];
}

Там Graphics.update должен визуализировать один кадр и задержать выполнение (не рендеринг) до следующего кадра

@interface Graphics () {
    static BOOL _frozen;
    static int _count;
    static int _frameCount;
    static float _frameRate;
    static double _lastFrameTimestamp;
}

@end

@implementation Graphics

+ (void)initialize {
    _frozen = NO
    _count = 0
    _frameCount = 0
    _frameRate = 30.0
    _lastFrameTimestamp = CACurrentMediaTime()
}

+ (void)freeze {
    _frozen = YES;
}

+ (void)update {
    if _frozen
      return
    end

    Video.nextFrame // some OpenGL ES magic to render next frame

    _count++

    now = CACurrentMediaTime()
    waitTill = _lastFrameTimestamp + _count * (1.0 / _frameRate)

    if now <= waitTill
      sleep(waitTill - now)
    else
      _count = 0
      _lastFrameTimestamp = CACurrentMediaTime()
    end

    _frameCount++
}

@end

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

Вот что я пытаюсь портировать: https://bitbucket.org/lukas/openrgss/src/7d9228cc281207fe00a99f63b507198ea2596ead/src/graphics.c (функцияGraphics_update)

2 ответа

Решение

Если вы используете Sparrow, вот как вы должны его обрабатывать:

SPSprite *sprite = [[SPSprite alloc] initWithContentsOfFile:@"sprite.png"]; // some sprite

[self addEventListener:@selector(enterFrame:) atObject:self forType:SP_EVENT_TYPE_ENTER_FRAME];

-(void)enterFrame:(SPEnterFrameEvent*)e {
    [Graphics update];
    sprite.x += 1; // moving sprite by one pixel each frame
    [Input update];
    [self update];
}

Воробей управляет игровым циклом для вас, используя SP_ENTER_FRAME_EVENT. Он будет вызываться каждый раз, когда придет время рендеринга. Примерно 30 раз в секунду (хотя это можно настроить).

Вы можете попробовать использовать Core Animation DisplayLink вместо цикла while. Вот как это обычно делается в графических рамках. CurrentRunLoop вызывает ваш метод обновления каждые 1/60 секунды.

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

CADisplayLink *displayLink;

// Set your update method
displayLink = [CADisplayLink displayLinkWithTarget:[Graphics class] 
                                          selector:@selector(update)];
// Set fps to device refresh rate (60)
[displayLink setFrameInterval:1.0f];

// Add your display link to current run loop
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

// Stop updating
[displayLink invalidate];

Последняя строка останавливает выполнение, поэтому не вызывайте его, пока не закончите цикл.

Другие вопросы по тегам