首先簡(jiǎn)單介紹下Gear VR頭盔上的功能按鍵:
Gear VR 第一代 的功能按鍵全部位于頭盔右側(cè),分別如下: 兩個(gè)音量增減鍵,用來(lái)調(diào)節(jié)手機(jī)的音量,功能和手機(jī)本身的音量鍵完全一樣一個(gè)返回鍵,類似android手機(jī)上的返回鍵,在VR游戲中可以用來(lái)實(shí)現(xiàn),返回主菜單,或者退出游戲之類的操作觸控區(qū)域,可以通過(guò)該區(qū)域識(shí)別玩家的點(diǎn)擊,按下,松開(kāi),雙擊,長(zhǎng)按,以及滑動(dòng)操作,這將是玩家在游戲內(nèi)的主要操作方式(還有一個(gè)就是頭部的轉(zhuǎn)動(dòng))。當(dāng)然從個(gè)人體驗(yàn)來(lái)講,因?yàn)樵搮^(qū)域位于頭部側(cè)方,不同于手機(jī)屏幕,不適合讓玩家通過(guò)滑動(dòng)來(lái)完成太復(fù)雜的操作,超過(guò)上下左右四方向以外的滑動(dòng)識(shí)別,體驗(yàn)都不是很好。



通過(guò)圖片和按鍵講解可以看到,gearVR 與暴風(fēng)魔鏡之流的設(shè)備的區(qū)別就是,它通過(guò)USB口連接手機(jī),然后玩家可以通過(guò)頭戴設(shè)備的輸入完成一些相對(duì)完善的操作。

Unity是如何映射這些功能按鍵的:用Unity創(chuàng)建一個(gè)空項(xiàng)目,GearVRTest_1_2,然后打開(kāi)InputManager:通過(guò)Edit-> Project Settings ->Input

點(diǎn)開(kāi)第一個(gè)Fire1:可以看到這個(gè)名稱對(duì)應(yīng)的按鍵是Left Ctrl(鍵盤(pán)上的左Ctrl鍵),以及mouse 0(鼠標(biāo)左鍵,同時(shí)也是觸屏手機(jī)的單點(diǎn)觸屏操作,也是GearVR touchpad的點(diǎn)擊操作)點(diǎn)開(kāi)第二個(gè)Fire1:可以看到對(duì)應(yīng)的是joystick button 0 (也就是手柄上第二排第一個(gè)按鍵)點(diǎn)開(kāi)唯一的一個(gè)Cancel:可以看到對(duì)應(yīng)的是escape(鍵盤(pán)左上角的esc鍵和安卓手機(jī)和GearVR的返回鍵),以及joystick button 1(手柄上的第二個(gè)按鍵)通過(guò)這些按鍵映射可以看到,我們可以通過(guò)合理的按鍵映射,可以使我們的VR游戲做到手機(jī),頭顯和藍(lán)牙手柄公用一套按鍵邏輯(VR頭顯bing)

