问题一:怎么使用SwiftUI实现AR界面
目前IOS开发AR有两个框架,一个是ARKit的ARSCNView,另一个是RealityKit的ARView
后者比前者更新功能更强大一些,也更推荐使用
现在我们普遍使用的语言是SwiftUI,而上面两个框架继承的都是UIKit,是旧的框架
因此,我们实现的时候需要使用其他的类来桥接这两个框架
UIViewRepresentable
协议类
通过该协议就可以让我们轻松地在 SwiftUI 界面上使用 UIView
这个协议有两个必须提供的方法:makeUIView
和 updateUIView
下面我使用ARView举例
struct TestARViewContainer: UIViewRepresentable {
func makeUIView(context: Context) -> ARView {
return ARView()
}
func updateUIView(_ uiView: ARView, context: Context) {
}
}
makeUIView
方法创建了一个要表示的 UIView, 在其生命周期里只会调用一次updateUIView
方法会在 UIView 的状态发生变化时被调用,所以在其生命周期内会被调用多次(即使这是一个空实现也会被调用多次)。
一般我们只会这用到makeUIView
问题二:AR模型有什么注意的点吗
有!AR模型仅支持usdz格式的,这是苹果独有的模型格式
这个格式我自认为很鸡肋,是因为模型里只能展示一个动画,也就是说,即使你的模型里有多个动画,也只能展示出一个,这个问题官方一直都没有管!!!
如果你要实现一个模型,在不同条件下做不同的动作,唯一解决的办法就是:
把每一个动作都单独提出来做成一个模型,当你需要切换动作的时候,直接切换包含对应动作的模型
模型动作分离的教程,我之前也有写过,小白的话可以看一下,很简单就能完成✅
但是代码部分的实现很头痛,因为模型加载时需要时间的,如果你切换动作的时候,先加载模型,再播放动画,会有明显的卡壳的感觉
这里我提供一个思路,就是使用全局静态,一次性异步加载所有我们需要的包含动作的模型
这里给大家代码示范一下
首先是静态类
//
// TestModel.swift
// ARDemo
//
// Created by hp on 2024/9/2.
//
import Foundation
import RealityKit
import Combine
import SwiftUI
enum ModelCategory: CaseIterable{
case rollover
case play
case shake
case sit
case stand
var label: String{
get{
switch self{
case .rollover:
return "Rollover"
case .play:
return "Play"
case .shake:
return "Shake"
case .sit:
return "Sit"
case .stand:
return "Stand"
}
}
}
}
class TestModel{
var name: String
var category: ModelCategory
var modelEntity: ModelEntity?
var scaleCompensation: Float
private var cancellable: AnyCancellable?
init(name: String, category: ModelCategory, scaleCompensation: Float = 1.0) {
self.name = name
self.category = category
self.scaleCompensation = scaleCompensation
}
func asyncLoadModelEntity(){
let filename = self.name + ".usdz"
self.cancellable = ModelEntity.loadModelAsync(named: filename)
.sink(receiveCompletion: { loadCompletion in
switch loadCompletion{
case .finished:
break
case.failure(let error): print("Unable ton load modelEntity for \(filename).Error: \(error.localizedDescription)")
}
}, receiveValue: { modelEntity in
self.modelEntity = modelEntity
self.modelEntity?.scale /= 10
self.modelEntity?.scale += self.scaleCompensation
print("modelEntity for \(self.name) has been loaded")
})
}
}
//使用单例模式,异步初始化所有狗模型
struct TestModels{
static let shared = TestModels()
var all: [TestModel] = []
private init(){
print("TestModels init")
let rollover = TestModel(name: "rollover", category: .rollover, scaleCompensation: 0.1/100)
let play = TestModel(name: "play", category: .play, scaleCompensation: 0.1/100)
let shake = TestModel(name: "shake", category: .shake, scaleCompensation: 0.1/100)
let sit = TestModel(name: "sit", category: .sit, scaleCompensation: 0.1/100)
let stand = TestModel(name: "stand", category: .sit, scaleCompensation: 0.1/100)
rollover.asyncLoadModelEntity()
play.asyncLoadModelEntity()
shake.asyncLoadModelEntity()
sit.asyncLoadModelEntity()
stand.asyncLoadModelEntity()
self.all += [rollover,play,shake,sit,stand]
}
func get(category: ModelCategory) ->[TestModel] {
return all.filter({
$0.category == category
})
}
}
使用单例模式后,在第一次创建AR模型的时候,会自动异步加载所有的模型,方便后续切换
比如我第一次加载一个坐着的狗模型,实际上会把其他所有动作的狗模型都加载了
var items = TestModels.shared.get(category: .sit)
那么接下来怎么替换呢
这里我给个样例
func replaceModel(action: ModelCategory){
guard let arView = arView else{return}
//arView.scene.anchors.removeAll()
if let entity = arView.scene.findEntity(named: "dog"){
//保存位置
var position = entity.position
//移除模型,后面那个数字是需要自己试的
arView.scene.anchors.remove(at: 2)
print("Remove Success!")
//新建有动画的模型
var items = TestModels.shared.get(category: action)
let model = items[0]
if let modelEntity = model.modelEntity{
let clonedEntity = modelEntity.clone(recursive: true)
//添加旋转角等特性
clonedEntity.generateCollisionShapes(recursive: true)
arView.installGestures([.translation, .rotation], for: clonedEntity)
//创建锚点
let anchorEntity = AnchorEntity(.plane(.horizontal, classification: .any, minimumBounds: SIMD2(repeating: 0)))
clonedEntity.name = "dog"
anchorEntity.addChild(clonedEntity)
arView.scene.addAnchor(anchorEntity)
print("Create success!")
//播放动画
clonedEntity.playAnimation((clonedEntity.availableAnimations[0].repeat()), transitionDuration: 0.5, startsPaused: false)
let animationDuration = clonedEntity.availableAnimations[0].definition.duration
let timer = Timer.scheduledTimer(withTimeInterval: animationDuration, repeats: false, block: { _ in
print("Animation over!")
})
}
}
}
实际上位置变量没有用到,默认如果删除模型后新加的位置还是原来的位置
这是我进过测试总结出来的
问题三:怎么使用Unity作AR开发呢
做过AR开发的,或多或少都知道Unity的ARFundation可以进行AR开发
我也使用过,但是开发的是安卓版本的!
IOS的版本做好之后,需要打包成Xcode可以调用的库文件
但这时候会有个问题,就是所有的AR部分你必须在Unity中全部实现,因为Xcode调用Unity的库文件,是不能传输信息的
所以我最后没有选择用Unity进行IOS端的AR开发!
好了这就是本次博客的全部内容了,欢迎大家与我一起分享和讨论,我们一起学习,一起进步!!!