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

如何从URL加载RealityKit中的ModelEntity?

从URL加载RealityKit ModelEntity的修复方案

问题分析

原代码核心问题在于直接在主线程用同步方法加载远程URL:RealityKit的ModelEntity.load(contentsOf:)同步方法不适合处理网络资源,会阻塞主线程且极易加载失败;另外冗余的强制类型转换也存在崩溃风险。

修复步骤

  1. 异步下载远程USDZ模型到本地临时目录
  2. 从本地临时文件路径加载ModelEntity
  3. 在主线程安全更新AR场景
  4. 移除冗余的强制类型转换
  5. 完善下载与加载全流程的错误处理

修改后的完整代码

import SwiftUI
import RealityKit
import ARKit
import FocusEntity

struct ARScreen : View {
    
    @StateObject var arScreenVM = ArScreenViewModel()
    
    @State private var isPlacementEnabled = false
    @State private var selectedModel = "sitdown.usdz"
    @State private var modelConfirmedForPlacement: String?
    
    init() {
        let navBarAppearance = UINavigationBar.appearance()
        navBarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
        navBarAppearance.tintColor = .black
    }
    
    var body: some View {
        NavigationView {
            ZStack(alignment: .bottom) {
                ARViewContainer(modelConfirmedForPlacement: $modelConfirmedForPlacement)
                PlacementButtonView(isPlacementEnabled: $isPlacementEnabled, 
                                    selectedModel: selectedModel, 
                                    modelConfirmedForPlacement: $modelConfirmedForPlacement)
            }
            .background(Color("Yellow").ignoresSafeArea())
        }
        .navigationBarTitle("", displayMode: .inline)
    }
    
    struct ARViewContainer: UIViewRepresentable {
        
        @Binding var modelConfirmedForPlacement: String?
        
        func makeUIView(context: Context) -> ARView {
            CustomARView(frame: .zero)
        }
        
        func updateUIView(_ uiView: ARView, context: Context) {
            guard let _ = modelConfirmedForPlacement else { return }
            
            print("Debug: 开始加载远程模型")
            guard let modelURL = URL(string: "https://developer.apple.com/augmented-reality/quick-look/models/vintagerobot2k/robot_walk_idle.usdz") else {
                print("无效的模型URL")
                modelConfirmedForPlacement = nil
                return
            }
            
            // 异步下载模型到临时目录
            Task {
                do {
                    let (tempURL, _) = try await URLSession.shared.download(from: modelURL)
                    // 加载本地临时文件
                    let modelEntity = try ModelEntity.load(contentsOf: tempURL)
                    
                    // 主线程更新AR场景
                    await MainActor.run {
                        let anchorEntity = AnchorEntity(plane: .any)
                        anchorEntity.addChild(modelEntity)
                        uiView.scene.addAnchor(anchorEntity)
                        
                        // 播放模型动画(先判断是否有动画)
                        if !modelEntity.availableAnimations.isEmpty {
                            modelEntity.playAnimation(modelEntity.availableAnimations[0].repeat(),
                                                      transitionDuration: 0.5,
                                                      startsPaused: false)
                        }
                        modelConfirmedForPlacement = nil
                    }
                } catch {
                    print("加载模型失败: \(error.localizedDescription)")
                    await MainActor.run {
                        modelConfirmedForPlacement = nil
                    }
                }
            }
        }
    }
    
    class CustomARView: ARView, FEDelegate {
        let focusSquare = FESquare()
        
        required init(frame frameRect: CGRect) {
            super.init(frame: frameRect)
            
            focusSquare.viewDelegate = self
            focusSquare.delegate = self
            focusSquare.setAutoUpdate(to: true)
            setupARView()
        }
        
        func setupARView() {
            let config = ARWorldTrackingConfiguration()
            config.planeDetection = [.horizontal, .vertical]
            config.environmentTexturing = .automatic
            if ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh) {
                config.sceneReconstruction = .mesh
            }
            session.run(config)
        }
        
        @MainActor required dynamic init?(coder decoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
}

struct PlacementButtonView: View {
    
    @Binding var isPlacementEnabled: Bool
    var selectedModel: String?
    @Binding var modelConfirmedForPlacement: String?
    
    var body: some View {
        Button(action: {
            print("Debug: 确认放置模型")
            modelConfirmedForPlacement = selectedModel
        }) {
            Image(systemName: "checkmark")
                .frame(width: 60, height: 60)
                .font(.title)
                .background(Color.white.opacity(0.75))
                .cornerRadius(30)
                .padding(20)
        }
    }
}

// 补充原代码缺失的ViewModel实现
class ArScreenViewModel: ObservableObject {}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ARScreen()
    }
}
#endif

关键修改点说明

  • 用URLSession.shared.download(from:)异步下载模型,彻底避免主线程阻塞
  • 下载完成后从本地临时URL加载模型,符合RealityKit的资源加载规范
  • 通过Task和await MainActor.run确保AR场景更新操作在主线程执行
  • 增加动画存在性判断,防止数组越界崩溃
  • 修复导航栏外观设置的冗余代码
  • 补充原代码缺失的ArScreenViewModel实现

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 14:17:03