布景

在体会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(地球自转)
    style C fill:#bbf,color:#fff
    click C "https://www.6hu.cc/post/7298775642261569575"
    功用点-->D(地球跟随鼠标拖动)
    style D fill:#bbf,color:#fff
    click D "https://www.6hu.cc/post/7299037876637351975"
    功用点-->E(卫星环绕地球滚动)
    style E fill:#bbf,color:#fff
    click E "https://www.6hu.cc/post/7300431522255241250"
    功用点-->G(沉浸式与窗口之间的切换)
    style G fill:#bbf,color:#fff
    click G "https://www.6hu.cc/spost/7300816733525901352"

卫星环绕地球滚动

这儿尽管只展现了卫星运动,其实月球和它相同,仅仅去掉了轨道。

[VisionOS] 拆分HelloWorld的功用点 - 卫星环绕地球滚动

import SwiftUI
import RealityKit
struct SatelliteAroundEarth: View {
    @State private var earthEntity: EarthEntity?
    var body: some View {
        RealityView { content in
            let earth = await EarthEntity(name: "")
            earth.setSunlight(intensity: 14)
            content.add(earth)
            self.earthEntity = earth
            earth.update()
        } 
    }
    init() {
        TraceComponent.registerComponent()
        TraceSystem.registerSystem()
    }
}
#Preview {
    SatelliteAroundEarth()
}

这个是HelloWorld里边最有意思的一个场景,地球在自转,一颗卫星环绕地球运转,后边拖着长长的白色轨道。

1. 资源

地球自转相同,一个一般的资源就可以了,不用在Reality Composer Pro里边增加任何组件。

2. 滚动

2.1 原理

假如要了解卫星怎样环绕地球滚动,首先要了解Entity

Entity我了解,便是一个3D的容器,它既可以加载一个本地的资源,也可以addChild增加其他Entity,相似UIView

旋转很好了解,在地球自转里边,我们现已自定义了一个RotationComponent绕Y轴旋转的组件,Entity.components增加组件就可以轻松完成旋转。

举个例子,如下图,Entity1增加了一个Y轴旋转的组件,那么Entity1就会环绕Y轴开端旋转。

[VisionOS] 拆分HelloWorld的功用点 - 卫星环绕地球滚动

Entity1增加一个Entity2,假如不改动Entity2的方位,默认两个原点都是[0,0,0],也便是Entity1Entity2一起往相同方向旋转,视觉上看起来是重叠的。

[VisionOS] 拆分HelloWorld的功用点 - 卫星环绕地球滚动

假如想要让Entity2环绕Entity1旋转呢?只需要给Entity2设置一个Z轴上的偏移量(默认单位m),[0,0.1,0]。还是把Entity1Entity2当作一个全体,这个全体是环绕Entity1的原点做Y轴旋转,那么视觉上看起来便是Entity2环绕Entity1旋转。

[VisionOS] 拆分HelloWorld的功用点 - 卫星环绕地球滚动

2.2 完成一个简略的环绕旋转

import SwiftUI
import RealityKit
import RealityKitContent
struct EarthAround: View {
    var body: some View {
        RealityView { content in
            guard let earth = await RealityKitContent.entity(named: "Earth") else {
                return
            }
            let orbit = Entity()
            // 增加一个Y轴旋转
            orbit.components.set(RotationComponent(speed: 0.5))
            content.add(orbit)
            // 为了让地球更立体,增加光线
      earth.setSunlight(intensity: 14)
      // 地球模型太大,适当的缩放,防止超出窗口
      earth.scale = SIMD3(repeating: 0.1)
      // 设置Z轴偏移
      earth.position = [0,0,0.1]
      orbit.addChild(earth)
        }
    }
    init() {
        RotationComponent.registerComponent()
        RotationSystem.registerSystem()
    }
}
#Preview {
    EarthAround()
}

用刚才的图了解一下,便是这样的。

[VisionOS] 拆分HelloWorld的功用点 - 卫星环绕地球滚动

2.3 自定义卫星组件

import SwiftUI
import RealityKit
import RealityKitContent
class SatelliteEntity: Entity {
    private var satellite = Entity()
    private let box = Entity()
    private let orbit = Entity()
    init(_ configuration: Configuration) async {
        super.init()
        guard let satellite = await RealityKitContent.entity(named: configuration.name) else { return }
        self.satellite = satellite
        orbit.components.set(RotationComponent(speed: 0))
        // 在Y轴上的旋转视点
        orbit.orientation = .init(angle: Float(configuration.initialRotation.radians), axis: [0,1,0])
        self.addChild(orbit)
        orbit.addChild(box)
        box.addChild(satellite)
    }
    @MainActor required init() {
        super.init()
    }
    func update(anchor: Entity, configuration: Configuration, speed: Float){
        // 在Z轴上的旋转视点,可以调整卫星与赤道的夹角
        let newOrientation = simd_quatf(angle: Float(configuration.inclination.radians), axis: [0, 0, 1])
        orientation = newOrientation
        // Y轴自旋转
        if var rotation: RotationComponent = orbit.components[RotationComponent.self]{
            rotation.speed = configuration.speedRatio * speed
            orbit.components[RotationComponent.self] = rotation
        }
        // 卫星所在box,缩放、方位
        box.scale = SIMD3(repeating: configuration.scale)
        box.position = [0,0,configuration.altitude]
        // 卫星更新轨道
        satellite.updateTrace(anchor: anchor,
                              width: configuration.traceWidth,
                              isVisible: configuration.isTraceVisible,
                              isPaused: false)
    }
}

那这儿就略微复杂一点了,里边涉及到的EntityComponent比较多。

[VisionOS] 拆分HelloWorld的功用点 - 卫星环绕地球滚动

白色轨道组件TraceComponent就没有深究了,里边涉及到MeshResourceTextureResource,现在还不是很懂。

2.4 卫星环绕地球滚动

import SwiftUI
import RealityKit
import RealityKitContent
class EarthEntity: Entity {
    /// The model that draws the Earth's surface features.
    private var earth: Entity = Entity()
    /// A container for artificial satellites.
    private var satellites = Entity()
    init(name: String) async {
        super.init()
        guard let earth = await RealityKitContent.entity(named: name) else {return}
        self.earth = earth
        await satellites = SatelliteEntity(.orbitSatelliteDefault)
        self.addChild(earth)
        self.addChild(satellites)
        update()
    }
    @MainActor required init() {
        super.init()
    }
    func update(){
        // 增加地球自转
        if var rotation: RotationComponent = earth.components[RotationComponent.self] {
            rotation.speed = 0.1
            earth.components[RotationComponent.self] = rotation
        } else {
            earth.components.set(RotationComponent(speed: 0.1))
        }
        // 更新卫星的数据
        (satellites as? SatelliteEntity)?.update(anchor: earth, configuration: .orbitSatelliteDefault, speed: 0.1)
        // 现在只用到了缩放
        move(
            to: Transform(
                scale: SIMD3(repeating: 0.3),
                rotation: orientation,
                translation: .zero),
            relativeTo: parent)
    }
}

现在是把所有逻辑都封装在EarthEntity,在外面运用就比较简略。

代码

都可以独自运转

2.2 完成一个简略的环绕旋转运转EarthAround.swift

2.4 卫星环绕地球滚动 运转SatelliteAroundEarth.swift,里边也包含了月球。

注意:只要了解了卫星环绕地球的原理,其实就可以复用到月球,所以卫星、月球的代码放在一起展现。当然也一个独自的月球环绕的Demo,逻辑是相同的。