visionOS空间计算实战开发教程Day 8 附属视图和动画

简介: 通过前面的学习我们已经知道setupContentEntity用于初始化实体对象,getTargetEntity用于根据模型的名称查找到指定模型。addCube(name: String, position: SIMD3<Float>, color: UIColor)中包含三个参数,name用于指定所创建盒子的名称,position用于指定盒子所处的位置,同样是按人所处位置为参照坐标,color用于指定盒体的颜色。

本节我们开始初探在visionOS中添加动画效果,我们的入口文件和ContentViewDay 6中并没有什么区别,所以重点来看ViewModelImmersiveView

首先是ViewModel.swift文件:

import SwiftUI
import RealityKit
class ViewModel: ObservableObject {
    private var contentEntity = Entity()
    func setupContentEntity() -> Entity {
        return contentEntity
    }
    func getTargetEntity(name: String) -> Entity? {
        return contentEntity.children.first { $0.name == name }
    }
    func addCube(name: String, position: SIMD3<Float>, color: UIColor) -> ModelEntity {
        let entity = ModelEntity(
            mesh: .generateBox(size: 0.5, cornerRadius: 0),
            materials: [SimpleMaterial(color: color, isMetallic: false)],
            collisionShape: .generateBox(size: SIMD3<Float>(repeating: 0.5)),
            mass: 0.0
        )
        entity.name = name
        entity.position = position
        entity.components.set(InputTargetComponent(allowedInputTypes: .indirect))
        entity.components.set(CollisionComponent(shapes: [ShapeResource.generateBox(size: SIMD3<Float>(repeating: 0.5))], isStatic: true))
        entity.components.set(HoverEffectComponent())
        contentEntity.addChild(entity)
        return entity
    }
    func playAnimation(entity: Entity) {
        let goUp = FromToByAnimation<Transform>(
            name: "goUp",
            from: .init(scale: .init(repeating: 1), translation: entity.position),
            to: .init(scale: .init(repeating: 1), translation: entity.position + .init(x:0 ,y: 0.4, z: 0)),
            duration: 0.2,
            timing: .easeOut,
            bindTarget: .transform
        )
        let pause = FromToByAnimation<Transform>(
            name: "pause",
            from: .init(scale: .init(repeating: 1), translation: entity.position + .init(x:0 ,y: 0.4, z: 0)),
            to: .init(scale: .init(repeating: 1), translation: entity.position + .init(x:0 ,y: 0.4, z: 0)),
            duration: 0.1,
            bindTarget: .transform
        )
        let goDown = FromToByAnimation<Transform>(
            name: "goDown",
            from: .init(scale: .init(repeating: 1), translation: entity.position + .init(x:0 ,y: 0.4, z: 0)),
            to: .init(scale: .init(repeating: 1), translation: entity.position),
            duration: 0.2,
            timing: .easeOut,
            bindTarget: .transform
        )
        let goUpAnimation = try! AnimationResource.generate(with: goUp)
        let pauseAnimation = try! AnimationResource.generate(with: pause)
        let goDownAnimation = try! AnimationResource.generate(with: goDown)
        let animation = try! AnimationResource.sequence(with: [goUpAnimation, pauseAnimation, goDownAnimation])
        entity.playAnimation(animation, transitionDuration: 0.5)
    }
}

通过前面的学习我们已经知道setupContentEntity用于初始化实体对象,getTargetEntity用于根据模型的名称查找到指定模型。addCube(name: String, position: SIMD3<Float>, color: UIColor)中包含三个参数,name用于指定所创建盒子的名称,position用于指定盒子所处的位置,同样是按人所处位置为参照坐标,color用于指定盒体的颜色。

接下来就是本节的一个重要的知识点了,playAnimation用于创建动画效果,传入的参数entity就是要实现动画效果的模型。

结构体FromToByAnimation可用于实现实体对象和场景的动画,方式为逐渐修改参数值。from值表示动画属性的起始值,to表示动画结束时的属性值,也可以通过by值让框架来计算动画结束时的值。duration顾名思义是指动画的时长,timing参数通过AnimationTimingFunction结构体中的定时函数进行指定,包含的值有:

  • default:生成默认过渡曲线的定时函数,不指定时此即为默认值。
  • linear:生成线性过渡效果的定时函数。
  • easeIn:生成淡入效果的定时函数。
  • easeOut:生成淡出效果的定时函数。
  • easeInOut:生成淡入淡出效果的定时函数。

bindTarget参数指定为tranform时表示动画作用于对象本身。

我们定义了三个动画函数,分别为goUppausegoDown,实现在纵坐标上完成上下移动0.4个单位的动画效果。接下来通过这三个动画定义来生成动画资源并指定动画的顺序,最后对实体对象调用playAnimation来播放动画,这里的transitionDuration参数与三个动画函数中用时的总和相同。

接下来是我们的ImmersiveView:

