Unity基础:Input输入系统入门——键盘、鼠标与触摸检测

Unity基础:Input输入系统入门------键盘、鼠标与触摸检测

大家好,欢迎回到Unity教程系列。前面几篇文章我们学了写脚本、调试代码,但真正让游戏具有交互性的,是"输入"。玩家按下键盘、点击鼠标、滑动屏幕------这些操作如何被Unity检测到并传递给游戏逻辑,就是今天这篇文章要讲的内容。

Unity的输入系统有两套:旧版Input Manager(Unity默认)和新版Input System(需要安装Package)。对初学者来说,先从旧版入手是最快的,新版Input System我会在后续进阶章节中详细讲解。

一、Unity输入系统概述

1.1 输入管理的本质

💡 游戏输入系统的核心任务很简单:检测用户的物理操作(按键、点击、触摸、摇杆),并将其转化为游戏可以使用的数据

Unity的Input Manager帮我们完成了底层的所有繁重工作------它已经知道键盘上每个按键的编号、鼠标的运动增量、触摸屏的多点触控信息。你不需要关心操作系统层面的输入细节,只需要调用Unity提供的Input API。

1.2 Input Manager的配置

打开 Edit > Project Settings > Input Manager,你会看到大量的输入轴(Axes)定义。默认配置包含:

  • Horizontal(水平轴):←→方向键 和 A/D键
  • Vertical(垂直轴):↑↓方向键 和 W/S键
  • Fire1、Fire2、Fire3(开火键):左Ctrl/鼠标左键、左Alt/鼠标右键、左Shift/鼠标中键
  • Jump(跳跃):空格键
  • Mouse X / Mouse Y(鼠标移动)
  • Mouse ScrollWheel(鼠标滚轮)

你可以在这里添加自定义的输入轴,给特定的按键或摇杆映射一个有意义的名称。

二、键盘输入检测

2.1 Input.GetKey ------ 持续检测

Input.GetKey(KeyCode key) 在按住指定键的每一帧都返回true。适合用于持续性的操作:

csharp 复制代码
void Update()
{
    // 按住W键持续向前移动
    if (Input.GetKey(KeyCode.W))
    {
        transform.Translate(Vector3.forward * speed * Time.deltaTime);
    }
    
    // 按住Shift键加速跑
    if (Input.GetKey(KeyCode.LeftShift))
    {
        currentSpeed = runSpeed;
    }
    else
    {
        currentSpeed = walkSpeed;
    }
}

2.2 Input.GetKeyDown ------ 按下瞬间

Input.GetKeyDown(KeyCode key) 只在按键被按下的那一帧返回true。适合触发一次性动作:

csharp 复制代码
void Update()
{
    // 按空格键跳跃(只触发一次)
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Jump();
    }
    
    // 按R键重新装弹
    if (Input.GetKeyDown(KeyCode.R))
    {
        Reload();
    }
    
    // 按Escape键打开/关闭菜单
    if (Input.GetKeyDown(KeyCode.Escape))
    {
        TogglePauseMenu();
    }
}

2.3 Input.GetKeyUp ------ 松开瞬间

Input.GetKeyUp(KeyCode key) 在按键被松开的那一帧返回true:

csharp 复制代码
void Update()
{
    // 松开Shift键时停止冲刺
    if (Input.GetKeyUp(KeyCode.LeftShift))
    {
        StopSprinting();
    }
    
    // 松开鼠标按键时停止蓄力
    if (Input.GetKeyUp(KeyCode.Mouse0))
    {
        ReleaseChargedAttack();
    }
}

2.4 常用KeyCode枚举

csharp 复制代码
// 字母键
KeyCode.A 到 KeyCode.Z

// 数字键
KeyCode.Alpha0 到 KeyCode.Alpha9
KeyCode.Keypad0 到 KeyCode.Keypad9  // 小键盘数字

// 功能键
KeyCode.Space       // 空格
KeyCode.Return      // 回车
KeyCode.Escape      // Esc
KeyCode.Tab         // Tab
KeyCode.Backspace   // 退格
KeyCode.Delete      // Delete

// 方向键
KeyCode.UpArrow / KeyCode.DownArrow
KeyCode.LeftArrow / KeyCode.RightArrow

// 修饰键
KeyCode.LeftShift / KeyCode.RightShift
KeyCode.LeftControl / KeyCode.RightControl
KeyCode.LeftAlt / KeyCode.RightAlt

