visionOS空间计算实战开发教程Day 10 照片墙

2023-11-30 09:28

本文主要是介绍visionOS空间计算实战开发教程Day 10 照片墙,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本例选择了《天空之城》的25张照片,组成5x5的照片墙)。首先我们在setupContentEntity方法中构建了一个纹理数组,将这25张照片添加到数组images中。其中封装了setup方法,借助于visionOS对沉浸式空间的支持,我们创建了三个平面,组成具有立体感的照片墙。

setup方法中调用了addChildEntities,对images随机打散,通过quotientAndRemainder方法对5求商取余来设置xy的值,从而生成5x5的照片,z轴上仅以平面为基准做了小小的调整。将准备好的位置和纹理,传入makePlane方法进行配置返回实体再分别添加到3个平面中。

为增加趣味性,这里还定义了toggleSorted()方法,在沉浸式空间内点击时会打散(randomSetChildPositions()方法),再次点击又会重置收起(resetChildPositions())。完整的ViewModel.swift文件内容如下:

import SwiftUI
import RealityKit@Observable
class ViewModel {private let planeSize = CGSize(width: 0.32, height: 0.18)private let maxPlaneSize = CGSize(width: 3.0, height: 2.0)private var  contentEntity = Entity()private var boardPlanes: [ModelEntity] = []private var images: [MaterialParameters.Texture] = []private var sorted = truefunc setupContentEntity() -> Entity {for i in 1..<26 {let name = "laputa\(String(format: "%03d", i))"if let texture = try? TextureResource.load(named: name) {images.append(MaterialParameters.Texture(texture))}}setup()return contentEntity}func toggleSorted() {if sorted {sorted.toggle()randomSetChildPositions()} else {sorted.toggle()resetChildPositions()}}// MARK: - Privateprivate func setup() {for i in 0..<3 {let boardPlane = ModelEntity(mesh: .generatePlane(width: 3, height: 2),materials: [SimpleMaterial(color: .clear, isMetallic: false)])boardPlane.position = SIMD3<Float>(x: 0, y : 2, z: -0.5 - 0.1 * Float(i + 1))contentEntity.addChild(boardPlane)boardPlanes.append(boardPlane)addChildEntities(boardPlane: boardPlane)}}private func addChildEntities(boardPlane: ModelEntity) {var i: Int = 0for image in images.shuffled().prefix(30) {let divisionResult = i.quotientAndRemainder(dividingBy: 5)let x: Float = Float(divisionResult.remainder) * 0.4 - 0.75let y: Float = Float(divisionResult.quotient) * 0.25 - 0.5let z: Float = boardPlane.position.z + Float(i) * 0.0001let entity = makePlane(name: "", position: SIMD3<Float>(x: x, y: y, z: z), texture: image)boardPlane.addChild(entity)i += 1}}private func makePlane(name: String, position: SIMD3<Float>, texture: MaterialParameters.Texture) -> ModelEntity {var material = SimpleMaterial()material.color = .init(texture: texture)let entity = ModelEntity(mesh: .generatePlane(width: 0.32, height: 0.18, cornerRadius: 0.0),materials: [material],collisionShape: .generateBox(width: 0.32, height: 0.18, depth: 0.1),mass: 0.0)entity.name = nameentity.position = positionentity.components.set(InputTargetComponent(allowedInputTypes: .indirect))return entity}private func move(entity: Entity, position: SIMD2<Float>) {let move = FromToByAnimation<Transform>(name: "move",from: .init(scale: .init(repeating: 1), translation: entity.position),to: .init(scale: .init(repeating: 1), translation: .init(x: position.x, y: position.y, z: entity.position.z)),duration: 2.0,timing: .linear,bindTarget: .transform)let animation = try! AnimationResource.generate(with: move)entity.playAnimation(animation, transitionDuration: 2.0)}private func randomSetChildPositions() {let size = CGSize(width: planeSize.width *  1.2, height: planeSize.height * 1.2)for boardPlane in boardPlanes {let newPoints = randomPoints(count: boardPlane.children.count, size: size)for i in 0..<boardPlane.children.count {let entity = boardPlane.children[i]move(entity: entity, position: newPoints[i])}}}private func resetChildPositions() {for boardPlane in boardPlanes {var i: Int = 0for entity in boardPlane.children {let divisionResult = i.quotientAndRemainder(dividingBy: 5)let x: Float = Float(divisionResult.remainder) * 0.4 - 0.75let y: Float = Float(divisionResult.quotient) * 0.25 - 0.5move(entity: entity, position: SIMD2<Float>(x, y))i += 1}}}private func randomPoints(count: Int, size: CGSize) -> [SIMD2<Float>] {var ret: [SIMD2<Float>] = []while ret.count < count {if let point = randomPoint(size: size, positions: ret) {ret.append(point)}}return ret}private func randomPoint(size: CGSize, positions: [SIMD2<Float>]) -> SIMD2<Float>? {for _ in 0..<5000 {let x = CGFloat.random(in: -maxPlaneSize.width...(maxPlaneSize.width / 2))let y = CGFloat.random(in: -maxPlaneSize.height...(maxPlaneSize.height / 2))let frame = CGRect(x: CGFloat(x), y: CGFloat(y), width: size.width, height: size.height)if positions.isEmpty {return SIMD2<Float>(Float(x), Float(y))} else {var intersects = falsefor position in positions {let f = CGRect(x: CGFloat(position.x), y: CGFloat(position.y), width: size.width, height: size.height)if f.intersects(frame) {intersects = true}}if !intersects {return SIMD2<Float>(Float(frame.minX), Float(frame.minY))}}}return nil}
}

ImmersiveView中发生了Tap事件后会调用其中的toggleSorted()方法,其它代码与此前的示例并没什么不同。

struct ImmersiveView: View {@State var model = ViewModel()var body: some View {RealityView { content incontent.add(model.setupContentEntity())}.onTapGesture {model.toggleSorted()}}
}

visionOS空间计算实战开发教程Day 10 照片墙

示例代码:GitHub仓库

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

这篇关于visionOS空间计算实战开发教程Day 10 照片墙的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/436497

相关文章

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Python实现精确小数计算的完全指南

《Python实现精确小数计算的完全指南》在金融计算、科学实验和工程领域,浮点数精度问题一直是开发者面临的重大挑战,本文将深入解析Python精确小数计算技术体系,感兴趣的小伙伴可以了解一下... 目录引言:小数精度问题的核心挑战一、浮点数精度问题分析1.1 浮点数精度陷阱1.2 浮点数误差来源二、基础解决

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模

Java 正则表达式的使用实战案例

《Java正则表达式的使用实战案例》本文详细介绍了Java正则表达式的使用方法,涵盖语法细节、核心类方法、高级特性及实战案例,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、正则表达式语法详解1. 基础字符匹配2. 字符类([]定义)3. 量词(控制匹配次数)4. 边