☰
Unity VR射击水果作业实战:从XR交互到打包exe完整方案
2026/9/29 5:21:17 网站建设 项目流程

简介:这份资源是面向高校学生与Unity初学者的VR期末团队作业完整方案,以HTC Vive为运行设备,实现了一款限时射击水果的虚拟现实小游戏。玩家需用弓箭射落从地面升起、挂着不同水果的气球,按水果种类计分,玩法覆盖各年龄段,可直接作为虚拟现实课程大作业或Unity VR入门练手项目。压缩包为zip格式,整体约300.11MB,内含Unity工程源码、导出的exe可执行文件、设计文档与演示视频,源码便于二次修改,exe可在HTC设备上直接运行验证,设计文档与录屏则帮助理解场景搭建、交互逻辑与计分机制。该资源已在教室HTC设备上实测可正常运行,目前已有2720人浏览学习,适合需要完整赛题方案、可运行Demo与排错参考的团队参考借鉴。

1. 从一份 Unity VR 射击水果作业说起:为什么值得动手做

如果你正在找一份能跑起来的 Unity VR 期末作业,或者想用一套完整源码把「VR 交互 + 射击判定 + 打包 exe」这条链路走通,那这个方向比想象中更值得投入。它不是一个只能看不能改的演示包,而是一个把 XR 交互、物理碰撞、UI 计分、场景管理和 Windows 打包串起来的完整小项目。射击水果这个玩法本身门槛不高,但恰好覆盖了 VR 开发里最容易翻车的几个点:手柄射线怎么发、水果被击中后怎么碎、分数怎么同步到 UI、打包成 exe 后为什么手柄突然失灵。很多人第一次做 VR 作业,卡的不是玩法设计,而是环境配置和打包环节。这篇笔记就按「先跑通、再改懂、最后能讲清楚」的顺序,把一套可复现的 Unity VR 射击水果方案拆开讲,源码结构、关键参数、导出 exe 的注意点都会落到具体操作上。适合正在赶期末作业的学生,也适合想快速验证 VR 交互原型的开发者。

2. 环境搭起来:Unity 版本、XR 插件与 Pico4 设备链路

2.1 为什么优先选 Unity 2022 LTS + XR Interaction Toolkit

做 VR 项目,版本选错后面全是玄学。我一般会锁定 Unity 2022.3 LTS,原因是这个版本对 XR Interaction Toolkit 的支持最稳,OpenXR 插件兼容性也好,导出 Windows exe 时不容易出现手柄输入丢失。热词里提到的「unity安装」「unity下载」其实指向同一个问题:装哪个版本。不要追最新 Tech 流,期末作业求的是能跑、能打包、能演示。

核心插件组合如下:

插件作用安装方式
XR Interaction Toolkit提供射线、抓取、传送等交互组件Package Manager 搜索安装
OpenXR Plugin统一各 VR 设备的运行时接口Package Manager 搜索安装
XR Plugin Management管理不同平台的 XR 加载随 OpenXR 自动带入

安装顺序建议先装 XR Plugin Management,再装 OpenXR,最后装 XR Interaction Toolkit。顺序反了容易出现 Input System 冲突,表现是手柄按键全部无响应。

2.2 从新建工程到 Pico4 能识别的最小配置

新建 3D Core 工程后,第一步不是急着拖模型,而是把 XR 环境激活。打开 Edit > Project Settings > XR Plug-in Management,勾选 OpenXR。然后在 OpenXR 的 Feature Groups 里确认 Interaction Profile 包含你用的手柄类型。Pico4 开发 Unity 时,常见做法是安装 Pico 官方的 XR SDK,但如果你只是做 Windows exe 演示,用 OpenXR 通用链路也能跑通。

# 如果你用命令行做 CI 构建,可以用这段检查 XR 是否启用 # 在 Unity 工程根目录执行,查看 ProjectSettings 里的 XR 配置 grep -r "OpenXR" ProjectSettings/ | head -20

这段命令的作用是快速确认 OpenXR 是否已经写入工程配置。如果输出为空,说明 XR 插件没启用成功,需要回到 Editor 里重新勾选。参数上重点看ProjectSettings/XRPackageSettings.asset这个文件,它记录了当前激活的 XR 加载器。

