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

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

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

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

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

importSwiftUIimportRealityKit@ObservableclassViewModel{privateletplaneSize=CGSize(width:0.32,height:0.18)privateletmaxPlaneSize=CGSize(width:3.0,height:2.0)privatevarcontentEntity=Entity()privatevarboardPlanes:[ModelEntity]=[]privatevarimages:[MaterialParameters.Texture]=[]privatevarsorted=truefuncsetupContentEntity()->Entity{foriin1..<26{letname="laputa\(String(format:"%03d",i))"iflettexture=try?TextureResource.load(named:name){images.append(MaterialParameters.Texture(texture))}}setup()returncontentEntity}functoggleSorted(){ifsorted{sorted.toggle()randomSetChildPositions()}else{sorted.toggle()resetChildPositions()}}// MARK: - Privateprivatefuncsetup(){foriin0..<3{letboardPlane=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)}}privatefuncaddChildEntities(boardPlane:ModelEntity){vari:Int=0forimageinimages.shuffled().prefix(30){letdivisionResult=i.quotientAndRemainder(dividingBy:5)letx:Float=Float(divisionResult.remainder)*0.4-0.75lety:Float=Float(divisionResult.quotient)*0.25-0.5letz:Float=boardPlane.position.z+Float(i)*0.0001letentity=makePlane(name:"", position: SIMD3<Float>(x: x, y: y, z: z), texture: image)boardPlane.addChild(entity)i+=1}}privatefuncmakePlane(name:String,position:SIMD3<Float>,texture:MaterialParameters.Texture)->ModelEntity{varmaterial=SimpleMaterial()material.color=.init(texture:texture)letentity=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))returnentity}privatefuncmove(entity:Entity,position:SIMD2<Float>){letmove=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)letanimation=try!AnimationResource.generate(with:move)entity.playAnimation(animation,transitionDuration:2.0)}privatefuncrandomSetChildPositions(){letsize=CGSize(width:planeSize.width*1.2,height:planeSize.height*1.2)forboardPlaneinboardPlanes{letnewPoints=randomPoints(count:boardPlane.children.count,size:size)foriin0..<boardPlane.children.count{letentity=boardPlane.children[i]move(entity:entity,position:newPoints[i])}}}privatefuncresetChildPositions(){forboardPlaneinboardPlanes{vari:Int=0forentityinboardPlane.children{letdivisionResult=i.quotientAndRemainder(dividingBy:5)letx:Float=Float(divisionResult.remainder)*0.4-0.75lety:Float=Float(divisionResult.quotient)*0.25-0.5move(entity:entity,position:SIMD2<Float>(x,y))i+=1}}}privatefuncrandomPoints(count:Int,size:CGSize)->[SIMD2<Float>]{varret:[SIMD2<Float>]=[]whileret.count<count{ifletpoint=randomPoint(size:size,positions:ret){ret.append(point)}}returnret}privatefuncrandomPoint(size:CGSize,positions:[SIMD2<Float>])->SIMD2<Float>?{for_in0..<5000{letx=CGFloat.random(in:-maxPlaneSize.width...(maxPlaneSize.width/2))lety=CGFloat.random(in:-maxPlaneSize.height...(maxPlaneSize.height/2))letframe=CGRect(x:CGFloat(x),y:CGFloat(y),width:size.width,height:size.height)ifpositions.isEmpty{returnSIMD2<Float>(Float(x),Float(y))}else{varintersects=falseforpositioninpositions{letf=CGRect(x:CGFloat(position.x),y:CGFloat(position.y),width:size.width,height:size.height)iff.intersects(frame){intersects=true}}if!intersects{returnSIMD2<Float>(Float(frame.minX),Float(frame.minY))}}}returnnil}}


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

structImmersiveView:View{@Statevarmodel=ViewModel()varbody:someView{RealityView{contentincontent.add(model.setupContentEntity())}.onTapGesture{model.toggleSorted()}}}


示例代码:GitHub仓库

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

相关文章
|
4月前
|
vr&ar
简单易懂的 全景图高清下载方法以及原理简要解析(支持下载建E、720yun、酷雷曼、景站、酷家乐、百度街景原图)
这篇文章介绍了一种简单易懂的全景图高清下载方法,使用在线网站全景管家,支持下载包括建E、720yun、酷雷曼等多个平台的全景图原图,并简要解析了全景图的原理和制作方法。
简单易懂的 全景图高清下载方法以及原理简要解析(支持下载建E、720yun、酷雷曼、景站、酷家乐、百度街景原图)
|
4月前
|
图形学 开发者 存储
超越基础教程:深度拆解Unity地形编辑器的每一个隐藏角落,让你的游戏世界既浩瀚无垠又细节满满——从新手到高手的全面技巧升级秘籍
【8月更文挑战第31天】Unity地形编辑器是游戏开发中的重要工具,可快速创建复杂多变的游戏环境。本文通过比较不同地形编辑技术,详细介绍如何利用其功能构建广阔且精细的游戏世界,并提供具体示例代码,展示从基础地形绘制到植被与纹理添加的全过程。通过学习这些技巧,开发者能显著提升游戏画面质量和玩家体验。
199 3
|
4月前
|
图形学 数据可视化 开发者
超实用Unity Shader Graph教程:从零开始打造令人惊叹的游戏视觉特效,让你的作品瞬间高大上,附带示例代码与详细步骤解析!
【8月更文挑战第31天】Unity Shader Graph 是 Unity 引擎中的强大工具,通过可视化编程帮助开发者轻松创建复杂且炫酷的视觉效果。本文将指导你使用 Shader Graph 实现三种效果:彩虹色渐变着色器、动态光效和水波纹效果。首先确保安装最新版 Unity 并启用 Shader Graph。创建新材质和着色器图谱后,利用节点库中的预定义节点,在编辑区连接节点定义着色器行为。
332 0
|
vr&ar
visionOS空间计算实战开发教程Day 6 拖拽和点击
在之前的学习中我们在空间中添加了3D模型,但在初始摆放后就无法再对其进行移动或做出修改。本节我们在Day 5显示和隐藏的基础上让我们模型可以实现拖拽效果,同时对纯色的立方体实现点击随机换色的功能。
112 2
|
7月前
|
C# 图形学
【Unity 3D】元宇宙案例之虚拟地球信息射线实战(附源码、演示视频和步骤 超详细)
【Unity 3D】元宇宙案例之虚拟地球信息射线实战(附源码、演示视频和步骤 超详细)
126 0
|
存储 Java
Java实现贪吃蛇大作战小游戏(完整教程+源码)额外实现积分和变速功能(下)
文章目录 1 开发环境及游戏展示 1.1 游戏主界面 1.2 移动界面 1.3 奖励界面 1.4 F加速功能界面 1.5 死亡界面 2 需求分析 3 系统设计 3.1 系统总体功能设计 3.2 系统总体流程设计 4 功能设计 4.1 贪吃蛇移动及加速功能设计 4.2 贪吃蛇吃食物加速及死亡判定功能的设计 4.2.1 贪吃蛇吃食物加速功能的设计 4.2.2 贪吃蛇死亡判定功能的设计 4.3 贪吃蛇主动加速功能的设计 4.4 贪吃蛇奖励机制功能的设计 5 项目结构与项目实现 5.1 项目结构及类间关系 5.2 项目完整源码 5.2.1 Images类
|
7月前
|
vr&ar
visionOS空间计算实战开发教程Day 11 标题动画
本文我们要在visionOS内实现一个标题输出的动画效果。主要讲ViewModifier协议,修饰符(modifier)应用于视图或另一个视图修饰符,生成原值的另一个版本。在希望创建一个可应用于不同视图的修饰符时可实现ViewModifier协议。
53 0
|
vr&ar 图形学
visionOS空间计算实战开发教程Day 2 使用RealityKit显示3D素材
我们在Day1中学习了如何创建一个visionOS应用,但在第一个Demo应用中我们的界面内容还是2D的,看起来和其它应用并没有什么区别。接下来我们先学习如何展示3D素材,苹果为方便开发人员,推出了RealityKit,接下来看如何使用。
123 0
|
缓存 Kubernetes API
数据缓存系列分享(三):通过 StableDiffusion 扩展插件实现网红爆款文字光影图
在文章《23秒完成从零开始搭建StableDiffusion》中我们详细讲解了通过ECI的数据缓存快速搭建StableDiffusion应用,用户通过模型网站选择好自己需要的模型,然后创建ECI数据缓存,即可快速部署自己的StableDiffusion应用。本文将基于StableDiffusion + 扩展插件 ControlNet 来完成实现网红爆款文字光影图
414 0
数据缓存系列分享(三):通过 StableDiffusion 扩展插件实现网红爆款文字光影图
|
Web App开发 数据可视化 容器
基于Threejs构建的3D立体空间实战入门
本文将简单介绍Threejs的相关基础知识,四个基础对象组件。最后采用一个实例一步一步的进行3D房间展示,最终达到可视化的效果。
465 0
基于Threejs构建的3D立体空间实战入门