如何确定 ScnNode 是在 SceneKit 中相机节点的视图方向的左侧还是右侧
How to determine if an ScnNode is left or right of the view direction of a camera node in SceneKit
我在 SceneKit 场景的中心位置 (0,0,0) 有一个摄像头
我正在围绕 y 轴旋转相机以显示场景的不同部分。
我想在屏幕的 left/right 一侧显示箭头,以显示用户必须朝哪个方向移动才能查看目标对象。
如何计算 ScnNode 是否位于我的视线方向的左侧或右侧?
我知道:
- 相机节点的位置和旋转
- 目标节点的位置
- 计算节点相对于相机的向量。节点位置减去相机位置计算向量
- 使用atan2 to convert the vector into an angle. The Apple docs are bit thin on explanation. There's a tutorial on raywenderlich.com.
例如
let v = CGPoint(x: nodePosition.x - cameraPosition.x, y: nodePosition.y - cameraPosition.y);
let a = atan2(v.y, v.x) // Note: order of arguments
角度以弧度为单位,范围在 -π 和 π(-180 到 180 度)之间。如果值为负,则节点位于相机矢量的左侧,正值表示节点位于右侧,零表示节点在正前方。
方便的是,如果需要,您可以按原样使用这个角度来实际执行旋转。只需将相机角度增加到节点角度的一部分即可。
例如
cameraRotation += a * 0.1
注意事项:
- "Forward" 或 "Up" 不一定与您的内部坐标系相同。通常 0 是在右边,所以如果你想显示一个箭头或其他 UI 你可能需要在角度上加上或减去一个量(通常是 -(π/2) 旋转四分之一圈)。
- 角度永远只在-π和+π之间。如果节点正好落在相机的正后方(恰好 180 度),您将得到 -π 或 +π,并且当节点越过此不连续点时,您可能会看到这两个极端之间的角度跳跃。您有时会在导航箭头指向目标然后突然轻弹到屏幕另一侧的游戏中看到这种副作用。有多种方法可以处理这个问题。一种简单的方法是在角度上使用低通滤波器(即随时间累积多个样本)。
projectPoint 函数有一种更简单的方法 在您的视图中将图像显示为 2d 元素,并根据它的位置定位用户应该看的地方
func getIfHeIsLookingNew(sceneWidth: CGFloat, nodePosition: SCNVector3){
if(nodePosition.z < 1){
if(nodePosition.x > (Float(sceneWidth))){
print("Look Right")
}else if(nodePosition.x < 0){
print("Look Left")
}else{
print("Correct")
return
}
}else if(nodePosition.x < 0){
print("Look Right")
}else{
print("Look Left")
}
}
并像下面这样使用它
let sceneWidth:CGFloat = self.sceneView.frame.width
getIfHeIsLookingNew(sceneWidth: sceneWidth, nodePosition: self.sceneView.projectPoint(targetNode.position))
我在 SceneKit 场景的中心位置 (0,0,0) 有一个摄像头 我正在围绕 y 轴旋转相机以显示场景的不同部分。
我想在屏幕的 left/right 一侧显示箭头,以显示用户必须朝哪个方向移动才能查看目标对象。
如何计算 ScnNode 是否位于我的视线方向的左侧或右侧?
我知道:
- 相机节点的位置和旋转
- 目标节点的位置
- 计算节点相对于相机的向量。节点位置减去相机位置计算向量
- 使用atan2 to convert the vector into an angle. The Apple docs are bit thin on explanation. There's a tutorial on raywenderlich.com.
例如
let v = CGPoint(x: nodePosition.x - cameraPosition.x, y: nodePosition.y - cameraPosition.y);
let a = atan2(v.y, v.x) // Note: order of arguments
角度以弧度为单位,范围在 -π 和 π(-180 到 180 度)之间。如果值为负,则节点位于相机矢量的左侧,正值表示节点位于右侧,零表示节点在正前方。
方便的是,如果需要,您可以按原样使用这个角度来实际执行旋转。只需将相机角度增加到节点角度的一部分即可。
例如
cameraRotation += a * 0.1
注意事项:
- "Forward" 或 "Up" 不一定与您的内部坐标系相同。通常 0 是在右边,所以如果你想显示一个箭头或其他 UI 你可能需要在角度上加上或减去一个量(通常是 -(π/2) 旋转四分之一圈)。
- 角度永远只在-π和+π之间。如果节点正好落在相机的正后方(恰好 180 度),您将得到 -π 或 +π,并且当节点越过此不连续点时,您可能会看到这两个极端之间的角度跳跃。您有时会在导航箭头指向目标然后突然轻弹到屏幕另一侧的游戏中看到这种副作用。有多种方法可以处理这个问题。一种简单的方法是在角度上使用低通滤波器(即随时间累积多个样本)。
projectPoint 函数有一种更简单的方法 在您的视图中将图像显示为 2d 元素,并根据它的位置定位用户应该看的地方
func getIfHeIsLookingNew(sceneWidth: CGFloat, nodePosition: SCNVector3){
if(nodePosition.z < 1){
if(nodePosition.x > (Float(sceneWidth))){
print("Look Right")
}else if(nodePosition.x < 0){
print("Look Left")
}else{
print("Correct")
return
}
}else if(nodePosition.x < 0){
print("Look Right")
}else{
print("Look Left")
}
}
并像下面这样使用它
let sceneWidth:CGFloat = self.sceneView.frame.width
getIfHeIsLookingNew(sceneWidth: sceneWidth, nodePosition: self.sceneView.projectPoint(targetNode.position))