接下来配置 Input System。XR Interaction Toolkit 的新版本默认依赖 Input System,如果工程里还是旧 Input Manager,手柄射线会拿不到值。在 Player Settings > Active Input Handling 里选 Both 或 Input System Package。选 Both 最省事,但打包体积会略大。

2.3 场景里必须放的三件套:XR Origin、交互管理器、射线

一个能跑的最小 VR 场景,层级里至少要有这三个对象:

  1. XR Origin:替代旧版 Camera Rig,负责把头显和手柄的位置映射到场景里。
  2. XR Interaction Manager:管理所有交互事件,没有它射线打出去也没反应。
  3. Locomotion System:如果你需要传送移动,就挂上它;纯站桩射击可以不加。

XR Origin 下面会自动生成 Camera Offset、Main Camera、LeftHand Controller、RightHand Controller。检查 Main Camera 的 Tracked Pose Driver 是否绑定到 Center Eye。手柄控制器上要加 XR Controller 组件和 Input Action Manager。很多人到这里发现射线不出现,八成是 Input Action Manager 没挂或者 Action Asset 没赋值。

提示:Pico4 通过串流助手连 Unity Editor 时,如果手柄在 Scene 视图里不动,先检查 OpenXR 的 Interaction Profile 是否勾选了 Pico 对应的手柄型号。没有官方 Profile 时,选 Oculus Touch Controller Profile 通常也能兼容大部分按键。

3. 射击水果的核心逻辑:射线发射、命中判定与水果碎裂

3.1 用 XR Ray Interactor 做射击射线的三个必调参数

射击水果的「射击」本质是一条从手柄发出的射线,命中水果后触发销毁和计分。XR Interaction Toolkit 自带 XR Ray Interactor,但默认配置是抓取用的,改成射击需要调三个地方。

第一,Ray Interactor 的 Line Type 选 Straight Line,不要用 Projectile Curve。曲线适合传送,直线才像枪口。第二,Interaction Layer Mask 只勾选 Fruit 层,避免射线打到地面或 UI。第三,Max Time 设为 0,表示射线常驻,不需要按住才显示。

// ShootRayController.cs // 挂载在手柄控制器上,负责发射射线并处理命中 using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; public class ShootRayController : MonoBehaviour { [SerializeField] private XRRayInteractor rayInteractor; [SerializeField] private InputActionProperty triggerAction; [SerializeField] private GameManager gameManager; private void OnEnable() { triggerAction.action.performed += OnTriggerPressed; triggerAction.action.Enable(); } private void OnDisable() { triggerAction.action.performed -= OnTriggerPressed; triggerAction.action.Disable(); } private void OnTriggerPressed(UnityEngine.InputSystem.InputAction.CallbackContext ctx) { // 用射线检测获取当前命中的第一个物体 if (rayInteractor.TryGetCurrent3DRaycastHit(out RaycastHit hit)) { FruitTarget fruit = hit.collider.GetComponent<FruitTarget>(); if (fruit != null) { fruit.OnHit(); gameManager.AddScore(fruit.scoreValue); } } } }

这段代码的逻辑是:监听扳机按键,按下时从 XR Ray Interactor 取当前命中结果,如果命中物体带 FruitTarget 组件,就调用命中方法并加分。参数上,rayInteractor必须在 Inspector 里拖入同一个物体上的 XR Ray Interactor 组件;triggerAction绑定到 XR Controller 的 Activate Action;gameManager是场景里的计分管理器。容易翻车的点是TryGetCurrent3DRaycastHit在射线没命中任何东西时返回 false,如果不做判空直接取 hit,会报空引用。

3.2 水果被击中后的碎裂:粒子、音效与销毁时机

水果碎裂不要直接 Destroy,否则音效和粒子还没播完物体就没了。常见做法是命中后先禁用 Collider 和 Renderer,播放粒子与音效,延迟 0.5 秒再销毁。

