背景

在体会HelloWorld时,很猎奇每个功用是怎么实现的,但是这个demo复用了很多功用、数据模型,刚开始理解起来就比较困难。所以我就先从功用点来看,将复用的功用、数据模型都剔除去,确保单一功用能解藕独自运行。

环境

Xcode:15.1 beta

VisionOS:1.0

整理功用

graph LR;
    功用点-->A(设置光照);
    style A fill:#bbf,color:#fff
    click A "https://www.6hu.cc/post/7298690615046651943"
    功用点-->B(手势滚动地球)
    style B fill:#bbf,color:#fff
    click B "https://www.6hu.cc/post/7298765809290706983"
    功用点-->C(地球自转)
    功用点-->D(地球跟从鼠标拖动)
    功用点-->E(卫星环绕地球滚动)
    功用点-->F(月球环绕地球滚动)
    功用点-->G(沉浸式与窗口之间的切换)

地球自转

为了让模型更立体,后续的一切例子,都默许增加了光照

[VisionOS] 拆分HelloWorld的功用点 - 地球自转

import SwiftUI
import RealityKit
import RealityKitContent
struct EarthRotation: View {
    @State var isRotation = false
    @State var curEarth:Entity = Entity()
    var body: some View {
        ZStack{
            RealityView { content in
                guard let earth = await RealityKitContent.entity(named: "Globe") else {
                    return
                }
                earth.setSunlight(intensity: 14)
                earth.scale = SIMD3(repeating: 0.3)
                earth.orientation = .init(angle: 0, axis: [0, 1, 0])
                curEarth = earth
                content.add(earth)
            } update: { content in
                curEarth.components.set(RotationComponent(speed: isRotation ? 0.5 : 0))
            }
            Toggle("Rotation", isOn: $isRotation)
                .toggleStyle(.button)
                .padding(.top, 240)
        }
    }
    init() {
        RotationComponent.registerComponent()
        RotationSystem.registerSystem()
    }
}
#Preview {
    EarthRotation()
}

1. 加载3D资源

这里和手势滚动地球不一样,能够不必给Entity增加InputComponent,由于这个时候咱们并不需要一个手势去触发旋转。只要加载一个一般的3D资源就能够了。

2.增加自转组件

这里用到的了RotationComponentRotationSystem,是一个彻底自定义的组件。

import SwiftUI
import RealityKit
/// Rotation information for an entity.
struct RotationComponent: Component {
    var speed: Float
    var axis: SIMD3<Float>
    init(speed: Float = 1.0, axis: SIMD3<Float> = [0, 1, 0]) {
        self.speed = speed
        self.axis = axis
    }
}
/// A system that rotates entities with a rotation component.
struct RotationSystem: System {
    static let query = EntityQuery(where: .has(RotationComponent.self))
    init(scene: RealityKit.Scene) {}
    func update(context: SceneUpdateContext) {
        for entity in context.entities(matching: Self.query, updatingSystemWhen: .rendering) {
            guard let component: RotationComponent = entity.components[RotationComponent.self] else { continue }
            entity.setOrientation(.init(angle: component.speed * Float(context.deltaTime), axis: component.axis), relativeTo: entity)
        }
    }
}

这里面会涉及到一个ECS架构: Entity、Component、System。

Entity负责加载资源,系统供给了旋转、位置、缩放等功用,能够增加多个Component

Component就是一些功用组件,有输入、光照等系统准备好的组件,也能够自定义组件,自定义组件如果需要更新就需要用到System,理论上Component也能够增加多个System

Systemupdate办法触发的两个时机:

  • RealityView初始化时(只会触发Systemupdate必定时刻,不会一向触发)
  • RealityViewupdate办法(每帧都会触发Systemupdate

[VisionOS] 拆分HelloWorld的功用点 - 地球自转

注意: 由于用到了updatingSystemWhen: .rendering,一旦开始更新之后,Systemupdate办法每帧都会触发,相当于一向在触发entity.setOrientation,所以才能够一向旋转下去。那么update办法里面必定不能做耗时的操作,目前我只能想到两个办法来中止改写,其他办法暂时还没找到。

1. 移除组件

其实移除组件后,并不能中止Systemupdate办法,只是查询不到RotationComponent

components.remove(RotationComponent.self)

2. 退出窗口

这个能够彻底中止Systemupdate办法

3.注册Component、System

在咱们使用的时候,必定要注册自定义的Component、System,否则就没有任何效果

init() {
    RotationComponent.registerComponent()
    RotationSystem.registerSystem()
}