// 鼠标
KeyCode.Mouse0  // 鼠标左键
KeyCode.Mouse1  // 鼠标右键
KeyCode.Mouse2  // 鼠标中键

// 功能键F1-F12
KeyCode.F1 到 KeyCode.F12

2.5 Input.anyKeyDown ------ 任意键

csharp 复制代码
void Update()
{
    // 检测是否按下了任意键(用于"按任意键开始")
    if (Input.anyKeyDown)
    {
        StartGame();
    }
}

三、轴输入(Axes Input)

3.1 Input.GetAxis ------ 平滑轴值

💡 Input.GetAxis(string axisName) 返回一个-1到1之间的平滑浮点值。这是Unity输入系统中使用最广泛的API。

csharp 复制代码
void Update()
{
    // 获取水平轴(←→方向键或A/D键)
    float horizontal = Input.GetAxis("Horizontal");
    
    // 获取垂直轴(↑↓方向键或W/S键)
    float vertical = Input.GetAxis("Vertical");
    
    // 组合成移动向量
    Vector3 movement = new Vector3(horizontal, 0, vertical);
    transform.Translate(movement * speed * Time.deltaTime);
}

GetAxis的特点:值是平滑过渡的。按键瞬间值不会立即从0跳到1,而是有一个渐变过程(类似加速度)。这让角色移动、镜头旋转等操作手感更加自然。

3.2 Input.GetAxisRaw ------ 原始轴值

Input.GetAxisRaw(string axisName) 与GetAxis的区别在于:它没有平滑过渡,值直接是-1、0或1。

csharp 复制代码
// 用于需要"即时响应"的场景(如2D格斗游戏的左右移动)
float rawHorizontal = Input.GetAxisRaw("Horizontal");
// rawHorizontal 的值只可能是 -1, 0, 或 1,没有过渡值

3.3 默认输入轴

轴名称 正方向键 负方向键 描述
Horizontal → / D ← / A 水平移动
Vertical ↑ / W ↓ / S 垂直移动
Mouse X 鼠标右移 鼠标左移 鼠标水平移动增量
Mouse Y 鼠标上移 鼠标下移 鼠标垂直移动增量
Mouse ScrollWheel 向上滚动 向下滚动 鼠标滚轮
Fire1 左Ctrl / 鼠标左键 - 主开火键
Fire2 左Alt / 鼠标右键 - 副开火键
Fire3 左Shift / 鼠标中键 - 第三开火键
Jump 空格键 - 跳跃

3.4 自定义输入轴

在Input Manager中点击一个轴的名称旁边的三角形展开详细设置:

  • Name:轴的名称(代码中调用的字符串)
  • Descriptive Name:描述性名称
  • Negative Button / Positive Button:负方向和正方向对应的按键
  • Alt Negative / Alt Positive:备选按键
  • Gravity:按键松开后值回弹到0的速度
  • Sensitivity:按键按下后值变化到1或-1的速度
  • Snap:勾选后按下反向键时值立即归零
  • Dead Zone:死区(摇杆用,小幅度偏移被忽略)

四、鼠标输入检测

4.1 鼠标按键检测

csharp 复制代码
void Update()
{
    // 检测鼠标按键(和键盘按键检测方式一样,使用KeyCode.Mouse0/1/2)
    if (Input.GetKeyDown(KeyCode.Mouse0))
    {
        Debug.Log("鼠标左键按下");
    }
    
    if (Input.GetKey(KeyCode.Mouse1))
    {
        Debug.Log("鼠标右键按住中");
    }
    
    // 也可以使用Button名称
    if (Input.GetButtonDown("Fire1"))
    {
        Debug.Log("Fire1按下(等同于鼠标左键或左Ctrl)");
    }
}

4.2 鼠标位置

csharp 复制代码
void Update()
{
    // 获取鼠标在屏幕上的像素坐标(左下角为原点)
    Vector3 mouseScreenPos = Input.mousePosition;
    
    // 从屏幕坐标生成射线(3D场景中的鼠标拾取)
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    
    if (Physics.Raycast(ray, out RaycastHit hit))
    {
        Debug.Log($"鼠标指向了:{hit.collider.gameObject.name}");
    }
}

4.3 鼠标移动增量

