用unity XR interaction Toolkit 制作垃圾分类虚拟仿真项目

news2024/11/27 8:48:46

项目效果演示:

垃圾分类虚拟仿真项目演示

1.环境配置

选择universal 3D(通用渲染管道)项目(不然导入素材包会丢失材质)。

选择Window->Package Manager,安装其中的XR interaction Toolkit。

选择其中的Samples,导入Starter Assets。

选择Edit->Project Settings->XR Plugin Management进行安装。

如果要用电脑模拟器进行VR控制,需要选择Edit->Project Settings->XR interaction Toolkit,勾选 Use XR Devcie Simulator in scenes。

选择Assets\Samples\XR Interaction Toolkit\2.5.4\Starter Assets路径位置下的DemoScene场景,打开运行,观察是否能进行控制。

2.选择界面场景制作(StartScene)

主要功能:射线交互实现主场景(mainScene)和测试场景(TestScene)两个场景的跳转。

新建场景命名为“StartScene”,将DemoScene场景文件拖入Hierarchy面板,把其中的XR interaction Setup拖入到StartScene场景中,点击运行,进行测试。

导入素材包Classification_Resourse.unitypackage,将Assets\Polytope Studio\Lowpoly_Demos\Environment_Free路径位置的Environment_Free场景文件中拖入Hierarchy面板。

把Environment_Free所有元素拖入到StartScene场景中并删除其中的Player游戏对象,把XR interaction Setup作为某个场景元素的子物体,对XR interaction Setup游戏对象的Transform进行reset重置,然后进行手动的坐标调整,就可以在场景中漫游。同时可以对场景元素进行删减与范围缩小,还可以通过设置下图中colliders的位置,限制主体漫游的范围。

导入素材包Classification_Resourse.unitypackage,新建画布Canvas和图像Image和按钮Button(Legacy),选择Assets\Resourse\01图片路径下中3D面板背景图片赋值给image组件,效果大致如下图:

接下来,为UI元素添加组件使其可以进行VR射线交互,为画布添加Tracked Device Graphic Raycaster组件,为按钮添加XR Poke Follow Affordance组件。

编写脚本SkipController.cs,用于控制脚本的跳转。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.SceneManagement;

//挂载在选择界面上,用于界面的跳转。

public class SkipController : MonoBehaviour

{

    // Start is called before the first frame update

    //控制返回主界面方法

    public void LoadScene(string sceneName)

    {

        SceneManager.LoadScene(sceneName); // 加载指定名称的场景

    }

}

新建空物体,命名为SkipController,将脚本挂载到该物体上,给两个按钮添加点击事件,如下图所示:

提前创建两个场景主场景(mainScene)和测试场景(TestScene),选择File->Build Settings,将三个场景拖入到Scene In Bulid中进行Build,随后测试是否能实现界面的跳转。

3.主界面场景制作(mainScene)

主要功能:通过VR抓取进行与垃圾的交互,需要将垃圾投入到正确的垃圾桶中,通过碰撞体检测和检测类型是否匹配,从而判断否正确分类,进行记录和反馈,也可以跳转到测试界面。

打开mainScene场景文件,导入素材包LowPolyTropicalEnvironment_LITE.unitypackage,将Assets\LowPolyTropicalEnvironment_LITE\Scenes路径位置的TropicalEnvironmentLite_Demo场景文件中拖入Hierarchy面板。

把TropicalEnvironmentLite_Demo所有元素拖入到StartScene场景中并删除其中的摄像头的游戏对象,再把DemoScene中的XR interaction Setup拖入到场景中,把其作为某个场景元素的子物体,进行坐标的调整,效果如下图,进行测试。

接下来进行垃圾分类场景的搭建,将Assets\Resources\02 模型 路径位置下的垃圾桶拖入场景中,调整四个垃圾桶的位置,将Assets\Resources\01 图片 路径位置下的干垃圾、有害垃圾、湿垃圾、可回收的图片拖入场景中,作为垃圾桶的子物体,进行位置的调整。

在Project面板右击,选择create->matertial,调整材料的颜色,并把该材质拖入到Scene中的垃圾桶上(调整整体和轮子的颜色)。

最终效果如下图所示:

为垃圾桶添加碰撞体,选择4个垃圾桶,添加Mesh Collider组件,不勾选Convex选项。

同时需要在底部创建一个碰撞体,用于检测是否分类正确。在Hierarchy面板中,选择垃圾桶右击,选择3D Object->Plane,作为其子物体,编写脚本Trash Can.cs:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

//用枚举类型区分垃圾类型

