12

ゲーム ライブラリを iPhone に移植しようとしています。SDL とは異なり、このライブラリは main() 関数を完全に制御するわけではなく、独自のコードからすばやく返される関数を介して通信します。たとえば、明らかな疑似コードは次のとおりです。

int main() {
  library_init();
  // game init code here
  while(we_have_not_quit_the_game) {
    library_message_loop();
    library_init_render();
    // render stuff
    library_end_render();
    // update game state
  }
  library_shutdown();
}

iPhone では、返されない UIApplicationMain 関数を呼び出す必要があるため、これが困難になります。library_init(); の後でユーザー コードに戻る方法はまったくありません。

私はそれが必要であるとは確信していません-イベントを処理するために使用できると思われる NSRunLoop があります。ただし、UIApplicationMain が他に重要なことを行っているかどうかはわかりません。(私は .nib ファイルを使用する予定がないことに注意してください.nib ファイルは、UIApplicationMain が行うことがわかっている他の唯一のものです。)

思いつく現実的なアイデアが 3 つありますが、どれも大がかりな実装作業であるため、失敗したアイデアを試して 1 日を費やす前に、これを経験した人がいるかどうかを知りたいと思います。

  • Init で新しいスレッドを生成し、そのスレッドで UIApplicationMain を実行します。スレッド間ですべてのイベントを通信するか (ugh)、単に UIApplicationMain スレッドをスリープ状態にして、メイン スレッドで CFRunLoop を使用します。ただし、UIApplicationMain は別のスレッドで実行されることを好まないと聞きました。
  • UIApplicationMain を完全に無視し、NSRunLoop を使用してください。重要な iPhone の設定を忘れてしまうのでしょうか? 知るか!
  • セットアップ後に UIApplicationMain コードから飛び出すために longjmp() で何か恐ろしいことを行い、ティアダウン中に重要なことを何もしないことを祈ります。

提案?

4

4 に答える 4

5

ここで自分の質問に答えているようです!実際のハードウェアでテストし、アプリストアに入れることができるようになるまで、回答を受け入れません. そうは言っても、どのオプションが機能しなかったかなど、最新の情報をここに残しておきます。

アイデア #1 : 各 NSRunLoop はスレッド固有であることが判明しました。別のスレッドで UIApplicationMain を作成すると、メッセージが表示されません。副作用として、これにより初期化がいつ終了したかを判断できなくなります。そのため、スレッドセーフではない何かがある場合、それは機能しません。スレッド間でメッセージを送信して、いつ初期化が終了したかを把握できるかもしれませんが、今のところ、これを行き止まりと呼んでいます。

アイデア #2 : UIApplicationMain は多くの微妙なことを行います。何が制限されているのかわかりませんが、UIApplicationMain を使用しないと何も機能しませんでした。アイデア#2はすぐに出てきました。

アイデア #3 : OS シグナルの受信は重要です。通話オーバーレイがあるかどうか、または終了しようとしているかどうかを知る必要があります。その上、セットアップ メッセージのいくつかは、アプリを適切に起動するために不可欠なようです。UIApplicationMain 内にいなくてもメッセージを送信し続ける方法を見つけることができませんでした。私が思いついた唯一のオプションは、NSRunLoop と CFRunLoop でした。どちらも機能しませんでした - メッセージは私が望んでいたようには届きませんでした。私はこれらを正しく使用していないかもしれませんが、いずれにせよ、アイデア #3 が出ています。

まったく新しいクレイジーなアイデア #4 : setjmp/longjmp を使用して、C/C++ でコルーチンを偽造することが可能です。トリックは、最初にスタック ポインターを重要なものを壊さない値に設定し、次に 2 番目のルーチンを開始してから、スタックが 2 つあるふりをして前後にジャンプすることです。「2 番目のコルーチン」がメイン関数から戻ることを決定した場合、事態は少し混乱しますが、幸いなことに、UIApplicationMain は決して返らないため、これは問題ではありません。

実際のハードウェアでスタック ポインターを明示的に設定する方法があるかどうかはわかりません。たとえば、その場で割り当てたデータのチャンクに設定する方法はありません。幸いなことに、それは問題ではありません。iPhone にはデフォルトで 1MB のスタックがあり、いくつかのコルーチンを簡単に収めることができます。

