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

server/2024/11/26 21:27:56/

项目效果演示:

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

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 updatevoid 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;//正确UIpublic GameObject incorrectUI;//错误UIpublic 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; // 判断题的Togglepublic 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.ppmy.cn/server/137811.html

相关文章

MiniPCIe 接口 CANFD卡

概述 基于MiniPCIe高性能接口CANFD卡采用标准MiniPCIe接口尺寸设计&#xff0c;方便PC机或嵌入式设备快速拓展出CANFD接口&#xff0c;实现数据采集与数据处理。 兼容高速CAN和CANFD两种通信需求、支持CAN2.0A、CAN2.0B协议&#xff0c;符合ISO11898-1规范。 规格 PC接口&…

Hadoop生态圈框架部署(二)- 配置IP地址映射为主机名及免密登录

文章目录 前言一、配置IP地址映射为主机名1. 虚拟机hadoop1配置主机名与 IP 地址的映射关系2. 虚拟机hadoop2配置主机名与 IP 地址的映射关系3. 虚拟机hadoop3配置主机名与 IP 地址的映射关系 二、配置免密登录1. 配置虚拟机hadoop1免密登录到hadoop1、hadoop2和hadoop32. 配置…

【C++】How the C++ Linker Works

如果是编译阶段的error&#xff0c;报错代码会以C开头(compiling)&#xff0c;比如语句结束少写了分号如果是链接阶段的error&#xff0c;报错代码会以LNK开头(linking)&#xff0c;比如缺少main函数 为什么缺少main函数就报错了呢&#xff1f; 我们打开设置&#xff0c;可以看…

SpringBoot环境下的学生请假管理平台开发

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

openpnp - 手工修改配置文件(元件高度,size,吸嘴)

文章目录 openpnp - 手工修改配置文件(元件高度,size,吸嘴)概述笔记parts.xmlpackages.xml 手工将已经存在的NT1,NT2拷贝出来改名备注END openpnp - 手工修改配置文件(元件高度,size,吸嘴) 概述 载入新板子贴片准备时&#xff0c;除了引入Named CSV文件&#xff0c;还要在ope…

Java的包、final关键字以及代码块

Java的包、final关键字以及代码块 一、包 包的作用 &#xff1a; ​ 包就是文件夹&#xff0c;用来管理各种不同功能的Java类 包名的书写规则&#xff1a; ​ 公司域名反写 包的作用&#xff0c;需要全部英文小写&#xff0c;见名知意 什么是全类名&#xff1a; ​ 包名…

录屏天花板,录课新玩法,人像+一切,PPT/PDF/视频/网页,也可即可录

上新啦 &#x1f4f1;&#x1f4bb; 录屏也能录课的万能神器——超级推荐&#xff01; 你是不是也在找一款能高效录屏、录课、轻松剪辑的小工具&#xff1f;作为一名需要频繁录制屏幕和课程内容的老师&#xff08;或内容创作者&#xff09;&#xff0c;我找到了这个宝藏App&…

青少年编程与数学 02-003 Go语言网络编程 02课题、网络分层模型

青少年编程与数学 02-003 Go语言网络编程 02课题、网络分层模型 课题摘要:一、网络分层模型&#xff08;一&#xff09;OSI七层模型&#xff08;Open Systems Interconnection Model&#xff09;&#xff08;二&#xff09;TCP/IP四层模型&#xff08;Transmission Control Pro…