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

news2024/11/19 21:25:51

本例选择了《天空之城》的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 = true
    
    func 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: - Private
    
    private 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 = 0
        
        for image in images.shuffled().prefix(30) {
            let divisionResult = i.quotientAndRemainder(dividingBy: 5)
            
            let x: Float = Float(divisionResult.remainder) * 0.4 - 0.75
            let y: Float = Float(divisionResult.quotient) * 0.25 - 0.5
            let z: Float = boardPlane.position.z + Float(i) * 0.0001
            
            let 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 = name
        entity.position = position
        entity.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 = 0
            for entity in boardPlane.children {
                let divisionResult = i.quotientAndRemainder(dividingBy: 5)
                let x: Float = Float(divisionResult.remainder) * 0.4 - 0.75
                let y: Float = Float(divisionResult.quotient) * 0.25 - 0.5
                move(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 = false
                for 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 in
            content.add(model.setupContentEntity())
        }
        .onTapGesture {
            model.toggleSorted()
        }
    }
}

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

示例代码:GitHub仓库

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1269704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决:ModuleNotFoundError: No module named ‘qt_material‘

解决&#xff1a;ModuleNotFoundError: No module named ‘qt_material’ 文章目录 解决&#xff1a;ModuleNotFoundError: No module named qt_material背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在使用之前的代码时&#xff0c;报错&…

基于asp.net 消防安全宣传网站设计与实现

目 录 1 绪论 1 1.&#xff11;课题背景 1 1.2 目的和意义 1 1.3主要研究内容 1 1.4 组织结构 2 2 可行性分析 3 2.1技术可行性 3 2.2经济可行性 3 2.3操作可行性 3 2.4系统开发环境 4 3 需求分析 7 3.1性能分析 7 3.2业务流程分析 7 3.3数据流程分析 9 4 系统设计 11 4.1系统…

drawio 流程图以图片保存

随笔记录 目录 1. drawio 介绍 2. 绘制流程图以白底图片保存 2.1 流程图原始图​编辑 2.2 修改配置 2.3 流程图以图片保存 2.4 图片保存后效果展示 1. drawio 介绍 是一款非常强大的开源在线的流程图编辑器&#xff0c;支持绘制各种形式的图表&#xff0c;提供了 Web…

Leetcode2336 无限集中的最小数字

题目&#xff1a; 现有一个包含所有正整数的集合 [1, 2, 3, 4, 5, ...] 。 实现 SmallestInfiniteSet 类&#xff1a; SmallestInfiniteSet() 初始化 SmallestInfiniteSet 对象以包含 所有 正整数。int popSmallest() 移除 并返回该无限集中的最小整数。void addBack(int nu…

[node]Node.js事件

[node]Node.js事件 EventEmitter属性方法error 事件 实例应用简单实例onceremoveListenerlistenerCounterror 事件完整实例 继承 事件循环事件驱动程序 Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列 Node.js 里面的许多对象都会分发事件&#xff1a;一个 n…

从0开始学习JavaScript--JavaScript 单例模式

单例模式是一种常见的设计模式&#xff0c;它保证一个类仅有一个实例&#xff0c;并提供一个全局访问点。在 JavaScript 中&#xff0c;单例模式通常用于创建唯一的对象&#xff0c;以确保全局只有一个实例。本文将深入探讨单例模式的基本概念、实现方式&#xff0c;以及在实际…

linux 磁盘扩容初始化挂载 笔记

目录 说明环境信息前提条件 操作步骤 说明 linux 系统磁盘扩容步骤 环境信息 系统信息&#xff1a;Linux version 4.19.90-23.8.v2101.ky10.aarch64cpu信息&#xff1a;Kunpeng-920 、aarch64、64-bit、HiSilicon 前提条件 有未初始化的用户磁盘操作系统可以支持当前磁盘的…

【Spring系列】DeferredResult异步处理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

VC++调试QT源码

环境&#xff1a;vs2017 qt 5.14.2 1&#xff1a;首先我们需要选择我们的源码路径 右键解决方案-》属性-》通用属性-》调试源文件-》在窗口内添加QT下载时的源码**.src文件夹**&#xff0c;这里最好把源码 D:\software\QT\path\5.14.2\Src 源文件里面的Src文件做一个备份出来…

