目的は、シーン内の任意の、しかし目に見える点を回ることです。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
}
}
あなたがこれで行くhow'd? – Confused