【问题标题】:How make a game loop on the iPhone without using NSTimer如何在不使用 NSTimer 的情况下在 iPhone 上制作游戏循环
【发布时间】:2009-08-29 11:43:51
【问题描述】:

为了将我的游戏干净利落地移植到 iPhone,我正在尝试制作一个不使用 NSTimer 的游戏循环。

我在一些示例代码中注意到,如果使用 NSTimer,你会在开始时设置类似

    self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:@selector(drawView) userInfo:nil repeats:YES];

drawView 看起来像这样:


- (void)drawView 
{
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    mFooModel->render();
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

当使用这种技术时,mFooModel 渲染得很好,但我想制作自己的游戏循环来调用 drawView,而不是让 NSTimer 每秒调用 drawView 60 次。我想要类似的东西:


while(gGameState != kShutDown)
{
    [self drawView]
}

不幸的是,当我这样做时,我得到的只是黑屏。为什么会这样?无论如何我可以实现我在这里描述的内容吗?

我想避免使用 NSTimer 的原因是因为我想在游戏循环中进行物理和 AI 更新。我使用自己的时钟/计时器来跟踪已经过去的时间,以便我可以准确地做到这一点。渲染尽可能快地进行。我尝试使用this article中描述的一些技术

这是一个有点冲动的问题(你在写了一整天代码后遇到的问题,卡住了,希望早上答案就在那里)

大家好。

【问题讨论】:

    标签: iphone opengl-es loops


    【解决方案1】:

    iPhoneOS 3.1 的另一个选项是使用新的 CADisplayLink api。这将在需要更新屏幕内容时调用您指定的选择器。

    displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(renderAndUpdate)];
    [displayLink setFrameInterval:2];
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    

    如果您需要更多示例代码,XCode 中的新 OpenGL 项目模板也会使用 CADisplayLink。

    【讨论】:

    • 是否需要为 displaylink 添加 NSAutoreleasePool
    • NSAutoreleasePool 不是必需的。在 XCode 中创建一个新的基于 OpenGL 的项目,您将了解如何使用它。
    【解决方案2】:

    如果您不想使用NSTimer,可以尝试手动运行NSRunLoop

    static BOOL shouldContinueGameLoop;
    static void RunGameLoop() {
        NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
        NSDate *destDate = [[NSDate alloc] init];
        do {
            // Create an autorelease pool
            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
            // Run the runloop to process OS events
            [currentRunLoop runUntilDate:destDate];
            // Your logic/draw code goes here
            // Drain the pool
            [pool drain];
            // Calculate the new date
            NSDate *newDate = [[NSDate alloc] initWithTimeInterval:1.0f/45 sinceDate:destDate];
            [destDate release];
            destDate = newDate;
        } while(shouldContinueGameLoop);
        [destDate release];
    }
    

    【讨论】:

    • 我忘了警告如果你的工作超过 1/45 秒(或者 runloop 停止超过这个时间)你会遇到卡顿、延迟触摸事件和其他奇怪的问题。一定要彻底测试。更新代码以解决这个问题是可能的,但特定于应用程序
    • run loop中所有的alloc/dealloc不是都影响性能吗?有没有办法将所有内容移出循环?
    • 几乎没有。池在那里,因此如果运行循环创建临时对象,它们将被释放而不是构建。两个NSDates 是九牛一毛。
    • 我很抱歉这个愚蠢的问题,但是这个代码必须插入到项目的哪个地方?它会覆盖一些可可类的一些静态方法和道具吗?还是我应该明确调用 RunGameLoop?
    【解决方案3】:

    虽然使用 CADisplayLink 是基于 3.1 的游戏的一个非常好的替代方案,但
    任何使用“计时器”的东西都是一个非常糟糕的主意。

    最好的方法是解耦 GPU 工作的老式“三重缓冲”。

    Fabien 在他的 Doom Iphone 评论中有一个很好的解释:
    http://fabiensanglard.net/doomIphone/

    【讨论】:

      【解决方案4】:

      注意使用self 作为displayLinkWithTarget 的目标,手册上说“新建的显示链接保留目标”。 马丁

      【讨论】:

        【解决方案5】:

        关于 Fabien 的 CADisplayLink 和 iPhone 的 Doom 文章,我给 Fabien 发了电子邮件,我认为最好的选择是主观的。性能方面的 DisplayLink 和三重缓冲应该相同,但 DisplayLink 仅适用于 > OS 3.1。所以这应该是你的决定因素。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多