从意义中恢复,而不是从数据包中恢复

从书报&#xff0c;录放机&#xff0c;电视机到智能手机&#xff0c;vr 眼镜&#xff0c;所有学习的&#xff0c;娱乐的工具或玩具&#xff0c;几乎都以光声诉诸视听&#xff0c;一块屏幕和一个喇叭。 视觉和听觉对任何动物都是收发信息的核心&#xff0c;诉诸视觉和听觉的光和…

达梦数据库使用

达梦数据库使用 &#x1f4d1;前言 本文主要是【达梦数据库】——达梦数据库简单使用的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他…

运维知识点-openResty

openResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——实现广告缓存测试企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——OpenResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRes…

机关单位档案分类及整理方法

机关单位档案主要包含文书档案、干部职工档案&#xff08;人事档案&#xff09;、会计档案、科技档案&#xff08;科学研究、基本建设、设备仪器、产品&#xff09;、诉讼档案、音像档案、照片档案、电子档案等等&#xff0c;这其中&#xff0c;不同种类&#xff0c;不同载体的…

卷积神经网络(CNN)注意力检测

文章目录 一、前言二、前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;2. 导入数据3. 查看数据 二、数据预处理1.加载数据2. 可视化数据4. 配置数据集 三、调用官方网络模型四、设置动态学习率五、编译六、训练模型七、模型评估1. Accuracy与Loss图2. …

ESP32-Web-Server 实战编程- 使用 AJAX 自动更新网页内容

ESP32-Web-Server 实战编程- 使用 AJAX 自动更新网页内容 概述 什么是 AJAX &#xff1f; AJAX Asynchronous JavaScript and XML&#xff08;异步的 JavaScript 和 XML&#xff09;。 AJAX 是一种用于创建快速动态网页的技术。 传统的网页&#xff08;不使用 AJAX&#…

更改AndroidStudio模拟器位置

C盘何等的珍贵&#xff0c;可是好多工具&#xff0c;软件非得默认安装在C盘。。导致C盘越来越紧张。。 在日常使用过程中&#xff0c;安装任何软件都会将其安装到非系统盘下&#xff0c;Android模拟器也不能例外。保护好C盘也是日常一个良好的习惯。 Android AVD默认路径&…

watch监听中重复触发如何解决?

在实际开发工程中通过获取后端数据监听判断数组中长度是否大于0从而调用其他的方法&#xff0c;但是如果data域中的数据出现变化的话&#xff0c;就会导致监听中的方法重复调用&#xff0c;导致一些不必要的bug&#xff0c;例如&#xff1a; 原理&#xff1a; watch监听的数据…

Android性能优化- 从SharedPreferences到MMKV

前言 前面Android性能优化 - 从SharedPreferences跨越到DataStore一文主要介绍了DataStore的实现原理&#xff0c;以及DataStore相对于SharedPreferences的提升&#xff0c;本文主要简述MMKV相对于SharedPreferences存储的使用及优劣势&#xff0c;以及MMKV原理&#xff0c;以…

又3本“On Hold”期刊被剔除!这本Elsevier旗下中科院2区TOP仍在调查中!

【SciencePub学术】 此前&#xff0c;继又2本期刊被“On Hold”&#xff01;标识后&#xff0c;仍处于“On Hold”状态的期刊有8本&#xff0c;其中包括4本SCI期刊和4本ESCI期刊。 2023年11月20日&#xff0c;科睿唯安更新了Web of Science核心期刊目录。 本次11月更新共64本期…

聚类分析例题 (多元统计分析期末复习)

例一 动态聚类&#xff0c;K-means法&#xff0c;随机选取凝聚点&#xff08;题目直接给出&#xff09; 已知5个样品的观测值为&#xff1a;1&#xff0c;4&#xff0c;5&#xff0c;7&#xff0c;11。试用K均值法分为两类(凝聚点分别取1&#xff0c;4与1&#xff0c;11) 解&…