2011-01-27 2 views
28

Во-первых, мой вопрос: как вы справляетесь с Run-Loop iOS?Игры для iOS и управление Run-Loop

Следующая причина: Я изучал это с помощью множества прототипов (v. Разработка на ранней стадии) и обнаружил ряд недоумений.

  • Во-первых, входные вопросы и цикл запуска приведет меня попробовать следующее:
    • при использовании наиболее рекомендуемую систему (CADisplayLink) я отметил, что некоторые сенсорные входы сбрасываются после загрузки CPU вызывает буфер flip (presentRenderBuffer) придется ждать кадра. Это происходит только на устройстве, а не в симуляторе (досадно - это, похоже, связано с ожиданием блокировки vsync по основному потоку &, как прикосновение к приходу приложения, входящее в комплект поставки &),
    • при использовании следующего Самая рекомендуемая система (NSTimer). Я заметил, что некоторые сенсорные входы отбрасываются, когда загрузка процессора достигает определенной точки в симуляторе, но не в устройстве (также досадно). NSTimer также приводит к гораздо более низкой точности, когда мои обновления обжигают
    • при использовании наименее рекомендуемой системы (запуск цикла цикла в собственном потоке, управляемом внутренне с помощью таймера высокой точности, построенного из mach_absolute_time, все мои проблемы с сенсорным входом исчезают, однако мой код ASSERT теперь ловушки в неправильном потоке и только если я проспал после программного прерывания. (Мой код подтверждения похож на http://iphone.m20.nl/wp/?p=1) Мне очень нравится, когда моя ловушка assert code сразу же на линии, которая вызвала проблему, поэтому это решение на самом деле не выполнимый для меня. труднее отлаживать
  • Во-вторых, потерянное время:
    • , исследуя систему, я обнаружил, что независимо от частоты кадров (странно, но я полагаю, что статистически это все еще имеет смысл w/vsync). Я жду примерно 22% времени на vsync. Я подтвердил это, перемещаясь по glFlush/glFinish и играя с тем, как часто я выполняю вызовы presentRenderBuffer. Это ключевое время, которое я хотел бы обрабатывать AI и т. Д., А не просто останавливаться на блокирующем вызове gl. Единственный способ, которым я могу думать обо всем этом, будет включать перенос рендеринга в собственный поток, но я не уверен, что для начала повторной архивации для многопоточности на однопроцессорном устройстве.

Так кто-нибудь нашел волшебную пулю вокруг этих вопросов? Кто-нибудь имеет архитектуру run-loop для убийцы, которая является ударной игрой на этой платформе? На данный момент похоже, что я должен выбрать меньшее из зла.

+0

Я должен отметить: когда я говорю «Входы отбрасываются», они фактически не отбрасываются, они просто отстают от любой части секунды до 10 секунд. Это не стандартное отставание, наблюдаемое на iOS и других устройствах с сенсорным экраном, но более похожее на то, что «потребление сообщений работает медленнее, чем генерация сообщений», что является суммарным (увеличивается с течением времени). – Mark

+0

Вы делаете все в основном потоке? Вы планировали переместить любую из этой обработки в фоновый поток с использованием GCD или тому подобного? –

+1

Вы также можете быть заинтересованы в обсуждении (как в ответах, так и комментариях) в этом вопросе: http://stackoverflow.com/questions/4739748/is-there-a-way-to-make-drawrect-work-right- теперь, когда экспериментируют несколько способов управления циклом запуска для обновлений пользовательского интерфейса. –

ответ

6

Для моих собственных проектов iOS я использую классический подход (создайте окно .nib, создайте класс, наследующий EAGLView, добавьте EAGLView к представлению в контроллере представления, который помещается в свой собственный .nib).

На работе я принял несколько иной подход, основанный на SDL, который вы можете проверить в нашей библиотеке с открытым доступом, APRIL. Основная цель APRIL - поддержка как можно большего числа платформ, сохраняя простоту (только управление окнами и вводами), а также четкое описание вопросов лицензирования и возможность использования. Наши разработчики хотят писать приложения на одной платформе (Windows, Mac или Linux, в соответствии с вкусами и желаниями), а затем мне передается код для адаптации к другим платформам.

В подходе, который мы используем в APRIL, вы не создаете никаких .nib, а при вызове UIApplicationMain вы указываете класс делегата в качестве его четвертого аргумента.Основной код игры остается абсолютно одинаковым для каждой платформы, и только код для конкретной платформы составляет #ifdef 'd в код или абстрагируется в вспомогательной библиотеке.

В приложение делегата вы создаете контроллер представления, и окно:

- (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, чтобы соответствовать ориентации устройства.)

Оба эти подхода используют CADisplayLink, если поддерживается ОС. Я не заметил никаких проблем с любым из методов, хотя мои частные проекты в основном основаны на акселерометре. Я подозреваю, что подход APRIL может привести к тому, что некоторые проблемы исчезнут.

+0

Это действительно увлекательный Иван. На самом деле вы описали, как достичь .. * контроль передается от фактического основного цикла UIApplication. Затем вы создаете свой собственный основной цикл * ... Удивительный! Замечательно! – Fattie

+0

похоже, что вы делаете что-то похожее на то, как мы были, то есть избавляемся от .nib и т. Д. Я нахожу ваш метод «doEvents» интересным, хотя, поскольку моя интуиция подсказывает мне проблемы, m при обработке событий. Мне придется экспериментировать с этим. Мне также интересно, как вы начинаете вещание в APRIL. Для моей инициализации цикла, когда я запускал вещи в своем собственном цикле запуска, я просто породил другой поток и управлял такими вещами, и это отлично работало, за исключением того, что прерывание программного обеспечения не срабатывало в неправильном потоке. – Mark

Смежные вопросы