iOSゲームと実行ループ管理 質問する

iOSゲームと実行ループ管理 質問する

まず、質問です。iOS の Run-Loop をどのように管理していますか?

次に理由を挙げます。私はさまざまなプロトタイプ(開発初期段階)でこのことを研究してきましたが、いくつかの不可解な問題を発見しました。

  • まず、入力の問題と実行ループにより、次のことを試すことになりました。
    • 最も推奨されるシステム (CADisplayLink) を使用すると、CPU 負荷によってバッファ フリップ (presentRenderBuffer) が 1 フレーム待機しなければならなくなると、特定のタッチ入力がドロップされることに気付きました。これはデバイス上でのみ発生し、シミュレーターでは発生しません (厄介なことに、これはメイン スレッドでの vsync ブロックの待機と、アプリの実行ループがタッチ入力を処理してメッセージを消費する方法に関連しているようです)
    • 次に推奨されるシステム(NSTimer)を使用すると、シミュレータではCPU負荷が一定のポイントに達すると特定のタッチ入力がドロップされるが、デバイスではドロップされないことに気付きました(これも厄介です)。NSTimerでは、更新が実行されるタイミングの精度も大幅に低下します。
    • 最も推奨されないシステム(mach_absolute_timeから構築された高精度タイマーで内部的に管理される独自のスレッドで実行ループを実行する)を使用すると、タッチ入力の問題はすべて解消されますが、私のアサートコードは間違ったスレッドでトラップされ、ソフトウェア割り込みの後にスリープする場合にのみ発生します。(私のアサートコードはhttp://iphone.m20.nl/wp/?p=1) 私はアサート コードが問題の原因となった行ですぐにトラップされるようにしたいので、この解決策は私にとっては実際には実行可能ではありません。デバッグが難しくなります。
  • 第二に、失われた時間:
    • システムを調べているうちに、フレームレートに関係なく (奇妙ですが、統計的には vsync ではまだ意味があると思います)、vsync で約 22% の時間を待機していることがわかりました。glFlush/glFinish を移動し、presentRenderBuffer 呼び出しの頻度を調整することで、これを確認しました。これは、ブロッキング gl 呼び出しで単に停止するのではなく、AI などを処理したいと思う重要な時間です。これを回避する方法として思いつくのは、レンダリングを独自のスレッドに移動することですが、シングル プロセッサ デバイスでマルチスレッドの再設計を開始する価値があるかどうかはわかりません。

では、これらの問題を回避する魔法の解決策を見つけた人はいますか? このプラットフォームで最高に優れた実行ループ アーキテクチャを持っている人はいますか? 現時点では、よりましな方を選ぶ必要があるようです。

ベストアンサー1

私自身の iOS プロジェクトでは、従来のアプローチ (ウィンドウ .nib を作成し、継承するクラスを作成しEAGLViewEAGLView独自の .nib に配置されたビュー コントローラーのビューに追加する) を使用します。

仕事では、オープンソースのライブラリで調べることができるSDLに触発された少し異なるアプローチを採用しました。4月APRIL の主な目標は、シンプルさ (ウィンドウと入力管理のみ) を維持しながら、ライセンスの問題を明確にし、無料で使用できるようにしながら、できるだけ多くのプラットフォームをサポートすることです。当社の開発者は、1 つのプラットフォーム (好みや要望に応じて、Windows、Mac、または Linux) でアプリを作成したいと考えています。その後、コードは私に引き渡され、他のプラットフォームに適応します。

APRIL で使用しているアプローチでは、.nibs を作成せず、 を呼び出すときにUIApplicationMain、デリゲート クラスを 4 番目の引数として指定します。ゲームのメイン コードは各プラットフォームでまったく同じままであり、プラットフォーム固有のものだけが#ifdefコードに組み込まれるか、ヘルパー ライブラリで抽象化されます。

アプリ デリゲートで、ビュー コントローラーとウィンドウを作成します。

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

起動を 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)
}

したがって、制御は 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);
    }

(ちなみに、ビュー コントローラーは、もちろん、デバイスの向きに合わせて UI の回転を簡素化するために使用されます。)

これら両方のアプローチは、CADisplayLinkOS でサポートされている場合に使用します。私のプライベート プロジェクトは主に加速度計に基づいていますが、どちらの方法でも問題は発生していません。APRIL アプローチを使用すると、問題の一部も解消されるのではないかと考えています。

おすすめ記事