You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ARKit问题:点击无法放置3D家具模型

解决ARKit家具放置应用点击无法放置模型的问题

我来帮你排查并修复这个问题!从你给出的代码片段来看,核心是几个关键环节没有处理到位,我整理了完整的修复方案和代码:

核心问题分析

你的现有代码只添加了点击手势,但没有启动AR会话进行平面检测,也没有实现点击后的模型放置逻辑,这就是点击后模型没反应的主要原因。

完整修复代码

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController {
    @IBOutlet weak var sceneView: ARSCNView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 初始化AR场景基础配置
        setupARSession()
        // 添加点击放置手势
        addTapGestureToSceneView()
        // 开启调试视图(可选,方便查看平面检测状态和特征点)
        sceneView.debugOptions = [.showFeaturePoints, .showWorldOrigin]
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // 启动AR世界追踪会话,开启水平面检测
        let configuration = ARWorldTrackingConfiguration()
        configuration.planeDetection = .horizontal
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // 离开页面时暂停AR会话,节省资源
        sceneView.session.pause()
    }
    
    // 配置AR会话基础设置
    private func setupARSession() {
        sceneView.delegate = self
        sceneView.scene = SCNScene()
    }
    
    // 添加点击手势识别器
    private func addTapGestureToSceneView() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapPlacement(_:)))
        sceneView.addGestureRecognizer(tapGesture)
    }
    
    // 处理点击放置模型的逻辑
    @objc private func handleTapPlacement(_ gesture: UITapGestureRecognizer) {
        let touchPoint = gesture.location(in: sceneView)
        
        // 射线检测:只寻找已经识别的水平面
        let hitResults = sceneView.hitTest(touchPoint, types: .existingPlaneUsingExtent)
        
        guard let hitResult = hitResults.first else {
            print("提示:请先等待APP识别到水平面(比如桌面、地板)后再点击")
            return
        }
        
        // 加载你的椅子模型(替换成实际的模型文件名)
        guard let chairScene = SCNScene(named: "你的椅子模型.scn") else {
            print("错误:无法找到椅子模型文件,请检查文件是否添加到项目中")
            return
        }
        
        // 获取模型节点(如果模型有特定节点名称,替换成实际名称;否则取第一个子节点)
        let chairNode = chairScene.rootNode.childNode(withName: "椅子节点名", recursively: true) ?? chairScene.rootNode.childNodes.first!
        
        // 设置模型位置为点击的平面坐标
        chairNode.position = SCNVector3(
            hitResult.worldTransform.columns.3.x,
            hitResult.worldTransform.columns.3.y,
            hitResult.worldTransform.columns.3.z
        )
        
        // (可选)调整模型大小,如果模型太大/太小的话
        chairNode.scale = SCNVector3(0.3, 0.3, 0.3)
        
        // 将模型添加到AR场景中
        sceneView.scene.rootNode.addChildNode(chairNode)
    }
}

// 扩展实现ARSCNViewDelegate,用于可视化检测到的平面(可选)
extension ViewController: ARSCNViewDelegate {
    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        // 当检测到新的水平面时,添加半透明平面提示用户
        guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
        let planeNode = createVisualPlane(with: planeAnchor)
        node.addChildNode(planeNode)
    }
    
    func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
        // 更新已检测平面的大小
        guard let planeAnchor = anchor as? ARPlaneAnchor,
              let planeNode = node.childNodes.first as? SCNPlane else { return }
        planeNode.width = CGFloat(planeAnchor.extent.x)
        planeNode.height = CGFloat(planeAnchor.extent.z)
        planeNode.position = SCNVector3(planeAnchor.center.x, 0, planeAnchor.center.z)
    }
    
    // 创建可视化的半透明平面
    private func createVisualPlane(with planeAnchor: ARPlaneAnchor) -> SCNNode {
        let plane = SCNPlane(width: CGFloat(planeAnchor.extent.x), height: CGFloat(planeAnchor.extent.z))
        let planeNode = SCNNode(geometry: plane)
        planeNode.position = SCNVector3(planeAnchor.center.x, 0, planeAnchor.center.z)
        planeNode.eulerAngles.x = -.pi / 2 // 旋转平面使其水平
        
        let material = SCNMaterial()
        material.diffuse.contents = UIColor.systemBlue.withAlphaComponent(0.3)
        plane.materials = [material]
        
        return planeNode
    }
}

关键修复点说明

  1. AR会话启动与平面检测
    viewWillAppear中启动了ARWorldTrackingConfiguration并开启了水平平面检测,这是ARKit识别真实水平面的基础。

  2. 点击手势的实际处理逻辑
    实现了handleTapPlacement方法,通过hitTest检测用户点击位置的已识别平面,确保模型准确放置在水平面上。

  3. 模型加载与位置设置
    修复了模型加载的逻辑,同时正确设置模型的位置为点击点的世界坐标。

额外注意事项

  • 确保你的.scn模型和PNG纹理文件已经正确添加到Xcode项目中,并且勾选了当前的target(在文件检查器的"Target Membership"里确认)。
  • 如果模型大小不合适,调整chairNode.scale的值即可。
  • 测试时需要在有清晰纹理的水平面上进行(比如桌面、地板),ARKit需要足够的视觉特征点才能识别平面。

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

火山引擎 最新活动