2011-01-27 47 views
28

Primero, mi pregunta: ¿cómo gestionas tu iOS Run-Loop?iOS Games y Run-Loop Management

Próxima mi razón: he estado investigando esto con una variedad de prototipos (v. Desarrollo de etapa inicial) y he encontrado una serie de problemas desconcertantes.

  • cuestiones primer lugar, de entrada y el bucle de ejecución me llevan a intentar lo siguiente:
    • cuando se utiliza el sistema más recomendado (CADisplayLink) He observado que ciertas entradas táctiles se dejan caer una vez que la carga de la CPU hace que el búfer voltear (presentRenderBuffer) para tener que esperar un marco. Esto ocurre solo en el dispositivo y no en el simulador (molestamente - esto parece estar relacionado con esperar el bloqueo vsync en el hilo principal & la forma en que el proceso de ejecución de la aplicación toca la entrada & come mensajes)
    • cuando se usa el siguiente sistema más recomendado (NSTimer) Noté que ciertas entradas táctiles se eliminan una vez que la carga de la CPU alcanza cierto punto en el simulador pero no en el dispositivo (también de manera molesta). NSTimer también da como resultado una menor precisión cuando mis actualizaciones disparan
    • cuando uso el sistema menos recomendado (ejecutando el ciclo de ejecución en su propio subproceso administrado internamente con un temporizador de alta precisión construido desde mach_absolute_time, todos mis problemas de entrada táctil desaparecen, sin embargo, mi código ASSERT ahora atrapa el hilo equivocado y solo si duermo después de la interrupción del software. (Mi código de afirmación es similar al http://iphone.m20.nl/wp/?p=1) Me gusta mucho tener mi trampa del código afirmar inmediatamente en la línea que causó el problema, por lo que esta solución es no es realmente viable para mí:. difíciles de depurar
  • en segundo lugar, el tiempo perdido:
    • mientras investigaba el sistema, descubrí que independientemente de la velocidad de fotogramas (extrañamente, pero supongo estadísticamente que todavía tiene sentido con vsync) estoy esperando aproximadamente el 22% del tiempo en la vsync. Lo he confirmado moviéndome glFlush/glFinish y jugando con la frecuencia con la que hago las llamadas de PresentBuffer. Este es el momento clave en el que me encantaría procesar IA, etc. en lugar de limitarme a bloquear una llamada gl. La única forma en que puedo pensar en esto sería mover la renderización a su propio hilo, pero no estoy seguro de si está justificado comenzar a volver a diseñar para multi-threading en un dispositivo de un solo procesador.

Así que alguien ha encontrado una bala mágica en torno a estas cuestiones? ¿Alguien tiene una arquitectura de bucle de ejecución arrolladora que es genial en esta plataforma? Por el momento parece que tengo que elegir el menor de los males.

+0

Debo señalar: cuando digo "Las entradas se han caído", en realidad no se han caído, simplemente se retrasan entre una fracción de segundo y hasta 10 segundos. Este no es el retraso estándar visto en iOS y otros dispositivos de pantalla táctil, pero más como el retraso "Consumo de mensajes más lento que la generación de mensajes" que es acumulativo (se alarga con el tiempo) – Mark

+0

¿Estás haciendo todo en el hilo principal? ¿Ha pensado en trasladar alguno de estos procesos a un hilo de fondo usando GCD o similar? –

+1

También le puede interesar la discusión (tanto en respuestas como en comentarios) en esta pregunta: http://stackoverflow.com/questions/4739748/is-there-a-way-to-make-drawrect-work-right- ahora, donde se experimentan varias formas de manipular el ciclo de ejecución para actualizaciones de UI. –

Respuesta

6

Para mis propios proyectos de iOS, utilizo el enfoque clásico (crear una ventana .nib, crear una clase que hereda EAGLView, agregar EAGLView a una vista en un controlador de vista que se coloca en su propio .nib).

En el trabajo, tomé un enfoque ligeramente diferente inspirado en SDL, que puede inspeccionar en nuestra biblioteca de código abierto, APRIL. El objetivo principal de APRIL es admitir tantas plataformas como sea posible, manteniendo la simplicidad (administración de ventanas y entradas solamente) y tener claridad sobre los problemas de licencia y la libertad de uso. Nuestros desarrolladores quieren escribir aplicaciones en una plataforma (Windows, Mac o Linux, de acuerdo con los gustos y deseos) y luego el código me es entregado para adaptarlo a otras plataformas.

En el enfoque que utilizamos en ABRIL, no crea ningún .nibs, y al llamar al UIApplicationMain, especifica la clase de delegado como su cuarto argumento.El código de juego principal sigue siendo el mismo para cada plataforma, y ​​solo las cosas específicas de la plataforma son #ifdef 'd en el código, o se abstraen en una biblioteca auxiliar.

En el delegado de la aplicación se crea el controlador de vista y la ventana:

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

Observe cómo retrasemos el inicio de un 0,2? Es por eso que menciono la vista de imagen de arriba. pantalla en blanco durante los 0,2 segundos, tendríamos que aparece inmediatamente después de Default.png y retardo adicional se introduce antes del control se transfiere a runMain :, que libera el control de la aplicación principal:

- (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) 
} 

Por lo tanto, ahora el el control nunca se transfiere al bucle principal real de UIApplication. Luego creas tu propio ciclo principal.

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

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

(En una nota lateral, se utiliza controlador de vista, por supuesto, para simplificar la rotación de interfaz de usuario para que coincida con la orientación del dispositivo.)

Ambos enfoques utilizan CADisplayLink si es compatible con el sistema operativo. No he notado ningún problema con ninguno de los métodos, aunque mis proyectos privados se basan principalmente en acelerómetros. Sospecho que el enfoque de ABRIL podría hacer que algunos de los problemas desaparezcan también.

+0

Eso es realmente fascinante Ivan. De hecho, usted ha descrito cómo lograr ... el control se transfiere lejos del bucle principal real de UIApplication. Luego creas tu propio bucle principal * ... ¡Increíble! ¡Maravilloso! – Fattie

+0

parece que estás haciendo cosas similares a la forma en que estábamos, es decir, deshacernos de la .nib, etc. Sin embargo, su método "doEvents" v. Es interesante, ya que mi intuición me dice que tengo problemas. m get están en el manejo de eventos. Voy a tener que experimentar con eso. También me interesa cómo estás iniciando cosas en ABRIL. Para la inicialización de mi bucle cuando ejecuté cosas en mi propio ciclo de ejecución, simplemente generé otro hilo y administré las cosas de esa manera, y funcionó de maravilla excepto por la interrupción del software que iniciaba con el hilo equivocado. – Mark