Swift实现:检测子SCNNode点击而非父节点点击
问题解决方案:SceneKit中子节点点击检测失效
问题根源
你的场景中地球父节点的几何体遮挡了子节点(点),导致hitTest优先命中层级更高的地球节点;同时默认的hitTest配置未针对性优化,无法优先检测到点节点。
核心解决步骤
1. 优化Hit Test检测逻辑
修改touchesBegan方法,配置hitTest选项以返回所有命中节点,并优先筛选点节点:
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first, touch.view == self.sceneView else { return } let touchLocation = touch.location(in: sceneView) // 配置hitTest选项,获取所有可见节点的命中结果 let hitTestOptions: [SCNHitTestOption: Any] = [ .firstFoundOnly: false, .ignoreHiddenNodes: true, .boundingBoxOnly: false, .searchRadius: 5.0 // 扩大检测范围,适配小点的点击 ] let hitTestResults = sceneView.hitTest(touchLocation, options: hitTestOptions) // 遍历结果,优先处理点节点 for result in hitTestResults { let tappedNode = result.node switch tappedNode.name { case "NewYorkDot": print("Tapped on New York! Position: \(tappedNode.position)") // 这里添加坐标转换逻辑 break case "Other": print("Tapped on a regular dot. Position: \(tappedNode.position)") // 这里添加坐标转换逻辑 break case "EarthNode": // 跳过地球父节点 continue default: continue } // 找到目标节点后终止遍历 break } }
2. 调整渲染顺序,让点节点优先显示
给地球节点和点节点的材质设置不同渲染顺序,确保点节点在地球前面渲染:
private func setupGlobe() { self.earthNode = EarthNode(radius: earthRadius, earthColor: earthColor, earthGlow: glowColor, earthReflection: reflectionColor) earthNode.name = "EarthNode" // 设置地球材质渲染顺序为0(更低层级) for material in earthNode.geometry!.materials { material.renderingOrder = 0 } sceneView.scene?.rootNode.addChildNode(earthNode) } private func setupDotGeometry() { // ... 原有代码 let dotGeometry = SCNSphere(radius: dotRadius) dotGeometry.firstMaterial?.diffuse.contents = dotColor dotGeometry.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant dotGeometry.firstMaterial?.renderingOrder = 1 // 点节点渲染顺序高于地球 let highlightGeometry = SCNSphere(radius: dotRadius) highlightGeometry.firstMaterial?.diffuse.contents = highlightColor highlightGeometry.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant highlightGeometry.firstMaterial?.renderingOrder = 1 let oceanGeometry = SCNSphere(radius: dotRadius) oceanGeometry.firstMaterial?.diffuse.contents = oceanColor oceanGeometry.firstMaterial?.lightingModel = SCNMaterial.LightingModel.constant oceanGeometry.firstMaterial?.renderingOrder = 1 // ... 原有代码 }
3. 可选:禁用地球节点的交互
如果不需要地球节点的点击响应,直接禁用其交互性,彻底排除干扰:
private func setupGlobe() { self.earthNode = EarthNode(radius: earthRadius, earthColor: earthColor, earthGlow: glowColor, earthReflection: reflectionColor) earthNode.name = "EarthNode" earthNode.isUserInteractionEnabled = false // 禁用地球节点交互 sceneView.scene?.rootNode.addChildNode(earthNode) }
坐标转换提示
将SCNVector3转换为经纬度可使用球面坐标公式:
func positionToCoordinate(_ position: SCNVector3, radius: Double) -> CLLocationCoordinate2D { let lat = asin(Double(position.y)/radius) * 180 / .pi let lon = atan2(Double(position.x), Double(position.z)) * 180 / .pi return CLLocationCoordinate2D(latitude: lat, longitude: lon) }
内容的提问来源于stack exchange,提问作者Ahmed Zaidan
相关产品推荐
相关产品推荐

