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

相关文章

SpringBoot中四种AOP实战应用场景及代码实现

《SpringBoot中四种AOP实战应用场景及代码实现》面向切面编程(AOP)是Spring框架的核心功能之一,它通过预编译和运行期动态代理实现程序功能的统一维护,在SpringBoot应用中,AO... 目录引言场景一:日志记录与性能监控业务需求实现方案使用示例扩展:MDC实现请求跟踪场景二:权限控制与

Android开发环境配置避坑指南

《Android开发环境配置避坑指南》本文主要介绍了Android开发环境配置过程中遇到的问题及解决方案,包括VPN注意事项、工具版本统一、Gerrit邮箱配置、Git拉取和提交代码、MergevsR... 目录网络环境:VPN 注意事项工具版本统一:android Studio & JDKGerrit的邮

Python开发文字版随机事件游戏的项目实例

《Python开发文字版随机事件游戏的项目实例》随机事件游戏是一种通过生成不可预测的事件来增强游戏体验的类型,在这篇博文中,我们将使用Python开发一款文字版随机事件游戏,通过这个项目,读者不仅能够... 目录项目概述2.1 游戏概念2.2 游戏特色2.3 目标玩家群体技术选择与环境准备3.1 开发环境3

windows和Linux使用命令行计算文件的MD5值

《windows和Linux使用命令行计算文件的MD5值》在Windows和Linux系统中,您可以使用命令行(终端或命令提示符)来计算文件的MD5值,文章介绍了在Windows和Linux/macO... 目录在Windows上:在linux或MACOS上:总结在Windows上:可以使用certuti

springboot使用Scheduling实现动态增删启停定时任务教程

《springboot使用Scheduling实现动态增删启停定时任务教程》:本文主要介绍springboot使用Scheduling实现动态增删启停定时任务教程,具有很好的参考价值,希望对大家有... 目录1、配置定时任务需要的线程池2、创建ScheduledFuture的包装类3、注册定时任务,增加、删

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

如何为Yarn配置国内源的详细教程

《如何为Yarn配置国内源的详细教程》在使用Yarn进行项目开发时,由于网络原因,直接使用官方源可能会导致下载速度慢或连接失败,配置国内源可以显著提高包的下载速度和稳定性,本文将详细介绍如何为Yarn... 目录一、查询当前使用的镜像源二、设置国内源1. 设置为淘宝镜像源2. 设置为其他国内源三、还原为官方

Maven的使用和配置国内源的保姆级教程

《Maven的使用和配置国内源的保姆级教程》Maven是⼀个项目管理工具,基于POM(ProjectObjectModel,项目对象模型)的概念,Maven可以通过一小段描述信息来管理项目的构建,报告... 目录1. 什么是Maven?2.创建⼀个Maven项目3.Maven 核心功能4.使用Maven H

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

Python列表去重的4种核心方法与实战指南详解

《Python列表去重的4种核心方法与实战指南详解》在Python开发中,处理列表数据时经常需要去除重复元素,本文将详细介绍4种最实用的列表去重方法,有需要的小伙伴可以根据自己的需要进行选择... 目录方法1:集合(set)去重法(最快速)方法2:顺序遍历法(保持顺序)方法3:副本删除法(原地修改)方法4: