Unity中的输入


任何游戏都应该提供给用户交互的方式,根据不同的设备有不同的输入方式,比如像传统小霸王机的手柄,电脑的鼠标、键盘,手机平板的触摸、位置、重力感应输入等。为了能够更好的整理Unity的输入系统,暂时将其分为移动平台的输入和传统的输入。

移动平台的输入

在手机和pad上主要的输入方式就是:触摸,重力加速器,虚拟键盘等。Unity将这些操作都封装到了UnityEngine.Input和UnityEngine.TouchScreenKeyboard这两个类里。

触摸

触摸相关的函数

在UnityEngine.Input类中为我们提供了触摸相关的函数,以及在UnityEngine命名空间中涉及到的类,相关函数和类如下表:

函数表:

函数名作用
multiTouchEnabled是否启用多点触摸
simulateMouseWithTouches启用/禁用使用触碰仿真鼠标的操作
touchCount在此帧中的触摸数量
touches在上一帧中的触摸点(Touch)信息
touchSupported标示当前运行此程序的设备是否支持触摸
GetTouch根据触摸点的索引获取触摸点的信息

类表:

类名作用
Touch触摸点信息
TouchPhase触摸点的状态信息

触摸的一个示例

此示例主要实现如下三个功能:

  1. 显示触摸点的属性信息
  2. 显示点击到的物体
  3. 测试仿真鼠标

示例代码,如下:

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功能:

  1. 实现一个Cube在场景中前后左右的走动,通过获取“Horizontal”和“Vertical”两个虚拟轴来控制
  2. 实现一个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


版权声明:本文为zw514159799原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。