Oculus Touch 自定义手势动画
作者:薛冰洁
在VR游戏中,手势交互是实现真实感沉浸感关键部分。以基于Unity的Oculus为例,当使用虚拟形象(Avatar)时,手持的Oculus Touch会在VR呈现蓝色的虚拟手掌,以交互操作调动手势动画来操控游戏。当然,虚拟双手交互已经不再新鲜,游戏设计师开始想摆脱Oculus的束缚,将蓝色的手转换成自己的模型和动画,从而既可以在视觉上100%操控玩家的双手形象,又能自定义更多的手势动画来实现更多交互。今天,我们就来详细解析如何在Oculus中加入自定义的双手模型和动画。
Oculus Avatar的蓝色双手
参考SDK
在今年发布的Unity 5 实例SDK中, Oculus 给出了自定义双手模型的实例,下载地址:https://developer.oculus.com/downloads/package/oculus-sample-framework-for-unity-5-project/ 我们就以此为起点,为大家讲解替换双手自定义模型的原理和步骤。
首先,先参考SDK中的案例。将上述Unity Package下载并导入后,打开Assets/SampleScenes/Hands/CustomHands 场景。在场景的层级中找到游戏物体Dynamic,在子层级中可以找到两个分别叫做CustomHandLeft和CustomHandRight的物体,这就是我们要找的双手。
SDK自带的手模型与动画
观察自定义手物体的层级分布(下图左)。在自定义手的层级中不仅有手的模型和骨骼,并且每段骨骼上都加入了Collider,用于同环境物体碰撞检测,以及自定义抓住物体位置和不同体积的Collider。这些位置和体积的信息在手抓物体时起着关键作用。当然,最重要的是手中控制手势动画的Animator和进行数据传输的Hand脚本(下图右)。换句话说,所有双手和Oculus Touch的交互(包括按键,触摸等)数据都会通过Hand脚本来进行判断和处理,并通手势Animator做出相应的动画。
Oculus Touch 分析
了解了层级和数据流向后,让我们回到Oculus Touch硬件上,来分析最终操控双手动画的数据。手势交互硬件,大体上分为三个区域:中指交互区,食指交互区,和大拇指交互区。其中中指交互区和食指交互区是扳机按钮,而大拇指交互区包括A/B/X/Y按键按钮和操纵杆。
Touch手指交互区
同时,我们参考Unity的Oculu Touch输入系统映射(参考地址:https://docs.unity3d.com/Manual/OculusControllers.html),从手柄中获取的信息包括:
● 中指扳机扣动参数(0.0-1.0)
● 食指扳机扣动参数(0.0-1.0)
● 食指扳机触碰参数(true/false)
● 大拇指点击 A/B/X/Y 按钮 (true/false)
● 大拇指触碰 A/B/X/Y 按钮(true/false)
● 大拇指点击操纵杆按钮 (true/false)
● 大拇指触碰操纵杆按钮 (true/false)
● 大拇指上下左右移动操纵杆数据(二维向量数据)
从功能角度考虑,虽然从大拇指的交互得到的数据很多,但是大拇指对不同按键的点击触碰并不会在手势动画的视觉上有大的区别,大拇指移动操纵杆和点击按钮也都只是细微动作变化,所以在制作手势动画时可以合并,所以我们需要参考的交互数据有:中指扳机扣动参数(float 0.0-1.0),食指扳机扣动参数(float 0.0-1.0),食指扳机触碰参数(boolean true/false),和大拇指触碰交互区参数(boolean true/false)。
双手动画关键帧及调用
由于所有的双手动画都是由参数控制的,所以我们只需在3D软件中制作动画的关键帧(单帧动画),并使用Unity Animator在关键帧之间进行过渡即可达到动画效果。
根据上述从硬件中获得的数据,我们考虑可以在Unity Animator中制作3个叠加的动画层,其中主动画层中解析两个扳机按钮的浮点型数据用来调配抓取握拳等动画,另外两个动画层则分别处理食指和拇指的触碰布尔型数据。
在主动画层中,关键手势包括:中指未扣动扳机(中指扳机参数为0)、中指扣动扳机(中指扳机参数为1)、食指未扣动扳机(食指扳机参数为0)、食指扣动扳机(食指扳机参数为1)。假设拇指和食指都保持触碰的前提下,将这些手势数据进行排列,并我们可以得到如下的图表:
可以看出,食指触碰且未扣动扳机的手势与扣动扳机的手势无视觉差异,所以我们在主动画层只需要三个不同的动画关键帧:扳机触摸手势、食指捏拿手势、握拳手势。定义中指扳机扣动浮点型参数Flex和食指的扳机扣动浮点型参数Pinch,并以这两个参数为坐标建立一个二维笛卡尔类型的树状动画(Blend Tree),用以调用动画,设置如下图:
除此之外,我们还需要处理食指与大拇指的触碰数据。假设中指扳机扣动到底,我们还需要食指未触碰(食指触碰参数为False)、食指触碰(食指触碰参数为true)、大拇指未触碰(拇指触碰参数为False)、和大拇指触碰手势(拇指触碰参数为true),具体所需动画如下图。
分别建立两个动画层处理食指和拇指的动画。以拇指为例,制作拇指抬起、接触的两个关键帧动画,并在Unity中设置单维类型的树状动画(Blend Tree),用定义触碰参数thumbsUp控制做出延时过度动画。值得注意的是,我们需要在主层动画之上叠加拇指动画,所以在动画软件中制作这两个关键帧时,只需要制作拇指骨骼动画,不需要操控其他关节的。具体设置参考下图,食指动画制作方式同理。
制作好Animator后,接下来我们创建脚本,在Update函数中获取Touch的数据,并实时操控Animator,关键代码如下:
// 定义参数flex,获取中指扳机扣动数据
float flex = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, m_controller);
//用flex操控中指扳机动画
m_animator.SetFloat(m_animParamIndexFlex, flex);
// 定义参数pinch,获取食指扳机扣动数据
float pinch = OVRInput.Get (OVRInput.Axis1D.PrimaryIndexTrigger, m_controller);
//用Flex操控食指扳机扣动动画
m_animator.SetFloat ("Pinch", pinch);
// 定义参数point,获取食指触碰数据
bool canPoint = !grabbing || grabPose.AllowPointing;
float point = canPoint ? m_pointBlend : 0.0f;
// 操控食指动画层
m_animator.SetLayerWeight(m_animLayerIndexPoint, point);
// 定义参数thumbsUp,获取拇指触碰数据
bool canThumbsUp = !grabbing || grabPose.AllowThumbsUp;
float thumbsUp = canThumbsUp ? m_thumbsUpBlend : 0.0f;
// 操控拇指动画层
m_animator.SetLayerWeight(m_animLayerIndexThumb, thumbsUp);
至此,我们就实现了基本的双手模型动画操控。
自定义动画延展
在理解基础原理,实现了基础的操控动画之上,我们也可以对双手动画进行延展,做出更多的自定义手势。比如可以在握拳动画中加入中间帧来使动画更流畅,也可以定义其他的抓取物体的手势:如捏取小物体、抓取大物体等不同动画,来最终达到对手势动画的绝对控制。