Version: 2019.2
传统游戏输入
移动键盘

移动设备输入

在移动设备上,Input 类提供对触摸屏、加速度计和地理/位置输入的访问。

通过 iOS 键盘可以访问移动设备上的键盘。

多点触控屏幕

iPhone、iPad 和 iPod Touch 设备最多可跟踪五根手指同时触摸屏幕。可通过访问 Input.touches 属性数组来获取在最后一帧期间触摸屏幕的每根手指的状态。

Android 设备对其跟踪的手指数量没有统一限制。相反,此限制因设备而异,可能是旧设备上的双手指触摸到某些新设备上的五指触摸。

每根手指的触摸由 Input.Touch 数据结构表示:

Property: Description:
fingerId The unique index for a touch.
position The screen position of the touch.
deltaPosition The screen position change since the last frame.
deltaTime Amount of time that has passed since the last state change.
tapCount The iPhone/iPad screen is able to distinguish quick finger taps by the user. This counter will let you know how many times the user has tapped the screen without moving a finger to the sides. Android devices do not count number of taps, this field is always 1.
phase Describes the state of the touch, which can help you determine whether the user has just started to touch screen, just moved their finger or just lifted their finger.
Began A finger just touched the screen.
Moved A finger moved on the screen.
Stationary A finger is touching the screen but hasn’t moved since the last frame.
Ended A finger was lifted from the screen. This is the final phase of a touch.
Canceled The system cancelled tracking for the touch, as when (for example) the user puts the device to their face or more than five touches happened simultaneously. This is the final phase of a touch.

以下是一个示例脚本,只要用户点击屏幕,就会发出一条射线:

using UnityEngine;

public class TouchInput : MonoBehaviour
{
    GameObject particle;

    void Update()
    {
        foreach(Touch touch in Input.touches)
        {
            if (touch.phase == TouchPhase.Began)
            {
                // 从当前触摸坐标构造一条射线
                Ray ray = Camera.main.ScreenPointToRay(touch.position);
                if (Physics.Raycast(ray))
                {
                    // 如果命中,则创建一个粒子
                    Instantiate(particle, transform.position, transform.rotation);
                }
            }
        }
    }
}

鼠标模拟

除了原生触摸支持,Unity iOS/Android 还提供鼠标模拟功能。可使用标准 Input 类中的鼠标功能。请注意,根据设计,iOS/Android 设备支持多指触摸。使用鼠标功能时仅支持单指触摸。此外,移动设备上的手指触摸可从一个区域移动到另一个区域,而它们之间没有移动。移动设备上的鼠标模拟将提供移动,因此与触摸输入相比非常不同。建议在早期开发期间使用鼠标模拟,但要尽快使用触摸输入。

加速度计

随着移动设备的移动,内置的加速度计会报告沿三维空间中的 三个主轴的线性加速度变化。硬件 将沿每个轴的加速度直接报告为重力值。值 1.0 表示沿给定轴约 +1g 的荷载,而值 –1.0 表示 –1g。如果将设备竖直握住(主屏幕按钮位于 底部)举到您正前方,则右侧为正 X 轴,上方为正 Y 轴, 指向您的方向为正 Z 轴。

可通过访问 Input.acceleration 属性来检索加速度计值。

以下是使用加速度计移动对象的示例脚本:

using UnityEngine;

public class Accelerometer : MonoBehaviour
{
    float speed = 10.0f;

    void Update()
    {
        Vector3 dir = Vector3.zero;
        // 我们假设设备与地面平行,
        // 主屏幕按钮位于右侧

        // 将设备的加速度轴重新映射到游戏坐标:
        // 1) 设备的 XY 平面映射到 XZ 平面
        // 2) 绕 Y 轴旋转 90 度

        dir.x = -Input.acceleration.y;
        dir.z = Input.acceleration.x;

        // 将加速度矢量限制为单位球体
        if (dir.sqrMagnitude > 1)
            dir.Normalize();

        // 使其每秒移动 10 米而不是每帧 10 米...
        dir *= Time.deltaTime;

        // 移动对象
        transform.Translate(dir * speed);
    }
}

低通滤波器

加速度计读数可能不稳定且噪声很大。对信号应用低通滤波可以使其平滑并消除高频噪声。

以下脚本显示了如何将低通滤波应用于加速度计读数:

using UnityEngine;

public class LowPassFilterExample : MonoBehaviour
{
    float accelerometerUpdateInterval = 1.0f / 60.0f;
    float lowPassKernelWidthInSeconds = 1.0f;

    private float lowPassFilterFactor;
    private Vector3 lowPassValue = Vector3.zero;

    void Start()
    {
        lowPassFilterFactor = accelerometerUpdateInterval / lowPassKernelWidthInSeconds;
        lowPassValue = Input.acceleration;
    }

    private void Update()
    {
        lowPassValue = LowPassFilterAccelerometer(lowPassValue);
    }

    Vector3 LowPassFilterAccelerometer(Vector3 prevValue)
    {
        Vector3 newValue = Vector3.Lerp(prevValue, Input.acceleration, lowPassFilterFactor);
        return newValue;
    }
}

LowPassKernelWidthInSeconds 的值越大,滤波值向当前输入样本收敛的速度越慢(反之亦然)。

在读取加速度计的读数时,我希望尽可能精确。该怎么办?

读取 Input.acceleration 变量不等于对硬件进行采样。简而言之,Unity 以 60Hz 的频率对硬件进行采样并将结果存储到变量中。实际情况有点复杂:如果 CPU 负载很大,加速度计采样不会以一致的时间间隔发生。结果,系统可能在一帧期间报告 2 个样本,然后在下一帧期间报告 1 个样本。

您可以访问加速度计在帧内执行的所有测量。以下代码将展示在最后一帧内收集的所有加速度计事件的简单平均值:

public class AccelerationEvents : MonoBehaviour
{ 
    void Update()
    {
        GetAccelerometerValue();
    }

    Vector3 GetAccelerometerValue()
    {
        Vector3 acc = Vector3.zero;
        float period = 0.0f;

        foreach(AccelerationEvent evnt in Input.accelerationEvents)
        {
            acc += evnt.acceleration * evnt.deltaTime;
            period += evnt.deltaTime;
        }
        if (period > 0)
        {
            acc *= 1.0f / period;
        }
        return acc;
    }
}
传统游戏输入
移动键盘