import SwiftUI
import RealityKit
struct ImmersiveView: View {
    @State var model = ViewModel()
    @State var cube1 = ModelEntity()
    @State var cube2 = ModelEntity()
    var body: some View {
        RealityView { content, attachments in
            content.add(model.setupContentEntity())
            cube1 = model.addCube(name: "Cube1", position: SIMD3<Float>(x: 1, y: 1, z: -2), color: .red)
            cube2 = model.addCube(name: "Cube2", position: SIMD3<Float>(x: -1, y: 1, z: -2), color: .blue)
            if let attachment = attachments.entity(for: "cube1_label") {
                attachment.position = [0, -0.35, 0]
                cube1.addChild(attachment)
            }
            if let attachment = attachments.entity(for: "cube2_label") {
                attachment.position = [0, -0.35, 0]
                cube2.addChild(attachment)
            }
        } attachments: {
            Attachment(id: "cube1_label") {
                Text("Cube1")
                    .font(.system(size: 48))
            }
            Attachment(id: "cube2_label") {
                Text("Cube2")
                    .font(.system(size: 48))
            }
        }
        .gesture(
            SpatialTapGesture()
                .targetedToEntity(cube1)
                .onEnded { value in
                    print(value)
                    model.playAnimation(entity: cube1)
                }
        )
        .gesture(
            SpatialTapGesture()
                .targetedToEntity(cube2)
                .onEnded { value in
                    print(value)
                    model.playAnimation(entity: cube2)
                }
        )
    }
}

cube1为红色,位于我们的右侧,cube2为蓝色,位于我们的左侧,为方便标识,我们通过RealityViewAttachmentContentBuilder(即attachments参数)来创建附属视图,但附属视图并不会自动添加到RealityView的对象上。我们需要显式地进行指定,这里attachments.entity中的forAttachmentid相对应,我们只添加了两个文本视图,并通过position指定了相对于父对象的位置。

接着通过SpatialTapGesture指定在点击相应盒子时执行前面所定义的playAnimation

运行应用,点击Show ImmersiveSpace会显示两个盒子,盒子下方分别显示一个附属视图,点击盒体会执行上下移动的动画。


示例代码:GitHub仓库

其它相关内容请见虚拟现实(VR)/增强现实(AR)&visionOS开发学习笔记

相关文章
|
5天前
使用 SAPGUI 的 Docking 控件将屏幕划分成若干子区域试读版
使用 SAPGUI 的 Docking 控件将屏幕划分成若干子区域试读版
22 6
使用 SAPGUI 的 Docking 控件将屏幕划分成若干子区域试读版
|
5天前
|
vr&ar
visionOS空间计算实战开发教程Day 11 标题动画
本文我们要在visionOS内实现一个标题输出的动画效果。主要讲ViewModifier协议,修饰符(modifier)应用于视图或另一个视图修饰符,生成原值的另一个版本。在希望创建一个可应用于不同视图的修饰符时可实现ViewModifier协议。
17 0
|
5月前
|
vr&ar
visionOS空间计算实战开发教程Day 10 照片墙
本例选择了《天空之城》的25张照片,组成5x5的照片墙)。首先我们在setupContentEntity方法中构建了一个纹理数组,将这25张照片添加到数组images中。其中封装了setup方法,借助于visionOS对沉浸式空间的支持,我们创建了三个平面,组成具有立体感的照片墙。
56 1
|
5月前
|
vr&ar
visionOS空间计算实战开发教程Day 6 拖拽和点击
在之前的学习中我们在空间中添加了3D模型,但在初始摆放后就无法再对其进行移动或做出修改。本节我们在Day 5显示和隐藏的基础上让我们模型可以实现拖拽效果,同时对纯色的立方体实现点击随机换色的功能。
38 2
|
5月前
|
vr&ar 图形学
visionOS空间计算实战开发教程Day 2 使用RealityKit显示3D素材
我们在Day1中学习了如何创建一个visionOS应用,但在第一个Demo应用中我们的界面内容还是2D的,看起来和其它应用并没有什么区别。接下来我们先学习如何展示3D素材,苹果为方便开发人员,推出了RealityKit,接下来看如何使用。
67 0
|
11月前
|
定位技术 API 图形学
unity-2D游戏官方案例--带视频案例(1)(层级渲染,物理碰撞,粒子动画,UI等多位基础一体化)
unity-2D游戏官方案例--带视频案例(1)(层级渲染,物理碰撞,粒子动画,UI等多位基础一体化)
137 1
|
12月前
|
数据可视化 前端开发
【React工作记录四十二】获取页面的可视化高度和宽度
【React工作记录四十二】获取页面的可视化高度和宽度
236 0
|
API Android开发
Material Design 实战 之 第六弹 —— 可折叠式标题栏(CollapsingToolbarLayout) & 系统差异型的功能实现(充分利用系统状态栏空间)
Material Design 实战 之 第六弹 —— 可折叠式标题栏(CollapsingToolbarLayout) & 系统差异型的功能实现(充分利用系统状态栏空间)
|
数据可视化 前端开发
#yyds干货盘点# 【React工作记录四十二】获取页面的可视化高度和宽度
#yyds干货盘点# 【React工作记录四十二】获取页面的可视化高度和宽度
192 0
#yyds干货盘点# 【React工作记录四十二】获取页面的可视化高度和宽度
|
SQL JavaScript 前端开发
【自然框架】之鼠标点功能现(二):表单控件的“应用”—— 代码?只写需要的!
? 【自然框架】之鼠标点功能现(一):单表的增删改查(即上次5月23日活动的一个主题)【Demo、源码下载】 ? ? ??????看了大家的回复,好像不少人误会了,我为了突出“鼠标点,功能现”,所以没有说代码,没有贴代码,这就让一些人认为我想要完全抛弃VS,自己写一个“平台”来代替,不好意思,您高估我了,我可达不到。
846 0
http://www.vxiaotou.com