任何游戏都应该提供给用户交互的方式,可以想象一个没有任何交互的游戏是什么样的。如果没有输入系统带来的,用户与游戏的交互那么游戏将不再是游戏,因为玩家将不能进行任何操作,那还怎么玩呢?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