// FruitTarget.cs using UnityEngine; using System.Collections; public class FruitTarget : MonoBehaviour { public int scoreValue = 10; [SerializeField] private GameObject sliceEffect; [SerializeField] private AudioClip hitSound; [SerializeField] private float destroyDelay = 0.5f; private bool isHit = false; public void OnHit() { if (isHit) return; // 防止一帧内多次触发 isHit = true; GetComponent<Collider>().enabled = false; GetComponent<MeshRenderer>().enabled = false; if (sliceEffect != null) Instantiate(sliceEffect, transform.position, Quaternion.identity); if (hitSound != null) AudioSource.PlayClipAtPoint(hitSound, transform.position); StartCoroutine(DestroyAfterDelay()); } private IEnumerator DestroyAfterDelay() { yield return new WaitForSeconds(destroyDelay); Destroy(gameObject); } }

isHit这个布尔量是血泪经验:VR 里手柄抖动可能导致同一帧内射线多次命中,不加锁会重复加分。destroyDelay默认 0.5 秒,如果粒子时长是 1 秒,就改成 1 秒。sliceEffect建议用 Unity 自带的 Particle System 做一个简单的球形扩散,不要用太重的特效,VR 里掉帧比画面丑更致命。

3.3 计分 UI 与游戏循环:从 ScriptableObject 到场景重开

计分不要每个水果单独找 UI 文本,用一个 GameManager 统一管理。分数变化时更新 UI,达到目标分数后显示重开按钮。

// GameManager.cs using UnityEngine; using TMPro; public class GameManager : MonoBehaviour { public static GameManager Instance; [SerializeField] private TextMeshProUGUI scoreText; [SerializeField] private GameObject restartPanel; [SerializeField] private int targetScore = 100; private int currentScore = 0; private void Awake() { Instance = this; } public void AddScore(int value) { currentScore += value; scoreText.text = $"Score: {currentScore}"; if (currentScore >= targetScore) { restartPanel.SetActive(true); } } public void RestartGame() { UnityEngine.SceneManagement.SceneManager.LoadScene( UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex); } }

targetScore设成 100 是给演示留余地,水果分值 10 的话打中 10 个就结束。restartPanel是一个 World Space 的 Canvas,挂在摄像机前方 2 米处,VR 里不要用 Screen Space Overlay,否则在头显里看不到。重开用LoadScene最稳,不要手动重置每个水果的位置,容易漏。

4. 导出 exe 与真机演示:打包设置、性能取舍与常见故障

4.1 Windows exe 打包的五个关键设置

导出 exe 是期末作业交付的硬指标。File > Build Settings 里选 Windows x86_64,然后重点检查这几项:

设置项推荐值原因
Architecturex86_6464 位系统兼容
Compression MethodLZ4HC加载快,体积可接受
Development Build演示时勾选方便看日志
Script Debugging演示时勾选出错能定位
Graphics APIDirect3D11VR 兼容性最好

Graphics API 不要选 Auto,Auto 在某些机器上会走 Vulkan,导致头显画面撕裂。手动指定 Direct3D11 最稳。如果热词里提到的「vr渲染器切换cpu gpu模式」让你困惑,在 Unity 里对应的是 Player Settings > Rendering 下的 Graphics API 列表,把 D3D11 拖到第一位即可。

4.2 打包后手柄失灵的排查顺序

打包 exe 后手柄没反应,按这个顺序查:

  1. 确认 OpenXR Runtime 是否安装。Windows 上需要装对应的 OpenXR Runtime,否则 exe 启动后找不到设备。
  2. 检查 exe 是否以管理员权限运行。热词里「unity is running with administrator privileges」提示过这个坑,某些设备在管理员权限下反而无法读取手柄输入。
  3. 看 Player.log。路径在C:\Users\用户名\AppData\LocalLow\公司名\产品名\Player.log,搜「XR」或「Input」关键字。
# 快速查看 Player.log 里的 XR 相关报错 # Windows 下用 PowerShell Get-Content "$env:LOCALAPPDATA\..\LocalLow\DefaultCompany\FruitShooter\Player.log" | Select-String "XR|Input|OpenXR"

这段命令帮你过滤日志里的 XR 和输入相关行。如果看到「No XR display subsystem found」,说明 OpenXR Runtime 没装好;如果看到「Input System is not enabled」,说明打包时 Active Input Handling 选错了。

4.3 性能优化:VR 里必须守住的帧率底线

