iOS游戏和运行循环管理

Mar*_*ark 28 iphone optimization opengl-es runloop ios

首先,我的问题是:你如何管理你的iOS Run-Loop?

接下来我的理由是:我一直在研究各种原型(v.早期开发),并发现了许多令人困惑的问题.

  • 首先,输入问题和运行循环让我尝试以下方法:
    • 当使用最推荐的系统(CADisplayLink)时,我注意到一旦CPU负载导致缓冲区翻转(presentRenderBuffer)必须等待一帧,某些触摸输入就会被丢弃.这只发生在设备而不是模拟器中(烦人的 - 这似乎与等待主线程上的vsync阻塞以及应用程序运行循环过程触摸输入和吃消息的方式有关)
    • 当使用下一个最推荐的系统(NSTimer)时,我注意到一旦CPU负载达到模拟器中的某个点而不是设备中的某个点(也令人讨厌),某些触摸输入就会被丢弃.NSTimer在我的更新发布时也会导致精度降低
    • 当使用推荐最少的系统(在内部使用由mach_absolute_time构建的高精度定时器管理自己的线程中运行运行循环时,我的所有触摸输入问题都会消失,但是我的ASSERT代码现在陷入了错误的线程,只有我睡着了跟随软件中断.(我的断言代码类似于http://iphone.m20.nl/wp/?p=1)我真的很喜欢我的断言代码陷阱导致问题,所以这个解决方案是对我来说不太适合:更难调试.
  • 第二,失去的时间:
    • 在调查系统时,我发现无论帧率如何(奇怪的是,但我认为统计上它仍然有意义w/vsync)我在vsync上等待大约22%的时间.我已经通过移动glFlush/glFinish并通过玩我多久经常进行presentRenderBuffer调用来证实这一点.这是我喜欢处理AI等关键时刻,而不是简单地停止阻塞gl调用.我能想到的唯一方法就是将渲染转移到它自己的线程中,但我不确定是否有理由开始在单处理器设备上重新构建多线程.

那么有没有人围绕这些问题找到了灵丹妙药?有没有人在这个平台上有一个杀手运行循环架构?目前看起来我必须选择较少的邪恶.

Iva*_*ica 6

对于我自己的iOS项目,我使用经典方法(创建一个窗口.nib,创建一个继承的类EAGLView,添加EAGLView到视图控制器中的视图,该视图控制器放在它自己的.nib中).

在工作中,我采用了一种略微不同的方法,灵感来自SDL,您可以在我们的开源库APRIL中进行检查.APRIL的主要目标是支持尽可能多的平台,同时保持简单性(仅限窗口和输入管理)并明确许可问题并免费使用.我们的开发人员希望在一个平台(Windows,Mac或Linux,根据品味和愿望)编写应用程序,然后将代码移交给我以适应其他平台.

在我们在APRIL中使用的方法中,您不创建任何.nib,并且在调用时UIApplicationMain,您将委托类指定为其第四个参数.对于每个平台,游戏的主要代码保持完全相同,并且只有特定于平台的内容才会#ifdef进入代码,或者在帮助程序库中进行抽象.

在app delegate中,您可以创建视图控制器和窗口:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    // create a window.
    // early creation so Default.png can be displayed while we're waiting for 
    // game initialization
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    // viewcontroller will automatically add imageview
    viewController = [[AprilViewController alloc] initWithWindow:window];
    [viewController loadView];

    // set window color
    [window setBackgroundColor:[UIColor blackColor]];

    // display the window
    [window makeKeyAndVisible];

    // thanks to Kyle Poole for this trick
    // also used in latest SDL
    // quote:
    // KP: using a selector gets around the "failed to launch application in time" if the startup code takes too long
    // This is easy to see if running with Valgrind

    [self performSelector:@selector(runMain:) withObject:nil afterDelay:0.2f];
}
Run Code Online (Sandbox Code Playgroud)

请注意我们如何延迟启动0.2?这就是为什么我提到上面的图像视图.在这0.2秒内,我们在Default.png之后立即显示空白屏幕,并且在控制转移到runMain:之前引入了额外的延迟,它将控制释放到主应用程序:

- (void)runMain:(id)sender
{       
    // thanks to Kyle Poole for this trick
    char *argv[] = {"april_ios"};
    int status = april_RealMain (1, argv); //gArgc, gArgv);
#pragma unused(status)
}
Run Code Online (Sandbox Code Playgroud)

所以,现在控件永远不会转移回UIApplication的实际主循环.然后创建自己的主循环.

    void iOSWindow::enterMainLoop()
    {
            while (mRunning) 
            {
                    // parse UIKit events
                    doEvents();
                    handleDisplayAndUpdate();
            }
    }

    void iOSWindow::doEvents()
    {
            SInt32 result;
            do {
                    result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE);
            } while(result == kCFRunLoopRunHandledSource);
    }
Run Code Online (Sandbox Code Playgroud)

(在旁注中,当然使用视图控制器来简化UI的旋转以匹配设备方向.)

CADisplayLink如果操作系统支持,这两种方法都会使用.我没有注意到任何一种方法的任何问题,虽然我的私人项目主要是基于加速计.我怀疑APRIL方法可能会使一些问题消失.