腾讯移动品质中心TMQ [腾讯 TMQ] 漫步 VR——Unity 语音聊天室开发

匿名 · 2016年09月08日 · 774 次阅读

作者:mulcyang;timyan;vaconzhang;matthewchen;barneyliu;

一、背景介绍

VR 是什么

虚拟现实 Virtual Reality 的英语缩写。VR 主要有手机盒子、头盔和一体机三种。

虚拟现实技术是一种可以创建和体验虚拟世界的计算机仿真系统它利用计算机生成一种模拟环境是一种多源信息融合的交互式的三维动态视景和实体行为的系统仿真使用户沉浸到该环境中。

智平测试组耗时一个月的时间,研发了一款基于 Oculus 的 VR 语音聊天室软件,现做个阶段性总结:

研发语言

  • 客户端: unity + C#

  • 语音 sdk: Apollo

  • 后台: python + Flask

硬件环境

  • VR 环境: oculus dk 2

  • 运行平台: windows

二、客户端开发

1、环境配置

引擎 unity 安装: https://unity3d.com/cn/get-unity/download/archive。

Unity5.1 版本后全力支持 VR 开发,所以最好下载 5.1 版本以后的版本。安装过程是一键式安装。

编译 Support 包:unity 可以很方便一直到多个平台上,依赖的即是下载安装对应 unity 版本的 Support-for-Editor。下载安装方式是,打开安装的 unity,随便新建一个工程,打开 File->Build Settings 如下图,如果没有下载平台的 Support-for-Editor,就会有下图中的 Open Download page,点击按钮就会下载,然后就是一键式安装,重启 unity 可生效。

Unity VR 支持:

1)下载 OVRPlugin for Unity5(https://developer.oculus.com/downloads/game-engines/1.3.2/OVRPlugin_for_Unity_5),将压缩包中对应的 unity 版本的文件夹 (如 5.4,5.3) 中的文件解压到 Unity 安装目录的/Editor/Data/VR/Unity/下边;

2)打开 Unity,Edit->Project Settings->players 设置 Virtual Reality Supported 如下图;

3)Oculus VR 的 runtime 版本大于 0.8;

4)设置 VR 的显示模式为 Direct(最新的 runtime 在 Oculus 的安装目录的/Support/oculus-runtime/DirctDisplayConfig.exe 设置)。完成这些步骤以后可以在 Unity 中直将工程运行在 VR 上。

Oculus runtime 安装:0.8 以下的版本可在 https://developer.oculus.com/downloads 直接下载,新版需要在https://developer.oculus.com先下载下载器,然后根据提示下载 800 多兆安装包,等待下载完(公司网络会墙,下载失败,这里建议使用无线网卡接 GuestWifi 使用 ****** 下载),将 VR 的 HDMI 和显卡连接 (电脑至少有独立显卡),Tracker 连 USB3.0(USB2.0 也可以,会影响 VR 的延时),然后就是一键式安装。

注意:1)在安装新版本的 runtime 之前要先卸载旧版本;
2)电脑显卡驱动版本更新为最新版),卸载 Microsoft Visual C++ 2015 相关的组件。
完成上面四个步骤的准备后便可以在 Unity 中尽情绘制你的 VR 世界了。

Oculus sdk 配置流程:

