Изучая 3D-графическое программирование для игр, я решил начать с простого, используя 3D Scene Kit. Моя первая игровая цель состояла в том, чтобы создать очень упрощенную имитацию MineCraft. Игра просто кубиков - как это сложно.
Ниже приводится цикл, который я написал, чтобы поместить ход 100 х 100 кубов (10 000), а производительность FPS была ужасной (~ 20 FPS). Является ли моя первоначальная игровая цель слишком много для Scene Kit или есть лучший способ приблизиться к этому?
Я прочитал другие темы в StackExchange, но не чувствую, что они отвечают на мой вопрос. Преобразование открытых поверхностных блоков в одну сетку не будет работать, поскольку SCNGeometry неизменна.
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)
}
ОБНОВЛЕНИЕ 12-30-2014: Я изменил код так, чтобы SCNBoxNode создавался один раз, а затем каждое дополнительное поле в массиве 100 x 100 создается с помощью:
var newBoxNode = firstBoxNode.clone()
newBoxNode.position = SCNVector3Make(x, y, z)
Это изменение, похоже, увеличило FPS до ~ 30 кадров в секунду. Другая статистика выглядит следующим образом (из статистики, отображаемой в SCNView):
10K (я предполагаю, что это призывы рисования?) 120K (я предполагаю, что это лица) 360K (Предполагая, что это число вершин)
Основная часть цикла выполнения находится в рендеринге (я оцениваю 98%). Общее время цикла составляет 26,7 мс (ouch). Я работаю на Mac Pro Late 2013 (6-ядерный/Dual D500 GPU).
Учитывая, что у игры в стиле MineCraft есть пейзаж, который постоянно изменяется на основе действий игроков, я не вижу, как я могу оптимизировать это в рамках набора сцен. Большое разочарование, так как мне очень нравится фреймворк. Мне бы хотелось услышать какие-то идеи о том, как я могу решить эту проблему - без этого я вынужден пойти с OpenGL.
ОБНОВЛЕНИЕ 12-30-2014 @14:00 ET: При использовании flattenedClone() я вижу значительное улучшение производительности. Теперь FPS имеет сплошную 60 кадров в секунду даже с большим количеством ящиков и двумя вызовами рисования. Однако размещение динамической среды (с поддержкой MineCraft) по-прежнему является проблематичным - см. Ниже.
Поскольку массив менял состав с течением времени, я добавил обработчик keyDown, чтобы добавить еще больший массив ячеек в существующую и приурочен к разнице между добавлением массива ящиков, что привело к гораздо большему количеству вызовов и добавлению в качестве flattenedClone. Вот что я нашел:
В keyDown я добавляю еще один массив из 120 x 120 ящиков (14 400 полей)
// This took .0070333 milliseconds
scene?.rootNode.addChildNode(boxArrayNode)
// This took .02896785 milliseconds
scene?.rootNode.addChildNode(boxArrayNode.flattenedClone())
Вызов flattenedClone() снова на 4 раза медленнее, чем добавление массива.
Это приводит к двум вызовам рисования, имеющим 293K граней и 878K вершин. Я все еще играю с этим и буду обновляться, если найду что-нибудь новое. В нижней строке, с моим дополнительным тестированием, я все еще чувствую, что набор неподвижных геометрических ограничений Scene Kit означает, что я не могу использовать фреймворк.