csharp 复制代码
void Update()
{
    // 获取鼠标本帧的移动增量
    float mouseX = Input.GetAxis("Mouse X");
    float mouseY = Input.GetAxis("Mouse Y");
    
    // 使用鼠标增量旋转视角(FPS游戏的标准做法)
    transform.Rotate(Vector3.up * mouseX * mouseSensitivity);
    cameraTransform.Rotate(Vector3.left * mouseY * mouseSensitivity);
}

⚠️ 注意Mouse X和Mouse Y返回的是"本帧的移动增量",而不是绝对坐标。这是为了处理FPS游戏中的鼠标旋转(FPS游戏通常不关心鼠标的绝对位置,只关心它移动了多少)。

4.4 鼠标滚轮

csharp 复制代码
void Update()
{
    float scroll = Input.GetAxis("Mouse ScrollWheel");
    
    if (scroll != 0)
    {
        // 滚轮向上为正,向下为负
        // 典型用途:缩放相机
        Camera.main.fieldOfView -= scroll * zoomSpeed;
        Camera.main.fieldOfView = Mathf.Clamp(Camera.main.fieldOfView, 15f, 90f);
    }
}

4.5 光标管理

csharp 复制代码
// 隐藏并锁定光标(FPS游戏标准做法)
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;

// 限制光标在窗口内但不隐藏
Cursor.lockState = CursorLockMode.Confined;

// 恢复正常光标
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;

五、移动端触摸检测

5.1 多点触控基础

csharp 复制代码
void Update()
{
    // 检测当前帧的触摸点数量
    if (Input.touchCount > 0)
    {
        // 获取第一个触摸点
        Touch touch = Input.GetTouch(0);
        
        switch (touch.phase)
        {
            case TouchPhase.Began:
                Debug.Log($"手指按下,位置:{touch.position}");
                break;
                
            case TouchPhase.Moved:
                Debug.Log($"手指移动,位置:{touch.position},增量:{touch.deltaPosition}");
                break;
                
            case TouchPhase.Stationary:
                // 手指没动但还按着
                break;
                
            case TouchPhase.Ended:
                Debug.Log("手指抬起");
                break;
                
            case TouchPhase.Canceled:
                Debug.Log("触摸被系统中断(如来电)");
                break;
        }
    }
}

5.2 遍历所有触摸点

csharp 复制代码
void Update()
{
    for (int i = 0; i < Input.touchCount; i++)
    {
        Touch touch = Input.GetTouch(i);
        
        // fingerId是触摸点的唯一标识(同一根手指的整个触摸周期不变)
        Debug.Log($"手指{touch.fingerId}:{touch.phase},位置:{touch.position}");
    }
}

5.3 Touch结构体的属性

属性 类型 描述
fingerId int 手指标识(同一手指不变)
position Vector2 当前触摸位置(屏幕像素坐标)
deltaPosition Vector2 自上一帧的位置变化
deltaTime float 自上一帧的时间间隔
tapCount int 连击次数
phase TouchPhase 触摸阶段(Began/Moved/Stationary/Ended/Canceled)
pressure float 按压力度(支持3D Touch的设备)
radius float 触摸区域的估算半径

5.4 移动端输入适配

csharp 复制代码
// 同时支持键盘、鼠标和触摸的移动控制
void Update()
{
    Vector3 movement = Vector3.zero;
    
    // PC端:键盘
    #if UNITY_STANDALONE || UNITY_EDITOR
    movement.x = Input.GetAxis("Horizontal");
    movement.z = Input.GetAxis("Vertical");
    #endif
    
    // 移动端:触摸(虚拟摇杆或滑动)
    #if UNITY_IOS || UNITY_ANDROID
    if (Input.touchCount > 0)
    {
        Touch touch = Input.GetTouch(0);
        // 简易滑动控制
        movement.x = touch.deltaPosition.x * touchSensitivity;
        movement.z = touch.deltaPosition.y * touchSensitivity;
    }
    #endif
    
    transform.Translate(movement * speed * Time.deltaTime);
}

六、Input Button------更抽象的输入层

6.1 GetButton / GetButtonDown / GetButtonUp

除了直接检测按键,Unity还提供了一层更抽象的输入:Button。Button通过名称来引用,具体的按键映射在Input Manager中配置。

csharp 复制代码
void Update()
{
    // 使用Button名称(更灵活,可以在InputManager中重新映射按键)
    if (Input.GetButtonDown("Jump"))
    {
        Jump();
    }
    
    if (Input.GetButton("Fire1"))
    {
        // 持续开火(按住连发)
    }
    
    if (Input.GetButtonUp("Fire2"))
    {
        // 松开副开火键
    }
}