public enum GarbageType

{

    recyclable,//可回收


    wet,//湿垃圾

    dry,//干垃圾

    hazardous//有害垃圾

}

//挂载在垃圾桶上

public class TrashCan : MonoBehaviour

{

    // Start is called before the first frame update

    void Start()

    {

        

    }

    public GarbageType type; // 垃圾桶类型

    //垃圾桶底部设置碰撞体,用于碰撞检测,返回垃圾桶本身和垃圾的类型给控制器进行逻辑判断。

    private void OnCollisionEnter(Collision collision)

    {

        Garbage garbage = collision.collider.GetComponent<Garbage>();

        if (garbage != null)

        {

            ClassificationManager.Instance.OnGarbageCollision(garbage.type, type);

            Debug.Log("当前垃圾桶的类型为:" + type + "    当前垃圾桶的类型为: " + garbage.type);

            if (type != garbage.type)

            {

                garbage.ResetToStartPosition();

            }

        }


    }

}

然后挂载在该物体上,同时Plane需要进行调整位置、颜色,设置对应的类型。

将Assets\Resources\02 模型 路径位置下的“桌子”模型拖入场景中,调整到合适的大小和为止,为其添加Box Collider组件,点击Edit Collider,调整其碰撞体大小,同时添加Rigidbody组件,勾选Is Kinematic选项,使其不会受碰撞影响。

将Assets\Resources\02 模型 路径位置下的“大猩猩手办”、“可乐罐”、“药品2”、“蛋糕”、“陶瓷杯”、“电池”的模型拖入场景中,部分模型需要手动拖动material进行填色。

随后选中所有物体,为其添加Box Collider、Rigidbody、XR Grab Interactable组件,在Box Collider组件中点击Edit Collider,单独调整其碰撞体大小,并将物体放置在桌面上。

新建脚本Garbage.cs,并挂载在物体上。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

//挂载在垃圾类上

public class Garbage : MonoBehaviour

{

    public GarbageType type; // 垃圾类型

    private Vector3 originalPosition; // 原始位置

    private Rigidbody rb;


    private void Start()

    {

        rb = GetComponent<Rigidbody>();

        originalPosition = transform.position;

    }

    //当被错误分类时调用,用于把垃圾重新放回桌子上

    public void ResetToStartPosition()

    {

        transform.position = originalPosition;

        rb.velocity = Vector3.zero;

        rb.angularVelocity = Vector3.zero;

    }

}

在Garbage(Script)组件中设置每个物体的类型Type,类型如下表:

模型

实际垃圾类型

设置的Type

大猩猩手办

可回收

Recycle

可乐罐

可回收

Recycle

药品

有害

Hazardous

蛋糕

湿垃圾

Wet

陶瓷杯

干垃圾

Dry

电池

有害

Hazardous

创建脚本ClassificationManager.cs,用于垃圾分类逻辑处理。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

using UnityEngine.SceneManagement;

using UnityEngine.Events;

//挂载在主界面(垃圾分类)上的控制器

public class ClassificationManager : MonoBehaviour

{

    public static ClassificationManager Instance; // 单例模式

    public GameObject correctUI;//正确UI

    public GameObject incorrectUI;//错误UI

    public AudioSource audiosource;//播放器

    public AudioClip correctSound;//正确音效

    public AudioClip incorrectSound;//错误音效

    public GameObject CountUI;//用于统计正确分类的个数

    private int Count=0;//个数

    private void Start()

    {

        correctUI.SetActive(false);

        incorrectUI.SetActive(false);

        CountUI.SetActive(true);

        CountUI.GetComponent<Text>().text = "你目前正确分类的物体个数为:" + Count;

    }

    //用于判断垃圾和垃圾桶是否是同一类别的

    public void OnGarbageCollision(GarbageType garbageType, GarbageType trashCanType)

    {

        if (garbageType == trashCanType)

        {

            CorrectDispose();

            Count += 1;

        }

        else

        {

            IncorrectDispose();


        }

        CountUI.GetComponent<Text>().text = "你目前正确分类的物体个数为:" + Count;

        Debug.Log("Your score is: " + Count);

    }

    private void CorrectDispose()

    {

        audiosource.clip = correctSound;

        audiosource.Play();

        incorrectUI.SetActive(false);

        //控制UI先显示,2秒后消失

        correctUI.SetActive(true);

        Invoke("DisappearObject", 2f);

       

    }

    private void IncorrectDispose()

