请求协助:ARKit中实现SCNNode(.dae格式3D角色)不同位置触摸交互
嘿,作为ARKit入门开发者,这个触摸角色不同部位触发不同动作的需求其实挺常见的,我来一步步带你实现~
实现ARKit中触摸3D角色不同部位触发动作的方案
核心思路
SceneKit里检测触摸角色特定部位的核心是用SCNHitTest获取触摸点对应的几何体/节点信息,再根据不同部位(比如头、手、躯干)绑定对应的动作或菜单选项。
步骤1:加载DAE角色并开启交互
首先确保你的DAE模型正确加载,并且给角色节点开启交互权限:
// 加载DAE模型文件 guard let characterScene = SCNScene(named: "YourCharacter.dae") else { return } // 获取角色根节点(记得替换成你DAE里的节点名称) let characterNode = characterScene.rootNode.childNode(withName: "CharacterRoot", recursively: true)! // 开启节点的用户交互(默认是关闭的) characterNode.isUserInteractionEnabled = true // 将角色添加到AR场景中 arView.scene.rootNode.addChildNode(characterNode)
小提示:最好在建模时把角色的每个可交互部位(头、手等)做成单独的子节点,或者给几何体设置明确的名称,这样后续判断会更方便。
步骤2:处理触摸事件
在你的AR视图控制器里,重写touchesBegan方法做命中测试:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } // 获取触摸在AR视图上的坐标点 let touchPoint = touch.location(in: arView) // 执行命中测试,只检测我们的角色节点 let hitResults = arView.hitTest(touchPoint, options: [ SCNHitTestOption.searchMode: SCNHitTestSearchMode.all.rawValue, SCNHitTestOption.rootNode: characterNode, SCNHitTestOption.ignoreHiddenNodes: true // 忽略隐藏节点 ]) guard let hitResult = hitResults.first else { return } // 拿到被触摸的节点和几何体信息 let touchedNode = hitResult.node let geometryName = hitResult.geometry?.name // 根据触摸部位处理动作 handleCharacterTouch(node: touchedNode, geometryName: geometryName) }
步骤3:区分部位触发对应动作
接下来写处理触摸的方法,根据节点名称或几何体名称判断部位:
func handleCharacterTouch(node: SCNNode, geometryName: String?) { // 情况1:角色部位是单独子节点,直接判断节点名称 switch node.name { case "Head": showActionMenu(title: "查看头部详情", action: { // 这里写头部对应的动作,比如弹出信息框、播放点头动画 print("触摸了角色头部") self.playCharacterAnimation(animationName: "HeadNod") }) case "LeftHand": showActionMenu(title: "触发左手动作", action: { // 播放挥手动画或者打开道具菜单 print("触摸了角色左手") self.playCharacterAnimation(animationName: "WaveLeft") }) case "Torso": showActionMenu(title: "打开角色主菜单", action: { // 弹出角色属性、换装等选项 print("触摸了角色躯干") }) default: // 触摸到未定义部位的默认处理 print("触摸了角色其他部位") } // 情况2:模型是合并的,用几何体名称判断(如果DAE里给几何体命名了) if let geoName = geometryName { switch geoName { case "HeadGeometry": // 同样处理头部动作 break // 其他几何体名称的判断逻辑... } } }
步骤4:实现菜单选项(可选)
如果需要弹出交互菜单,可以用系统的UIAlertController或者自定义弹窗:
func showActionMenu(title: String, action: @escaping () -> Void) { let alert = UIAlertController(title: "操作选项", message: title, preferredStyle: .actionSheet) alert.addAction(UIAlertAction(title: "确认", style: .default, handler: { _ in action() })) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) present(alert, animated: true) }
额外:播放角色动画
如果你的DAE里包含动作动画,可以用下面的方法播放:
func playCharacterAnimation(animationName: String) { guard let characterScene = SCNScene(named: "YourCharacter.dae") else { return } guard let animation = characterScene.animation(forKey: animationName) else { return } // 播放动画,设置循环模式等 animation.repeatCount = 1 characterNode.addAnimation(animation, forKey: animationName) }
注意事项
- 用Xcode的SceneKit编辑器打开DAE文件,确认每个部位的节点/几何体名称是否正确,避免判断出错。
- 如果角色有复杂的层级结构,命中测试时可以设置
recursively参数来遍历子节点。
内容的提问来源于stack exchange,提问作者Anup G Prasad
相关产品推荐
相关产品推荐

