简介面向 VR/AR 开发初学者与独立开发者的虚拟展览项目创建脚本案例也是一份聚焦 VR/AR 软件/插件开发方向的落地参考。内容围绕从零搭建沉浸式展览展开覆盖项目初始化与配置、3D 场景搭建、交互功能开发、UI 与音效设计、发布部署及后期维护的完整链路可应用于教育展厅、产品展示、数字博物馆等场景解决开发流程不清、模块规划混乱的问题。资源为 1 个 doc 格式文档压缩包整体仅 21KB轻量易读文档以分步骤脚本说明为主包含引擎选择、SDK/插件安装、模型优化、交互逻辑编写、性能与兼容性测试等具体知识点便于对照 Unity 或 Unreal Engine 项目直接修改使用。已有 802 人浏览学习。读者可获得结构化的开发路线图、交互设计思路与排错切入点从而快速构建一个具备基本交互与沉浸感的虚拟展览原型。1. 需求背景虚拟展览该选VR还是AR以及这套脚本帮谁解决什么我第一次给别人搭虚拟展览时一上来就纠结引擎选 Unity 还是 Unreal结果两周过去了场景还是几个白模摆在空房间里。后来拿到一份名为《VR/AR开发脚本案例创建虚拟展览体验》的完整流程脚本才意识到这个项目真正该先想清楚的是“用户是站着看还是走起来看”也就是 VR 和 AR 的定位问题。这份脚本不是一份能直接运行的代码文件而是一条从新建设置、场景搭建、交互开发到 UI/音效、发布维护的完整项目工作流帮你把“做出来了”和“能交付”之间的每一步都补上。它的受众很明确正在做毕设或课设的学生以及接了不少需求、手里却没有一份规范流程的小团队开发者。这套方案解决的核心问题有三层第一项目初始化毫无头绪文件夹、环境、参数没人给你定版本第二交互逻辑容易翻车射线点击没反应、手柄照不到展品基本都是流程没走全第三发布之后被反馈“头昏眼花、点不中、加载慢”追根溯源都在前面的设计和性能优化阶段。2. 总体设计六阶段流程与项目初始化的落地要点2.1 技术选型先定交互方式再定引擎很多开发者的第一反应是“Unity 好还是 Unreal 好”但这份脚本给出的思路是先定交互方式再选引擎。VR 场景下用户戴着头盔头部追踪是基础手部射线或手柄按钮是主要交互手段AR 场景下用户用手机或眼镜手势识别和屏幕点击更常见。交互方式一定引擎就好选了中小型虚拟展览场景面数可控、交互逻辑偏点击和弹窗我一般会用 UnityC# 脚本调试效率高Asset Store 里现成的展品模型和 UI 资源也丰富如果要做大型写实空间追求电影级光影和大量三角面Unreal Engine 的 C 管线会更合适但学习成本和构建耗时都会明显上升。SDK 与插件的安装要跟着设备走。使用 Oculus Rift、HTC Vive 这类头显就以对应官方 SDK 为主再补 OpenXR 插件统一接口只做手机 AR 展示则优先使用 AR Foundation 搭配平面检测。不要一上来把各类插件全装进去插件之间版本冲突是后期最难排查的黑匣子问题。2.2 项目初始化三步建立项目骨架脚本里的“创建项目文件夹”看起来最简单但也是一步最容易偷懒的。我的做法是先建一个以项目简称命名的根目录再按“文档、素材、场景、脚本、构建输出”拆分这样后期无论是自己找文件还是交给别人接手都不至于翻半天目录。目录结构如下VirtualExhibition/ ├─ Docs/ # 需求说明、测试记录、会议纪要 ├─ Assets/ # Unity/UE 工程资源目录 │ ├─ Models/ # 展品与展厅 3D 模型FBX/OBJ │ ├─ Materials/ # 材质球与贴图 │ ├─ Scenes/ # 主场景、测试场景 │ ├─ Scripts/ # C# 脚本交互/UI/工具 │ └─ Audio/ # 背景音乐与环境音效 └─ Build/ # 构建输出目录环境配置紧随其后。引擎选择稳定正式版而非最新抢先版硬件 SDK 先装对应设备的运行时和驱动Git 或 SVN 任选其一关键是在步骤二就把版本管理建起来否则后面“改坏了一个文件后悔药都没得吃”。设置项目参数时分辨率建议按目标设备的原生分辨率走常见头显按 1920×1080 左右起步桌面预览可以放宽到 2560×1440帧率直接锚定 60fps这是 VR 防眩晕的最低安全线。新建项目后在初始化脚本里固定帧率和画质这比指望玩家自己调设置靠谱得多using UnityEngine; public class ProjectInitializer : MonoBehaviour { private void Awake() { // 锁定目标帧率VR 场景低于 60fps 容易导致明显眩晕 Application.targetFrameRate 60; // 关闭垂直同步避免帧率波动引起画面撕裂 QualitySettings.vSyncCount 0; // 中高档画质预设兼顾视觉效果与渲染性能 QualitySettings.SetQualityLevel(2, true); } }这里把vSyncCount设为 0是为了让帧率完全交给引擎的 targetFrameRate 控制而不是被显示器的刷新率拖着走。实际运行中若发现某些设备撑不住 60fps优先降的不是帧率目标而是阴影分辨率和实时灯光数量。2.3 脚本流程划分六个阶段各自的价值这份脚本把整个项目切成六个阶段项目初始化、场景搭建、交互功能开发、UI 与音效设计、发布与部署、后期维护与更新。每个阶段都有明确的产出物也是后期验收的依据。阶段主要工作阶段产出物项目初始化目录、环境、项目参数可运行的空场景工程场景搭建模型导入、布展、灯光材质静态主场景交互功能开发交互设计、脚本编写、调试可点击、可导航的动态场景UI 与音效设计菜单、指示、背景音频完整可体验版本发布与部署构建、兼容性测试、上架应用安装包后期维护反馈收集、修复、更新迭代版本这套划分的逻辑是“每一阶段都能独立验收”。作为项目负责人我一般会要求前一阶段全部确认后再进入下一阶段尤其是“场景搭建”和“交互开发”不要并行否则模型还没定稿就写点击逻辑改一步崩一路的情况非常常见。2.4 脚本工程结构把“流程脚本”拆到开发目录里“脚本”这个词容易让人误解为一份代码实际上这份资源更像是开发手册。落地时我们要把它拆成工程目录里的具体职责这直接决定后面维护的成本。一个规整的 C# 脚本目录大概长这样Assets/Scripts/ ├─ Interaction/ │ ├─ ExhibitInteractor.cs # 展品点击交互 │ ├─ TeleportNavigation.cs # 场景瞬移导航 │ └─ HandGestureInput.cs # 手势识别入口 ├─ UI/ │ ├─ MainMenuController.cs # 主菜单逻辑 │ └─ DetailPanel.cs # 展品详情面板 ├─ Audio/ │ └─ AmbientAudioManager.cs # 环境音与背景乐管理 └─ Utility/ ├─ ProjectInitializer.cs # 帧率与画质初始化 └─ DebugLogger.cs # 日志工具这样的好处是测试反馈“点展品没反应”时第一时间只翻 Interaction 目录“背景音乐声音太大”就直奔 Audio 目录。模块边界画清楚后续找人接手也只能好收拾。3. 场景搭建与交互开发展品陈列的逻辑和核心脚本实现3.1 3D 模型导入与场景布展的工程习惯在你把展品丢进场景之前必须做一次模型清理。开发引擎对 FBX 和 OBJ 都兼容但建模软件里的单位会有隐患某些建模工具默认单位是厘米引擎默认单位是米导入后展品可能变成一座大楼。我一般会在导入前确认单位统一并打开模型检查器看一眼缩放比例确认无误后再进场景。场景布置需要有明确的观展动线。虚拟展厅的访客不是来乱逛的他们希望有一条视线路径能带自己看到重点展品。把重点展品摆位在视觉中心线上非重点展品沿墙布置展品底座高度以虚拟相机所在的水平高度上下 20 度为最佳视野区。调整位置、大小和方向时尽量用对齐工具吸附到地面避免“看起来一样高实际差了 0.3 米”的尴尬。3.2 灯光与材质让“白模”变成展厅的关键参数展品的质感不是模型给的是灯光和材质给的。场景里通常配一盏定向光模拟展厅顶灯负责整体照明开启阴影时注意控制阴影距离否则远处展品的阴影会和贴图糊成一团再用若干点光源重点照亮每个展柜点光源的最大距离建议控制在 5 到 10 米让光线落到展品上就衰减掉避免光源过多导致实时光照计算翻倍。材质方面核心三个参数是金属度、光滑度和贴图分辨率。展品如果是青铜器金属度拉高、光滑度中等如果是织物类展品金属度归零光滑度降低。不要一股脑全用标准材质球反射探头只给主展厅放一个小展柜区域用光照贴图烘焙替代实时反射画面不会差性能却能省出一大截。3.3 交互设计三件套射线、事件系统与手柄输入虚拟展览的交互可以分成三档基础档是“看”——头部追踪跟着转就行标准档是“点”——用手柄射线指向展品扣扳机查看详情进阶段是“摸”——手势识别抓取展品的 3D 模型。这份脚本重点覆盖了前两档它在此基础上加手势识别扩展点恰好是毕设项目最合理的范围。“点”这一档的核心是一条射线从手柄出发打到展品上触发逻辑。下面是一个典型的展品点击交互脚本using UnityEngine; public class ExhibitInteractor : MonoBehaviour { public float rayLength 50f; // 射线长度覆盖远端展品 public Transform rayOrigin; // 射线出发点通常挂到手柄模型上 public LineRenderer rayLine; // 用于显示射线的辅助线 private void Update() { // 按下手柄主键才发射射线平时不显示射线避免干扰视线 if (Input.GetButtonDown(Fire1)) { Ray ray new Ray(rayOrigin.position, rayOrigin.forward); rayLine.SetPosition(0, rayOrigin.position); rayLine.SetPosition(1, rayOrigin.position rayOrigin.forward * rayLength); if (Physics.Raycast(ray, out RaycastHit hit, rayLength)) { ExhibitItem item hit.collider.GetComponentExhibitItem(); if (item ! null) { item.ShowDetail(); // 弹开展品详情面板 } } } } }写这个脚本时有几个细节容易忽略rayOrigin必须挂到手柄的实际模型位置而不是主相机否则你“指”的方向和视线方向不一致用户会觉得射线不听使唤射线长度设成 50 米左右能让用户在展厅入口就把尽头的大展品指中但也不用太长否则会穿透墙面点到隔壁展品。点击之外的导航也有讲究。平滑移动在 VR 里最容易引发眩晕虚拟展览我强烈建议用瞬移或“点哪走哪”的方式。瞬移的核心逻辑是先圈定可移动区域再用射线落点做二次确认避免用户一脚踩进墙里。3.4 测试与调试模拟器和真机完全是两码事交互逻辑写完先别急着打包。在引擎编辑器里测试时逻辑漏洞能暴露大部分但头部追踪的延迟、手柄射线的手感偏差只有真机测试才测得出。我常用的做法是维护一份测试清单包含“点击展品弹出详情”“瞬移落点在可到达区域”“射线穿过展品间隙命中后方展品”这三条重点用例每改一版都全量回归一遍。调试工具也值得说一说。Unity 的 Console 窗口能帮你发现空引用和材质报错但 VR 模式下开着编辑器调试很容易头晕。更稳妥的做法是给关键交互加上运行时日志同步输出到设备端再统一回传电脑分析。脚本里的 DebugLogger 工具就是干这个的。4. UI、音效与发布部署沉浸感的收尾和上线前检查4.1 UI 设计菜单、导航与视野跟随虚拟展览的 UI 和普通手机界面完全是两个思路。普通 UI 是贴在屏幕上的VR/AR 的 UI 放进场景里要么做成世界空间菜单悬在展厅入口处用户可以“走近、伸手、按下”要么做成视野跟随面板始终固定在用户视线右下角不打断观展动线。关键一点是 UI 的交互必须和你的交互方案对齐。如果你已经用射线点击展品那菜单按钮也应该用同一套射线逻辑而不是 UI 上单独再搞一套鼠标事件否则就会陷入“展品能点、菜单点不动”的诡异状态。所以在 MainMenuController 里按钮事件我统一绑定到 Raycast 结果上确保整套交互只有一条链路。音量控制这些常规设置也要放进场景里而不是藏在系统设置里。下面这段音量控制的逻辑非常直白public class VolumeSliderHandler : MonoBehaviour { public UnityEngine.UI.Slider bgmSlider; public AudioSource bgmSource; private void Start() { // 初始音量为 0.5避免开场音乐过响吓到用户 bgmSlider.value 0.5f; bgmSource.volume bgmSlider.value; bgmSlider.onValueChanged.AddListener(OnVolumeChanged); } private void OnVolumeChanged(float value) { // 实时同步背景音量同时把值存入 PlayerPrefs 以便下次启动读取 bgmSource.volume value; PlayerPrefs.SetFloat(BGMVolume, value); } }把音量写入 PlayerPrefs 是一个很容易被忽略但很影响体验的细节——用户调好一次音量下次打开又还原成默认值就会产生“这个 App 怎么这么不听话”的抱怨。对毕设答辩来说这种细节也完全能作为“考虑到了用户体验”的加分项讲出来。4.2 音效背景音乐、环境音和空间音频音效设计有两个层次第一层是确定“有没有”第二层是确定“从哪来”。背景音乐开着播放时音量压到 0.2 到 0.4作为氛围底衬环境音效放进展厅的四个角落能明显增强空间感。真正让体验有质的飞跃的是空间音频——给每个展品绑定一个 AudioSource当用户走近时音量逐渐抬高走远了自动衰减。这比全局播放一段音轨要自然得多。AudioSource 的 3D 音效设置需要注意两个参数Spatial Blend设为 1纯 3DMax Distance设成 1.5 到 2 米。如果某个展品的解说音频是全场景播报那可以保持 2D 模式不受距离影响两种混用可以让空间层次更清晰。4.3 构建发布从构建设置到应用商店构建在流程里是第六步但准备工作要从第二步就开始目标平台定的是 PC 还是独立头显直接决定前期的分辨率和性能方案。安卓头显打包时Build Settings 里的纹理压缩格式、脚本后端IL2CPP 还是 Mono都会影响最终包体和运行效率IL2CPP 的效果更稳定但首次构建时间长我一般会在测试阶段先用 Mono 加速迭代正式发版再切到 IL2CPP。发布前我习惯把下面这张表打印出来逐项打勾避免“到了答辩现场才发现手柄键位没映射对”之类的低级问题。检查项通过标准设备兼容性目标头显/手机均能正常启动无闪退交互完整性射线可点击所有按钮展品详情可关闭性能最低值持续运行 10 分钟帧率不低于 55fps音频与 UI背景音乐音量可调UI 无错位遮挡应用商店提交时按照商店后台的清单准备图标、截图、描述和隐私政策即可。独立站或学术展示场景不需要走商店提供一份带安装说明的构建包就够了但版本号要写清楚方便后续维护。4.4 后期维护与迭代用反馈驱动更新应用发布不是结束而是用户反馈的起点。脚本里的后期维护给出了两条线一是问题修复二是内容迭代。在实践中最有效的方法是给每个展品预留一个配置文件模型路径、详细介绍、音频名称都记在配置里这样后期上新展品时只需要替换配置和模型不需要重新编译整个程序。5. 避坑与排查最容易翻车的五个实践问题5.1 掉帧与眩晕不是所有人都有“铁胃”现象用户戴上头显两三分钟就开始头晕摘下设备直说不舒服。看监控面板帧率在 40 到 55fps 之间反复跳。原因帧率达不到 60fps 且频繁波动是 VR 眩晕最主要诱发因素。场景里实时灯光太多、模型没有 LOD、纹理压缩没做都会拖累渲染性能。解决先砍阴影——把定向光阴影质量从中档调低、阴影距离从 100 米降到 30 米往往能立刻救回来。再检查纹理导入设置统一改成压缩格式模型在导入设置里勾选 LOD 生成远处展品自动用低模替代。最后回到那个 ProjectInitializer 脚本确认目标帧率锁在 60不设上限也不设下限。5.2 点击展品没反应射线、EventSystem 和遮罩三连查现象手柄按键有反馈但展品就是弹不出详情面板。原因八成是射线根本没打到展品上或者打到了但被 UI 面板挡住了。还有一部分原因是 EventSystem 没创建导致 UI 按钮类事件接收不到。解决第一步在 Scene 视图里打开射线显示确认射线是否真的指到展品上第二步检查展品碰撞体是否存在——模型只有网格没有 Collider射线永远不会命中第三步检查展品前方有没有不可见的 UI 遮罩。这三步能解决 90% 的“点不动”问题。剩下 10% 是射线长度太短把 rayLength 从默认 10 米改到 50 米再试。5.3 头盔显示画面偏移SDK 版本和相机设置现象用户戴好头显画面中心总是歪的低头看手时觉得手不在该在的位置。原因最常见的两个——SDK 版本与引擎版本不匹配跟踪数据解析出了问题或是主相机没有标记为跟踪参考点引擎不知道用什么坐标系定位。解决切换 SDK 版本时先查官方兼容性表格不要盲目升到最新。已经出现偏移的检查场景里有没有多个主相机只保留一个把 XR 相机组件的 Tracking Origin 设为 Floor 或 Device分别对应站姿和坐姿场景。如果是模拟器导致的偏移重进一次模拟器基本能恢复。5.4 包体异常膨胀模型和贴图没压缩现象项目本身不大打包出来却有两个多 G 的安装包且首屏加载慢得让人怀疑死机。原因模型面数过多贴图全是 4096 分辨率的原始图音频文件是未压缩 WAV 格式。这三个因素加起来包体能不大吗解决模型在导入时设置 Mesh Compression 为 High贴图分辨率超 2048 的改用 1024 或 512 的压缩版本音频统一转成 ADPCM 或 Vorbis 压缩格式。打包时在 Build Settings 里启用 Texture CompressionASTC 或 ETC2安卓包体通常能压掉 40% 以上。5.5 UI 文字发虚Canvas 缩放和字体反锯齿现象在头显里看 UI 菜单字体边缘全是锯齿凑近看甚至看不清按钮名。原因Canvas 的渲染模式选成了 Screen Space Overlay分辨率缩放策略不对UI 被拉伸后像素就开始糊。VR 场景要遵循“UI 是场景的一部分”的原则使用 World Space 模式才清晰。解决把 Canvas 渲染模式改为 World Space按实际尺寸设置 1:1 缩放放在离用户 2 到 3 米远的位置高度与视线平齐。字体设为 TMPTextMeshPro开启反锯齿和动态字体大小画面一下就干净了。这也是最容易“从截图上看不出来、真机一戴就露馅”的问题做项目时务必提前注意。6. 验收与答辩先把自己当用户再把方案讲清楚6.1 一份能直接用的验收清单验收这件事不能等到答辩前一天才开始。我从这个项目里带出来的习惯是每次提交版本前强制自己走一遍“用户 10 分钟体验流程”——戴上设备从头走到尾看一遍展品、点一遍按钮、调一遍音量、退出重进一次。走完填下面这张表任何一项不过关就当场修不拖进下一篇开发。验收维度通过标准视觉展品材质清晰灯光不刺眼场景无明显穿模交互所有展品可点击详情可打开且可关闭菜单响应正常性能连续运行 10 分钟不头晕帧率稳定在 55fps 以上体验新手不靠讲解也能完成“看展品 → 看详情 → 切展区”6.2 答辩时三个必答口径评审最常问的三个问题提前准备好口径比临场发挥稳得多。被问“为什么选 Unity 不选 Unreal”时从项目体量回答中小型虚拟展览交互复杂度中等Unity 的 C# 生态和资产资源能满足需求开发周期可控。被问“这个项目的难点是什么”时不要只说“实现了点击”要说性能调优——如何把帧率稳定在 60fps如何平衡灯光效果和渲染压力。被问“你自己测试过吗”展示验收清单和测试记录这条最有说服力。我第一次独立带虚拟展厅项目也想“跑通就是万事大吉”结果被评委一句话问住“你连续戴十分钟看过帧率曲线吗”从那以后我每次提交版本都强制自己把 10 分钟体验流程走完把测试记录截图存进 Docs 目录答辩时直接拍出来当证据。希望这份用心整理的流程脚本和实战排查也能帮你把项目做得让人一眼信服。本文还有配套的精品资源点击获取