我有一个UIViewController,它使用ARSCNView并通过Scenekit向它添加一些元素,如下例所示.一切都工作正常,除非我打电话给当前显示这个视图控制器,它需要很长的时间或延迟才能在屏幕上显示.
@IBOutlet var sceneView: ARSCNView! override func viewDidLoad() { super.viewDidLoad() sceneView.showsStatistics = DebugSettings.isDebugActive for (index, coach) in coachPositions.enumerated() { let coachGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.005) let coachNode = TrainEngineNode(position: SCNVector3Make(0, Float(index) * 0.1, -0.5), geometry: coachGeometry) sceneView.scene.rootNode.addChildNode(coachNode) } self.sceneView.autoenablesDefaultLighting = true } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Create a session configuration let configuration = ARWorldTrackingConfiguration() // Run the view's session sceneView.session.run(configuration) }正如大家所指出的那样,延迟是你在设置场景时阻塞主线程的事实.所有UI更新都发生在主线程上,并且显示刷新 60 times per second,除了可以做120Hz的最新iPad专业版.这意味着您所执行的任何同步工作单元必须在不到1/60 = 0.016667秒内完成.
您的初始化程序TrainEngineNode(position:geometry :)可能正在执行诸如从DAE或OBJ文件加载资源的工作,这可能是所有时间的所在.我这样说,因为如果你改变这一行:
let coachNode = TrainEngineNode(position: SCNVector3Make(0, Float(index) * 0.1, -0.5), geometry: coachGeometry)
为此,我需要这样做,因为你的问题中没有提供TrainEngineNode:
let coachNode = SCNNode(geometry: coachGeometry)
那么iPhone 7没有明显的延迟.
SceneKit和ARKit不要求您对任何特定线程进行更改,因此只要您遇到这种情况,您就可以将工作卸载到后台队列,或者最好是您管理的串行队列.在适当的时间进行工作有一些注意事项,例如在ARSession完全运行后将对象添加到场景中.对于创建ARKit应用程序时可能遵循的模式的一些优秀想法,我推荐Apple提供的this sample.
简化示例如下:
class SimpleARViewController: UIViewController { @IBOutlet weak var sceneView: ARSCNView! weak var activityIndicator: UIActivityIndicatorView? var coachPositions = [1, 2, 3, 4, 5] let updateQueue = DispatchQueue(label: "com.example.apple-samplecode.arkitexample.serialSceneKitQueue") override func viewDidLoad() { super.viewDidLoad() sceneView.alpha = 0 view.backgroundColor = .black sceneView.isPlaying = false sceneView.session.delegate = self let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge) activityIndicator.startAnimating() activityIndicator.translatesAutoresizingMaskIntoConstraints = false view.addSubview(activityIndicator) NSLayoutConstraint.activate([view.centerXAnchor.constraint(equalTo: activityIndicator.centerXAnchor, constant: 0), view.centerYAnchor.constraint(equalTo: activityIndicator.centerYAnchor, constant: 0)]) self.activityIndicator = activityIndicator sceneView.autoenablesDefaultLighting = true } private func loadScene() { SCNTransaction.begin() SCNTransaction.disableActions = true for (index, _) in self.coachPositions.enumerated() { let coachGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.005) // Simulates loading time of `TrainEngineNode(position:geometry:)` usleep(500000) let coachNode = SCNNode(geometry: coachGeometry) coachNode.worldPosition = SCNVector3Make(0, Float(index) * 0.1, -0.5) self.sceneView.scene.rootNode.addChildNode(coachNode) } SCNTransaction.commit() self.isLoading = false } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let configuration = ARWorldTrackingConfiguration() sceneView.session.run(configuration) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) sceneView.session.pause() } var isLoading = true var hasLoaded = false } extension SimpleARViewController: ARSessionDelegate { func session(_ session: ARSession, didUpdate frame: ARFrame) { // Waiting until after the session starts prevents objects from jumping around if hasLoaded == false { hasLoaded = true updateQueue.async { [weak self] in self?.loadScene() } } else if isLoading == false { guard let activityIndicator = self.activityIndicator else { return } DispatchQueue.main.async { UIView.animate(withDuration: 0.35, animations: { [weak self] in self?.sceneView.alpha = 1 activityIndicator.alpha = 0 }, completion: { _ in activityIndicator.removeFromSuperview() }) } } } }
此代码导致以下交互:
正如您在gif中看到的那样,视图控制器显示没有延迟,因为所有加载“工作”现在都在我们的专用串行队列上完成.通过将TrainEngineNode的创建与它们添加到场景的时间分离,可以进一步改善这个人为的例子.为了简洁起见,我保持逻辑与您已经拥有的逻辑类似,同时提供相对强大的初始化.对于3D对象加载的更强大/抽象的实现,我建议查看VirtualObjectLoader以及它在上述示例项目中的使用方式.