任何游戲都應該提供給用戶交互的方式,可以想象一個沒有任何交互的游戲是什么樣的。如果沒有輸入系統帶來的,用戶與游戲的交互那么游戲將不再是游戲,因為玩家將不能進行任何操作,那還怎么玩呢?Unity作為一個號稱跨平台性能最好的游戲引擎,那么它給我們提供了哪些輸入呢?為了能夠更好的整理Unity的輸入系統,暫時將其分為移動平台的輸入和傳統的輸入。
移動平台的輸入
在手機和pad上主要的輸入方式就是:觸摸,重力加速器,虛擬鍵盤等。Unity將這些操作都封裝到了UnityEngine.Input和UnityEngine.TouchScreenKeyboard這兩個類里。
觸摸
觸摸相關的函數
在UnityEngine.Input類中為我們提供了觸摸相關的函數,以及在UnityEngine命名空間中涉及到的類,相關函數和類如下表:
函數表:
函數名 | 作用 |
---|---|
multiTouchEnabled | 是否啟用多點觸摸 |
simulateMouseWithTouches | 啟用/禁用使用觸碰仿真鼠標的操作 |
touchCount | 在此幀中的觸摸數量 |
touches | 在上一幀中的觸摸點(Touch)信息 |
touchSupported | 標示當前運行此程序的設備是否支持觸摸 |
GetTouch | 根據觸摸點的索引獲取觸摸點的信息 |
類表:
類名 | 作用 |
---|---|
Touch | 觸摸點信息 |
TouchPhase | 觸摸點的狀態信息 |
觸摸的一個示例
此示例主要實現如下三個功能:
- 顯示觸摸點的屬性信息
- 顯示點擊到的物體
- 測試仿真鼠標
示例代碼,如下:
public class TouchInputTest : MonoBehaviour {
public Camera m_mainCamera = null;
private bool m_isRatating = false;
private GameObject m_objRatation = null;
private float m_nSpeedRatation = 30.0f;
private float m_nTotalAngle = 0;
private const int nMaxSelectedSize = 5;
private string[] m_strSelectedGameObject = new string [nMaxSelectedSize]{"", "", "", "", ""};
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
//將是否支持觸碰
if (Input.touchSupported)
{
print("Number of touches:" + Input.touchCount);
print("Length of touches:" + Input.touches.Length);
print("---------------------------------------------");
for (int i = 0; i < Input.touches.Length; ++i )
{
Touch tch = Input.touches[i];
//打印觸摸點的信息
print("Index:" + tch.fingerId);
print("State:" + tch.phase.ToString());
print("Positon:" + tch.position);
print("TapCount:" + tch.tapCount);
print("deltaPosition:" + tch.deltaPosition);
print("deltaTime:" + tch.deltaTime);
//通過射線拾取物體
if (m_mainCamera != null)
{
Ray ray = m_mainCamera.ScreenPointToRay(tch.position);
RaycastHit rayHitInfo;
Physics.Raycast(ray, out rayHitInfo);
if (rayHitInfo.transform)
{
if (0 == i)
m_objRatation = rayHitInfo.transform.gameObject;
m_strSelectedGameObject[i] = rayHitInfo.transform.name;
}
else
{
m_strSelectedGameObject[i] = "";
}
}
else
{
print("Main camera is null.");
}
}
for (int i = Input.touches.Length; i < nMaxSelectedSize; ++i)
{
m_strSelectedGameObject[i] = "";
}
//檢測是否支持使用觸摸仿真鼠標操作。1個手指操作為左鍵,2個手指操作代表右鍵,3:個手指代表中鍵
//這你通過兩個手指單機,來模仿鼠標右鍵單擊,單兩個手指單擊時,選中的物體沿Y軸旋轉360度。
if (Input.simulateMouseWithTouches)
{
if (Input.GetMouseButton(1) && !m_isRatating)
{
m_isRatating = true;
}
}
}
else
{
print("touch is not supported.");
return;
}
if (m_isRatating)
{
float yRotation = m_nSpeedRatation * Time.deltaTime;
if (m_nTotalAngle >= 360)
{
m_isRatating = false;
m_nTotalAngle = 0;
}
m_nTotalAngle += yRotation;
m_objRatation.transform.Rotate(0, yRotation, 0);
}
}
void OnGUI()
{
if (GUILayout.RepeatButton("Enable/Disable MulitTouch(" + Input.multiTouchEnabled.ToString() + ")"))
{
Input.multiTouchEnabled = !Input.multiTouchEnabled;
}
if (GUILayout.RepeatButton("Enable/Disable simulateMouseWithTouches(" + Input.simulateMouseWithTouches.ToString() + ")"))
{
Input.simulateMouseWithTouches = !Input.simulateMouseWithTouches;
}
for (int i = 0; i < nMaxSelectedSize; ++i)
{
GUILayout.Label("Index(" + i + "):" + m_strSelectedGameObject[i].ToString());
}
}
}
重力加速器
當我們垂直正對手機(手機屏幕對着我們的臉)的時候,重力什么怎么樣呢?它有哪些方向,以及在每個方向上的加速度是多少呢?
現在的手機或者pad一般都能對三個方向的力進行采集,分別是X,Y和Z。X的正方向水平向左,Y的正方向垂直向上,Z的正方向面向自己。為了能更形象的說明這些問題,我簡單的畫了一個圖,下圖為我們垂直正對手機時候的重力加速圖:
上圖中的兩個圓都表示的是兩個3D球體的前視圖。由上圖看,當我們垂直正對手機時候,中間的藍色球體受到了來自地球-9.8米/秒的二次方加速度,那么這時候我們訪問Input.acceleration.y的時候,其值就是一個接近-9.8的值,其他軸上都趨近於0。
在Unity中訪問重力加速器的信息
重力加速器的信息被放在了UnityEngine.Input中。具體函數或字段見下表:
函數名 | 作用 |
---|---|
acceleration | 存放當前3個軸上感應到的加速度 |
accelerationEvents | 在上一幀期間Unity引擎采集到的所有重力加速器信息(每個方向上的加速度和時間增量) |
accelerationEventCount | 在上一幀期間Unity引擎采集到的所有重力加速度的次數 |
重力加速器示例
本示例就一個功能,我們在場景中放一個Cube,當手機向指定方向偏轉時,Cube就向指定方向移動。示例代碼如下:
public class AccInputTest : MonoBehaviour {
private float speed = 1.5f;
//控制信息的打印時間
private float fInterval = 1000;
private float fCurTime = 0;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
Vector3 dir = Vector3.zero;
//unity的X軸的正方向是向左的
dir.x = -Input.acceleration.x;
dir.y = Input.acceleration.y;
dir.z = 0;
if (fCurTime >= fInterval)
{
Debug.Log("X:" + Input.acceleration.x + " Y:" + Input.acceleration.y + " Z:" + Input.acceleration.z);
fCurTime = 0;
}
dir *= Time.deltaTime;
fCurTime += Time.deltaTime;
transform.Translate (dir * speed);
}
}
虛擬鍵盤
在游戲中我們點擊輸入框(NGUI或Unity自帶控件)都會自動彈出虛擬鍵盤,當然我們也可以手動的彈出虛擬鍵盤,下面主要介紹如何手動的彈出鍵盤。鍵盤的操作被Unity放在了UnityEngine.TouchScreenKeyboard中。鍵盤的操作非常簡單,下面以一個簡單的示例來說明如何打開一個虛擬鍵盤,以及獲取輸入的數據。在一個腳本里的OnGUI函數中輸入如下代碼:
void OnGUI()
{
TouchScreenKeyboard.hideInput = true;
if (GUILayout.Button ("KeyBoard:ASCIICapable")) {
keyboard = TouchScreenKeyboard.Open("", TouchScreenKeyboardType.ASCIICapable, false, false, false, false);
}
if (GUILayout.Button ("KeyBoard:Default")) {
keyboard = TouchScreenKeyboard.Open("", TouchScreenKeyboardType.Default, false, false, false, false);
}
if (GUILayout.Button ("KeyBoard:EmailAddress")) {
keyboard = TouchScreenKeyboard.Open("", TouchScreenKeyboardType.EmailAddress, false, false, false, false);
}
if (GUILayout.Button ("KeyBoard:NamePhonePad")) {
keyboard = TouchScreenKeyboard.Open("", TouchScreenKeyboardType.NamePhonePad, false, false, false, false);
}
if (GUILayout.Button ("KeyBoard:NumberPad")) {
keyboard = TouchScreenKeyboard.Open("", TouchScreenKeyboardType.NumberPad, false, false, false, false);
}
if (GUILayout.Button ("KeyBoard:NumbersAndPunctuation")) {
keyboard = TouchScreenKeyboard.Open("", TouchScreenKeyboardType.NumbersAndPunctuation, false, false, false, false);
}
if (GUILayout.Button ("KeyBoard:PhonePad")) {
keyboard = TouchScreenKeyboard.Open("", TouchScreenKeyboardType.PhonePad, false, false, false, false);
}
if (GUILayout.Button ("KeyBoard:URL")) {
keyboard = TouchScreenKeyboard.Open("", TouchScreenKeyboardType.URL, false, false, false, false);
}
GUILayout.Label("");
if (keyboard != null) {
GUILayout.Label (keyboard.text);
} else {
GUILayout.Label ("keyboard is null.");
}
}
其他輸入
Ps:在移動平台的輸入中,Unity還為我們提供了諸如:位置,指南針,陀螺儀等設備的信息輸入。在這里就不在詳述了,詳情參見官方文檔
傳統的輸入
像鼠標,鍵盤,操作桿和手柄這樣的輸入設備。現在暫且將其定義為傳統的輸入設備,以便區分前面的移動平台的輸入。在Unity中還為我們抽象出來一個叫做虛擬軸或虛擬按鈕的概念出來,在下面將分別介紹這兩種(其實是一種,后者是由前者虛擬出來的)輸入方式。
鼠標,鍵盤,控制桿,手柄
由於操作桿和手柄沒有設備,就不做介紹了,它跟其他的操作是一樣的。
鍵盤
函數 | 作用 |
---|---|
GetKey | 獲取鍵盤指定鍵是否按下(只要按下就是True抬起就是False) |
GetKeyDown | 獲取鍵盤指定鍵是否按下(按下那一刻是True) |
GetKeyUp | 獲取鍵盤指定鍵是否按下(彈起那一刻是True) |
anyKey | 是否按住了“任意鍵”(只要按下就是True抬起就是False) |
anyKeyDown | 是按下了“任意鍵”(按下那一刻是True) |
對應的按鍵枚舉參見KeyCode
鼠標
函數 | 作用 |
---|---|
GetMouseButton | 獲取鼠標指定鍵是否按下(只要按下就是True抬起就是False) |
GetMouseButtonDown | 獲取鼠標指定鍵是否按下(按下那一刻是True) |
GetMouseButtonUp | 獲取鼠標指定鍵是否彈起(彈起那一刻是True) |
注:0對應於鼠標左鍵,1對應於與鼠標右鍵,2對應於鼠標中鍵。
雖然上面的函數能夠直接獲取到指定鍵是否按下,但是我們一般不會直接這么使用。使用Untiy提供的虛擬軸和按鍵能更靈活的控制我們的輸入。比如我可以將空格定義攻擊鍵,也可以隨時改變這個鍵。虛擬軸還有一個好處,就是我們可以同時接受多輸入,比如我可以接受鍵盤的空格作為攻擊,也可以使用操作桿或手柄上的一個鍵作為攻擊鍵,因為我們獲取的虛擬軸或按鈕都是一樣(“Fire1”)的。這里可能說的有點抽象,不好懂,下面我會通過一個例子來說明這些。
虛擬控制軸(Virtual Axes)
虛擬軸的編輯
下面對每個參數簡單的說明,在看說明時對應下圖一起看。圖如下:
參數名 | 作用 |
---|---|
Name | 虛擬軸的名字(獲取虛擬軸時就需要傳入這個名字) |
Descriptive Name | 正方向上的控制鍵的描述信息 |
Descriptive Negative Name | 反方向上的控制鍵的描述信息 |
Negative Button | 主控制鍵反方向上對應的控制鍵 |
Positive Button | 主控制鍵正方向上對應的控制鍵 |
Alt Negative Button | 副控制鍵反方向上對應的控制鍵 |
Alt Positive Button | 副控制鍵正方向上對應的控制鍵 |
Gravity | 向中間值歸位時的速度 |
Dead | 中間值的閾值,就是小於這個值則被定義為是中間值了 |
Sensitivity | 向目標歸位時的速度 |
Snap | 是否需要平滑,如果沒有的話那么就只有-1,0(中間值),1這三個值 |
Invert | 將上面的正反方向顛倒 |
Type | 使用那些輸入控制鍵,一般使用鼠標和鍵盤,如果你開發的是使用操作桿的那么就是操作桿作為輸入控制 |
Axis | 抱歉不能理解 |
Joy Num | 抱歉不能理解 |
相關函數
函數名 | 作用 |
---|---|
GetAxis | 獲取指定軸上當前采集的值,范圍為[-1,1] |
GetAxixRaw | 獲取指定軸上當前采集的值,這個函數獲取的是沒有平滑過渡的值,也就是只有-1,0,1這三個值 |
GetButton | 獲取指定虛擬按鈕是否按下,當按下的是否為True,談起的是否為:False |
GetButtonDown | 獲取指定虛擬按鈕是否按下,當按下那一刻為True |
GetButtonUp | 獲取指定虛擬按鈕是否抬起,當抬起那一刻為True |
虛擬軸或按鈕的示例
此示例主要實現2功能:
- 實現一個Cube在場景中前后左右的走動,通過獲取“Horizontal”和“Vertical”兩個虛擬軸來控制
- 實現一個Cube在場景中的旋轉,通過自定義的控制軸“Rotation”實現,“Rotation”我們通過鼠標右鍵和鍵盤的空格鍵來控制。
示例代碼如下:
public class AxesTest : MonoBehaviour {
private float m_nSpeed = 5.0f;
private bool m_isRotating = false;
private float m_nRotationSpeed = 30.0f;
private float m_nCurRotationAngle = 0;
private const float m_nMaxAngle = 360;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Rotation") && !m_isRotating)
{
m_isRotating = true;
}
if (m_isRotating)
{
float nRotation = Time.deltaTime * m_nRotationSpeed;
transform.Rotate(0, nRotation, 0);
m_nCurRotationAngle += nRotation;
}
if (m_nCurRotationAngle >= m_nMaxAngle)
{
m_nCurRotationAngle = 0;
m_isRotating = false;
}
float nXDeltaDistance = Input.GetAxis("Horizontal") * m_nSpeed * Time.deltaTime;
float nYDeltaDistance = Input.GetAxis("Vertical") * m_nSpeed * Time.deltaTime;
if (nXDeltaDistance.Equals(0) && nYDeltaDistance.Equals(0))
return;
print("X:" + Input.GetAxis("Horizontal").ToString() + " Y:" + Input.GetAxis("Vertical").ToString());
transform.Translate(-nXDeltaDistance, nYDeltaDistance, 0);
}
}
總結
Unity將其主要的輸入都放到了UnityEngine.Input類中,內部檢查或采集到輸入信息就將其結果放入Input中的對應字段,用於表示輸入的狀態。輸入大致分為兩類,一個是移動平台的輸入,像觸摸,虛擬鍵盤,重力加速感應器,羅盤,陀螺儀,GPS(位置)等,另一類是傳統的輸入,像鍵盤,鼠標,操作桿和手柄等。
參考文獻
Unity官方文檔1:http://docs.unity3d.com/Manual/Input.html
Unity官方文檔2:http://docs.unity3d.com/ScriptReference/Input.html