2012-12-19 16 views
7

Meine App verwendet GLKit, um 3D-Szene mit OpenGL ES zu rendern.iOS OpenGL ES Bildschirm Rotation während Hintergrund Apps Leiste sichtbar

Alles funktioniert gut, bis auf eine Sache. Wenn ich meine App auf dem iPad starte und die Hintergrund-Apps-Leiste anzeigen lasse (mit doppeltem "Home" -Knopf) und dann die Geräteausrichtung ändert, wird die Szene falsch aktualisiert (das zuletzt gerenderte Bild wird einfach gestreckt, um ein neues Rechteck zu füllen).

Ich fand den Grund. Wenn die Hintergrund-Apps-Leiste angezeigt wird, wird GLKViewController'spaused automatisch auf YES festgelegt (Anwendungsdelegat erhält -applicationWillResignActive:) und es findet kein Rendering statt, bis diese Leiste geschlossen wird.

Ich habe in Apple-Guides (OpenGL ES Programming Guide for iOS/Implementing a Multitasking-aware OpenGL ES Application) gefunden, dass nach dem Empfang -applicationWillResignActive: Anwendung sollte aufhören GL Rendering oder wird beendet. So scheint es, dass alles in Ordnung ist, außer schlechtem Rendering nach der Rotation :)

Ich überprüfte einige OpenGL-Spiele. Sie wurden auch "pausiert", wenn diese Leiste angezeigt wird, aber pausierte Szene bei Gerätedrehung irgendwie korrekt aktualisieren. Wie erreichen sie das?

Antwort

2

tl; dr: vielleicht brauchen Sie nur die GLKView ‚s contentMode-UIViewContentModeRedraw

Zum einen setzen Ich glaube nicht, dass Ihre Anwendung tritt tatsächlich in den Hintergrund, ich denke, es wird nur inaktiv. Die Unterscheidung zwischen den Delegiertenmethoden applicationWillResignActive und applicationDidEnterBackground. Unter der Annahme, dass die Anwendung nur inaktiv ist, verwenden Sie Folgendes, falls sie tatsächlich in den Hintergrund gestellt wird, siehe unten.

Die Apple-Dokumentation besagt, dass Sie "OpenGL ES Framerates" herunterregnen sollten, wenn applicationWillResignActive aufgerufen wird, nicht, dass OpenGL ES-Aufrufe nicht zulässig sind, die nur passiert, nachdem die Anwendung in den Hintergrund tritt.

Dies bedeutet, dass GLKit 's GLKView/GLKViewController in dieser Hinsicht ein wenig übereifrig sein kann. Um es zu beheben müssen Sie sicherstellen, dass:

  1. Die GLKView ‚s contentMode ist auf UIViewContentModeRedraw
  2. Die GLKView‘ s drawRect Methode den Rahmen nicht zeichnen, auch wenn die Anwendung inaktiv ist, aber der Rahmen ist geändert, aber nicht den Rahmen zeichnen (das sind OpenGL ES-Aufrufe), wenn die Anwendung im Hintergrund ist.

Allerdings ist meine Vermutung, dass die drawRect Methode nicht einmal genannt wird erhalten, wenn die Anwendung im Hintergrund, so dass Sie wahrscheinlich das OpenGL ES in dem glkView:drawInRect Delegatmethode ruft nicht wirklich kümmern.Der Grund, dass diese Funktion in Ihrer Situation nicht aufgerufen wird, ist, dass die Ansicht nicht ungültig wird. Der Grund für die nicht für ungültig erklärt wird, ist zweifach:

  1. Der Hauptrahmen Schleife in GLKViewController, die die Ansicht invalidiert periodisch von der Eigenschaft paused angehalten wird.
  2. Die GLKViewcontentMode ist wahrscheinlich der Standard

Als GLKViewdrawRect Methode ‚UIViewContetModeScaleToFill‘ ist wahrscheinlich nicht einmal auf der paused Eigenschaft sucht, ändert nur die contentMode schon genug sein kann.


Ich würde die folgende Lösung vorschlagen, wenn die Anwendung tatsächlich in den Hintergrund tritt. Da Sie OpenGL ES-Aufrufe nicht im Hintergrund ausführen dürfen, ist die Lösung ziemlich einfach:

