2012-12-19 11 views
7

La mia app utilizza GLKit per il rendering di scene 3D con OpenGL ES.Rotazione schermo iOS OpenGL ES mentre barra applicazioni in background è visibile

Tutto funziona bene, tranne una cosa. Quando lancio la mia app in iPad e visualizzo la barra delle applicazioni in background (con doppio clic sul pulsante "Home") e poi cambia l'orientamento del dispositivo, la scena viene aggiornata erroneamente (l'ultima immagine renderizzata viene semplicemente stirata per riempire un nuovo rettangolo).

Ho trovato il motivo. Quando viene visualizzata la barra delle applicazioni in background, GLKViewController'spaused è impostato su YES automaticamente (il delegato dell'applicazione riceve -applicationWillResignActive:) e non viene eseguito alcun rendering fino a quando questa barra non viene chiusa.

Ho trovato nelle guide Apple (OpenGL ES Programming Guide for iOS/Implementing a Multitasking-aware OpenGL ES Application) che dopo aver ricevuto l'applicazione -applicationWillResignActive: deve interrompere il rendering GL o verrà terminato. Quindi sembra tutto ok, eccetto il brutto rendering dopo la rotazione :)

Ho controllato alcuni giochi OpenGL. Sono anche diventati "in pausa" quando questa barra viene visualizzata, ma in qualche modo aggiorna correttamente la scena in pausa sulla rotazione del dispositivo. Come ottengono questo?

risposta

2

tl; dr: forse solo bisogno di impostare s' contentMode-UIViewContentModeRedraw

il GLKView In primo luogo non credo che l'applicazione entra in realtà in secondo piano, penso che diventa solo inattiva. La distinzione tra i metodi delegati applicationWillResignActive e applicationDidEnterBackground. Supponendo che l'applicazione sia solo inattiva, usa quanto segue, nel caso in cui venga effettivamente messa in secondo piano, vedi sotto.

La documentazione Apple dice che è necessario "ridurre i framerate di OpenGL ES" quando viene chiamato il numero applicationWillResignActive, non che le chiamate OpenGL ES non siano consentite, ciò accade solo dopo che l'applicazione viene messa in background.

Ciò significa che GLKit 's GLKView/GLKViewController può essere un po' troppo zelante in questo senso. Per risolvere il problema è necessario fare in modo che:

  1. Il GLKView 's contentMode è impostato su UIViewContentModeRedraw
  2. Il GLKView' s drawRect metodo fa disegnare il telaio anche quando l'applicazione è inattiva, ma il telaio è modificato, ma non disegnare il frame (ovvero utilizzare chiamate OpenGL ES) quando l'applicazione è in background.

Tuttavia, la mia presunzione è che il metodo drawRect non ha nemmeno ottenere chiamato quando l'applicazione è in background, quindi probabilmente non si hanno veramente a preoccuparsi per l'OpenGL ES chiama il metodo glkView:drawInRect delegato.La ragione per cui questa funzione non viene chiamata nella tua situazione è che la vista non viene invalidata. La ragione per non essere invalidato è duplice:

  1. il ciclo telaio principale in GLKViewController che invalida la vista periodicamente viene messa in pausa dal struttura paused.
  2. Il GLKViewcontentMode è probabilmente il default 'UIViewContetModeScaleToFill'

Come il metodo GLKViewdrawRect è probabilmente nemmeno guardando la proprietà paused, semplicemente cambiando il contentMode potrebbe già essere sufficiente.


Vorrei proporre la seguente soluzione se l'applicazione effettivamente passa in secondo piano. Poiché non ti è permesso utilizzare le chiamate OpenGL ES mentre sei in esecuzione in background, la soluzione è piuttosto semplice:

Fai tutte le chiamate di OpenGL ES che devi fare per supportare ciò che vuoi prima di entrare in background.

Cioè, in applicationWillResignActive effettuare le seguenti operazioni: ciclo

  1. Pausa il vostro gioco (fatta impostando GLKViewController 's paused)
  2. mettere in pausa il ciclo di rendering (fatto impostando GLKViewController' s paused)
  3. Afferra il framebuffer corrente per lo stato di orientamento corrente
  4. Rendi lo stato di gioco corrente ancora una volta con un framebuffer e una finestra che corrispondono allo stato dell'orientamento ruotato e d afferrare che FrameBuffer

Inoltre è necessario GLKView s' contentMode da impostare a UIViewContentModeRedraw modo che il metodo drawRect è effettivamente chiamato il fotogramma della visualizzazione è stato modificato dal cambiamento di orientamento.

Infine nel drawRect metodo GLKView s' è necessario verificare se paused è YES o NO nel caso NO fare il rendering come normale, nel caso YES prendere uno dei framebuffer salvati in applicationWillResignActive e disegnare alla vista con regolari chiamate UIKit.

Non sono sicuro di come questa soluzione di hacker si integri con GLKit, potrebbe essere necessario qualche sottoclasse.

+0

Ho appena testato un mio progetto nel simulatore, l'animazione si interrompe quando viene aperta la barra delle applicazioni, tuttavia quando ruoto l'ipad virtuale il fotogramma ** è ** ridisegnato con il nuovo rapporto aspetto. Ciò sembra accadere indipendentemente dalla modalità di contenuto, testando su iPad iOS 6.1. – wich

+0

Il problema principale è che la scena viene ridisegnata solo alle nuove proporzioni (risultato).Gli oggetti sulla mia scena dipendono dal fotogramma della vista: ricalcolo le loro dimensioni e posizioni per riempire il maggior spazio possibile sullo schermo. Quindi durante l'animazione voglio ricalcoli regolari e non "dimensioni iniziali e posizioni" -> "dimensioni e posizioni finali" - qualche transizione animata da uno all'altro. E in caso di riutilizzo dello stesso framebuffer, vediamo lo stretching anziché il riposizionamento – kpower

+0

A meno che non inizi ad hackerare con GLKit, non otterrai un'animazione l'una dall'altra, dato che il frameloop viene messo in pausa e tu non vuoi (non dovrebbe) avere il frameloop continua quando l'applicazione è inattiva, cioè ciò accade anche quando ricevi una chiamata in arrivo o simili sull'iPhone. L'unica cosa che puoi fare è semplicemente ridisegnare la scena in base ai nuovi limiti della vista, che funziona correttamente in GLKit. Personalmente non penso sia probabile che la rotazione del dispositivo cambierà le presentazioni anziché animarle. – wich

0

Implementare il metodo delegato (se non l'avete già fatto)

-(void)applicationWillEnterForeground e riattiva gli GLKViewController da lì.

Da quello che ho capito dalla tua domanda è possibile mantenere il gioco in pausa ma ridimensionare la glView anche in questo metodo, ma senza vedere alcun codice è anche difficile vedere veramente cosa sta succedendo.

+0

'GLKViewController' non viene automaticamente inserito quando si entra in primo piano. Quando la barra delle applicazioni in background è visibile, l'app è in stato "background" e questa volta ho bisogno dell'aggiornamento di 'GLKView'. – kpower

+0

@kpower Penso di capire la tua domanda ora. Il fatto è che l'app è in esecuzione ma hai fatto doppio clic sul pulsante Home. Se hai un timer che controlla il tuo ciclo di rendering allora sì, lo invalida durante il didenterbackground e non lo renderà attivo. Dovrebbe continuare ad animare com'era. –