通過(guò)代碼獲取玩家操作:我們要實(shí)現(xiàn)的是一個(gè)獲取玩家輸入的腳本,而不處理具體邏輯,行為邏輯代碼通過(guò)委托的方式從該腳本獲取玩家的操作
using UnityEngine;
using System.Collections;
using System;
namespace com.bt.gearVR
{
publicclassVRInput : MonoBehaviour
{
publicenumSwipeDirection
{
NONE,
UP,
DOWN,
LEFT,
RIGHT,
};
publicevent Action OnSwipe;//在touchPad上滑動(dòng)
publicevent Action OnClick;//點(diǎn)擊touchPad
publicevent Action OnDown;//按下
publicevent Action OnUp;//抬起
publicevent Action OnDoubleClick;//雙擊
publicevent Action OnCancel;//點(diǎn)擊touchPad的返回鍵
[SerializeField]privatefloatm_DoubleClickTime=0.3f;//有效雙擊的最大時(shí)間間隔
[SerializeField]privatefloatm_SwipeWidth=0.3f;//有效滑動(dòng)的最小位移
privateVector2 m_MouseDownPosition;//記錄手指按下的位置
privateVector2 m_MouseUpPosition;//記錄手指抬起的位置
privatefloatm_LastMouseUpTime;//上一次手指抬起的時(shí)間,用來(lái)檢測(cè)雙擊
privatefloatm_LastHorizontalValue;
privatefloatm_LastVerticalValue;
publicfloatDoubleClickTime { get {returnm_DoubleClickTime; } }
// Use this for initialization
voidStart()
{
}
// Update is called once per frame
privatevoidUpdate()
{
CheckInput();
}
privatevoidCheckInput()
{
SwipeDirection swipe = SwipeDirection.NONE;
if(Input.GetButtonDown("Fire1"))//手指觸碰到touchPad
{
m_MouseDownPosition =newVector2(Input.mousePosition.x, Input.mousePosition.y);
if(OnDown!=null)
{
OnDown();
}
}
if(Input.GetButtonUp("Fire1"))//手指離開(kāi)touchPad
{
m_MouseUpPosition =newVector2(Input.mousePosition.x, Input.mousePosition.y);
swipe = DetectSwipe();//
}
if(swipe==SwipeDirection.NONE)
{
swipe = DetectKeyboardEmulatedSwipe();
}
if(OnSwipe!=null)
{
OnSwipe(swipe);
}
if(Input.GetButtonUp("Fire1"))
{
if(OnUp!=null)
{
OnUp();
}
if(Time.time-m_LastMouseUpTime
/// 檢測(cè)滑動(dòng)方向
///
/// 滑動(dòng)方向,上下左右
privateSwipeDirection DetectSwipe()
{
Vector2 swipeData = (m_MouseUpPosition - m_MouseDownPosition).normalized;
bool swipeIsVertical = Mathf.Abs(swipeData.x) < m_SwipeWidth;
bool swipeIsHorizontal = Mathf.Abs(swipeData.y) < m_SwipeWidth;
if(swipeData.y>0f && swipeIsVertical)
{
returnSwipeDirection.UP;
}
if(swipeData.y<0&& swipeIsVertical)
{
returnSwipeDirection.DOWN;
}
if(swipeData.x>0&& swipeIsHorizontal)
{
returnSwipeDirection.RIGHT;
}
if(swipeData.x<0&& swipeIsHorizontal)
{
returnSwipeDirection.LEFT;
}
returnSwipeDirection.NONE;
}
//檢測(cè)鍵盤(pán)或者手柄模擬的晃動(dòng)方向
privateSwipeDirection DetectKeyboardEmulatedSwipe()
{
floathorizontal = Input.GetAxis("Horizontal");
floatvertical = Input.GetAxis("Vertical");
bool noHorizontalInputPreviously = Mathf.Abs(m_LastHorizontalValue)
bool noVerticalInputPreviously = Mathf.Abs(m_LastVerticalValue)
m_LastHorizontalValue = horizontal;
m_LastVerticalValue = vertical;
if(vertical > 0f && noVerticalInputPreviously)
returnSwipeDirection.UP;
if(vertical < 0f && noVerticalInputPreviously)
returnSwipeDirection.DOWN;
if(horizontal > 0f && noHorizontalInputPreviously)
returnSwipeDirection.RIGHT;
if(horizontal < 0f && noHorizontalInputPreviously)
returnSwipeDirection.LEFT;
returnSwipeDirection.NONE;
}
privatevoidOnDestroy()
{
OnSwipe =null;
OnClick =null;
OnDoubleClick =null;
OnDown =null;
OnUp =null;
}
}
}
然后是測(cè)試代碼,后面會(huì)上傳項(xiàng)目路徑,會(huì)有一個(gè)控制玩家移動(dòng)的腳本,這里不再貼出
using UnityEngine;
using System.Collections;
namespace com.bt.gearVR
{
publicclassVRInputTest : MonoBehaviour
{
[SerializeField]privateVRInput m_VRInput;
//該腳本激活時(shí),注冊(cè)對(duì)玩家操作的監(jiān)聽(tīng)
voidOnEnable()
{
m_VRInput.OnCancel += OnCalcel;
m_VRInput.OnClick += OnClick;
m_VRInput.OnDown += OnDown;
m_VRInput.OnUp += OnUp;
m_VRInput.OnDoubleClick += OnDoubleClick;
m_VRInput.OnSwipe += OnSwip;
}
voidOnSwip(VRInput.SwipeDirection swipeDirection)
{
if(swipeDirection!=VRInput.SwipeDirection.NONE)
{
Debug.Log("Unity+OnSwipe"+ swipeDirection);
}
}
//取消對(duì)玩家輸入的監(jiān)聽(tīng)
voidOnDisable()
{
m_VRInput.OnCancel -= OnCalcel;
m_VRInput.OnClick -= OnClick;
m_VRInput.OnDown -= OnDown;
m_VRInput.OnUp -= OnUp;
m_VRInput.OnDoubleClick -= OnDoubleClick;
m_VRInput.OnSwipe -= OnSwip;
}
voidOnCalcel()
{
Debug.Log("Unity+OnCancel");
}
voidOnClick()
{
Debug.Log("Unity+OnClick");
}
voidOnDown()
{
Debug.Log("Unity+OnDown");
}
voidOnUp()
{
Debug.Log("Unity+OnUp");
}
voidOnDoubleClick()
{
Debug.Log("Unity+OnDoubleClick");
}
// Use this for initialization
voidStart()
{
}
}
}
注1:GearVR第二代又新加了一個(gè)Home鍵,功能類似于手機(jī)上的Home鍵,游戲內(nèi)用不到這里不再列出
注2:玩家在游戲內(nèi)移動(dòng)時(shí),不可以直接移動(dòng)攝像機(jī)本身,因?yàn)镚earVR限制了攝像機(jī)的移動(dòng),但是可以把攝像機(jī)掛到一個(gè)父節(jié)點(diǎn)下,通過(guò)移動(dòng)父節(jié)點(diǎn)來(lái)移動(dòng)攝像機(jī)注3:本教程中的部分代碼和圖片來(lái)源于Unity官方的教程,VRSamples,感興趣的讀者可以去AssetsStore下載Demo下載地