[VisionOS] 拆分HelloWorld的功能点 - 卫星围绕地球转动

背景

在体验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(沉浸式与窗口之间的切换)

卫星围绕地球转动

这里虽然只展示了卫星运动,其实月球和它一样,只是去掉了轨迹。

swift 复制代码
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轴开始旋转。

Entity1添加一个Entity2,如果不改变Entity2的位置,默认两个原点都是[0,0,0],也就是Entity1Entity2同时往相同方向旋转,视觉上看起来是重叠的。

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

2.2 实现一个简单的围绕旋转

Swift 复制代码
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()
}

用刚才的图理解一下,就是这样的。

2.3 自定义卫星组件

swift 复制代码
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比较多。

白色轨迹组件TraceComponent就没有深究了,里面涉及到MeshResourceTextureResource,目前还不是很懂。

2.4 卫星围绕地球转动

swift 复制代码
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,逻辑是一样的。

相关推荐
HarderCoder5 个月前
Apple Vision Pro 学习资料
visionos
苹果API搬运工5 个月前
试玩 RealityComposerPro 中的 Shader Graph:用圆环制作一个 Meta Logo
visionos·增强现实
苹果API搬运工5 个月前
我开源了个手势匹配框架,让你在模拟器调试 visionOS 手部追踪功能!
visionos·增强现实
苹果API搬运工5 个月前
只需三板斧!带你入门 visionOS 空间计算的数学与几何基础
visionos·增强现实
-九月新辰-6 个月前
Unity VisionOS开发流程
unity·游戏引擎·visionos
XR基地6 个月前
XR 世界导览021 | AVP App Store上线网页版、如何在AVP上快速匹配手势、AVP开发岗位
visionos
XR基地6 个月前
XR 世界导览#020-visionOS 1.1/Xcode 17.3/PICO 5.9.0 更新了!
unity3d·visionos
xChester6 个月前
visionOS 应用图标设计
前端·ios·visionos
XR基地6 个月前
XR 世界导览#019-Let's visionOS 大会/在 AVP 上玩手机或查看Spline的3D模型/用代码写ShaderGraph
unity3d·visionos
xChester7 个月前
VisionPro开发 - 轻松实现天空盒并添加光照
前端·ios·visionos