You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求协助: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:06:00