微软MR开发经验总结

本文的经验总结的原文是我的一位哥们(也是一位技术大咖)雷哥在2017年12月份在微软MR开发者培训的时撰写。当时也是为了分享技术给更多的MR开发者,整理了本经验总结。现在已过去两年多了,我因为工作的变动,也没有更多的关注微软MR的发展。对于和Lei哥一起工作的那一段日子,我甚是怀念。

分享希望对微软MR的开发者有所帮助,仅此而已。

一、把你的应用以3D模型形式放入悬崖小屋。

配置

https://developer.microsoft.com/en-us/windows/mixed-reality/implementing_3d_app_launchers 。一般来说,最好建议开发者把这个页面以及这个页面下面的几个相关链接都看完理解了。

模型、材质、纹理制作要领

https://developer.microsoft.com/en-us/windows/mixed-reality/3d_app_launcher_design_guidance

模型需要的格式为glb,用Blender 这个建模工具可以导出这种格式。不过制作精良肯定需要美术人员制作了。制作工具直接用Blender也可以用其他工具建模然后通过Blender转格式。

Blender要导出Glb格式模型需要装一个 叫 glTF-Blender-Exporter的插件。地址如下:https://github.com/KhronosGroup/glTF-Blender-Exporter

举一个简单的例子:

直接在Blender中建一个UV Sphere 。默认会放在原点(世界坐标为0,0,0),这时候,在Blender的属性面板点击 橙色按钮,表示展开当前物体的基本属性编辑面板。

下面的Rotation 的 X值改为90度。缩放为0.5.表示球的直径为0.5米。放在MixedReality Home中 1米 看起来有点大了。0.3m-0.5m左右即可。不过这个大小还是根据个人根据需求定。

最左面的位置改为图上所示。这时候,前视图如下:

模型导出后按照最上面的链接说明放到工程里,稍微配置一下即可。当应用从 悬崖小屋的 主菜单中拖拽出来后 就是这么一个球。而不是一个平面窗口了。

不需要给开发者主动讲,当开发者问到时,要知道这么个流程讲给他。

关于Blender制作工具和那个gltf Exporter插件的安装,这里就不写了,我们自己私下演练好。

另外,要注意的是系统必须是最新版本,Windows 16299  SDK同样得安装,安装后,Unity导出UWP工程选择最新SDK 会自动识别,其他不用管。