    {

        audiosource.clip = incorrectSound;

        audiosource.Play();

        correctUI.SetActive(false);

     

        incorrectUI.SetActive(true);

        Invoke("DisappearObject", 2f);

        

    }

    //挂载在按钮上,界面跳转

    public void LoadScene(string sceneName)

    {

        SceneManager.LoadScene(sceneName); // 加载指定名称的场景

    }

    private void DisappearObject()

    {

        correctUI.SetActive(false);

        incorrectUI.SetActive(false);

    }

}

在场景中创建一个空物体,命名为ClassificationManager,把创建的脚本挂载上去,为其添加Audio Source组件,对脚本中的Audiosource进行赋值,同时将Assets\Resources\03 音效 路径位置下的“正确音效”、“错误”文件拖入到Correct Sound和Incorrect Sound中进行赋值,效果如下图所示:

接下来,需要制作UI界面,需要在场景中新建画布Canvas、用于显示已分类数量的Text(Legacy)和用于跳转界面的按钮Button(Legacy)。同时新建3个Image游戏对象,用于显示背景、正确图像和错误图像,将Assets\Resources\01 图片 路径位置下“回顾3”、“正确”、“错误”的图片赋值给Image,修改UI元素名称,便于区分,调整UI元素位置,最终效果如下图所示:

为了使其能进行VR射线交互,给画布添加Tracked Device Graphic Raycaster组件,为按钮添加XR Poke Follow Affordance组件。

接下来继续给ClassificationManager物体进行参数的赋值。

进行测试,是否能进行判断垃圾分类的正误并进行反馈。

4.测试场景制作(TestScene)

主要功能:能通过射线交互,能进行选择题选项的选择和判断题的对错的选择。

首先导入素材,打开TestScene场景文件,将DemoScene场景文件拖入Hierarchy面板,把其中的XR interaction Setup拖入到StartScene场景中,Assets\Resources\02 模型 路径位置下的房屋模型导入到场景中,调整位置,让XR interaction Setup处于房屋之中,效果大致如下图,进行测试。

接下来要进行答题界面的制作,我们可以打开Assets\Resources\06 考题 路径位置下的垃圾分类考题文件,进行题干和选项的内容的设置。

首先新建一个画布Canvas,调整位置,为其添加TrackedDeviceGraphicRaycaster组件。新建Image子物体,设置画布的背景图片。

然后题目中有单选题和判断题,需要进行区分。创建脚本Question.cs,用于区别题型和设置答案:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

//挂载在具体题目上(判断题、选择题)

public class Question: MonoBehaviour

{

    public Toggle[] options; // 单选题的选项

    public int correctAnswerIndex; // 单选题正确答案的索引

    public Toggle trueFalseToggle; // 判断题的Toggle

    public bool correctAnswer; // 判断题的正确答案

    public bool isChoice; // 判断是选择题还是判断题

}

接下进行题目的UI制作。

选择题的制作(以第一题为例):

创建一个空物体作为新建画布的子物体,命名为Question1。为其挂载Question脚本,并设置属性如下图所示(4个选项、D选项为正确选项,为选择题),点击Options下方“+”号,设置数量为4,设置Correct Answer Index的值为3,勾选 Is Choice选项。

同时为Question1游戏对象添加Toggle Group组件,用于控制其子对象的Toggle只能被选中一个,用于模拟单选题。同时勾选Allow Switch Off 选项,允许程序运行时,可以没有默认的勾选选项。

在Question1下新建Text(Lecary)和4个Toggle,作为其子物体,调整位置、颜色,并对选项进行取名(1_A,1_B,1_C,1_D),便于与后面的选择题选项区分。同时需要为每一个Toggle游戏对象添加XR Poke Follow Affordance组件。

选项新建完成后,对Question1的Question组件中的Option进行赋值,如下图所示:

这样一道选择题的设置就完成了。

判断题的制作(以第二题为例):

创建一个Toggle作为画布Panel的子物体,命名为Question2,为其挂载XR Poke Follow Affordance组件,同时挂载Question脚本,并设置属性如下图所示(答案为正确,为判断题),勾选Correct Answer选项,不勾选Is Choice。

新建一个Text(Legacy)作为Question2游戏对象的子物体,编辑其内容,显示题干,如下图所示:

这样一道判断题就编辑好了,按如下的两种方式将5道题目编辑好后(注意命名要进行区分),我们还需要提交按钮和返回主界面的按钮Button和显示分数的UI。在画布中新建两个按钮Button(Legacy),分别命名为returnButton、SubmitButton,新建Text(Legacy),命名为Score,在Hierarchy面板中将returnButton拖到到Score下,作为其子物体。

