Ucząc się programowania grafiki w grach 3D, zdecydowałem się zacząć od prostoty, korzystając z zestawu Scene Kit 3D API. Moim pierwszym celem było zbudowanie bardzo uproszczonego mimika MineCraft. Gra w kostki - jak ciężko może być.Wydajność zestawu scen z testem kostki
Poniżej znajduje się pętla, którą napisałem, aby umieścić jazdę z 100 x 100 kostek (10 000), a wydajność FPS była fatalna (~ 20 FPS). Czy mój początkowy cel gry jest zbyt duży dla Scene Kit, czy jest lepszy sposób, aby się do tego zbliżyć?
Czytałem inne tematy dotyczące StackExchange, ale nie sądzę, aby odpowiedzieć na moje pytanie. Konwersja odsłoniętych bloków powierzchniowych do pojedynczej siatki nie będzie działać, ponieważ parametr SCNGeometry jest niezmienny.
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 30.12.2014: I zmodyfikowane kodu tak SCNBoxNode jest tworzony po czym każde dodatkowe pole w macierzy 100 x 100 jest utworzony przez:
var newBoxNode = firstBoxNode.clone()
newBoxNode.position = SCNVector3Make(x, y, z)
Wydaje się, że ta zmiana zwiększyła FPS do ~ 30 fps. Pozostałe statystyki przedstawiają się następująco (ze statystyk wyświetlanych w SCNView): (? Zakładam, że jest to narysować połączenia)
10K 120K (zakładam to twarze) 360k (zakładając, że jest to liczba wierzchołków)
Większość pętli uruchamiania znajduje się w trybie renderowania (domyślnie 98%). Całkowity czas pętli to 26,7ms (ouch). Pracuję na komputerze Mac Pro Late 2013 (6-rdzeniowym procesorze graficznym Dual D500).
Biorąc pod uwagę, że gra w stylu MineCraft ma krajobraz, który nieustannie zmienia się w zależności od działań graczy, nie widzę sposobu na zoptymalizowanie tego w ramach zestawu scen. Wielkie rozczarowanie, ponieważ bardzo podoba mi się framework. Chciałbym usłyszeć czyjś pomysł, w jaki sposób mogę rozwiązać ten problem - bez tego jestem zmuszony iść z OpenGL.
AKTUALIZACJA 12-30-2014 @ 2:00 pm ET: Widzę znaczącą poprawę wydajności przy użyciu spłaszczonego Clone(). FPS ma teraz solidne 60 klatek na sekundę, nawet z większą liczbą skrzynek i DWÓCH wywołań rysunkowych. Jednak dostosowanie dynamicznego środowiska (tak jak obsługuje MineCraft) nadal stanowi problem - patrz poniżej.
Ponieważ tablica zmieniłaby skład z biegiem czasu, dodałem procedurę obsługi klucza, aby dodać jeszcze większą tablicę pól do istniejącej i określonej w czasie różnicy między dodaniem tablicy pól, co skutkuje znacznie większymi połączeniami w porównaniu do dodawania jako spłaszczonego klonu. Oto co znalazłem:
Na keyDown dodam kolejną tablicę 120 x 120 pól (14400 pola)
// This took .0070333 milliseconds
scene?.rootNode.addChildNode(boxArrayNode)
// This took .02896785 milliseconds
scene?.rootNode.addChildNode(boxArrayNode.flattenedClone())
Wywoływanie flattenedClone() znowu jest 4x wolniej niż dodanie tablicę.
Powoduje to wykonanie dwóch wywołań rysowania o powierzchni 293K i wierzchołkach 878K. Nadal gram z tym i zaktualizuję, jeśli znajdę coś nowego. Podsumowując, z dodatkowymi testami nadal uważam, że niezmienne geometryczne ograniczenia zestawu scenograficznego oznaczają, że nie mogę wykorzystać struktury.
W jakim środowisku testujesz? Gdzie jest twoje wąskie gardło wydajności? Zobacz temat Budowanie gry z rozmową SceneKit [z WWDC 2014] (http://developer.apple.com/videos/wwdc/2014), aby uzyskać wskazówki dotyczące śledzenia tego ostatniego. – rickster
Nie znam scenekitu, ale ogólnie podejście "naiwne" będzie raczej powolne. Uważają, że gra taka jak Minecraft prawdopodobnie nie powoduje renderowania żadnych bloków, które są całkowicie ukryte przez innych, implementuje instancje (rysowanie tych samych bloków za jednym razem) oraz inne optymalizacje ogólne i związane z grą. SceneKit to renderer ogólnego przeznaczenia, więc będziesz musiał sprawdzić, jakie rodzaje optymalizacji mogą zostać zaimplementowane, a co najlepiej sprawdza się w SceneKit. Jeśli zdecydujesz, że potrzebujesz więcej kontroli niskiego poziomu, powinieneś powrócić do GLKit lub surowego OpenGL. – LearnCocos2D
Połączenia typu "wyciągnij 10K" to * sposób * za dużo. Spróbuj dążyć do czegoś bliższego 100. Możesz znacznie zmniejszyć liczbę wywołań rysowania poprzez spłaszczenie geometrii ('flattenedClone()'). Jeśli jedno pudełko zostanie później oddzielone działaniem użytkownika, zajmie się tym pudełkiem po tej czynności i nie pozostawi całej twojej sceny w stanie oddzielonym, tylko dlatego, że użytkownik może z nią współdziałać. –