前几天我就是没有安装Windows秋季创意版的最新开发 SDK,以为系统是最新了就行了,导致试了2-3天才成功。(SDK和系统是不一样的,SDK是开发环境包,系统只是运行环境。这个SDK的地址是:https://developer.microsoft.com/zh-cn/windows/downloads/windows-10-sdk,有EXE版和ISO版,下载EXE版直接安装即可。如果是安装最新的VS2017,那么这个SDK会集成一起安装,就不用单独下载安装了)。

二、MotionController的开发相关。

  硬件安装、配置这里就不说了。

 需要参考的页面是:

https://developer.microsoft.com/en-us/windows/mixed-reality/Gestures_and_motion_controllers_in_Unity.html#rendering_the_motion_controller_model_in_unity

这个页面会具体列出Motion Controller 每个输入按钮或轴对应Unity的哪些Keycode或者输入轴。

在Unity里甚至在任何软件实现里。只有两种输入大类:一种叫轴(Axis),一种叫按钮式。

当然另外还有一种是鼠标,这里不做介绍。新兴的HMD编程环境里又多了一种Pointing模式,用户凝视就属于这种。当然Pointing不一定只限定只用头部相机凝视,也可以用手持输入设备的某些特定按键发出类似头部凝视的射线进行Pointing操作。(这里只是简单罗列一下,不做更多介绍了)

轴式的返回值 是一个范围,最大最小。比如GamePad或者MotionController的摇杆 能上能下能左能右。我们可以统一合并一下左右一个轴(Horizontal),上下一个轴(Vertical)。最小值一般为-1,最大值一般为1。

轴在Unity中是通过 Input.GetAxis(“轴名称”)读取。这个轴名称是在Unity的Input配置面板配置。(Unity编辑器 Edit菜单->Project Settings->Input)

按钮式(键盘或者其他输入设备上的按钮全是这种类型)一般有两种状态,三种用法。一种状态为按下(变化),另一种为抬起(变化)。

用法有1、当 按下 事件发生时,2、当 抬起 事件发生时。3、判断当前按钮是否按下状态(保持)。

按钮式的读取在Unity中有两种方法。一种用Input.GetKeyDown(“名称”) 、Input.GetKeyUp(“名称”)、Input.GetKey (“名称”)

另一种用 Input.GetButtonDown(“名称”) 、Input.GetButtonUp(“名称”)、Input.GetButton (“名称”)

当然这两种用哪种都一样,也不知道为什么Unity提供两个,可能是听起来更让用户不混淆吧。

但是这两种 包括轴需要传的参数都是某个具体的名称。实际上真实的设备每个输入是没有名称的。所以Unity给了一种机会让我们在Input配置面板新建一个输入项,用名称表达(更容易表达识别),然后内部跟具体的按键标识对应起来维护在一个映射表中。其实只是Unity做了一个Mapping而已。

Unity的内部维护了一个KeyKode的枚举。列出了所有可能的按键以及给你准备了足够的按钮项来跟真实的设备的输入项对应。(例如列出了键盘的所有按键 和 另外扩展的足够多数量的Joystick Button)

补充一下,其实读取按钮式的值还可以通过这些KeyCode作为参数读取。这几个函数为:

Input.GetKeyDown(KeyCode) 、Input.GetKeyUp(KeyCode)、Input.GetKey (KeyCode).

说完这些。接着要讲一下如何用Unity的API读取MotionController设备的所有按键。

轴一般来说没几个,水平 和 竖直 轴 一般Unity的Input面板都内置了。不过可能有的轴需要根据上面链接中的说明做一下修改。

轴没几个还好说,那么是不是每个Button我都要在面板中设置一个名称。感觉好繁琐。

那么按钮有没有其他好办法。当然可以。可以通过Input.GetKey (KeyCode).这种API直接填入对应的KeyCode就可以读了。这样几乎所有的Button都不用配置直接可以读。

上面图中列的 Button 6 或者 Button 7 之类的 其实对应的KeyCode 分别对应 Unity 的Keycode枚举中的 KeyCode.JoystickButton0 – KeyCode.JoystickButton19. Button 6 对应KeyCode.JoystickButton6  依次类举。

那么看上面图中。现在知道了左手Controller的扳机是否按下对应的是 KeyCode.JoystickButton6。

然后在Unity的脚本中 通过一句 Input.GetKey(KeyCode.JoystickButton6)就可以得到扳机是否按下状态。

如果您想简单实现,自己公司的产品未来不会讲究复杂的架构,不会考虑一套代码中同时支持各种设备的输入,那么用Unity的大一统 输入API(Input.XXX)最简单不过了。

最后,MotionController除了可以用Unity自身的大一统 Input API实现。还可以通过调用Windows 平台特有的接口来实现,(Unity实现了Windows底层的输入,在XR.WSA.Input中)。这个可以参考微软出的 MixedReality Tookit基础开发包。(地址:https://github.com/Microsoft/MixedRealityToolkit-Unity/),这个工具包抽象了MR开发环境的各种输入接口。以接口以及监听的方式读取输入。

两种实现方式的优缺点:

Unity Input.XXX 方式,需要稍微配置,全配置稍微麻烦但还可以接受。如果只是配置部分必须的按键则比较简单实用,方便快捷。适用于简单架构,不考虑太多扩展的应用场景。另外的好处是比较亲民,对于广大做Unity游戏开发的开发者来说上手不费吹灰之力。

另外这种方式属于轮询式读取,需要您在您的脚本里添加Update函数,在Update中轮询,性能损耗自然就稍多费一点点。

MRTK的那种属于调用UWP特定的方式,属于响应式事件开发模式。性能效率比轮询式要高些。但麻烦的就是要引入MRTK工具包或者自己从Unity为Windows UWP平台提供的 XR.WSA.Input命名空间下的一些输入类中实现监听。对未来扩展要求高的,建议引入MRTK工具包到自己工程使用他的一套。

不过坏处是增加了学习成本,引入了额外的不少代码。

最后,一句话:视需求而定。

三、Stationary 模式 和 Room房间模式的区别和注意点。

下面总结来自于实践试验:

Stationary 模式:

InputTracking.Recenter() 只在本模式下有用。因为Stationary模式下其实用户也是可以走动可以转向的,当走的远离原来的位置了,那么就Reset一下位置。回到开始的位置和方位。

应用启动后场景中其他物体位置不会跟相机位置产生突变。而Room模式会相对人眼位置统一下移。 

该模式下如果设置InputTracking.disablePositionalTracking = true; 则视景与相机的相对位置不再变化。但程序可以在运行过程中修改相机位置。

这种模式适用于模拟驾驶,开飞机,等等与传统游戏类似的应用场景。不过仅仅利用VR头盔的转向能力。(代码中根据需要改变相机位置)

也比较适合观看360度视频。也可以原地打僵尸模式(僵尸从四面八方来袭)(代码中不动态修改相机位置,则相机位置被锁定,只能转向)

InputTracking.disablePositionalTracking = false。则不能改变相机位置(因为被MR系统托管)。

该情况下与Room模式差别不大,能行走,能转向。只是没有Room模式的会把场景中其他物体放地平面这个概念而已。

@Stationary 模式下的设计准则:与传统游戏相似。发布前默认相对场景中其他某个物体的高度是多少,运行后相对高度就是多少。除非游戏运行后头盔高度变了(比如站在凳子上了或者蹲下了)。

RoomScale模式:Y = 0平面代表地面。

如果设置InputTracking.disablePositionalTracking = false。(默认为False)该模式下代码修改相机位置无效(因为被MR系统托管)。

则相机的高度由系统托管。会在Unity发布时的相机高度基础之上加上人眼的高度(这个值站立时差不多是1.8米。坐立时差不多是1.4米)。

该状态下,Unity编辑器中内置的相机高度最好是0,让它位于地平面的位置,因为MR系统会选用这个初始位置高度作为地平面。应用运行后,相机位置由MR系统托管(如果允许)。

该状态是房间模式下的正常模式选择。适合在一块舞台区域走动,通过走动观察周围环境,进行交互的一种模式。

如果设置InputTracking.disablePositionalTracking = true。则可以用代码动态修改相机位置。

该状态与Stationary 模式下 的表现类似。只不过有地平面的概念。

@RoomScale模式 下场景设计准则:

场景中所有物体基于Y = 0 这个平面参考摆放。相机最后发布前默认也要放置到Y=0平面上。不要期望在编辑状态的相机与世界其他物体的相对高度与运行时一致。比方说在发布前的编辑场景中相机位置与另一个物体位置齐平。则运行后,那个物体就会在你的脚底所在的高度面上。

相机转向是没法被禁用的。总是由MR系统托管。(InputTracking.disablePositionalTracking 仅仅是禁止跟踪位置)

Immersive Headsets 默认的是RoomScale模式。而Hololens默认的是Stationary模式。

不过需要注意的是:XRDevice.SetTrackingSpaceType(TrackingSpaceType.Stationary);这种调用不要放在Update里一直调用,否则就会产生与系统 初衷 不同 的效果。一般来说

四、其他平台移植到UWP的要领

最费事的莫过于几点:

把跟过去平台相关的代码都删去。工程中不要留有跟过去的平台相关的代码(代码调用或者引用,包括Prefab。仅仅跟Unity相关或者跟自己写的代码可以保留,比如材质、资源、动画、跟老平台不相关的Prefab等都可以保留)

什么叫跟过去平台相关的?就是比如过去是HTC Vive.那么就肯定有引入SteamVR Plugins,这里面包含一些独属于他们自己平台的Native DLL。对于UWP来说,并不适用,甚至根本就无法使用。所以凡是直接或间接引用这些DLL的脚本要么改掉,要么删掉。

建议:自己写的业务类脚本 并且跟这些DLL无任何关系的 可以保留。如有关系的,建议函数保留,将调用关系注释掉或者删掉。然后再在UWP模式下逐渐恢复自己的工程。直至能在Unity中能运行不报错为止。

另外如果您的应用中使用了其他带有Native DLL的第三方插件,那么也是一个大麻烦,发布到UWP,肯定不能运行,因为这些插件在您过去的环境里是用的是Win32模式下的DLL。UWP的 DLL和 Win32的DLL不相互通用。因此要么重新检查一下您现在使用的这个插件是否有UWP版本,如果有,还好。没有就只能重写或者更换其他支持UWP的插件了。(纯C#代码写的插件一般都支持)

另外就是输入控制的移植。如果您过去使用的是Unity 的 Input.XXX来读取的,那么按上面的MotionController部分讲的 对应的键值替换掉即可。如果过去使用的是过去平台相关的但也是响应式的 与XR.WSA.Input的监听模式很像的,对应的换成UWP对应的这种就行了。保持一致性 移植,相对可以改的最少。

五、优化相关。

其实MR应用大多是Unity层面的优化,以及写代码功力和经验方面的优化。

Unity方面,微软也列出了一些建议。比如:

https://developer.microsoft.com/en-us/windows/mixed-reality/performance_recommendations_for_unity

或者Unity官方文档提供的 :

https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity.html

其他,既然是Unity开发者也是有经验的开发者,去网上查找即可,不再一一罗列。

性能测试,Unity 和 VS都有性能测试工具。具体使用方法,请搜索。

六、Unity PlayerSettings For UWP

查看 https://docs.unity3d.com/Manual/class-PlayerSettingsWSA.html

Unity导出UWP工程 可以使用.net scriptend 也可以使用 IL2CPP scriptend

七、Unity提供的与UWP或者Hololens相关的文档介绍。

https://docs.unity3d.com/Manual/Windows.html

文中的一些技术笔记、工具、链接可能已经过时失效,我暂时也没有精力验证和更新,也不知是否有必要?所以还请谅解。

至此本文完!

有疑问可以文章底部留言。

为您推荐

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注