调整其位置,最后效果如下图所示:

到此为止,我们UI界面制作完毕。

接下来我们需要新建脚本TestController.cs,用于实现判断对错、交互的功能:

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;

using UnityEngine.SceneManagement;

//挂载在测试界面的控制器上。

public class TestController : MonoBehaviour

{

    public List<Question> questions; // 包含所有问题的列表

    public int score = 0; // 最终得分

    public GameObject ScoreUI;

    private void Start()

    {

        ScoreUI.SetActive(false);  

    }

    // 用户点击提交按钮时调用的方法

    public void SubmitAnswers()

    {

        score = 0;

        foreach (Question question in questions)

        {      //如果是选择题

            if (question.isChoice)

            {

                // 单选题

                if (question.options[question.correctAnswerIndex].isOn)

                {

                    score++;

                }

            }

            else

            {

                // 判断题

                if (question.trueFalseToggle.isOn== question.correctAnswer)

                {

                    score++;

                }

            }

        }

        ScoreUI.SetActive(true);

        ScoreUI.GetComponent<Text>().text = "你的最终得分是:" + (score*1.0/questions.Count)*100;

        Debug.Log("Your score is: " + score);

        // 可以在这里添加代码显示得分或者转到下一个场景

    }

    //控制返回主界面方法

    public void LoadScene(string sceneName)

    {

        SceneManager.LoadScene(sceneName); // 加载指定名称的场景

    }

}

新建空物体,命名为TestController,挂载上该脚本,对Questions和Score UI属性进行赋值,如下图所示:

为两个按钮分别添加跳转场景的功能和提交答案的功能,添加点击事件,如下图所示:

最后进行测试,功能是否正常。

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

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

相关文章

[vulnhub]Kioptrix: Level 1.2 (#3)

https://www.vulnhub.com/entry/kioptrix-level-12-3,24/ 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的&#xff0c;所以靶机IP是169 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-29 13:16 CST …

iQOO手机怎样将屏幕投射到MacBook?可以同步音频吗?

众所周知&#xff0c;苹果品牌的设备自己有AirPlay的投屏功能&#xff0c;iPhone要投屏到MacBook只要连接同一网络&#xff0c;然后开启AirPlay就可以投屏。但其他品牌的手机没有AirPlay&#xff0c;怎么将手机屏幕投射到MacBook呢&#xff1f; 安卓系统的手机可以使用无线投屏…

C++初阶(七)--类和对象(4)

目录 ​编辑 一、再谈构造函数 1.构造函数体赋值 2.初始化列表 二、类型转换 1.隐式类型转换 2.explicit关键字 3.类类型之间的对象隐式转换 三、static成员函数 1.概念 2.特性 3.面试题&#xff1a; 四、友元函数 1.基本介绍 2.回顾&#xff1a; 3.友元类&am…

【水下生物数据集】 水下生物识别 深度学习 目标检测 机器视觉 yolo(含数据集)

一、背景意义 随着全球海洋生态环境的日益变化&#xff0c;水下生物的监测和保护变得愈发重要。水下生物种类繁多&#xff0c;包括螃蟹、鱼类、水母、虾、小鱼和海星等&#xff0c;它们在海洋生态系统中扮演着关键角色。传统的水下生物监测方法通常依赖于人工观察&#xff0c;效…

QT相机连接与拍照

先看效果 初始化 auto mainLayout new QHBoxLayout(this);m_viewfinder new QCameraViewfinder(this);m_viewfinder->setStyleSheet("border-radius: 20px;background-color:rgb(43,48,70)");mainLayout->addWidget(m_viewfinder,8); 选择相机 void camera…

uniapp position: fixed 兼容性不显示问题

position: fixed; bottom: 0;以上运行到微信小程序时正常&#xff0c;但是h5会出现不显示的问题。 解决方法 修改为&#xff1a; position: fixed; bottom: var(--window-bottom, 0);

数据库数据恢复—Oracle ASM磁盘组掉线 ,ASM实例无法挂载的数据恢复案例

Oracle数据库数据恢复环境&故障&#xff1a; Oracle ASM磁盘组由4块磁盘组成。Oracle ASM磁盘组掉线 &#xff0c;ASM实例不能mount。 Oracle数据库故障分析&恢复方案&#xff1a; 数据库数据恢复工程师对组成ASM磁盘组的磁盘进行分析。对ASM元数据进行分析发现ASM存储…

基于 NXP iMX8MP 平台简单测试 PySide6 应用

By Toradex秦海 1). 简介 Python binding 的 Qt GUI 库一直以来有两种&#xff0c;最初是由 Riverbank Computing 公司在 2000 年初发布&#xff0c;基于 Qt4 版本推出的 PyQt4&#xff0c;后来随着 Qt 库版本的演进也陆续跟进到 PyQt5 和最新的 PyQt6&#xff0c;由于发布的…