VR 项目低于 72 帧就会晕,射击水果虽然场景简单,但粒子特效和实时阴影是两大杀手。我一般会做三件事:关掉实时阴影改用烘焙、粒子最大数量限制在 50 以内、水果模型面数控制在 500 面以下。热词里的「unity游戏优化」在 VR 场景里优先级最高,因为帧率直接决定可用性。

在 Quality Settings 里把 Shadow Distance 设为 0 或很小值,Anti Aliasing 用 2x 或 4x,不要开 8x。如果用的是 Pico4 一体机模式,还要在 Pico 开发者后台里把 CPU 和 GPU 等级调到最高,否则默认会降频。

5. 避坑与排查:VR 射击水果作业里最容易翻车的五件事

5.1 射线打不中水果,但手柄明明有震动

现象:扣扳机手柄有震动反馈,但水果不消失,分数也不变。

原因:XR Ray Interactor 的 Interaction Layer Mask 没有勾选 Fruit 层,或者水果的 Collider 被禁用。另一种可能是射线被手柄自身的 Collider 挡住,射线起点在模型内部。

解决:把水果统一放到 Fruit 层,Ray Interactor 的 Layer Mask 只勾 Fruit。手柄控制器的 Collider 设为 Ignore Raycast,或者把射线起点偏移到手柄前方 0.1 米。

5.2 打包后 UI 在头显里看不见

现象:Editor 里计分文本正常,导出 exe 后头显里一片空白。

原因:Canvas 用了 Screen Space Overlay,VR 渲染模式下 Overlay 不会显示在头显里。

解决:Canvas 的 Render Mode 改成 World Space,缩放设为 0.01,位置放在摄像机前方 2 米、视线下方 0.3 米处。TextMeshPro 的字体材质要选 Mobile 或 SDF,不要用普通字体。

5.3 水果被击中后重复加分

现象:打一个水果加了 30 分,实际分值只有 10。

原因:射线在单帧内多次命中同一个 Collider,或者水果的 OnHit 被多个脚本调用。

解决:在 FruitTarget 里加isHit布尔锁,如 3.2 节代码所示。同时检查 XR Ray Interactor 的 Raycast Trigger Interaction 是否设为 Collide,不要用 Overlap。

5.4 导出 exe 时报错「Could not find OpenXR Runtime」

现象:Build 成功,但双击 exe 弹出错误框,提示找不到 OpenXR Runtime。

原因:目标机器没装 OpenXR Runtime,或者 Runtime 版本与打包时不一致。

解决:在目标机器上安装对应头显的 OpenXR Runtime。如果是 Pico,装 Pico 的串流助手和 XR Runtime;如果是 Quest,装 Oculus PC 客户端。演示前一定在目标机器上先跑一遍,不要只在开发机上测。

5.5 场景切换后手柄追踪丢失

现象:重开游戏后手柄位置卡住不动,头显画面正常。

原因:LoadScene 后 XR Origin 重新实例化,但 Input Action Asset 没有重新绑定。

解决:把 XR Origin 做成 Prefab,重开时不要销毁它,只重置水果和分数。或者用 DontDestroyOnLoad 保住 XR Origin,场景里只加载游戏逻辑部分。

6. 把作业变成能讲清楚的作品:源码结构、演示视频与验收技巧

一套能拿得出手的 VR 射击水果作业,不只是 exe 能跑,还要让人看懂你的设计。源码目录建议按功能分文件夹:Scripts 放 C# 脚本,Prefabs 放水果和特效,Scenes 放主场景,Materials 放材质,Audio 放音效。设计文档不用写太长,重点写清楚三个东西:交互流程图、计分规则表、打包环境说明。演示视频控制在 2 分钟内,开头 10 秒展示头显画面和手柄操作,中间 60 秒打水果和计分,最后 20 秒展示重开和打包后的 exe 运行画面。

验收时老师常问的问题就几个:射线怎么发射的、命中怎么判定、分数怎么同步、打包遇到什么问题。你如果能指着 ShootRayController 说清楚TryGetCurrent3DRaycastHit的返回值逻辑,指着 FruitTarget 说清楚isHit锁的作用,基本就稳了。我自己的习惯是每次打包前把 Player Settings 截图存档,演示前 30 分钟在目标机器上完整跑一遍,确认手柄、头显、音效都正常。这个习惯帮我省过至少三次现场翻车。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询