Как повернуть SCNSphere с помощью распознавателя жестов панорамирования

Я создал SCNSphere, и теперь она выглядит как планета. Это именно то, что я хочу. Моя следующая цель - позволить пользователям вращать сферу с помощью распознавателя жестов панорамирования. Им разрешено вращать его вокруг оси X или Y. Мне просто было интересно, как я могу это сделать. Это то, что у меня есть до сих пор.

origin = sceneView.frame.origin
node.geometry = SCNSphere(radius: 1)
node.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "world.jpg")

let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(CategoryViewController.panGlobe(sender:)))
sceneView.addGestureRecognizer(panGestureRecognizer)

func panGlobe(sender: UIPanGestureRecognizer) {
    // What should i put inside this method to allow them to rotate the sphere/ball
}

person user8426652    schedule 24.08.2017    source источник
comment
Почему вы хотите добавить жест к просмотру сцены? Найдите hitTest. Могу сказать, что это будет нелегко.   -  person El Tomato    schedule 24.08.2017
comment
Поскольку вид сцены содержит мяч. Поэтому я хочу, чтобы этот жест применялся только к шару / сфере.   -  person user8426652    schedule 24.08.2017
comment
Хорошо, что мне делать?   -  person user8426652    schedule 24.08.2017


Ответы (2)


У нас есть 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.

Это небольшая демонстрация, которую я создал:

Демо


У этого подхода есть два основных недостатка.

  1. Он вращается только вокруг начала координат узла и работает правильно только в том случае, если положение узла равно SCNVector3Zero.

  2. При этом не учитывается ни скорость жеста, ни сфера не продолжает вращаться после остановки жеста. При таком подходе нелегко достичь эффекта, аналогичного табличному представлению, когда вы можете перевернуть палец, а табличное представление быстро прокручивается, а затем замедляется. Одним из решений было бы использовать для этого физическую систему.

person jlsiewert    schedule 24.08.2017

Ниже представлено то, что я пробовал, не уверен, что он точен в отношении углов, но ... этого хватило для большинства моих потребностей ....

@objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) {

    let translation = gestureRecognizer.translation(in: gestureRecognizer.view!)

    let x = Float(translation.x)
    let y = Float(-translation.y)
    let anglePan = (sqrt(pow(x,2)+pow(y,2)))*(Float)(Double.pi)/180.0

    var rotationVector = SCNVector4()
    rotationVector.x = x
    rotationVector.y = y
    rotationVector.z = 0.0
    rotationVector.w = anglePan


    self.earthNode.rotation = rotationVector
}

Пример Github-EarthRotate

Вращение Земли с помощью жеста

person anoop4real    schedule 11.04.2018