💡 使用Button而不是直接检测KeyCode的好处是:玩家可以在游戏设置中重新映射按键,你的代码不需要修改------只需要在Input Manager中更改Button的映射即可。

七、完整的角色控制器示例

csharp 复制代码
using UnityEngine;

public class CharacterController : MonoBehaviour
{
    [Header("移动设置")]
    public float walkSpeed = 5f;
    public float runSpeed = 10f;
    public float jumpForce = 8f;
    
    [Header("视角设置")]
    public float mouseSensitivity = 2f;
    public float maxLookAngle = 80f;
    
    private Rigidbody rb;
    private Camera playerCamera;
    private float verticalLookRotation;
    private bool isGrounded;
    
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        playerCamera = GetComponentInChildren<Camera>();
        
        // FPS游戏标准光标设置
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
    }
    
    void Update()
    {
        // --- 鼠标视角控制 ---
        float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
        float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
        
        // 水平旋转(角色整体旋转)
        transform.Rotate(Vector3.up * mouseX);
        
        // 垂直旋转(只旋转相机,限制角度)
        verticalLookRotation -= mouseY;
        verticalLookRotation = Mathf.Clamp(verticalLookRotation, -maxLookAngle, maxLookAngle);
        playerCamera.transform.localRotation = Quaternion.Euler(verticalLookRotation, 0, 0);
        
        // --- 移动 ---
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        
        Vector3 movement = transform.right * horizontal + transform.forward * vertical;
        
        float currentSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed;
        movement *= currentSpeed;
        
        // 保持Y轴速度(重力影响)
        movement.y = rb.velocity.y;
        rb.velocity = movement;
        
        // --- 跳跃 ---
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        }
        
        // --- 其他操作 ---
        if (Input.GetKeyDown(KeyCode.E))
        {
            Interact();
        }
        
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Cursor.lockState = CursorLockMode.None;
            Cursor.visible = true;
        }
    }
    
    void Interact()
    {
        // 交互逻辑
    }
}

八、本篇总结

✅ 本文核心知识点回顾:

  • Input.GetKey检测键盘持续按下,GetKeyDown检测按下瞬间,GetKeyUp检测松开瞬间
  • Input.GetAxis返回平滑过渡的轴值(-1到1),GetAxisRaw返回离散值(-1/0/1)
  • Input.mousePosition获取鼠标屏幕坐标,Mouse X/Y获取移动增量
  • Input.GetTouch获取移动端触摸信息,支持多点触控
  • Cursor.lockState管理光标隐藏和锁定
  • 使用Button名称而非直接检测KeyCode,方便后期重新映射按键

📝 下一篇文章将深入讲解Time.deltaTime------让物体移动与帧率无关的核心概念。

动手任务

① 用Input.GetKey实现WASD移动+空格跳跃

② 用Input.GetAxis和Mouse X/Y实现FPS视角旋转

③ 在移动设备上测试触摸检测


关于作者:拥有多年Unity开发经验的游戏开发者,专注Unity系统化教学与最佳实践分享。

相关推荐
zhchyun200820 小时前
【Unity UI 进阶】仿 Element UI 打造企业级 Unity UI 组件库(05)
ui·unity·游戏引擎
_ZHOURUI_H_1 天前
不做完整 ECS,只优化数据布局:Unity EasyECS 到底是什么
unity·性能优化·游戏引擎
郝学胜-神的一滴2 天前
Horse3D 游戏引擎研发笔记(七):Clydesdale——从流式日志到多输出订阅
c++·qt·unity·游戏引擎·图形渲染·unreal engine·opengl
caimouse2 天前
ReactOS 窗口系统分析(23):键盘 — keyboard.c + kbdlayout.c
c语言·开发语言·计算机外设
江上闲人.3 天前
Wardogs战犬\战狗Beta测试启动报错UE崩溃卡顿?解决方法来了
游戏·游戏引擎·虚幻·战犬·战狗·wardogs
小贺儿开发3 天前
Unity 程序员编曲花絮:汪峰《河流》细节修复
科技·学习·unity·程序员·音乐·demo·写代码
598866753@qq.com3 天前
Unity UniText笔记
unity·unitext
杨丰玮4183 天前
从零手写Java飞机躲障碍游戏|Swing绘图、鼠标跟随、计时器碰撞检测实战(五)
java·python·游戏·游戏引擎·图形渲染·动画·贴图