Unity-使用者輸入互動詳解
1.簡介
輸入操作是遊戲的基礎操作之一。
Unity支援的操作方式:
- 滑鼠、鍵盤,小鍵盤(PC)
- 搖桿(主機)
- 觸屏操作、重力傳感器、手勢(移動平台)
- VR,AR
- 麥克風,攝像頭
2. 虛拟輸入軸(Virtual axes)
虛拟控制軸将不同的輸入裝置(比如鍵盤或搖杆的按鍵)都歸納到一個統一的虛拟控制系統中。
(比如鍵盤的w、S鍵以及搖桿搖杆的上下運動預設都統一映射到豎直(Verica)輸入軸上)
這樣就屏蔽了不同裝置之間的差異,讓開發者可以用一套非常簡單的輸入邏輯,同時相容多種輸入裝置。
使用 輸入管理器(Input Manager) 可以檢視、修改或增删虛拟軸。
現代的遊戲中往往允許玩家在遊戲中自定義按鍵,是以使用Unity的輸入管理器就更為重要要了。
通過一層虛拟軸間接操作,可以避免在代碼中直接寫死操作按鈕,而且還能通過動态修改虛拟軸的設定來改變鍵位的功能。
關于虛拟輸入軸,還有一些需要知道的内容:
- 腳本可以直接通過虛拟軸的名稱讀取那個軸的輸入狀态。
- 建立Unity工程時,預設建立了以下虛拟軸:
- 橫向輸入和縱向輸入被映射在鍵盤的W、A、S、 D鍵以及方向鍵上
- Fire1、 Fire2、 Fire3這三個按鈕映射到了滑鼠的左、中、右鍵以及鍵盤的Ctrl. AIt等鍵位上
- 滑鼠移動可以模拟搖杆輸入(和滑鼠光标在螢幕上的位置無關),且被映射在專門的滑鼠偏移軸上
- 其他常用虛拟軸,例如跳躍(Jump) 、确認(Submit) 和取消(Cancel)
## 2.1 添加和編輯虛拟輸入軸
要添加新的虛拟輸入軸,隻需要單擊主菜單的Edit > Projet Setings > Input 選項,單擊路街檢視視窗中會顯示一個輸入管理器,在裡面就可以修改或添加虛拟軸了。
注意:
虛拟軸具有正、負兩個方向。英文記作Positive和Negative.。
某些相反的動作可以隻用一個軸來表示。
比如,如果搖杆向上為正,那麼向下就是同一個軸的負方向。
每個虛拟軸可以映射兩個按鍵,第二個按鍵作為備用,功能一樣,備用的英文為Alterative。
2.2 虛拟輸入軸屬性表
下表是虛拟輸入軸的屬性表:
屬性 | 功能 |
---|---|
Name | 軸的名字。在腳本中用這個名字來通路這個軸 |
Descriptive Name | 描述性資訊,在某些視窗中顯示出來以友善檢視(正方向) |
Descriptive Negative Name | 描述性資訊,在某些視窗中顯示出來以友善檢視(負方向) |
Ncgative Button | 該軸的負方向,用于綁定某個按鍵 |
Positive Button | 該軸的正方向,用于綁定某個按鍵 |
Alt Negative Button | 該軸的負方向,用于綁定某個備用按鍵 |
Alt Positive Button | 該軸的正方向,用于綁定某個備用按鍵 |
Gravity | 軸回中的力度 |
Dead | 軸的死區 |
Sensitivity | 敏感度 |
Snap | 保持式按鍵。比如按住下方向鍵,則一直保持下的狀态,直到再次按上方向鍵 |
Invert | 如果勾選,則交換正負方向 |
Type | 控制該虛拟軸的類型, 比如搖桿、鍵盤是兩種不同的類型 |
Axis | 很多搖桿的輸入不是按鈕式的,這時就不能配置到Button裡面,而是要配置到這裡。可以了解為實際的操作軸 |
Joy Num | 當有多個控制器時,要填寫控制器的編号 |
上表中的Gravity、Dead 等屬性需要解釋一下。
2.2.1 Gravity
現代遊戲的方向輸入和早期遊戲的方向輸入不太一樣。
早期遊戲中,上、中、下都是離散的狀态,可以直接用1、0、-1來表示。
而現代遊戲輸入往往具有中間狀态,比如0、0.35、 0.5、0.7、1, 是帶有多級梯度的。
比如輕推搖杆代表走路,推到底就是跑步。
是以現代遊戲的輸入預設都是采用多梯度的模式。
雖然鍵盤沒有多級輸入的功能,但Unity依然會模拟這個功能,也就是說當你按住W鍵時,這個軸的值會以很快的速度逐漸從0增加到1。
是以,上表中Gravity和Sensitivity的含義就不難了解了,它們影響着虛拟軸從1到0、從0到1的速度以及敏感度。
具體調試方法這裡不再介紹,建議使用預設值
2.2.2 Dead
還有死區需要單獨說明。
由于實體搖桿、搖杆會有一些誤差,比如,搖桿放着不動時,某些搖桿的輸出值可能會在0.05 和0.08之間浮動。這個誤差有必要在程式中排除。
是以Unity設計了死區的功能,在該值範圍内的抖動被忽略為0,這樣就可以過濾掉輸入裝置的誤差。
2.3 在腳本中處理輸入
讀取輸入軸的方法很簡單,代碼如下:
float value = Input.GetAxis ("Horizontal");
得到的值的範圍為-1~1,預設位置為0。
這個讀取虛拟軸的方法與具體控制器是鍵盤還是搖桿無關。
如果用滑鼠控制虛拟軸,就有可能由于移動過快導緻值超出-1~1的範圍。
注意:
可以建立多個相同名字的虛拟軸。
Unity 可以同時管理多個同名的軸,最終結果以變化最大的軸為準。
這樣做的原因是很多遊戲可以同時用多種裝置進行操作。
比如PC遊戲可以用鍵盤、滑鼠或搖桿進行操作,手機遊戲可以用重力感應器或搖桿進行操作。
這種設計有助于使用者在多種操作裝置之間切換,且在腳本中不用去關心這一點。
2.4 按鍵名稱
要映射按鍵到軸上,需要在正方向輸入框或者負方向輸入框中輸入正确的按鍵名稱。
按鍵名稱的規則和例子如下。
- 正常按鍵: A、B、…
- 數字鍵: 1、2、…
- 方向鍵: Up、 Down、 Left、 Right…
- 小鍵盤鍵: [1]、 [2]、 [3]、 [+]、 [equals]…
- 修飾鍵: Right+Shift、 Lef+Shift、Right+Ctrl、Left+Ctrl、Right+Alt、Left+Alt、Right+Cmd、Left+Cmd…
- 滑鼠按鈕: mouse 0、mouse 1、mouse2 …
- 搖桿按鈕(不指定具體的搖桿序号) : joystick button 0、joystick button 1…
- 搖桿按鈕(指定具體的搖桿序号): joystick 1 button 0、joystick 1 button 1…
- 特殊鍵: Backspace、 Tab、 Retur、 Escape、 Space、 Delete、 Enter、 Insert、 Home、Page Up…
- 功能鍵: FI、F2、…
可以使用KeyCode枚舉類型來指定案件,與用字元串的效果一緻
3. 在PC端輸入
unity為開發者提供了input庫,來支援鍵盤事件,滑鼠事件以及觸摸事件。
3.1 鍵盤事件
一般的PC鍵盤有104個不同的按鍵,在程式中通過監聽這些按鍵事件,進而進一步執行邏輯操作。
如:射擊遊戲中,W表示前進,S表示後退,A表示左移,D表示右移。
3.1.1 按下事件
在腳本中,用input.GetKeyDown( )方法将按鍵值作為參數,監聽此按鍵是否被按下。
按下傳回true,否者傳回false。
例如:
if (Input.GetKeyDown (KeyCode.W))
{
Debug.Log("您按下了W鍵");
}
if (Input.GetKeyDown (KeyCode.Space))
{
Debug.Log("您按下了空格鍵");
}
3.1.2 擡起事件
擡起事件完全依賴于按下事件,因為隻有按下才有擡起。
我們用Input.GetKeyUp( )方法監聽擡起事件
按鍵擡起後,傳回true,否則傳回false。
if (Input.GetKeyUp (KeyCode.W))
{
Debug.Log("您擡起了W鍵");
}
3.1.3 長按事件
長按事件是監聽某一按鍵是否處于一直按下的狀态
通過**Input.GetKey( )**來判斷鍵盤中某一按鍵是否被一直按着。
if (Input.GetKey (KeyCode.A))
{
//記錄按下的幀數
keyFrame++;
Debug.Log("A連按:" + keyFrame+"幀");
}
if (Input.GetKeyUp (KeyCode.A))
{
//擡起後清空幀數
keyFrame=0;
Debug.Log("A按鍵擡起");
}
3.1.4 任意鍵事件
在程式中還可以監聽按鍵中的任意按鍵是否被按下
常見于加載完遊戲後,按任意鍵進入。
if(Input.anyKeyDown)
{
Debug.Log("任意鍵被按下");
}
3.1.4執行個體——組合按鍵
在經典的格鬥遊戲中,會有組合鍵發出牛逼的大招,而這個功能的事件思路其實不難:
在玩家按下某一鍵後,便開始時間記數,在某一時間内按出所需要的鍵便發出大招。
using UnityEngine;
using System.Collections.Generic;
using System;
public class Script_07_05 : MonoBehaviour
{
//方向鍵上的貼圖
public Texture imageUp;
//方向鍵下的貼圖
public Texture imageDown;
//方向鍵左的貼圖
public Texture imageLeft;
//方向鍵右的貼圖
public Texture imageRight;
//按鍵成功的貼圖
public Texture imageSuccess;
//自定義方向鍵的儲存值
public const int KEY_UP = 0;
public const int KEY_DOWN = 1;
public const int KEY_LEFT = 2;
public const int KEY_RIGHT = 3;
public const int KEY_FIRT = 4;
//連續按鍵的事件限制
public const int FRAME_COUNT = 100;
//倉庫中儲存技能的數量
public const int SAMPLE_SIZE = 3;
//每組技能的按鍵數量
public const int SAMPLE_COUNT = 5;
//技能倉庫
int[,] Sample =
{
//下 + 前 + 下 + 前 + 拳
{KEY_DOWN,KEY_RIGHT,KEY_DOWN,KEY_RIGHT,KEY_FIRT},
//下 + 前 + 下 + 後 + 拳
{KEY_DOWN,KEY_RIGHT,KEY_DOWN,KEY_LEFT,KEY_FIRT},
//下 + 後 + 下 + 後 + 拳
{KEY_DOWN,KEY_LEFT,KEY_DOWN,KEY_LEFT,KEY_FIRT},
};
//記錄目前按下按鍵的鍵值
int currentkeyCode =0;
//标志是否開啟監聽按鍵
bool startFrame = false;
//記錄目前開啟監聽到現在的時間
int currentFrame = 0;
//儲存一段時間内玩家輸入的按鍵組合
List<int> playerSample;
//标志完成操作
bool isSuccess= false;
void Start()
{
//初始話按鍵組合連結清單
playerSample = new List<int>();
}
void OnGUI()
{
//獲得按鍵組合連結清單中儲存按鍵的數量
int size = playerSample.Count;
//周遊該按鍵組合連結清單
for(int i = 0; i< size; i++)
{
//将按下按鍵對應的圖檔顯示在螢幕中
int key = playerSample[i];
Texture temp = null;
switch(key)
{
case KEY_UP:
temp = imageUp;
break;
case KEY_DOWN:
temp = imageDown;
break;
case KEY_LEFT:
temp = imageLeft;
break;
case KEY_RIGHT:
temp = imageRight;
break;
}
if(temp != null)
{
GUILayout.Label(temp);
}
}
if(isSuccess)
{
//顯示成功貼圖
GUILayout.Label(imageSuccess);
}
//預設提示資訊
GUILayout.Label("連續組合按鍵1:下、前、下、前、拳");
GUILayout.Label("連續組合按鍵2:下、前、下、後、拳");
GUILayout.Label("連續組合按鍵2:下、後、下、後、拳");
}
void Update ()
{
//更新按鍵
UpdateKey();
if(Input.anyKeyDown)
{
if(isSuccess)
{
//按鍵成功後重置
isSuccess = false;
Reset();
}
if(!startFrame)
{
//啟動時間計數器
startFrame = true;
}
//将按鍵值添加如連結清單中
playerSample.Add(currentkeyCode);
//周遊連結清單
int size = playerSample.Count;
if(size == SAMPLE_COUNT)
{
for(int i = 0; i< SAMPLE_SIZE; i++)
{
int SuccessCount = 0;
for(int j = 0; j< SAMPLE_COUNT; j++)
{
int temp = playerSample[j];
if(temp== Sample[i,j])
{
SuccessCount++;
}
}
//玩家按下的組合按鍵與倉庫中的按鍵組合相同表示釋放技能成功
if(SuccessCount ==SAMPLE_COUNT)
{
isSuccess = true;
break;
}
}
}
}
if(startFrame)
{
//計數器++
currentFrame++;
}
if(currentFrame >= FRAME_COUNT)
{
//計數器逾時
if(!isSuccess)
{
Reset();
}
}
}
void Reset ()
{
//重置按鍵相關資訊
currentFrame = 0;
startFrame = false;
playerSample.Clear();
}
void UpdateKey()
{
//擷取目前鍵盤的按鍵資訊
if (Input.GetKeyDown (KeyCode.W))
{
currentkeyCode = KEY_UP;
}
if (Input.GetKeyDown (KeyCode.S))
{
currentkeyCode = KEY_DOWN;
}
if (Input.GetKeyDown (KeyCode.A))
{
currentkeyCode = KEY_LEFT;
}
if (Input.GetKeyDown (KeyCode.D))
{
currentkeyCode = KEY_RIGHT;
}
if (Input.GetKeyDown (KeyCode.Space))
{
currentkeyCode = KEY_FIRT;
}
}
}
3.2 滑鼠事件
和鍵盤事件一樣,滑鼠一般隻有3個按鍵,左鍵、右鍵和中鍵。
具體如下:
3.2.1 按下事件
Input.GetMouseButtonDown()
來判斷滑鼠哪個按鍵被按下:
- 傳回值為0代表滑鼠左鍵被按下
- 傳回值為1代表滑鼠右鍵被按下
- 傳回值為2代表滑鼠中鍵被按下
3.2.2 擡起事件
Input.GetMouseButtonUp()
方法監聽滑鼠按鍵的擡起事件
3.2.3 長按事件
Input.GetMouseButton()
監聽滑鼠某個按鍵是否一直處于按下狀态
4. 在移動端輸入
對于移動裝置來說,Ioput類還提供了觸屏、加速度計以及通路地理位置的功能。
此外,移動裝置上還經常會用到虛拟鍵盤,即在螢幕上操作的鍵盤,Uity中也有相應的通路方法。
本小節專門讨論移動裝置特有的輸入方式。
4.1 多點觸摸
iPhone、iPad、安卓等裝置提供同時捕捉多個手指觸摸操作的功能,通常可以處理最多5根手指同時觸摸螢幕的情況。
通過通路Input.touches屬性,可以以數組的方式處理多個手指目前的位置等資訊。
安卓裝置上多點觸摸的規範相對靈活,不同的裝置能捕捉的多點觸摸操作的數量不盡相同。
- 較老的裝置可能隻支援1到2個點同時觸摸
- 新裝置可能會支援5個點同時觸摸
每一個手指的觸摸資訊以Input.Touch結構體來表示。
Input.Touch的屬性清單:
屬性 | 功能 |
---|---|
fingerld | 該觸摸的序号 |
position | 觸摸在螢幕上的位置 |
dcllaPosition | 目前觸摸位置和前一個觸摸位置的差距 |
doltaTime | 最近兩次改變觸摸位置之間的操作時間的間隔 |
tapCount | IPhone/Ipad裝置會記錄使用者短時間内單擊螢幕的次數,它表示使用者多次單擊操作且沒有将手拿開的次數。安卓裝置沒有這個功能,該值保持為1 |
phase | 觸摸的階段。可以用它來判斷是剛開始觸摸、觸摸時移動,還是手指剛剛離開螢幕 |
phase的取值是個枚舉,枚舉值如下:
-
Began
手指剛接觸到螢幕
-
Moved
手指在螢幕上滑動
-
Stationary
手指接觸到螢幕但還未滑動
-
Ended
手指離開了螢幕。
這個狀态代表着一次觸摸操作的結束
-
Cancceled
系統取滑了這次觸屏操作。
例如當使用者拿起手機進行通話,或者觸損點多于9個的時候,這次觸摸操作就會被取消。
這個狀态也代表這次觸摸操作結束
4.2 模拟滑鼠操作
絕大部分移動裝置可以用觸屏模拟滑鼠操作。
比如使用Input.mousePosition屬性不僅可以獲得滑鼠光标的位置,也可以獲得移動裝置上觸摸的位置。
這個功能的原理不難了解,畢竟觸屏可以支援多點觸摸,而滑鼠則是單點操作,這個功能屬于向下相容。
在移動平台的遊戲的開發階段可以暫時用滑鼠操作代替觸屏操作,但是稍後應當修改為觸屏專用的方式,因為操作手感和功能會有很大差別。
4.3 加速度計
當移動裝置移動時,内置的加速度計會持續報告目前加速度的值,這個值是一個三維向量,因為物體的運動是任意方向的。
這個數值和重力加速度的表示方法類似:
- 在某個軸方向上,1.0代表該軸具有+1.0g的加速度
- 而負值則代表該軸具有相反方向的加速度。
正常豎直持手機(Home鍵在下方)時:
- X軸的正方向朝右
- Y軸的正方向朝上
- Z軸的正方向從手機指向使用者
通過Input.aceleation屬性可以直接通路加速度計目前的數值。