原文链接:点击打开链接
作者是用iPhone适配的,我是用的Android手机且由于SDK版本的关系所以会有些小细节不一样。
游戏工程地址:忍者飞镖
GoogleVR SDK for Unity:SDK 或 https://developers.google.com/vr/
欢迎……来到三维空间!
可以很负责任的说每个人都在期待着虚拟现实体验,自从现代经典电影《割草者》的上映,向我们预示了一个可喜的未来……额,通过虚拟现实
我们可以拥有心灵感应类似的力量。
在这篇指导内,你会通过谷歌纸盒学习将一个简单的Unity游戏变成一个VR游戏来帮助人类准备好心灵感应的能力和虚拟现实。此文,你会学习到:
1.集成一个VR摄像机到你的工程
2.修改UI元素让它们工作在VR模式
3.让按钮在VR模式下可以选中
4.在运行中以编程的方式完成VR和普通模式的切换
Note:在写的时候,通过VR拥有思维控制和成为超人一般的能力不是一个可行的科技,不管《割草者》展示给我们的那些。因此,直到有了思维控制API,
坚持掌握先驱者(VR技术~_~)。
什么是谷歌纸盒?
原理上创建一个VR体验是很直接的。替换掉显示在你屏幕上的单个图片,变成两张图片~
他们来自两个摄像机隔开一点距离放置,用户通过左眼看左边的图片,反之亦然,创建出有深度的景象。
而且,通过正确的传感器数据,你可以检测到用户面朝哪个方向。结合到你创建的3D场景,你会得到一个沉浸式的体验。
通常,这需要一些相当复杂精妙的硬件去显示两个高分辨率的图像,连同追踪用户的头部,把所有的内容放进一个小且足够明亮设备中不会伤害你的颈部。
然而,结果证明这样一个高科技先进设备正是你口袋的arm架构的手机。几乎所有的智能手机,它是纸盒之前的idea。你的手机有显示和传感器可以把他们变成
一个可用的VR设备,只需要一个纸盒子和两个塑料透镜。
开始吧
Note:你需要使用不少的UnityGUI的一些知识,如果你从来没有接触过,你可能需要学习一下UGUI
为了开始制作你自己的VR游戏,你需要以下内容:
1.5.x版本的Unity开发套件
2.一部智能手机,iPhone 5或Android 4.1以后。
3.一个装手机的VR设备
Note:如果你对Unity不熟悉也不了解相应借口,你可以去Unity官网进行学习。
没有VR设备我能否进行开发?
当然阔以,运行在手机上就是这样滴:
当播放的时候如果你盯住屏幕,你可以近似的得到一个3D的体验。如果你移动你的手机,通过绑住你的头你可以控制它,模拟点击通过敲击屏幕(装进盒子
了怎么敲? 所以最好还是有个蓝牙手柄……当然不装进去点着玩儿也不错)~
尽管你可以通过使用VR一体机玩游戏去感受它是什么感觉,但并不意味着你应该这样做。有点像吃巧克力和听别人说巧克力是什么味道。当然,你会得到一般
的印象,但并不一样。
长话短说:如果你没有耐心等到VR设备的到来,你仍然可以学习这篇向导,但有正确的装备去操作你会得到更多。
例子游戏——忍者回旋镖
花点儿时间玩玩你的例子游戏,下载链接在文章开始处。
接下来打开你的Unity,选择Open Project打开StarterNinja文件夹,然后打开NinjaAttack工程。
选择Assets目录下的MainScene打开,然后开始玩游戏。
左边是你的忍者,怪物会穿过屏幕,点击屏幕上的任何地方发射忍者飞镖打倒他们!打倒20个怪物你就赢了,但是如果怪物到达了左边的红色区域,你就输了。
从谷歌VR的SDK开始
首先你需要下载SDK,文章开始处有找寻相应SDK的方法,自行下载~
接下来导入到你的工程,从Unity的菜单处点击Asset\Import Package\Custom Package……之后选择GoogleVRForUnity.unitypackgage,也就是你下载的SDK。
确认所有的都选中了,去掉选中DemoScenes文件夹(就是一些例子导不导入随意),然后点击导入按钮。
乘热打铁
为了让你的游戏成为VR模式,你需要展示一些快速的暗招。
在GoogleVR\Prefabs文件夹中,将GvrViewerMain预制体拖到Hierachy中。设置它的坐标几乎是你忍者角色的坐标(-5.53, 1.13, 0.122)
和Y轴旋转90度
你会意识到这个坐标比忍者的中心位置高一点,实际上你是通过它的眼睛看出去的。GvrViewerMain默认以第一个主相机为目标来进行相机的分身
处理所以你需要将主相机也调整到对应的位置,让主相机刚好渲染出角色视角看到的场景(可以通过修改之前的相机到Perspective模式,推荐新建
一个相机然后关闭正交相机,之后动态切换相机的时候还需要默认的相机)。
现在在你的编辑器中运行游戏,你应该看到场景被重新组装成了3D的!如果此时你按住Alt键然后晃动你的鼠标,相机会像你转动你的头一样运动。
把这个场景在你的安卓设备上跑起来
在你的编辑器里运行游戏是好的,但是我最后一次检查了没有合适的VR设备和好的电脑这样运行起来是痛苦的,因此我们工作在手机上。
1.选中File\Build Settings——Android应该被选择为你的默认平台
2.点击Player Settings到达inspector界面
3.在Resolution and Presentation下选择Default Orientation为Landscape Left
4.在Other Settings里把Bundle Identifier改成合适的组织名称(com.xxx.xxxx 默认的编译会报错)
连接上你的手机和电脑之后,选择Build and Run之后选择输出文件夹,随意路径。
Unity会导出你的工程,之后会将会发布到你的手机上,装进你的VR设备调节好视距之类的就开始体验吧。
Note:如果发布到手机,手机启动应用时出现了什么错误,你可以用Android的IDE查看相应的log信息,或者adb logcat > d:\logcat.txt
将运行的log抓取出来然后判断是哪里出错了。
重新制作这个游戏!
总的来说可以观看你的游戏世界是很棒的,但是你需要玩儿它。独特的讲,你想要往面朝的地方发射忍者飞镖,那是你刚开始玩儿游戏所体会到的。
UI层面,纸盒支持一个按钮(我用的淘宝上买的,有蓝牙手柄~),会感受到限制,但是结合到你头部的动作跟踪,它允许你更加完整的交互。
在游戏中,你会检测用户的GvrViewer.Instance.VRModeEnabled属性。你会检查GvrViewer.Instance.Triggered按钮是否
按下的属性。如果都为true,你就会在面朝的方向发射一个飞镖。
打开你的 NinjaStarLauncher.cs 脚本,你可以在GameLogic 游戏物体上找到。
创建一个新的私有变量:
private Vector3 _vrShooterOffset;
Start()
方法中初始化它:
_vrShooterOffset = new Vector3(0.0f, -0.4f, 1.0f);
用以下代码替换你的 Update():
void Update () {
//1
if (GvrViewer.Instance.VRModeEnabled && GvrViewer.Instance.Triggered && !_gameController.isGameOver) {
GameObject vrLauncher = GvrViewer.Instance.GetComponentInChildren().gameObject;
// 2
LaunchNinjaStarFrom(vrLauncher, _vrShooterOffset);
} else if (!GvrViewer.Instance.VRModeEnabled && Input.GetButtonDown("Fire1") &&
!_gameController.isGameOver) {
// This is the same code as before
Vector3 mouseLoc = Input.mousePosition;
Vector3 worldMouseLoc = Camera.main.ScreenToWorldPoint(mouseLoc);
worldMouseLoc.y = ninja.transform.position.y;
ninja.transform.LookAt(worldMouseLoc);
LaunchNinjaStarFrom(ninja, _shooterOffset);
}
}
它你会让你的游戏运行起来,看看Update方法做了啥:
1.首先检测你是否处于VR模式且用户点击了按钮被单例模式的GvrViewer.Instance
检测属性的变化。
2.之后你可以调用LaunchNinjaStarFrom()
方法去实例化一个忍者飞镖,传进去两个参数:
A.首先是你的头部游戏对象,谷歌VR库为你准备好了,应该已经指向了正确的方向.
B.第二个是一个轻度的偏移,因此这个方法实例化一个忍者飞镖在你的前面一点下方一点,看起来更加的正常,总不希望飞镖
从你的眼睛里面发射出来吧?很酷,但很奇怪~
你的飞镖在被生成出来的时候就注定要在你面朝的方向飞走,它会朝着正确的地方开火~
换个方式尝试!在这个点上,你可以旋转你的头部去射击坏人,输还是赢逻辑仍然有用。
修复Game Over菜单
正如你所观察到的那样,当游戏结束的时候留下的还是之前的游戏结束菜单。没有以正确的3D模式去显示,所以没法去点击它。
游戏当前使用的是画布显示——UGUI里面的——去显示Game Over,总是显示在游戏窗口的最上层。
这个画布对很多的游戏都是很实用的,它会自动缩放自身去适应你的屏幕,不管你的游戏摄像机在做什么,它对不同的屏幕尺寸是友好的。
但在这种情况下,你需要一个GUI画布存在于世界坐标中,会被3D场景渲染,同样因为你不想让它随着摄像机的移动而没有变化。
你想要你的用户可以朝上看朝下看,看向不同的UI元素通过点击按钮激活它。
创建一个新的Canvas
在Hierachy中选中并复制一份GameOverCanvas
将副本重命名为VRGameOverCanvas,为了与原始的作区分把其子对象GameOverTxt重命名成 VRGameOverTxt.
在VRGameOverCanvas的组件Canvas中,把Render Mode变成World Space
在Rect Transform组件中,改变属性Position为(-2.24, 1.1, 0.07),和Y Rotation为90.
最后,把X和Y的缩放为0.009.所有设置好了之后为像这样滴:
之后,你应该看见两个画布会重叠(游戏没有运行的时候):
这些值是从哪里来的?老实说,我也是瞎搞了很久直到把他们弄起来在纸盒内看起来比较好。
有时候编程更像是一门艺术而不是科学……
支持两种画布
接下来,你要修改GameController.cs去意识到两种画布的存在。打开脚本并修改,这个脚本同样挂载在GameLogic游戏物体上。
在类中加上这两个公有变量:
public Canvas VRGameOverCanvas;
public Text VRGameOverTxt;
将 GameOver()
public void GameOver(bool didIWin) {
isGameOver = true;
_didIWin = didIWin;
string finalTxt = (_didIWin) ? "You won!" : "Too bad";
if (GvrViewer.Instance.VRModeEnabled) {
VRGameOverCanvas.enabled = true;
VRGameOverTxt.text = finalTxt;
} else {
gameOverCanvas.enabled = true;
gameOverTxt.text = finalTxt;
}
}
这会显示合适的画布和文字对象,取决于你是否处于VR模式 ( GvrViewer.Instance.VRModeEnabled
).
在你保存了你的脚本之后,你需要为你的新变量赋上正确的值。
找到GameController 脚本,像下图一样赋值。
Note:你是否在想你为什么要支持两种画布而不是只是替换到现在有的呢?因为最终你需要支持两种模式,调整状态!
如果你现在运行你的游戏,你会看见合适的VR模式游戏结束界面。可以朝上看朝下看,看不同的部分,当你点击Play Again按钮时就会消失。
Note:至少,理论上看起来应该是这样的。
练习中,5.3.4p2的Unity会有一个BUG,世界坐标系的GUI画布不会渲染到RenderTexture(这啥?),所以谷歌的SDK使用扭曲修正——你在
两个摄像机中看见的鱼眼效果。
作为一个暂时的变通方案,你可以在GvrViewerMain对象的Gvr Viewer组件上改变Distortion Correction为None。
这意味着你可能会看见轻微的扭曲,当你把游戏放入你的VR设备区看的时候,但至少你能够看见你的GUI。
添加凝视输入
幸运滴,Unity内置支持相机中心点作为一个鼠标,当使用世界坐标系的GUI的时候,但你需要提供一个附加的脚本让其在VR模式下工作。
首先,把你的主相机放置在GvrViewerMain对象的子节点上:
选择你的VRGameOverCanvas 对象,你会看见Event Camera选项,将VR Main Camera选中(之前你修改的主相机)。
点击在Hierachy中的EventSystem 对象,添加GazeInputModule 脚本。这是一个让UGUI明白谷歌VR相机是怎么工作的。
选中VR Mode Only选项框,因为你只是想要工作在VR模式而不是普通的版本。
最终把你的Gaze InputModule往上移保证它的优先级,最终看起来会像这样:
现在尝试一下!当你聚焦到PlayAgain按钮上,它应该变成绿色,然后点击一下屏幕就会开始新的游戏!
细微的游戏调整
因此,也许你发现在VR模式的这个版本很难。这个部分是因为在VR模式下你减小了视野,在你看向错误的方向的时候敌人也更容易溜走。
同时,你不能和以前一样快速的转变你的方向去瞄准,物理上被你的脖子转动速度所限制。
你不想让选择玩VR模式的玩家遭罪!所有你怎么修正这个呢?
额,我会建议减少敌人的速度。你会怎么想?
选中你在Prefab文件夹内的EvilSlimeEnemy预制体,打开挂载上面的EnemyMover.cs脚本。加上以下代码,
正好在之前设置thisSpeed的地方:
if (GvrViewer.Instance.VRModeEnabled) {
thisSpeed *= 0.85f;
}
这会让你的敌人更集中一点,如果你想让它们空出一些空间,找到 EnemySpawner.cs
在你设置了launchInterval
之后:
if (GvrViewer.Instance.VRModeEnabled) {
launchInterval *= 1.1f;
}
这会让你的VR游戏更加容易尝试——足够让你的玩家选择这样玩儿不感到坑爹。
修改屏幕上显示的分数
其他的UI元素你需要在屏幕上定位的是分数对象,你将尝试轻微的不同方式。仍然需要出现在在合适的VR模式,你想要修改它
在你的摄像机看向任何地方的时候。
想象把你的分数投影在一片持续运动的玻璃上,因此它总是与你看向的地方保持2米远的距离。这是你将要创建的效果。
你将要实现它通过另一个渲染在3D世界的画布,将它变成GoogleVR Head 对象的子物体(这是原文作者的实现方式,我没弄成
不知道原文作者的效果是不是分数面板跟着头一起动的,我的是固定在一个地方的。因为它的SDK貌似是有一个相机的,可以放置到
头部跟踪,而我的SDK是没有相机,通过渲染主相机达到的,没法去跟踪头部运动~)。
然后在GvrViewerMain中右击新建一个Canvas,重命名为VRScoreCanvas将Render Mode变为World Space。
设置以下值:
1.Position:(0,1,2.5)
2.Width:400, Height: 100
3.Rotation:(0,0,0)
4.Scale:(0.0115, 0.0115, 1)
当你设置好了之后,应该像下面一样:
在你的VRScoreCanvas右击添加UI\Text子物体,重命名为VRScoreTxt。把锚点设置为左上方,Position的值为(150, -65, 0)和Width为60。
在文本组件中,设置字体大小为18对其方式为右对齐,值为999,完成后像这样:
它也许看起来很奇怪的位于你屏幕的中央,但是在VR模式,你看见的比你实际世界要少很多,所以结果上这文字是很靠近你的视野边界的。
自由调试让它看起来在你的手机上合适。
接下来添加这个物体来显示你的分数,过程和你显示GameOver文字很类似。
打开GameController.cs然后添加一个公有变量:
public Text VRScoreTxt;
然后,你会每次更新你的VRScoreTxt去更新分数,在ResetGame()方法中添加这一行,刚好在你更新scoreTxt之后:
VRScoreTxt.text = "--";
接下来添加这一行在GotOne()方法里面,刚好在你更新scoreTxt之后:
VRScoreTxt.text = "" + _currScore;
保存你的脚本,回到Unity你会发现GameLogic游戏对象上的GameController组件有了空处,将VRScoreTxt赋值。
进入和跳出VR模式
自从你的游戏工作在普通模式和VR模式,你需要让用户有能力去切换他们。
一个UI很直接就可以实现,你添加一个简单的按钮,可以切换VR模式和普通模式。福利来了,谷歌VR的SDK自己有一个返回按钮,你可以
用它来返回普通模式。
试一下!
首先,你将要添加一些代码去切换VR模式。选中GameLogic游戏物体,点击添加组件,新增CardboardSwapper脚本(我新增这个脚本会编译不过,
所以搞成了Changer脚本,不知道是不是命名被sdk的哪里重复了)。
打开,然后替换以下代码:
public class CardboardSwapper : MonoBehaviour {
public GameObject[] cardboardObjects;
public GameObject[] monoObjects;
// Turn on or off VR mode
void ActivateVRMode(bool goToVR) {
foreach (GameObject cardboardThing in cardboardObjects) {
cardboardThing.SetActive(goToVR);
}
foreach (GameObject monoThing in monoObjects) {
monoThing.SetActive(!goToVR);
}
GvrViewer.Instance.VRModeEnabled = goToVR;
// Tell the game over screen to redisplay itself if necessary
gameObject.GetComponent().RefreshGameOver();
}
public void Switch() {
ActivateVRMode(!GvrViewer.Instance.VRModeEnabled);
}
void Update () {
if (GvrViewer.Instance.BackButtonPressed) {
Switch();
}
}
void Start() {
ActivateVRMode(false);
}
}
这个类中最重要的方法是ActivateVRMode当你改变了GvrViewr.Instance.VRModeEnable,就激活了VR模式。
剩下的逻辑就是激活或关闭在场景中不同的游戏物体,取决于你是否在VR模式。调用Start()方法中调用ActivateVRMode(false)
就会让你的游戏以普通模式开始。
你同样会意识到当点击返回按钮时会调用Switch()方法,你可以在编辑器中用esc键模式模拟GvrViewer.Instance.BackButtonPressed,一个
让你毫无疑问很趁手的测试特性。
你需要添加更多的一些游戏逻辑到你的GameController脚本里面让其正确选择游戏结束的模式。
打开GameController.cs添加如下方法:
public void RefreshGameOver() {
gameOverCanvas.enabled = false;
VRGameOverCanvas.enabled = false;
if (isGameOver) {
GameOver(_didIWin);
}
}
保存所有,然后回到Unity给两种游戏物体数组赋值,就是你创建的转换开关类,挂在GameLogic上:
在Prefabs文件夹下找到CardboardButton推到Canvas的子节点,设置Position(-50, 50, 0):
在你按钮的游戏物体的下方添加点击时间去调用CardboardSwapper.Switch()方法,如下:
再次尝试你的游戏,就可以切换场景了:
祝贺!你像个老板样切换模式了!