2016-10-13 2 views
1

Целью является орбита вокруг произвольной, но видимой точки в сцене.SceneKit: орбита вокруг произвольной точки без прыжка с камеры или смещения точки привязки к центру?

Как кастрюли пользователя, камера должна двигаться и вращаться вокруг этой точки привязки.

Это прекрасно работает, если опорная точка находится посередине экрана.

Однако, если опорная точка находится вне центра - скажем, на левом краю экрана - камера мгновенно перепрыгивает, когда она приводит точку привязки в центр.

Приведенный ниже код исправил проблему прыжка, но он дезориентирует пользователей тем, что опорная точка медленно перемещается в центр.

Движение камеры подключено к жесту панорамы.

Любые идеи о том, как решить эту проблему?

let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(didSceneViewPanOneFinger)) 
panRecognizer.minimumNumberOfTouches = 1 
panRecognizer.maximumNumberOfTouches = 1 
panRecognizer.delegate = self 
sceneView.addGestureRecognizer(panRecognizer) 


func didSceneViewPanOneFinger(_ sender: UIPanGestureRecognizer) { 
    // Pick any visible point as long as it's not in center 
    let anchorPoint = SCNVector(-15, 0, 0) 

    // Orbit camera 
    cameraNode.orbitPoint(anchorPoint: anchorPoint, translation: sender.translation(in: sender.view!), state: sender.state) 
} 


class CameraNode: SCNNode { 
    // Vars 
    let headNode = SCNNode() 
    var curXRadians = Float(0) 
    var curYRadians = Float(0) 
    var directLastTranslationY = Float(0) 
    var reach = Float(10) 
    var aimingPoint = SCNVector3() 
    var lastAnchor:SCNVector3! 


    init(reach: Float) { 
     super.init() 
     self.reach = reach 

     // Call <doInit> only after all properties set 
     doInit() 
    } 


    fileprivate func doInit() { 
     // Add head node 
     headNode.camera = SCNCamera() 
     headNode.camera!.zNear = Double(0.1) 
     headNode.position = SCNVector3(x: 0, y: 0, z: 0) 
     addChildNode(headNode) 

     // Position camera 
     position = SCNVector3(x: 0, y: minY, z: reach) 
    }  


    func orbitPoint(anchorPoint: SCNVector3, translation: CGPoint, state: UIGestureRecognizerState) { 
     // Get pan distance & convert to radians 
     var xRadians = GLKMathDegreesToRadians(Float(translation.x)) 
     var yRadians = GLKMathDegreesToRadians(Float(translation.y)) 

     // Get x & y radians, adjust values to throttle rotate speed 
     xRadians = (xRadians/3) + curXRadians 
     yRadians = (yRadians/3) + curYRadians 

     // Limit yRadians to prevent rotating 360 degrees vertically 
     yRadians = max(Float(-M_PI_2), min(Float(M_PI_2), yRadians)) 

     // Save original position 
     if state == .began { 
      aimingPoint = lastAnchor ?? anchorPoint 
     } else { 
      aimingPoint = SCNVector3.lerp(vectorStart: anchorPoint, vectorEnd: aimingPoint, t: 0.99) 
     } 

     // Rotate around <anchorPoint> 
     // * Compute distance to <anchorPoint>, used as radius for spherical movement 
     let radius = aimingPoint.distanceTo(position) 
     var newPoint = getPointOnSphere(aimingPoint, hAngle: yRadians, vAngle: xRadians, radius: radius) 
     if newPoint.y < 0 { 
      yRadians = directLastTranslationY 

      newPoint = getPointOnSphere(aimingPoint, hAngle: yRadians, vAngle: xRadians, radius: radius) 
     } 

     // Set rotation values to avoid Gimbal Lock 
     headNode.rotation = SCNVector4(x: 1, y: 0, z: 0, w: yRadians) 
     rotation = SCNVector4(x: 0, y: 1, z: 0, w: xRadians) 

     print("cam pos: \(position). anchor point: \(anchorPoint). radius: \(radius).") 

     // Save value for next rotation? 
     if state == .ended { 
      curXRadians = xRadians 
      curYRadians = yRadians 
      lastAnchor = aimingPoint ?? anchorPoint 
     } 
     directLastTranslationY = yRadians 
    } 


    // Your position in 3d is given by two angles (+ radius, which in your case is constant) 
    // here, s is the angle around the y-axis, and t is the height angle, measured 'down' from the y-axis. 
    func getSphericalCoords(_ s: Float, t: Float, r: Float) -> SCNVector3 { 
     return SCNVector3(-(cos(s) * sin(t) * r), 
          sin(s) * r, 
          -(cos(s) * cos(t) * r)) 
    } 


    fileprivate func getPointOnSphere(_ centerPoint: SCNVector3, hAngle: Float, vAngle: Float, radius: Float? = nil) -> SCNVector3 { 
     // Update <radius>? 
     var radius = radius 
     if radius == nil { 
      radius = reach 
     } 

     // Compute point & return result 
     let p = centerPoint - getSphericalCoords(hAngle, t: vAngle, r: radius!) 
     return p 
    } 
} 
+0

как ты пойти с этим? – Confused

ответ

1

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

В центре обзора камеры, рядом с точкой поворота, зафиксируйте фиктивный объект, который используется в качестве ограничения камеры на «взгляд». Этот фиктивный объект прикреплен к точке вращения, поэтому камера вращается по своему желанию.

Я надеюсь, что эта схема объясняет лучше, чем мои слова:

enter image description here

+0

это похоже на много триггеров .... – Fluidity

+0

Нет, это ограничения, для фиктивного объекта, а затем для узла, который является rotationPoint. Нет необходимости в математике. Если бы математика была необходима, я бы не смог! Ограничения @Fluidity – Confused

+0

- это фиксированное соединение SpriteKit и SceneKit (в основном). Они несколько полезны для блокировки вещей и создания позиционных отношений ... @Fluidity – Confused

Смежные вопросы