1、下载 OVRPlugin for Unity5(https://developer.oculus.com/downloads/game-engines/1.3.2/OVRPlugin_for_Unity_5),将压缩包中对应的 unity 版本的文件夹 (如 5.4,5.3) 中的文件解压到 Unity 安装目录的/Editor/Data/VR/Unity/下边。

2、下载 Oculus Utilities for Unity5 (https://developer.oculus.com/downloads/game-engines/1.5.0/Oculus_Utilities_for_Unity_5),在 Unity3d 中 import Custom Package,导入下载压缩包中 OculusUtilties 目录下的 OculusUtilities.unitypackage 官方插件包,在我们的工程下会生成 OVR 总目录,看下其中包含的子目录:

  • Editor:包含了新增 Unity 编辑器功能的脚本。
  • Materials:包含了内置图形组件所需的材质。
  • Meshes:包含 OVR 脚本所需的网格,例如 TrackerBounds。
  • Moonlight:包含了开发 GearVR 所需的一些基本的类、材质、组件等。
  • Prefabs:包含了三个支持 VR 场景开发的预置体 OVRCameraRig 相机预置体、OVRTrackerBounds 位置跟踪预置体、OVRPlayerController 角色控制预置体。
  • Scenes:demo 场景。
  • Scripts:包含用来绑定 VR 框架和 Unity 组件的 C# 脚本。
  • Textures:部分组件用到的纹理贴图。

3、将 OVRPlayerController 组件添加到场景中,调整位置,大小,碰撞器等。

4、调整 OVRPlayerController 里的 CharacterController 以适应角色控制的需求。

5、根据需求修改 OVRPlayerController 源码,适配产品提出的需求。

6、焦点控制逻辑在 OVR/Gaze/目录下,目前焦点控制方式支持 Click 和 Gaze 两种方式,Gaze 的响应时间支持自定义,需要添加到 2D 控件的 EventSystem 中,并需要与默认的 StandaloneInputModule 的 enable 保持互斥状态。

官方文档 https://developer.oculus.com/documentation](https://developer3.oculus.com/documentation/)

2、聊天室主要功能模块业务逻辑架构图

2.1 多人实时语音聊天

2.2 客户端/后台用户管理

2.3 语音发送彩蛋

三、聊天室动画功能实现

聊天室动画涉及的内容主要有

  • 1、人物在房间内的走动
  • 2、彩蛋效果的添加
  • 3、多个角色(三个)动画的适配

第一部分:人物在房间内的走动

人物走动部分的实现稍微复杂一些,用到了 2D Blendtree,这是一个什么概念呢?

首先解释下 BlendTree,他是把多个动画进行合并到一个状态中,举个例子:把左前,往前走,右前的动画放到一个 BlendTree 中,用一个 float 型参数控制;好处:减少状态个数

2D 是什么概念呢,还是举个例子:如果我想要用键盘的左右方向键控制动画的左前右转,用上下键控制 idle 走和跑,那么我可以用两个 float 变量分别控制两个方向的动画,这就是 2D BlendTree。

来,让我们为聊天室的走动添加 2D BlendTree 吧!

第一步:添加动画资源,添加如下动画资源(直接导入 fbx 文件)

第二步:调整动画资源中的 animationchip,完成以下的动画片段

这里有个坑提前预防说一下:

1、Loop Time 选项一定选上啊,否则动画只转向不运动啊

2、这个选项勾上啊,要不让动画会跑到地面底下啊

第三步:在状态机中添加 BlendTree(右键添加),双击进入 BlendTree 界面进行设置:1、设置 direction 和 speed 两个参数;2、选择为 2DBlendTree;

3、添加动画;

4、调整动画在坐标系中的位置,便于代码控制

注,可以通过调整 BlendTree 中的 speed 和 direction 控制块预览动画效果。
第四步:代码控制,主要是控制横向的方向和纵向的速度。
代码很简单,就不用多讲了

第二部分:彩蛋效果的添加

彩蛋效果是,按键盘的某些按键可以触发一些动画效果;再聊天室中,也可以通过语音触发一些动画效果,这里做的就是动画效果。

还记得前面提到的动画控制器中的 layer 层嘛,就是通过不同层添加的动画效果

有两种方式添加,第一种方式是,直接从其他状态切换到菜单动画;优点是:非常简单;缺点是:动画不会叠加,要终止之前的动画后,才能播放菜单动画。

第二种是添加一个新的 layer,为新的层添加 Avatar 蒙板,此种添加的好处是,添加的蒙板动画可以和底层动画叠加,可以在底层动画运动的同时做出菜单动作,例如:在走动的过程中挥手。缺点是:要自己做蒙板(其实也很简单啦)

因为聊天室内基本上走动比较少,之前预定是静止时添加彩蛋,需求决定实现,所以采用的是第一种方式,因为比较简单,这里就不赘述了。

我们重点说下第二种方式:

第一步:为状态机添加一个新层,命名为 Wave

第二步:创建一个蒙板,并且配置新添加的层

1、在 assets 的某个文件中创建蒙板:右键》create》avatar mask,我们这里命名为 WaveMask;

2、单击此文件,在右侧 Inspector 中查看,配置蒙板如下图(除右手臂外,其他都禁用)

3、配置 Wave 层如下

第三步:添加彩蛋动画

1、添加挥手动画资源,把动画拖动到 wave 层,设置如下(添加一个空的状态不加任何动画)

2、添加一个 WaveBool 的 bool 型变量控制挥手(初始值设为 false)

3、添加 EmptyState 到 IdleWave 的 transition 的条件控制(为 true 时触发此状态)

4、添加控制代码在初始化代码中设置动画层的权重为 1

通过按下 H 键,控制播放挥手

注意:EmptyState—>IdleWave 的 transition 中下面参数最好设置为 false

IdleWave—>EmptyState 的 transition 中下面参数一定设置为 true

要在 Start() 函数中初始化时,设置层级的权重为 1(不仅是配置 wave 层时层级权重为 1 喔)

最后,点击运行,在按方向键控制运动的同时,按下 H 键,你会发现,你的动画同时挥手了。

#### 第三部分:多个角色(三个)动画的适配
其实 Unity 动画系统 Mecanim 的 retargeting,我觉的有两层意思

第一,就是导入的模型骨骼,和系统内部的内置的骨骼肌肉进行映射关联

第二,是把已知的动画控制器 control 和导入的新模型进行匹配,大家还记得这个图嘛

导入了带有蒙皮的动画模型,生成 avatar,关联动画控制器,新导入的模型就按照控制器的方式动起来了。

### 四、VR 视线交互
Unity 的 Camera 支持 Raycasting 特性,其实际功能是从视野中心向世界场景中投射一条线,射线 Ray 是 object,能够指向视野中的具体的点,可以返回相应的坐标或者触碰到的物体信息,这个信息可以通过 RaycastHit 对象获取到。

代码实例

using UnityEngine;
using System.Collections;

public class eyeControl : MonoBehaviour 
{
Ray ray;RaycastHit hitInfo;
public Transform mainCamera; void Update ()
 {
//声明 ray,方向是主 Camera 的前方
ray = new Ray(mainCamera.transform.position, mainCamera.transform.forward);
//Physics 检测碰撞体,得到被视线碰撞到的物体 objectHit

if (Physics.Raycast (ray, out hitInfo)) {
Transform objectHit = hitInfo.transform;
//针对碰撞体做处理,实现视线交互动作
}
}
}

需要注意的是如果想要 Raycast 能够碰撞对应物体进行控制,需要给 object 添加 Collider,Collider 的形状可以和物体模型完全一致。

也可以设置为特定几何形状的碰撞体,如胶囊形状、球形。

Oculus OVRPlugin Camera 绑定问题

使用 Oculus VR 进行开发调试时,需要使用 Oculus 开发插件里的 OVRPlayerController 来进行 Camera 绑定,否则对应的视线无法从正确的 VR 摄像头发出。

选中 OVRPlayerController 中的 CenterEyeAnchor,使视线和中央视野锚点绑定


视线周围实现了一个进度圈,在视线碰撞到物体时,视线周围会出现对应的进度条提示用户当前存在操作。

实现效果图如下:

### 五、研发小 tips:
####1、实现的难点和注意点:
UI

在传统的非 VR 项目中,UI 通常显示在界面的顶部,用来显示生命值,得分之类的信息,通常被称为 HUD。在 Unity 中,添加 HUD 样式的非剧情型 UI 相对简单,只需要在 UI Canvas 的 Render Mode 中选择 Screen Space——Overlay 或者是 Screen Space-Camera。

但是这种 UI 界面对 VR 基本不适用,我们的眼睛无法聚焦在如此近的物体上,而 Unity VR 中根本就不支持 Screen Space-Overlay。

和前面的 UI 不同的是,我们需要将 UI 放置到环境中,并在 Canvas 的 Render Mode 中选择 World Space 模式。通过这种方式,就可以让用户的眼睛聚焦到 UI 上了。

因为 UI 被放置在环境中,有可能我们通过摄像机看到 UI 会是侧面,或者是反过来的。

如果要使得 UI 一直在摄像机中正常显示,可以将 UI 的 rotation 绑定到 mainCamera 的 rotation 上。如下图,使用 Oculus 时,我们可以将 UI 绑定到 CenterEyeAnchor 上。

GameObject

1、如果是在场景中固有的 GameObject,即将 GameObject 直接拖到 Hierarchy 中的,在其他 GameObject 的脚本中需要使用的该 GameObject 的一些属性是,尽量避免使用 GameObject.Find(),而是在脚本中申明 public GameObject;然后在 Script 直接赋值。

2、GameObject 的 SendMessage() 方法,可以方便调用到绑定在 GameObject 上的脚本里的方法,但是调不到 GameObject 的子物体上的脚本。

3、GameObject.Instantiate(Resources.Load("Dude_CharacterNet")) 
as GameObject 创建物体只能在主线程中创建,不能在子线程中进行。Mono 脚本的主线程有 Start(),UpDate(),Awake() 等。

调试

1、Unity 在 IDE 里面运行工程时,在 Console 界面可以看到开发哥打的 Log 以及系统抛的异常警告等。

2、在编译的时候勾选 play->use player log,运行编译生成的 exe 时,在同级目录EXECNAME_Data\ouyput_log.txt 的文件中保存有 Debug.Log 的信息。

3、在执行 EXE 时实时输出 Debug.Log 的方法:http://blog.csdn.net/cartzhang/article/details/49818953

32 位 dll

当 unity IDE 是 64 位,使用了 32 位 dll 时,在 IDE 里面运行项目时,会提示 dll 找不到的状况 (System.DllNotFoundException)。这时应将工程编译成 exe 后再运行,且编译的时候 Architeture 应选择 X86。

本章完~

原文链接:enter link description here


TMQ(腾讯移动品质中心)是腾讯最早专注在移动 APP 测试的团队
我们专注于移动测试技术精华,饱含腾讯多款亿级 APP 的品质秘密,文章皆独家原创,我们不谈虚的,只谈干货!

扫码关注我们

扫一扫 关注 TMQ
精彩分享不断
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册