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

ARKit/SceneKit加载USDZ模型时自定义节点名被覆盖如何解决

问题说明

基于ARKit与SceneKit开发iOS增强现实应用时,加载.usdz模型到场景后,点击模型获取节点名称,仅能返回模型自带的内部节点名,无法获取到开发者自定义设置的节点名称。

  • 模型加载实现代码:
let mdlAsset = MDLAsset(url: urlPath)
mdlAsset.loadTextures()
let asset = mdlAsset.object(at: 0) // 提取第一个资源对象
var assetNode = SCNNode(mdlObject: asset)
assetNode = SCNNode(mdlObject: asset)
assetNode.name = "Node-2"
sceneView.scene.rootNode.addChildNode(assetNode)
  • 点击事件命中测试代码:
@objc func handleTap(recognizer: UITapGestureRecognizer){
       
    let location = recognizer.location(in: sceneView)
    let results = sceneView.hitTest(location, options: nil)

    guard recognizer.state == .ended else { return }

    if results.count > 0 {
        let result = results[0] as SCNHitTestResult
        let node = result.node  
        print(node.name)    
    }     
}
  • 实际运行表现:点击模型后控制台打印Optional("Sphere_0"),为模型内置的子节点名称,不符合预期的自定义值Node-2。
故障原因

SceneKit的hitTest方法默认返回射线检测命中的最内层绑定了几何体的节点,而非外层容器节点。
通过SCNNode(mdlObject:)从MDLAsset转换生成的节点是外层容器节点,.usdz模型的实际网格、材质资源全部挂载在该容器节点的多层子节点上。代码仅给最外层容器节点设置了自定义名称,点击时射线命中的是内部承载几何体的子节点,因此返回的是模型预设的内部节点名。

修复方案

方案1:沿父节点链向上查找目标节点

拿到命中的内层节点后,逐层遍历父节点链路,匹配到设置了自定义名称的目标节点即可,该方案不会破坏原有节点结构,推荐使用:

@objc func handleTap(recognizer: UITapGestureRecognizer){
    let location = recognizer.location(in: sceneView)
    let results = sceneView.hitTest(location, options: nil)
    guard recognizer.state == .ended, let firstHit = results.first else { return }
    
    var hitNode = firstHit.node
    // 向上遍历父节点直到找到自定义命名的节点
    while let parentNode = hitNode.parent {
        if parentNode.name == "Node-2" {
            hitNode = parentNode
            break
        }
        hitNode = parentNode
    }
    print(hitNode.name) // 输出Optional("Node-2"),符合预期
}

方案2:统一设置模型所有节点的名称

加载模型后递归遍历所有子节点,将所有节点的名称统一设置为自定义值,适合不需要区分模型内部子节点的场景:

let mdlAsset = MDLAsset(url: urlPath)
mdlAsset.loadTextures()
let asset = mdlAsset.object(at: 0)
let assetNode = SCNNode(mdlObject: asset)
assetNode.name = "Node-2"

// 递归遍历所有子节点设置名称
func setNodeNameRecursively(_ node: SCNNode, name: String) {
    node.name = name
    node.childNodes.forEach { setNodeNameRecursively($0, name: name) }
}
setNodeNameRecursively(assetNode, name: "Node-2")

sceneView.scene.rootNode.addChildNode(assetNode)

内容的提问来源于stack exchange,提问作者Federico

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 06:45:41