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系统化教学与最佳实践分享。