已知Y高度,如何在SceneKit中通过点击SCNView获取X、Z坐标?
在SCNView中点击空白处获取指定Y高度的世界坐标(X、Z)
核心思路是通过屏幕点击坐标生成3D空间射线,再计算这条射线与指定Y高度平面的交点,从而得到对应的X、Z坐标。具体实现步骤如下:
1. 转换屏幕坐标为3D射线
利用SCNView的unprojectPoint(_:)方法,将屏幕点击点分别转换为近裁剪面(Z=0)和远裁剪面(Z=1)上的3D点,这两个点共同定义了一条穿过点击位置的空间射线。
2. 计算射线与指定Y平面的交点
射线的参数方程可以表示为:nearPoint + t * direction,其中direction是远平面点减去近平面点的方向向量。我们需要找到参数t使得射线的Y分量等于目标高度targetY,代入后即可解出对应的X、Z值。
完整代码实现(Swift)
func getTargetWorldPosition(at touchPoint: CGPoint, in scnView: SCNView, targetY: Float) -> SCNVector3? { // 将屏幕坐标转为近、远平面的3D点 let nearPoint = scnView.unprojectPoint(SCNVector3(touchPoint.x, touchPoint.y, 0)) let farPoint = scnView.unprojectPoint(SCNVector3(touchPoint.x, touchPoint.y, 1)) // 计算射线方向向量 let direction = SCNVector3( x: farPoint.x - nearPoint.x, y: farPoint.y - nearPoint.y, z: farPoint.z - nearPoint.z ) // 避免射线与目标平面平行(Y方向无分量) guard abs(direction.y) > 0.0001 else { return nil } // 求解参数t,让射线Y分量等于目标高度 let t = (targetY - nearPoint.y) / direction.y // 计算最终的X、Z坐标 let targetX = nearPoint.x + t * direction.x let targetZ = nearPoint.z + t * direction.z return SCNVector3(targetX, targetY, targetZ) }
使用示例
在点击手势回调中,先通过常规hitTest检测是否有相交物体,无相交时调用上述方法获取坐标并放置物体:
@objc func handleScreenTap(_ gesture: UITapGestureRecognizer) { guard let scnView = gesture.view as? SCNView else { return } let touchPoint = gesture.location(in: scnView) // 先检测是否点击到场景中的物体 let hitResults = scnView.hitTest(touchPoint, options: nil) if hitResults.isEmpty { // 假设要放置在Y=0的高度平面上 if let targetPos = getTargetWorldPosition(at: touchPoint, in: scnView, targetY: 0) { // 创建示例物体并放置 let cube = SCNBox(width: 0.2, height: 0.2, length: 0.2, chamferRadius: 0) let cubeNode = SCNNode(geometry: cube) cubeNode.position = targetPos scnView.scene?.rootNode.addChildNode(cubeNode) } } }
注意事项
- 若射线几乎平行于目标Y平面(
direction.y趋近于0),会返回nil以避免计算误差; - 确保
targetY的取值符合你的场景坐标系(SceneKit默认Y轴向上); - Objective-C实现逻辑完全一致,仅需调整语法细节。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

