У нас есть ViewController, который содержит узел sphereNode
, содержащий нашу сферу. Чтобы повернуть сферу, мы могли бы использовать UIPanGestureRecognizer
. Поскольку распознаватель сообщает общее расстояние, пройденное нашим пальцем по экрану, мы кэшируем последнюю точку, о которой нам сообщили.
var previousPanPoint: CGPoint?
let pixelToAngleConstant: Float = .pi / 180
func handlePan(_ newPoint: CGPoint) {
if let previousPoint = previousPanPoint {
let dx = Float(newPoint.x - previousPoint.x)
let dy = Float(newPoint.y - previousPoint.y)
rotateUp(by: dy * pixelToAngleConstant)
rotateRight(by: dx * pixelToAngleConstant)
}
previousPanPoint = newPoint
}
Мы вычисляем dx
и dy
, исходя из того, сколько пикселей наш палец прошел в каждом направлении с момента последнего вызова распознавателя. С помощью pixelToAngleConstant
мы преобразуем значение нашего пикселя в угол (в рандах), чтобы повернуть нашу сферу. Используйте большую константу для более быстрого вращения.
Распознаватель жестов возвращает state
, который мы можем использовать, чтобы определить, начался ли жест, закончился или был перемещен палец. Когда жест начинается, мы сохраняем расположение пальцев в previousPanPoint
. Когда наш палец движется, мы вызываем указанную выше функцию. Когда жест завершается или отменяется, мы очищаем наш previousPanPoint
.
@objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
switch gestureRecognizer.state {
case .began:
previousPanPoint = gestureRecognizer.location(in: view)
case .changed:
handlePan(gestureRecognizer.location(in: view))
default:
previousPanPoint = nil
}
}
Как мы вращаем нашу сферу? Функции rotateUp
и rotateRight
просто вызывают нашу более общую функцию rotate(by: around:)
, которая принимает не только угол, но и ось вращения. rotateUp
вращается вокруг оси x, rotateRight
вокруг оси y.
func rotateUp(by angle: Float) {
let axis = SCNVector3(1, 0, 0) // x-axis
rotate(by: angle, around: axis)
}
func rotateRight(by angle: Float) {
let axis = SCNVector3(0, 1, 0) // y-axis
rotate(by: angle, around: axis)
}
rotate(by:around:)
в этом случае относительно прост, потому что мы предполагаем, что узел не перемещается / мы хотим повернуться вокруг начала локальной системы координат узла. Когда мы смотрим на общий случай, все немного сложнее, но этот ответ - лишь небольшая отправная точка.
func rotate(by angle: Float, around axis: SCNVector3) {
let transform = SCNMatrix4MakeRotation(angle, axis.x, axis.y, axis.z)
sphereNode.transform = SCNMatrix4Mult(sphereNode.transform, transform)
}
Мы создаем матрицу вращения из angle
и axis
и умножаем старый transform
нашей сферы на вычисленную, чтобы получить новую transform
.
Это небольшая демонстрация, которую я создал:
![Демо](https://i.imgur.com/0C55Dkc.gif)
У этого подхода есть два основных недостатка.
Он вращается только вокруг начала координат узла и работает правильно только в том случае, если положение узла равно SCNVector3Zero
.
При этом не учитывается ни скорость жеста, ни сфера не продолжает вращаться после остановки жеста. При таком подходе нелегко достичь эффекта, аналогичного табличному представлению, когда вы можете перевернуть палец, а табличное представление быстро прокручивается, а затем замедляется. Одним из решений было бы использовать для этого физическую систему.
person
jlsiewert
schedule
24.08.2017