Führen Sie alle OpenGL ES-Aufrufe aus, die Sie ausführen müssen, bevor Sie den Hintergrund eingeben.

Das heißt, in applicationWillResignActive folgendes tun:

  1. Spiel Schleife Pause (erledigt, indem GLKViewController 's paused)
  2. Pause Ihre Render-Schleife (erledigt, indem GLKViewController' s paused)
  3. Greifen Sie den aktuellen Framebuffer für den aktuellen Orientierungsstatus
  4. Rendern Sie den aktuellen Spielstatus noch einmal mit einem Framebuffer und Ansichtsfenster, die dem gedrehten Ausrichtungszustand entsprechen d Schnappen die
  5. Framebuffer

Außerdem müssen Sie GLKView ‚s contentMode zu UIViewContentModeRedraw eingestellt werden, so dass die drawRect Verfahren tatsächlich, nachdem der Rahmen der Ansicht aufgerufen wird, durch die Orientierungsänderung geändert.

schließlich in GLKView ‚s drawRect Methode müssen Sie prüfen, ob pausedYES oder NO im NO Fall tun, um das Rendering als normal, in der einen der Framebuffer in applicationWillResignActive gespeichert nehmen YES Fall ist, und es in die Ansicht zieht mit reguläre UIKit Anrufe.

Ich bin nicht sicher, wie gut diese hackish Lösung mit GLKit integrieren würde, benötigen Sie möglicherweise einige Unterklassen.

+0

Ich habe gerade in einem meiner Projekte im Simulator getestet, die Animation pausiert, wenn die Appleiste geöffnet wird, aber wenn ich das virtuelle iPad rotiere, wird der Rahmen ** mit dem neuen Seitenverhältnis neu gezeichnet. Dies scheint unabhängig vom Content-Modus zu geschehen und testet mit iPad iOS 6.1. – wich

+0

Das Hauptproblem ist, dass die Szene nur auf ein neues (Ergebnis-) Seitenverhältnis neu gezeichnet wird.Objekte in meiner Szene hängen vom Ansichtsrahmen ab - ich berechne ihre Größen und Positionen neu, um so viel Bildschirmfläche wie möglich zu füllen. Also während der Animation möchte ich reibungslose Neuberechnungen und nicht "Start Größe & Positionen" -> "Endgröße & Positionen" - einige animierte Übergang von einem zum anderen. Und im Falle der Wiederverwendung des gleichen Framebuffers sehen wir Stretching statt Neupositionierung – kpower

+0

Wenn Sie nicht mit GLKit hacken, erhalten Sie kein Animationsformular, da der Frameloop unterbrochen wird und Sie nicht wollen (haben) frameloop wird fortgesetzt, wenn die Anwendung inaktiv ist, dh dies geschieht auch, wenn Sie einen eingehenden Anruf oder eine solche auf dem iPhone erhalten. Das einzige, was Sie tun können, ist, die Szene basierend auf den neuen Sichtgrenzen neu zu zeichnen, was in GLKit korrekt funktioniert. Persönlich glaube ich nicht, dass es wahrscheinlich eine Gerätedrehung ist, die Präsentationen wechselt, anstatt sie zu animieren. – wich

0

Implementieren Sie die delegierte Methode (wenn Sie es nicht bereits getan haben)

-(void)applicationWillEnterForeground und die GLKViewController von dort unpause.

Von dem, was ich von Ihrer Frage verstanden habe, können Sie das Spiel pausiert halten, aber die glView in dieser Methode auch ändern, aber ohne Code zu sehen ist auch schwierig, wirklich zu sehen, was vor sich geht.

+0

'GLKViewController' wird beim Eintritt in den Vordergrund automatisch pausiert. Wenn die Hintergrund-App-Leiste sichtbar ist, befindet sich die App im "Hintergrund" -Status und dieses Mal muss ich "GLKView" aktualisieren. – kpower

+0

@kpower Ich glaube, ich verstehe Ihre Frage jetzt. Die Sache ist, wenn die App läuft, aber Sie doppelt auf die Home-Taste geklickt haben. Wenn Sie einen Timer haben, der Ihre Rendering-Schleife steuert, dann ja, entwerten Sie ihn während didenterbackground und nicht willresignactive. Es sollte weiter so animieren wie es war. –