4、输入系统入门:旧版Input Manager、获取键盘/鼠标输入、Axis虚拟轴、Input.GetKey与Input.GetButton

好,咱们今天来聊聊Unity的输入系统。说实话,这是每个做游戏的人绕不开的第一道坎。你想想看,玩家怎么跟你的游戏世界互动?不就是靠键盘、鼠标、手柄这些外设嘛。

Unity提供了两套输入方案:一套是经典的旧版Input Manager,另一套是后来推出的新版Input System Package。我个人习惯,在快速原型或者小项目里,还是用旧版居多——它简单、直接,不用折腾包管理。今天咱们就先把旧版吃透。

4.1 旧版Input Manager长什么样?

打开Unity编辑器,菜单栏选 Edit → Project Settings → Input Manager。你会看到一大串列表,里面全是预设好的虚拟轴(Axes)。

嗯,这里要注意:这些轴不是物理轴,而是逻辑轴。说白了,就是把键盘上的W键、手柄的左摇杆、鼠标的移动,统统映射到一个叫“Vertical”或者“Horizontal”的名字上。你写代码的时候不用管玩家按的是键盘还是手柄,直接读轴的名字就行。

我刚开始做项目时,总觉得这玩意儿可有可无。直到有一次,我写死了Input.GetKey(KeyCode.W)来让角色前进,结果测试时发现手柄玩家根本动不了……从那以后,我再也不敢跳过Input Manager的配置了。

4.2 获取键盘输入:Input.GetKey

这是最原始的方式。你想检测某个键是否被按下,就用它。

// 检测W键是否被按住
if (Input.GetKey(KeyCode.W))
{
    Debug.Log("W键正在被按住");
}

// 检测W键按下的那一帧
if (Input.GetKeyDown(KeyCode.W))
{
    Debug.Log("W键刚刚被按下");
}

// 检测W键松开的那一帧
if (Input.GetKeyUp(KeyCode.W))
{
    Debug.Log("W键刚刚被松开");
}

三种状态:GetKey(按住)、GetKeyDown(按下瞬间)、GetKeyUp(松开瞬间)。

注意:GetKeyDown只在按下那一帧返回true,之后立即变false。如果你在Update里写了一个while循环去等它,那大概率会错过输入。我踩过这个坑,调试了半天才发现是帧率问题。

4.3 获取鼠标输入

鼠标输入分两类:按键和移动。

鼠标按键:

// 左键
if (Input.GetMouseButton(0))
{
    Debug.Log("左键按住");
}

// 右键
if (Input.GetMouseButtonDown(1))
{
    Debug.Log("右键按下");
}

// 中键
if (Input.GetMouseButtonUp(2))
{
    Debug.Log("中键松开");
}

参数0、1、2分别对应左键、右键、中键。这个顺序别记反了,我见过有人把1当成左键,结果右键开火……

鼠标移动:

float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");

// 通常用来控制视角旋转
transform.Rotate(Vector3.up * mouseX * sensitivity);

这里返回的是增量值,不是绝对坐标。说白了,就是这一帧鼠标移动了多少像素。你把它乘上一个灵敏度系数,就能控制镜头转动速度。

4.4 Axis虚拟轴:Input.GetAxis

这是旧版Input Manager的精髓。你想想看,如果每个按键都要单独写GetKey,那代码得多乱?虚拟轴就是来解决这个问题的。

Unity默认提供了几个常用轴:

轴名称 默认按键 用途
Horizontal A/D 或 左/右方向键 水平移动
Vertical W/S 或 上/下方向键 垂直移动
Mouse X 鼠标水平移动 视角水平旋转
Mouse Y 鼠标垂直移动 视角垂直旋转
Fire1 左Ctrl 或 鼠标左键 开火/交互
Jump 空格键 跳跃

用法很简单:

float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");

Vector3 move = new Vector3(h, 0, v);
transform.Translate(move * speed * Time.deltaTime);

这里有个细节:GetAxis返回的是-1到1之间的浮点数。你按A键,它慢慢滑到-1;你松开,它慢慢滑回0。这种平滑过渡让移动手感很自然。

小技巧:如果你想要那种“按下去立刻满速,松开立刻停”的硬手感,可以用Input.GetAxisRaw。它只返回-1、0、1三个值,没有平滑过渡。我在做平台跳跃游戏时,跳跃用GetAxisRaw,移动用GetAxis,手感刚刚好。

4.5 Input.GetButton 与 Input.GetButtonDown

GetKey类似,但GetButton是绑定到虚拟轴名字上的。说白了,它让你用逻辑名字代替物理按键。

// 检测Fire1轴是否被按住
if (Input.GetButton("Fire1"))
{
    Debug.Log("开火中...");
}

// 检测Fire1轴按下的那一帧
if (Input.GetButtonDown("Jump"))
{
    Debug.Log("跳跃!");
}

// 检测Fire1轴松开的那一帧
if (Input.GetButtonUp("Fire1"))
{
    Debug.Log("停止开火");
}

这样做的好处是:如果以后你想把跳跃键从空格改成手柄的A键,只需要在Input Manager里改映射,代码一行都不用动。

核心原则:能用GetButton就别用GetKey。代码里出现KeyCode.W这种硬编码,迟早会给你带来麻烦。我见过一个项目,所有按键都写死了,结果要适配手柄时,改了几百处地方……那叫一个酸爽。

4.6 实战:一个简单的角色移动脚本

咱们把今天学的串起来,写一个完整的角色控制脚本:

using UnityEngine;

public class SimplePlayerController : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float rotateSpeed = 100f;

    void Update()
    {
        // 移动:用虚拟轴
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        Vector3 move = new Vector3(h, 0, v);
        transform.Translate(move * moveSpeed * Time.deltaTime, Space.World);

        // 旋转:用鼠标
        float mouseX = Input.GetAxis("Mouse X");
        transform.Rotate(Vector3.up * mouseX * rotateSpeed * Time.deltaTime);

        // 跳跃:用GetButtonDown
        if (Input.GetButtonDown("Jump"))
        {
            GetComponent<Rigidbody>().AddForce(Vector3.up * 5f, ForceMode.Impulse);
        }

        // 开火:用GetButton
        if (Input.GetButton("Fire1"))
        {
            Debug.Log("开火中...");
        }
    }
}

这个脚本虽然简单,但涵盖了今天讲的所有核心内容。你把它挂到角色身上,就能用WASD移动、鼠标转视角、空格跳跃、左键开火。

4.7 避坑指南

最后,分享几个我踩过的坑:

  • Input.GetAxis 放在 FixedUpdate 里? 别。FixedUpdate的调用频率跟物理帧走,不稳定。输入检测永远放Update里。
  • 鼠标锁定: 做FPS游戏时,记得用Cursor.lockState = CursorLockMode.Locked把鼠标锁在屏幕中心。不然鼠标一移出窗口,视角就乱跳。
  • 多平台兼容: 如果你打算发布到手机或主机,旧版Input Manager可能不够用。那时候就得考虑新版Input System了。不过那是后面章节的事,今天先把基础打牢。

好,今天就到这儿。下一章咱们会深入Input Manager的配置面板,看看怎么自定义轴、怎么处理多按键冲突。到时候你会发现,原来输入系统也可以玩出花来。