Около года назад я заметил, что головоломка Soma Cubes Пита Хейна, изобретенная еще в 1933 году, не продается в магазинах мобильной связи.
Желание попробовать написать игру для iOS давно сверлило мой воспаленный мозг и я наконец решился, тем более что особого дизайна требовалось не так уж и много (рисование куба в Блендере не в счет).
Пазл содержит 7 элементов, составленных из кубиков, из которых собираются другие различные фигурки ( Википедия ).
Требования, сразу предъявляемые к игре, сводились к двум пунктам: 1. Не используйте в разработке сторонние фреймворки.
2. Для управления фигурами и сценой не следует использовать кнопки – только Распознаватели.
Под пунктом 2 я имею в виду, что не подходит сценарий, когда нужно выбрать объект и затем нажать кнопки управления.
1. Подготовка
экспортируем нарисованный в Blender куб в файл .dae и помещаем его в папку «art.scnassets» нашего игрового проекта, при создании которого SceneKit был указан как Game Tech. Мы получаем доступ к импортированной сцене и объекту в сцене следующим образом:
Третья строка просто красит грани куба в нужный цвет. Теперь вы можете клонировать объект, задать координаты и добавить его в родительскую фигуру, которая представляет собой просто объект класса SCNNode. Аналогично размещаем фигурку для сборки в центре сцены, предварительно сделав куб немного меньше.let cubeScene = SCNScene(named: "art.scnassets/cube.dae") let cubeNode1 = cubeScene!.
rootNode.childNodeWithName("Cube", recursively: false) cubeNode1?.
geometry?.
materialWithName(CUBE_MATERIAL_NAME)?.
diffuse.contents = COLORS_FOR_PRIMITIVES[1]
2. Вращение, подъем, опускание и перетаскивание объектов.
Для игры необходимо уметь вращать фигурку на 90 градусов вокруг всех трех осей, что поначалу вызывало некоторые затруднения, но потом меня вдруг осенило (через неделю), что комбинаций поворотов вокруг двух осей достаточно для всех случаи.
Для реализации плана был выбран UISwipeGestureRecouncer. Итак, свайп влево и вправо по фигуре вращается вокруг вертикальной оси (Y) независимо от положения камеры, свайп вверх и вниз вращает фигуру либо вокруг X, либо вокруг Z (в зависимости от положения камеры).
Естественно использовать UIPanGestureRecouncer для перетаскивания объектов по плоскости XZ, но вам необходимо установить зависимость между «смахиванием» и «перетаскиванием», чтобы оба обработчика работали.
panGestureRecognizer.requireGestureRecognizerToFail(swipeGestureRecognizer)
Код для перетаскивания объектов для интересующихся func handlePanGestures(recogniser: UIPanGestureRecognizer){
if recogniser.state == .
Began { let location = recogniser.locationInView(recogniser.view) let hits = sceneView.hitTest(location,options: nil) as! [SCNHitTestResult] for hit in hits { if Utils.nodeHasPrefix(hit.node.parentNode!, prefix: "fig"){ selectedNode = hit.node.parentNode saveOldPosition(selectedNode) let worldCoord = selectedNode.position let projectedOrigin = sceneView.projectPoint(worldCoord) curZ = projectedOrigin.z let unProj = sceneView.unprojectPoint(SCNVector3Make(Float(location.x), Float(location.y), projectedOrigin.z)) ofset = SCNVector3Make(selectedNode.position.x - unProj.x, selectedNode.position.y - unProj.y, selectedNode.position.z - unProj.z) break } } } if recogniser.state == .
Changed { let curScreenPoint = SCNVector3Make(Float(location.x), Float(location.y), curZ) let curWorld = sceneView.unprojectPoint(curScreenPoint) let posPlusOffset = SCNVector3Make(curWorld.x + ofset.x, curWorld.y + ofset.y , curWorld.z + ofset.z) let newPosition = SCNVector3Make(posPlusOffset.x , selectedNode.position.y , posPlusOffset.z ) let projectedOrigin2 = sceneView.projectPoint(newPosition) curZ = projectedOrigin2.z selectedNode.position = newPosition } if recogniser.state == .
Ended || recogniser.state == .
Failed{
selectedNode.position.x = Utils.clamp(selectedNode.position.x, min : -9 , max: 9)
selectedNode.position.y = round(selectedNode.position.y)
selectedNode.position.z = Utils.clamp(selectedNode.position.z, min : -9 , max: 9)
if testCollision(selectedNode){
selectedNode.position = selectedNodeOldPosition
}else{
testGameOver(selectedNode as! SimpleFigure)
}
selectedNode = nil
}
}
Для поднятия и опускания одной единицы по оси Y используются два экземпляра UITapGestureRecouncer, только один из них имеет NumberOfTapsRequired = 2, а также указывается зависимость: tapRecognizer.requireGestureRecognizerToFail(doubleTapRecognizer)
3. Управление камерой
Камера добавляется в SCNNode в координатном центре на некотором расстоянии от него и направлена на него.То есть камера расположена как бы на поверхности сферы, и для поворота камеры достаточно повернуть родительский SCNNode на определенный угол.
Для увеличения и уменьшения масштаба мы используем UIPinchGestureRecouncer и достаточно «масштабировать» тот же SCNNode в обработчике: let cameraNode = SCNNode()
cameraNode.name="mainCamera"
cameraNode.camera = SCNCamera()
cameraNode.camera!.
zFar = 100
cameraNode.position = SCNVector3(x: 0.0, y: 0.0, z: 8.0)
let cameraOrbit = SCNNode()
cameraOrbit.position = SCNVector3(x: 0.0, y: 0.0, z: 0.0)
cameraOrbit.addChildNode(cameraNode)
cameraOrbit.eulerAngles.x = -Float(M_PI_4)
Я использовал свойство eulerAngles объекта cameraOrbit для увеличения угла поворота в тех же обработчиках, что и для фигур, после первой идентификации объекта в SCNHitTestResult.
4. Прочие мелочи
Чтобы протестировать столкновения, просто проверьте, что координаты каждого куба текущей фигуры (который был выпущен, повернут и т. д.) совпадают с кубами остальных шести фигур.Уровни — это просто текстовый файл с координатами.
По итогам разработки этой головоломки хочу сказать, что iOS оставила у меня приятные впечатления, особенно реализация UIGestureRecouncers. Всем спасибо за внимание и надеюсь, что вышесказанное кому-то поможет. Теги: #кубики сома #пазлы #разработка для iOS #разработка игр #Swift
-
Цементит
19 Oct, 24 -
Действительно Новая «Новая Реальность»
19 Oct, 24 -
Ошибка В Формуле Проверки Условия Делоне.
19 Oct, 24