如何从iconfont中获取字体图标并应用到微信小程序中去?

下面我们一一个微信小程序的登录界面的制作为例来说明&#xff0c;如何从iconfont中获取字体图标是如何应用到微信小程序中去的。首先我们看效果。 这里所有的图标&#xff0c;都是从iconfont中以字体的形式来加载的&#xff0c;也就是说&#xff0c;我们自始至终没有使用一张…

「Mac畅玩鸿蒙与硬件14」鸿蒙UI组件篇4 - Toggle 和 Checkbox 组件

在鸿蒙开发中,Toggle 和 Checkbox 是常用的交互组件,分别用于实现开关切换和多项选择。Toggle 提供多种类型以适应不同场景,而 Checkbox 支持自定义样式及事件回调。本篇将详细介绍这两个组件的基本用法,并通过实战展示它们的组合应用。 关键词 Toggle 组件Checkbox 组件开…

Axure设计之多级菜单导航教程(中继器)

在数字化时代&#xff0c;优化产品设计&#xff0c;提升用户界面交互&#xff0c;是产品设计着重考虑的点。针对传统菜单导航复杂繁琐的问题&#xff0c;本设计提出了一套灵活的菜单导航方案&#xff0c;结合中继器与动态面板&#xff0c;实现一键搜索、菜单收藏、多级菜单导航…

基于uniapp微信小程序的旅游系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

SpringBoot篇(自动装配原理)

目录 一、自动装配机制 1. 简介 2. 自动装配主要依靠三个核心的关键技术 3. run()方法加载启动类 4. 注解SpringBootApplication包含了多个注解 4.1 SpringBootConfiguration 4.2 ComponentScan 4.3 EnableAutoConfiguration 5. SpringBootApplication一共做了三件事 …

Python 爬虫的寻宝大冒险:如何捕获 API 数据的宝藏

在这个信息爆炸的数字时代&#xff0c;数据就像是隐藏在网络深处的宝藏&#xff0c;等待着勇敢的探险家去发现。今天&#xff0c;我们要讲述的是如何成为一名 Python 爬虫探险家&#xff0c;装备你的代码工具&#xff0c;深入 API 的迷宫&#xff0c;捕获那些珍贵的数据宝藏。 …

blender雕刻基础 笔记

一、教学视频来源 案例5&#xff1a;荧光树桩_雕刻基础_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Bt4y1E7qn/?p18&share_sourcecopy_web&vd_sourced9dc363bbfe0ac72dbaa04823c59231e 二、笔记 1. 启动blender的雕刻模式 启动雕刻模式有两种方式&#x…

NVR小程序接入平台/设备EasyNVR多个NVR同时管理多平台级联与上下级对接的高效应用

政务数据共享平台的建设正致力于消除“信息孤岛”现象&#xff0c;打破“数据烟囱”&#xff0c;实现国家、省、市及区县数据的全面对接与共享。省市平台的“级联对接”工作由多级平台共同构成&#xff0c;旨在满足跨部门、跨层级及跨省数据共享的需求&#xff0c;推动数据流通…

利用Kubernetes原生特性实现简单的灰度发布和蓝绿发布

部分借鉴地址: https://support.huaweicloud.com/intl/zh-cn/bestpractice-cce/cce_bestpractice_10002.html 1.原理介绍 用户通常使用无状态负载 Deployment、有状态负载 StatefulSet等Kubernetes对象来部署业务&#xff0c;每个工作负载管理一组Pod。以Deployment为例&#x…

江协科技STM32学习- P25 UART串口协议

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

【c语言】运算符汇总(万字解析)

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;C语言 目录 前言 一、c语言运算符的分类 二、各运算符的功能及使用 1. 算数运算符 - * / % 2. 位运算符 二进制和进制转换 二进制转十进制 十进制…

使用Python和OpenCV实现火焰检测

使用Python和OpenCV实现火焰检测 项目解释&#xff1a; 此 Python 代码是使用 OpenCV、线程、声音和电子邮件功能的火灾探测系统的简单示例。 以下是它的功能的简单描述&#xff1a; 导入库&#xff1a;代码首先导入必要的库&#xff1a; cv2&#xff1a;用于图像和视频处理…