Nell'apprendimento della programmazione grafica 3D per i giochi, ho deciso di iniziare in modo semplice utilizzando l'API 3D di Scene Kit. Il mio primo obiettivo di gioco è stato quello di creare un mimo molto semplificato di MineCraft. Un gioco di soli cubi: quanto può essere difficile.Scene Kit Performance con test del cubo
Di seguito è riportato un ciclo che ho scritto per posizionare una corsa di 100 x 100 cubi (10.000) e la prestazione FPS era abissale (~ 20 FPS). Il mio obiettivo di gioco iniziale è troppo alto per Scene Kit o c'è un modo migliore per avvicinarsi a questo?
Ho letto altri argomenti su StackExchange ma non sento che rispondano alla mia domanda. La conversione dei blocchi di superfici esposte in una singola mesh non funzionerà poiché SCNGeometry è immutabile.
func createBoxArray(scene : SCNScene, lengthCount: Int, depthCount: Int) {
let startX : CGFloat = -(CGFloat(lengthCount) * CUBE_SIZE) + (CGFloat(lengthCount) * CUBE_MARGIN)/2.0
let startY : CGFloat = 0.0
let startZ : CGFloat = -(CGFloat(lengthCount) * CUBE_SIZE) + (CGFloat(lengthCount) * CUBE_MARGIN)/2.0
var currentZ : CGFloat = startZ
for z in 0 ..< depthCount {
currentZ += CUBE_SIZE + CUBE_MARGIN
var currentX = startX
for x in 0 ..< lengthCount {
currentX += CUBE_SIZE + CUBE_MARGIN
createBox(scene, x: currentX, y: startY, z: currentZ)
}
}
}
func createBox(scene : SCNScene, x: CGFloat, y: CGFloat, z: CGFloat) {
var box = SCNBox(width: CUBE_SIZE, height: CUBE_SIZE, length: CUBE_SIZE, chamferRadius: 0.0)
box.firstMaterial?.diffuse.contents = NSColor.purpleColor()
var boxNode = SCNNode(geometry: box)
boxNode.position = SCNVector3Make(x, y, z)
scene.rootNode.addChildNode(boxNode)
}
UPDATE 2014/12/30: Ho modificato il codice in modo che lo SCNBoxNode viene creata una volta e poi ogni scatola aggiuntiva nella matrice di 100 x 100 viene creato attraverso:
var newBoxNode = firstBoxNode.clone()
newBoxNode.position = SCNVector3Make(x, y, z)
Sembra che questo cambiamento abbia aumentato il valore di FPS a ~ 30 fps. Le altre statistiche sono le seguenti (dalle statistiche visualizzate nella SCNView): (? Presumo che ciò è trarre le chiamate)
10K 120K (suppongo questo è facce) 360K (Supponendo che questo è il conteggio dei vertici)
La maggior parte del ciclo di esecuzione è in Rendering (sto stimando il 98%). Il tempo totale del loop è 26.7ms (ahi). Sono in esecuzione su Mac Pro Late 2013 (6-core w/Dual D500 GPU).
Dato che un gioco in stile MineCraft ha un paesaggio che cambia continuamente in base alle azioni dei giocatori, non vedo come posso ottimizzarlo entro i confini di Scene Kit. Una grande delusione perché mi piace molto il framework. Mi piacerebbe sentire le idee di qualcuno su come posso affrontare questo problema - senza quello, sono costretto ad andare con OpenGL.
UPDATE 2014/12/30 @ 14:00 ET: Sto vedendo un notevole miglioramento delle prestazioni quando si utilizza flattenedClone(). L'FPS ora è un solido 60fps anche con più caselle e due chiamate di disegno. Tuttavia, l'adattamento di un ambiente dinamico (come supporta MineCraft) si sta ancora rivelando problematico - vedi sotto.
Dato che la matrice cambierebbe la composizione nel tempo, ho aggiunto un gestore keyDown per aggiungere un array di box ancora più grande all'unità esistente e cronometrato la differenza tra l'aggiunta della serie di riquadri risultante in un numero di chiamate molto maggiore rispetto all'aggiunta come una chiave piatta. Ecco cosa ho trovato:
Su keyDown aggiungo un altro array di 120 x 120 caselle (14.400 scatole)
// This took .0070333 milliseconds
scene?.rootNode.addChildNode(boxArrayNode)
// This took .02896785 milliseconds
scene?.rootNode.addChildNode(boxArrayNode.flattenedClone())
Calling flattenedClone() ancora una volta è 4x più lento di quello di aggiungere l'array.
Questo risulta in due chiamate di disegno con 293 K facce e 878 K vertici. Sto ancora giocando con questo e aggiornerò se trovo qualcosa di nuovo. In conclusione, con i miei test aggiuntivi sento ancora i vincoli geometrici immutabili di Scene Kit che non posso sfruttare la struttura.
In che ambiente si prova? Dov'è il collo di bottiglia delle prestazioni? Vedi la costruzione di un gioco con TalkKit talk [dal WWDC 2014] (http://developer.apple.com/videos/wwdc/2014) per suggerimenti su come rintracciare quest'ultimo. – rickster
non conosco il set di scene ma generalmente l'approccio "ingenuo" sarà piuttosto lento. Considera che un gioco come Minecraft probabilmente non garantisce il rendering di blocchi completamente nascosti da altri, che implementa l'istanziamento (disegnando gli stessi blocchi in un colpo solo) e altre ottimizzazioni generali e specifiche del gioco. SceneKit è un renderizzatore generico, quindi dovrai provare a vedere quale tipo di ottimizzazioni possono essere implementate e cosa funziona meglio per SceneKit. Se si determina che è necessario un controllo di livello più basso, potrebbe essere necessario ripristinare GLKit o OpenGL non elaborato. – LearnCocos2D
Le chiamate 10K per disegnare sono * troppo * troppo. Prova a mirare a qualcosa più vicino a 100. Puoi ridurre notevolmente il numero di richiami disegnando appiattendo la geometria ('flattenedClone()'). Se una casella dovesse essere successivamente separata dall'azione di un utente, tratterei quella casella su quell'azione e non lascerò l'intera scena in uno stato separato, solo perché l'utente potrebbe interagire con essa. –