私が現在行っていることは、alloca() を使用してスタック ポインターを 768 キロバイト先にプッシュし、次に UIApplicationMain を生成し、setjmp/longjmp を使用して「UI ルーチン」と「メイン ルーチン」の間を行ったり来たりすることです。これまでのところ、これは機能しています。

警告:

  • 「UI ルーチン」に処理するメッセージがない場合を知ることは不可能であり、処理するメッセージがない場合は、それがなくなるまで無期限にブロックされます。0.1ミリ秒ごとにトリガーするタイマーを作成することでこれを解決しています。タイマーがトリガーされるたびに、「メイン ルーチン」に戻り、1 つのゲーム ループを実行してから、「UI ルーチン」に戻って別のタイマー ティックを行います。ドキュメントを読むと、「タイマー呼び出し」が無限に積み重ならないことがわかります。「終了」メッセージを適切に取得しているようですが、まだ完全にテストすることはできておらず、他の重要なメッセージもテストしていません。(幸いなことに、合計 4 つのメッセージしかなく、そのうちの 1 つはセットアップ関連です。)

  • 最近のほとんどの OS は、スタック全体を一度に割り当てることはありません。iPhoneはおそらくその1つです。私が知らないのは、スタック ポインターを 3/4 メグ前方にバンプすると、いわば「その後ろ」にすべてが割り当てられるかどうかです。もしそうなら、私は事実上 4 分の 3 メガの RAM を浪費している可能性があり、これは iPhone ではかなりの量です。これは、ポインタを少しだけ前方にバンプすることで処理できますが、これは実際にはスタックサイズの災害を助長します.スタックを事実上、ポインタを前方にバンプするだけに制限するため、事前にこれを把握する必要があります. スタック内のいくつかのセンチネル データと、適切な監視およびスタック サイズの問題のログ システムを組み合わせることで、おそらくこれを解決できますが、これは重要な問題です。(または、スタック ポインターをネイティブ ハードウェアで直接いじる方法がわかれば、malloc()/new[] 数キロバイトをスタック ポインターでポイントし、それを新しいスタックとして使用するだけです。どのくらいのスペースが必要かを把握する必要がありますが、あまり機能していないことを考えると、それほど多くはないと思います。)

  • これは現在、実際のハードウェアでテストされていません (1 週間か 2 週間待ってください。最初に別のプロジェクトを完成させる必要があります)。

  • Apple が私が何をしているのかを把握し、App Store に提出しようとしたときに巨大な REJECTED ステッカーを貼るかどうかはわかりません。これは、API に対する彼らの意図からわずかに外れていると言えます。成功を祈っている。

この投稿を更新し続け、機能することを確認したら、正式に受け入れます。

更新が遅くなりました: 他にもいろいろと気を散らしてしまいました。それ以来、私はいくつかの変化を経験してきたが、その結果、Apple の開発にはあまり興味がなくなった。私の現在のアプローチはうまくいかない兆しを見せていませんでしたが、それを肉付けし続ける動機は本当にありません. ごめん!気が変わったら、これをさらに更新しますが、Outlook はあまり良くありません。

于 2010-02-06T05:55:03.880 に答える
1

私はNSApplicationMain()Info.plistを読んでアプリに何かをすることを知っているので(最初のnibファイルをUIApplicationMain()取得するなど)、iPhoneでも同じことをすると思います(最初のステータスバースタイルを取得する、default.png画像をズームインするなど)。そのようなものは他のどこにも公開されていないため、副作用なしでアプリを起動するには、それが呼び出す関数を実行する必要があります。それらをリバースエンジニアリングしてコピーすることは間違いありません(そして、それらが行うことはすべてパブリックSDKにあることを願っています)。

于 2010-02-24T03:43:52.433 に答える
0

UIApplication 内からゲーム ループを開始してみませんか? (はい、それを含めることはできませんが、それmain()は問題ではありません。) 一部のデリゲート メッセージは適切な候補です。

于 2010-02-03T04:52:42.533 に答える
0

その main() 関数を取得して、変更せずに iPhone で動作させることが目標ですか?

ライブラリのユーザーが iPhone プラットフォームについて考えるのを完全に遮断する方法はないように思われます。ユーザーは、コード署名などのために XCode を処理する必要があります。

そのため、ユーザーに main() 関数を分割して、applicationDidFinishLaunching および適切なタイマーから呼び出すことができるいくつかの部分に分割する必要があることを伝えることは、誰にとってもそれほど不便ではないようです。

于 2010-02-03T00:35:46.750 に答える