背景
在体验HelloWorld时,很好奇每个功能是怎么实现的,但是这个demo复用了很多功能、数据模型,刚开始理解起来就比较困难。所以我就先从功能点来看,将复用的功能、数据模型都剔除掉,保证单一功能能解藕单独运行。
环境
Xcode:15.1 beta
VisionOS:1.0
梳理功能
graph LR;
功能点-->A(设置光照);
style A fill:#bbf,color:#fff
click A "https://juejin.cn/post/7298690615046651943"
功能点-->B(手势转动地球)
style B fill:#bbf,color:#fff
click B "https://juejin.cn/post/7298765809290706983"
功能点-->C(地球自转)
style C fill:#bbf,color:#fff
click C "https://juejin.cn/post/7298775642261569575"
功能点-->D(地球跟随鼠标拖动)
style D fill:#bbf,color:#fff
click D "https://juejin.cn/post/7299037876637351975"
功能点-->E(卫星围绕地球转动)
style E fill:#bbf,color:#fff
click E "https://juejin.cn/post/7300431522255241250"
功能点-->G(沉浸式与窗口之间的切换)
style G fill:#bbf,color:#fff
click G "https://juejin.cn/spost/7300816733525901352"
地球跟随鼠标拖动
在这里可以明显看到窗口是有边界的,触碰到边界时,整个模型都很会切掉。如果我们在加载模型后,看不到效果,要考虑下是不是超出窗口了,适当的调整模型的位置,窗口的大小。
import SwiftUI
import RealityKit
import RealityKitContent
struct EarthPlacement: View {
var body: some View {
RealityView { content in
guard let earth = await RealityKitContent.entity(named: "Globe") else {
return
}
content.add(earth)
earth.setSunlight(intensity: 14)
earth.scale = SIMD3(repeating: 0.3)
}
.placementGestures(initialPosition: Point3D([450,300.0,100.0]))
}
}
#Preview {
EarthPlacement()
}
1. 加载3D资源
和手势转动地球一样,资源必须要添加一个InputComponent
,这样才可以添加手势。
2. 添加手势
import SwiftUI
import RealityKit
extension View {
/// Listens for gestures and places an item based on those inputs.
func placementGestures(
initialPosition: Point3D = .zero,
axZoomIn: Bool = false,
axZoomOut: Bool = false
) -> some View {
self.modifier(
PlacementGesturesModifier(
initialPosition: initialPosition,
axZoomIn: axZoomIn,
axZoomOut: axZoomOut
)
)
}
}
/// A modifier that adds gestures and positioning to a view.
private struct PlacementGesturesModifier: ViewModifier {
var initialPosition: Point3D
var axZoomIn: Bool
var axZoomOut: Bool
@State private var scale: Double = 1
@State private var startScale: Double? = nil
@State private var position: Point3D = .zero
@State private var startPosition: Point3D? = nil
func body(content: Content) -> some View {
content
.onAppear {
position = initialPosition
}
.scaleEffect(scale)
.position(x: position.x, y: position.y)
.offset(z: position.z)
// Enable people to move the model anywhere in their space.
.simultaneousGesture(DragGesture(minimumDistance: 0.0, coordinateSpace: .global)
.handActivationBehavior(.pinch)
.onChanged { value in
if let startPosition {
let delta = value.location3D - value.startLocation3D
position = startPosition + delta
} else {
startPosition = position
}
}
.onEnded { _ in
startPosition = nil
}
)
// Enable people to scale the model within certain bounds.
.simultaneousGesture(MagnifyGesture()
.onChanged { value in
if let startScale {
scale = max(0.1, min(3, value.magnification * startScale))
} else {
startScale = scale
}
}
.onEnded { value in
startScale = scale
}
)
.onChange(of: axZoomIn) {
scale = max(0.1, min(3, scale + 0.2))
startScale = scale
}
.onChange(of: axZoomOut) {
scale = max(0.1, min(3, scale - 0.2))
startScale = scale
}
}
}
和手势转动地球类似,添加了DragGesture
手势,在onChanged
、onEnded
中改变position
。MagnifyGesture
放大手势,我暂时是没有用,模拟器也不知道怎么模拟。