Рендеринг петли в игре
Мне нужно организовать цикл рендеринга в моем приложении определенным образом (есть причины).
Допустим, у меня есть
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];
Последняя строка останавливает выполнение, поэтому не вызывайте его, пока не закончите цикл.