![]()
许多开发人员利用面向 PC 和移动设备的 Unity* 游戏创建了卓越的任务和游戏。 现在,虚拟现实功能已经集成至 Unity 3D,我们需要了解如何更新旧版游戏,以提供出色的虚拟现实体验。本文将向开发人员介绍如何转换现有的 Unity 游戏,使其兼容 HTC Vive* 虚拟现实硬件。
- 目标虚拟现实类别: 高端虚拟现实
- 目标虚拟现实硬件: HTC Vive
- 目标 Unity 版本: Unity 5.0
本文将涵盖以下主题:
- 重新设计旧版游戏,以提供全新的虚拟现实体验
- 集成 SteamVR* 插件的技巧
- 开始使用控制器脚本
- 全新 C# 到旧版 JavaScript* 解决方案
- 从 2D 图形用户界面迁移至 3D 用户界面
设计全新的虚拟现实体验
虽然旧版 Unity 游戏并非专为虚拟现实而设计,但是由于 Unity 是一种模拟真实图形和物理的 3D 环境,很有可能您在改善虚拟现实体验方面已经取得了一定的进展。 添加控制器脚本前,您首先需要充分考虑旧版游戏和它所提供的全新的虚拟现实体验。 以下技巧可以帮助您设计适合旧版游戏的虚拟现实体验:
通过设计消除运动眩晕: 标准 Unity 游戏或应用的游戏体验非常令人难忘,但是无法欺骗您的感官, 认识这一点非常重要。 如果您创建了一款非常刺激的高速赛车游戏,然后将它移植到虚拟现实,用户的大脑将会接受非常混乱、错误的信号,最终引起用户的条件反射。 问题是当您的大脑看到虚拟现实世界中的第一人称运动时,它会向您的腿和驱干发送信号,以平衡眼前的运动。 某些用户可能很享受这种游戏体验,但对于大多数用户来说,这是一个非常糟糕的体验,尤其在用户直立时,有可能会摔倒。 如果他们坐着玩游戏,感知到的运动不会造成太大的问题,同时增强了安全性。 评估游戏的最佳体验,考虑建议坐式或站立的游戏体验,以及调整移动,避免用户在场景中遇到平衡问题。
![]()
对于站立的玩家,消除运动眩晕的一个有效方法是取消全部的运动,添加远距传动特性,使游戏玩家在空间中随意转换。 借助远距传动特性,您可以以用户的身份站立,将各个方向的移动距离限制在几英尺以内,用户可以将控制器指向游戏中的某个空间,通过远距传动到达那个空间。 例如,如果 FPS 射击玩家借助远距传动特性进行远距离移动,虽然他们只能步行前进或后退几步,但是游戏效果却更好。
我的游戏示例最初设计为第一人称空间驾驶体验。 因此,我决定提供坐式体验,而非空间定位体验, 这样做将不会产生混乱的大脑信号,避免了用户维持双腿平衡的过程中出现双腿僵硬的现象,同时用户能够在场景中自由移动。 我还决定降低多数运动的幅度,使运动缓和了许多。 作为一款空间游戏,实施效果非常不错;在空间中漂浮时,不会因为转弯或运动而感到眩晕。 在坐式体验中,您仍可以感受到运动,但是运动更为可控,不会在大脑中产生杂乱的信号,也无需调整或适应运动。 (如欲进一步迁移至虚拟现实的原始游戏版本,请在查看 本文,这是一篇利用超极本™ 创建 Unity 游戏的教程。)
面向第一人称设计: 虚拟现实面临的另一个主要问题是第一人称体验。 如果旧版游戏不是专为第一人称视角而设计,需要考虑如何使第三人称视角变为有趣的第一人称体验,或者如何将游戏转换为第一人称视角。
我的游戏属于混合第一人称游戏,但是包含镜头跟随动作的特性。 我认为它太被动,只是间接的第一人称体验。 因此,我决定将人物与动作直接锁定,而非通过脚本间接跟随。
自然交互式用户界面或控制。 由于摄像头的位置不固定,而典型的屏幕菜单在摄像头中显示,因此,交互式用户界面和控制是另一大问题。 摄像头的位置就是用户正在查看的区域。 务必妥善解决控制、菜单和自然交互性问题。 由于多数控制台控制位于游戏手柄,而非屏幕上,因此,控制台游戏开发人员优势非常明显。 如果您设计的移动游戏使用了触摸控制、复杂键或可用鼠标的屏幕控制,您需要重新思考您的控制方案
我的游戏包含了 4 个控制选项:左转、右转、前进和发射。 移动版游戏使用了屏幕可触控控制,PC 版游戏使用了按键命令。 对于虚拟现实版本,我开始借鉴坦克的控制进行设计: 左控制器顺时针旋转,右控制器逆时针旋转等。测试前,这一切似乎比较可行。 我很快发现我的左拇指想要控制两个旋转方向, 事实证明,我之前用游戏手柄玩游戏,无法克服这种肌肉记忆。 HTC Vive 控制器是手持的,手指可以自由放置(和手柄相似),因此,我进行了重新设计。 左控制手柄负责控制方向,右触发器为汽车提供燃料,利用右拇指发射激光,和手柄中的 A 或 X 按钮用法相似。
利用 360 度: 虚拟现实游戏提供 360 度全方位的视角,如果游戏只允许玩家只向前移动,是对虚拟现实优势的一种浪费。 实际上,这是常规版游戏所面临的问题。 我设计了面向所有敌人的迷你-HUD 显示模式,您可以查看敌人是否从两侧或后面靠近您的战舰。 利用虚拟现实技术,只需向左或向右扭头便能轻松观察周围环境。 我将专门针对前进视角以外的危险进行改进,以充分利用 360 度全方位的虚拟现实体验。
针对示例游戏的虚拟现实重新设计决策包含以下几个方面:
- 采用坐姿玩法,以避免眩晕和双腿抽搐的症状
- 禁用跟踪摄像头,将摄像头锁定车辆的前部
- 抑制旋转,以避免平衡问题,缓和游戏的移动
- 将控制映射到 HTC Vive 控制器中,该控制器与手柄控制的布局类似。
- 创建特性,在更广阔的全景中展示视图和动作
添加 SteamVR 插件
这既不是最简单的操作,也不是最严峻的挑战, 完全取决于代码的老旧程度以及它对 SteamVR 工具套件的兼容。 我将介绍它预期的工作方式以及可能需要您执行的操作。
清理您的游戏,以安装最新版 Unity: 首先,复制原始游戏。 下载最新版 Unity。 打开该版本的旧版游戏。 如有提示,更新 API。 打开游戏并检查控制台是否存在过时的 API,建议您进行更新。
安装 SteamVR 插件: 如果现代版 Unity 能够正常运行游戏,可以下载并导入 SteamVR 插件。 转至 Unity 商店。 搜索 SteamVR 插件, 请选择唯一一个带有熟悉的 Steam 标识的插件。 单击 Import。
选择 All 以导入全部特性、模型和脚本。 所有内容在 SteamVR 文件夹中有序地排列,便于您弄清哪些脚本和资产来自插件。
![]()
测试示例场景: 转至下一步之前,确保 HTC Vive 硬件已经连接且正常运行。 在 Unity 中转至 Project 选项卡,打开 SteamVR 文件夹,选择 Scenes 文件夹,然后选择示例场景。 您将看到一个堆着箱子的场景和 SteamVR 截屏。 进入场景并带上头盔。 您应该看到全面的虚拟现实场景以及 HTC Vive 控制器。 如果您看到了上述场景和控制器,可以断定 SteamVR 插件正常运行。
如果没有 – 尝试创建新的项目: 如果您看不到控制器或 SteamVR 示例无法在头盔中加载,需要创建一个新的空白项目。 导入 SteamVR 工具套件并尝试示例场景。 如果这样仍不奏效,表示您的 Unity 版本或 Steam 硬件出现了问题。
新项目能够正常运行,但是旧项目无法正常运行: 如果全新项目运行良好,但是原始游戏无法兼容 SteamVR 插件,需要再次创建空白项目,安装 SteamVR 工具套件,测试套件是否正常运行,然后将您的游戏迁移至新项目。 为了完成上述操作,首先关闭项目。 在清理后的 Unity 项目中,复制 Assets 文件夹中的全部内容, 转至新项目中的 Asset 文件夹并粘帖。 打开 Unity 项目,开始测试虚拟现实。 这种方法被证实有效。 但是,您的游戏已被损坏, 许多资产的所有项目设置已经丢失,包括从项目组件到游戏资产(如模型、声音文件等)的链接。转至每个资产和每个组件,以确保游戏对象和声音文件正确链接。 这个过程费时费力, 我已经经历过两到三次。 也没有那么糟糕,但是非常琐碎。
更换摄像头: 进行下一步操作之前,请带上 HTC Vive 头盔并正常运行游戏,您应该能够通过转动头部来移动摄像头,还能环顾游戏场景。 但是您仍看不到 HTC Vive 控制器,将虚拟现实应用于摄像头的关键技术还未设置。 下面是实现集成的最关键步骤, 操作起来非常简单,让人难以相信。 为了在应用中添加虚拟现实特性,只需将场景中的主摄像机替换为 SteamVR 文件夹中的 Prefab 摄像头。 就这么简单! 但是,现有摄像头仍有大量的脚本或设置。 不要立即替换摄像头, 建议您以子对象的形式将 Prefab 摄像头放入现有摄像头。
![]()
然后,在现有摄像头的 Properties 面板中关闭摄像头及其全部组件。 在属性面板中,务必取消选中摄像头旁边的选框。
![]()
然后,复制粘帖摄像头组件和设置至 Prefab 内的 Camera Head 或 Camera Eye 子对象。 移动、复制并调整设置,戴上 HTC Vive 头盔后,天空盒和其他组件正常运行。
此时,您的旧版游戏已启用了虚拟现实技术。 您应该能够运行该场景,戴上虚拟现实头盔,环顾游戏四周,将看到手上的控制器。 下面介绍如何实现控制器和脚本兼容现有的游戏逻辑。
控制器脚本
如果您和我一样利用 JavaScript 开发游戏,可能会面临更大的挑战,但是不管采用哪种方法,实现控制器脚本与游戏的兼容相当简单。 如果您的游戏中包含 JavaScript,且需要控制器兼容这些脚本,请查看以下章节。 对场景中的左、右控制器进行以下操作。 将控制器(作为子资产)添加到主摄像头下的 Prefab 摄像头中。 Tracked Controller 脚本位于 Extra 文件夹内。 将它拖放到 Camera Prefab 内的控制器(左)中。 控制器(右)和控制器(左)的操作相同。
![]()
完成后,在 Inspector 中显示 Trigger Pressed 和其它项目,在游戏中使用这些项目时将进行开关检查。
使用控制器的概念和事件侦听器方法相似。 为了捕获一个事件(如触发或触摸板),需要创建一个 C# 脚本。 以下脚本可以通过许多论坛获取。 在本例中,我向您展示了如何创建事件触发器,以启动触发器、释放触发器、触摸控制器板以及从控制器板上抬起手指。
C# 脚本在启动触发器或触摸触摸板时触发事件:
using UnityEngine;
using System.Collections;
public class EasyController : MonoBehaviour {
private SteamVR_TrackedController device;
void Start () {
device.TriggerClicked += Trigger
device.TriggerUnclicked += UnTrigger ;
device.PadTouched += PadTouch;
device.PadUntouched += PadLift;
}
void Trigger(object sender, ClickedEventArgs e)
{
// Place Code Here for Trigger
}
void UnTrigger(object sender, ClickedEventArgs e)
{
// Place Code Here for Lifting Trigger
}
void PadTouch(object sender, ClickedEventArgs e)
{
// Place Code Here for Touching Pad
}
void PadLift(object sender, ClickedEventArgs e)
{
// Place Code Here for UnTouching Pad
}
}
控制器技巧: 开发游戏时,您会发现无法在虚拟现实中区分左右控制器。 建议在任意控制器上添加一个 3D 资产,如灯、球体或有助于区分左右控制器的任何组件, 以达到开发目的。 稍后,在用户界面章节介绍生成图标和标签的方法,以帮助用户了解如何使用控制器。
此外,可以在除开关以外的其他情况下使用触摸板。 本脚本会根据用户敲击触摸板的位置,支持用户控制游戏。 以下脚本支持您通过敲击触摸板左侧和右侧触发活动。
C# 脚本利用来自虚拟现实触摸板的 X 和 Y 值
using UnityEngine;
using System.Collections;
using Valve.VR;
public class myTouchpad : MonoBehaviour
{
SteamVR_Controller.Device device;
SteamVR_TrackedObject controller;
void Start()
{
controller = gameObject.GetComponent<SteamVR_TrackedObject>();
}
void Update()
{
device = SteamVR_Controller.Input((int)controller.index);
//If finger is on touchpad
if (device.GetTouch(SteamVR_Controller.ButtonMask.Touchpad))
{
//Read the touchpad values
touchpad = device.GetAxis(EVRButtonId.k_EButton_SteamVR_Touchpad);
touchstate = device.GetPress(EVRButtonId.k_EButton_SteamVR_Touchpad);
if (touchpad.x < 0)
{
// Add code if left side of controller is touched
}
if (touchpad.x > 0)
{
// Add code if right side of controller is touched
}
}
else
{
// Add code if pad is not touched
}
}
}
C# 和 JavaScript 问题
SteamVR 脚本是以 C# 编写的,导致 HTC Vive 控制器难以甚至无法与现有的 JavaScript 游戏逻辑交互。 利用以下方法可以轻松实现交互,且无需进行转换。 但是,强烈建议您最终将 JavaScript 移植到 C#。
为什么它们彼此缺少交流: 在 Unity 中的脚本存在编译顺序。 可以将 C# 变量迁移至 JavaScript,或者将 JavaScript 迁移至 C# 变量,前提是需要将脚本迁移至 Standard Assets 文件夹。 首先编译这个文件夹,然后应用它们。 但是,由于您需要 JavaScript 来查看 SteamVR 值,Steam 脚本必须位于 Standard Assets 文件夹,且必须第一个进行编译。 如果您移动了它们,会破坏 SteamVR 插件, 因此,无法将 C# 变量传输至 JavaScript。
但是还有其他方法。
一个简单的变通方法: 我突然想起,C# 和 JavaScript 均可以在游戏对象上接受和设置值。 例如,两种脚本类型均可获得和/或定义该场景游戏对象上的标签值。 游戏对象标签本身就是在脚本之间传输的变量。 例如,如果场景中的 LaserCannon 最初被标记为 “notfired”,触摸板事件可以在 C# 中将 LaserCannon.tag 设置为 “fired”。 现有的 JavaScript 能够查找每帧的对象标签值。 当 LaserCannon.tag = “fired”(由 C# 脚本编写)时,JavaScript 将它们挑选出来,并运行发射激光炮的函数。 这个技巧支持 C# 将事件和值传输至 JavaScript,也支持 JavaScript 传输事件和值至 C#。
利用上述 C# 示例介绍如何利用 JavaScript 共享变量。 如果我敲击了触摸板的一侧,相应粒子发射器将在 C# 中变更标识值。 打开粒子发射器、播放声音、碰撞检测和相关点的代码全部位于现有的 JavaScript 中,随附于左右粒子发射器。 因此,首先需要在 C# 中明确这些粒子发射器。 将 “rthrust” 和 “thrust” 声明为 GameObjects。 然后在 Start 部分,将对象 “lthrust” 定义为场景中的左粒子发射器,将 “rthrust” 定义为右粒子发射器。
C# 添加游戏对象至脚本
public class myTouchpad : MonoBehaviour
{
public GameObject rthrust;
public GameObject lthrust;
SteamVR_Controller.Device device;
SteamVR_TrackedObject controller;
void Start()
{
controller = gameObject.GetComponent<SteamVR_TrackedObject>();
rthrust = GameObject.Find("PartSysEngineRight");
lthrust = GameObject.Find("PartSysEngineLeft");
}
然后,在 If 语句内部(该语句确定触控板的左右侧是否被触碰)添加了代码,以更改标签名称 “lthrust” 和 “rthrust”。 (需要指出的是:您可能认为方向反了,但是在空间中,向右转意味着启动左助推器。)
触摸触摸板时,C# 更改对象的标签值
if (touchpad.x < 0)
{
lthrust.tag = "nolthrust";
rthrust.tag = "rthrust";
}
if (touchpad.x > 0)
{
rthrust.tag = "northrust";
lthrust.tag = "lthrust";
}
最终,在随附于每个粒子发射器的 JavaScript 中,在 if 语句结尾处添加了一个附加条件"|| this.gameObject.tag='rthrust'",以检查标签是否等于 C# 脚本设置的值。
JavaScript 基于来自 C# 标签更新执行游戏逻辑
if (Input.GetKey ("left") || this.gameObject.tag=="rthrust"){
GetComponent.<ParticleSystem>().enableEmission= true;
GetComponent.<AudioSource>().PlayOneShot(thrustersright);
}
情况就是如此: C# 和 JavaScript 的交流。 同样的技术可以以相反的方向使用。 这是一个简单的变通解决方案,用于在两个语言和脚本之间启动控制器以及创建基本游戏体验。 完成任务后,建议您将 JavaScript 转换为 C#。
C# 技巧: 如果您刚接触 C#,可以采用以下技巧。 如果对某些数字执行加法、乘法或除法后可能产生小数,需要将变量声明为浮点。 还需要将执行加减乘除运算的数字设置为浮点集。
- 声明如下所示: public float: myVar ;
- 计算如下所示:myVar = (float)otherVar +1f
将 2D 图形用户界面转换为 3D 用户界面
根据创建 Unity 游戏的年代的不同,您可能使用了图形用户界面菜单、按钮或其他屏幕控制和用户界面元素。 我的旧版游戏专为平板电脑和 PC 而设计,因此包含屏幕和图形用户界面。 在启动菜单、标题图形、屏幕得分和警报中使用了图形用户界面。 头盔中禁用上述功能,但是可以在 PC 屏幕上查看。 在 Google 搜索后发现,需要将图形用户界面转换为用户界面,这是因为用户界面能够在游戏的 3D 空间内显示,支持它们在虚拟现实中可见性与互动性,但是图形用户界面不具备这些特性。
面向虚拟现实的用户界面技巧:用户界面的启用方法非常简单,只需在 Hierarchy 面板中右键单击,添加用户界面并选择 Canvas。 在默认情况下,Canvas 设定为屏幕空间,因此全部尺寸和位置属性在 Inspector 中显示为灰色。 但是在 Properties Inspector 中的 Canvas 模块中,您可以将屏幕视图更改为世界空间。
![]()
更改后,可以在场景中的任何地方对用户界面特性进行放置与排列。 Canvas 设置完成后,可以在 canvas 中添加特定的用户界面项目,如按钮、图像或文本项目。
为了进行测试,将文本用户界面元素作为子对象添加至 Canvas 项目(请参阅下图左侧)。 在 inspector 的文本输入字段中输入 "Hello World"。 如果您看不到场景中的字,请转至文本的 Properties Inspector。 在 Paragraph 下面,将水平和垂直溢出更改为溢出。 我发现缩放比例有误,默认的字体太大,溢出设置支持您看清文字(甚至在字体过大的情况下)。 您可以利用 Inspector 中缩放特性缩小 Canvas 或文本。 尝试不同的缩放和字体,以确保字体清晰。 如果您的字体边缘是锯齿状,降低缩放并扩大字体。
![]()
控制器用户界面: 为控制器提供指令是用户界面的一个绝佳使用案例。 标记左、右控制器,为控制器添加图标,以标记控制器的作用和位置。 左侧是我的启动界面,为每台控制器添加了用户界面,帮助用户辨别左右控制器。 在主游戏场景,控制器配备了图形用户界面,以展示如何操作游戏。
![]()
最终,我发现更新游戏并没有那么难,在这一过程中,我思考了如何改进交互,为用户比初始版本更为浸入式、更直观的体验。 仅仅通过练习,我便获取了关于更新原始虚拟现实游戏的更多知识。 请观看以下视频,查看应用虚拟现实技术的游戏示例。
视频地址:https://dn-moderncode.qbox.me/game/updating-older-unity-games-for-vr.mp4
如果您有任何看法或者想和我讨论本次经历,在下方添加评论或通过 Twitter @bobduffy与我联系。