Unity的输入系统
在 Unity 中,鼠标和键盘操作是交互开发的核心,主要通过 Input 系统 实现(包括旧版 Input Manager 和新版 Input System)。以下是详细的操作方法、常用 API 和实战示例,覆盖大部分场景:
一、核心概念:两种 Input 系统
Unity 提供两种输入系统,根据项目需求选择:
| 特性 | 旧版 Input Manager(默认) | 新版 Input System(推荐) |
|---|---|---|
| 易用性 | 简单直观,无需额外安装 | 功能强大,支持多设备、重映射 |
| 兼容性 | 全平台支持,无需配置 | 需要安装包(Package Manager) |
| 代码复杂度 | 低(静态 API 调用) | 中等(需配置 Input Action) |
| 适用场景 | 快速原型、简单项目 | 复杂交互、多平台项目(PC/主机/移动) |
切换 Input 系统
- 新版 Input System 需先在 Package Manager 中安装
Input System包。 - 安装后会提示切换输入系统,选择 Yes(重启 Unity 生效)。
二、旧版 Input Manager(快速上手)
适合快速开发,直接通过 Input 类静态方法获取输入,无需额外配置。
1. 键盘操作
(1)基础按键检测(按下/抬起/长按)
| 功能 | API 示例 | 说明 |
|---|---|---|
| 按键按下(单次触发) | Input.GetKeyDown(KeyCode.Space) |
按键按下的第一帧返回 true(如跳跃) |
| 按键抬起(单次触发) | Input.GetKeyUp(KeyCode.Space) |
按键抬起的第一帧返回 true |
| 按键长按(持续触发) | Input.GetKey(KeyCode.W) |
按键按住期间每帧返回 true(如移动) |
(2)常用 KeyCode 枚举(键盘按键)
- 方向键:
KeyCode.UpArrow、KeyCode.DownArrow、KeyCode.LeftArrow、KeyCode.RightArrow - 字母数字:
KeyCode.A、KeyCode.1、KeyCode.Zero - 功能键:
KeyCode.Space(空格)、KeyCode.Escape(ESC)、KeyCode.LeftShift(左Shift) - 特殊键:
KeyCode.Return(回车)、KeyCode.Tab(Tab)、KeyCode.Backspace(退格)
(3)轴向输入(如 WASD/方向键移动)
Unity 默认配置了 轴向输入(无需额外设置),直接获取 -1~1 之间的数值,适合平滑移动:
// 水平轴:A/左箭头返回 -1,D/右箭头返回 1,无操作返回 0
float horizontal = Input.GetAxis("Horizontal");
// 垂直轴:S/下箭头返回 -1,W/上箭头返回 1,无操作返回 0
float vertical = Input.GetAxis("Vertical");
// 平滑移动示例(绑定到 Transform)
Vector3 moveDir = new Vector3(horizontal, 0, vertical).normalized;
transform.Translate(moveDir * speed * Time.deltaTime);
Input.GetAxis("Horizontal"):带平滑过渡(从 0 渐变到 ±1),适合移动。Input.GetAxisRaw("Horizontal"):无平滑(直接返回 -1/0/1),适合快速响应(如菜单切换)。
2. 鼠标操作
(1)鼠标按键检测
| 功能 | API 示例 | 说明 |
|---|---|---|
| 左键按下 | Input.GetMouseButtonDown(0) |
0=左键,1=右键,2=中键(滚轮按下) |
| 左键抬起 | Input.GetMouseButtonUp(0) |
单次触发 |
| 左键长按 | Input.GetMouseButton(0) |
持续触发 |
(2)鼠标位置(屏幕坐标)
- 屏幕坐标原点:左下角(x=0, y=0),右上角(x=Screen.width, y=Screen.height)。
- API:
Vector3 mousePos = Input.mousePosition;// 示例:获取鼠标在屏幕中央的偏移 float offsetX = Input.mousePosition.x - Screen.width / 2; float offsetY = Input.mousePosition.y - Screen.height / 2;
(3)鼠标滚轮(滚动量)
- API:
float scroll = Input.mouseScrollDelta.y;- 向上滚动:返回正值(默认 1)
- 向下滚动:返回负值(默认 -1)
- 示例(缩放物体):
float scroll = Input.mouseScrollDelta.y; transform.localScale += new Vector3(scroll * 0.1f, scroll * 0.1f, scroll * 0.1f); transform.localScale = Vector3.Max(transform.localScale, new Vector3(0.5f, 0.5f, 0.5f)); // 限制最小缩放
(4)鼠标拖拽(屏幕空间)
通过检测鼠标按下 + 鼠标位置变化实现拖拽:
private bool isDragging = false;
private Vector3 offset; // 鼠标与物体的偏移(避免物体瞬移到鼠标位置)
void OnMouseDown() // 鼠标点击物体时触发(需物体有 Collider)
{
// 计算鼠标世界坐标与物体中心的偏移
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
offset = transform.position - hit.point;
isDragging = true;
}
}
void OnMouseUp()
{
isDragging = false;
}
void Update()
{
if (isDragging)
{
// 将鼠标屏幕坐标转换为世界坐标
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
transform.position = hit.point + offset; // 保持偏移,平滑拖拽
}
}
}
- 注意:物体必须有 Collider(如
BoxCollider),OnMouseDown才会触发。
三、新版 Input System(推荐,功能更强)
新版 Input System 支持多设备、按键重映射、自定义输入行为,适合复杂项目。以下是核心用法:
1. 前期配置
- 安装
Input System包(Package Manager → 搜索 Input System → 安装)。 - 创建 Input Action 资源:右键 Project 窗口 → Input Actions → 命名(如
PlayerInputActions)。 - 双击打开配置窗口,添加输入映射(示例:移动、跳跃、鼠标拖拽):
- 新建 Action Map(如
Player)。 - 添加 Action(如
Move,类型设为Value→Vector2;Jump设为Button)。 - 为 Action 绑定按键:
Move:绑定 WASD(Keyboard/W/A/S/D)和方向键(Keyboard/Arrow Keys/Up/Down/Left/Right)。Jump:绑定 Space(Keyboard/Space)。MouseDrag:绑定 Left Button(Mouse/Left Button)。
- 新建 Action Map(如
2. 代码中使用(C#)
(1)基础输入检测(通过回调)
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
private PlayerInputActions inputActions;
void Awake()
{
// 初始化 Input Action
inputActions = new PlayerInputActions();
// 绑定按键回调(Jump 按下时触发)
inputActions.Player.Jump.performed += OnJumpPerformed;
// 绑定移动轴向输入(持续回调)
inputActions.Player.Move.performed += OnMovePerformed;
inputActions.Player.Move.canceled += OnMoveCanceled; // 移动停止时触发
}
void OnJumpPerformed(InputAction.CallbackContext context)
{
Debug.Log("跳跃触发!");
// 跳跃逻辑(如添加向上的力)
GetComponent<Rigidbody>().AddForce(Vector3.up * 5f, ForceMode.Impulse);
}
void OnMovePerformed(InputAction.CallbackContext context)
{
Vector2 moveInput = context.ReadValue<Vector2>(); // 获取 WASD/方向键输入(x=-1~1, y=-1~1)
Vector3 moveDir = new Vector3(moveInput.x, 0, moveInput.y).normalized;
transform.Translate(moveDir * 3f * Time.deltaTime);
}
void OnMoveCanceled(InputAction.CallbackContext context)
{
// 移动停止时的逻辑(如减速)
}
void OnEnable()
{
inputActions.Enable(); // 启用输入
}
void OnDisable()
{
inputActions.Disable(); // 禁用输入(避免后台触发)
}
}
(2)鼠标操作(新版 Input System)
绑定鼠标相关 Action 后,通过 ReadValue 获取输入:
// 1. 配置 Input Action:
// - 添加 Action `MousePosition`(类型 Value → Vector2),绑定 Mouse/Position。
// - 添加 Action `MouseScroll`(类型 Value → Vector2),绑定 Mouse/Scroll。
// - 添加 Action `LeftClick`(类型 Button),绑定 Mouse/Left Button。
// 2. 代码中读取:
void Awake()
{
inputActions = new PlayerInputActions();
// 鼠标点击回调
inputActions.Player.LeftClick.performed += ctx => Debug.Log("鼠标左键点击!");
// 鼠标滚轮回调
inputActions.Player.MouseScroll.performed += ctx =>
{
Vector2 scroll = ctx.ReadValue<Vector2>();
Debug.Log("滚轮滚动:" + scroll.y);
};
}
void Update()
{
// 实时获取鼠标位置(屏幕坐标)
Vector2 mousePos = inputActions.Player.MousePosition.ReadValue<Vector2>();
Debug.Log("鼠标位置:" + mousePos);
}
(3)鼠标拖拽(世界空间,新版)
private bool isDragging = false;
private Vector3 offset;
void Awake()
{
inputActions = new PlayerInputActions();
// 鼠标按下时开始拖拽
inputActions.Player.LeftClick.performed += ctx => StartDrag();
// 鼠标抬起时结束拖拽
inputActions.Player.LeftClick.canceled += ctx => isDragging = false;
}
void StartDrag()
{
Ray ray = Camera.main.ScreenPointToRay(inputActions.Player.MousePosition.ReadValue<Vector2>());
if (Physics.Raycast(ray, out RaycastHit hit) && hit.collider.gameObject == gameObject)
{
offset = transform.position - hit.point;
isDragging = true;
}
}
void Update()
{
if (isDragging)
{
Ray ray = Camera.main.ScreenPointToRay(inputActions.Player.MousePosition.ReadValue<Vector2>());
if (Physics.Raycast(ray, out RaycastHit hit))
{
transform.position = hit.point + offset;
}
}
}
四、常见问题与注意事项
-
物体不响应鼠标事件:
- 确保物体有 Collider(如
BoxCollider、SphereCollider),且Is Trigger未勾选(或勾选后用OnTrigger事件)。 - 确保相机是
MainCamera(或代码中指定正确的相机)。
- 确保物体有 Collider(如
-
轴向输入无响应:
- 旧版:检查
Edit → Project Settings → Input Manager → Axes中是否有Horizontal和Vertical轴(默认存在)。 - 新版:检查 Input Action 中是否正确绑定了按键,且代码中启用了
inputActions。
- 旧版:检查
-
鼠标位置转换问题:
- 屏幕坐标 → 世界坐标:必须用
Camera.ScreenPointToRay(2D 用Camera.ScreenToWorldPoint,需设置 z 轴距离)。 - 示例(2D 鼠标位置转换):
Vector3 mouseScreenPos = new Vector3(inputActions.Player.MousePosition.ReadValue<Vector2>().x, inputActions.Player.MousePosition.ReadValue<Vector2>().y, Camera.main.nearClipPlane); Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(mouseScreenPos); mouseWorldPos.z = 0; // 2D 项目中 z 轴设为 0
- 屏幕坐标 → 世界坐标:必须用
-
输入延迟:
- 旧版:
Input.GetAxis有平滑过渡,如需即时响应改用Input.GetAxisRaw。 - 新版:确保 Action 类型正确(Button 适合单次触发,Value 适合持续输入)。
- 旧版:
五、实战示例:PC 端第三人称移动(旧版 Input Manager)
using UnityEngine;
public class ThirdPersonMovement : MonoBehaviour
{
public float moveSpeed = 5f;
public float rotateSpeed = 10f;
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.freezeRotation = true; // 冻结旋转,避免物理碰撞导致旋转
}
void Update()
{
// 获取键盘输入
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
Vector3 moveDir = new Vector3(horizontal, 0, vertical).normalized;
// 移动
if (moveDir.magnitude > 0.1f)
{
// 旋转朝向移动方向
Quaternion targetRot = Quaternion.LookRotation(moveDir);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, rotateSpeed * Time.deltaTime);
// 平移
rb.MovePosition(transform.position + moveDir * moveSpeed * Time.deltaTime);
}
// 鼠标滚轮缩放相机
float scroll = Input.mouseScrollDelta.y;
Camera.main.fieldOfView = Mathf.Clamp(Camera.main.fieldOfView - scroll * 2f, 40f, 80f);
}
}
总结
- 简单项目/原型:用旧版 Input Manager,代码简洁,无需配置。
- 复杂项目/多平台:用新版 Input System,支持重映射、多设备,扩展性更强。
- 核心要点:鼠标操作需结合 Collider 和射线检测,键盘操作优先使用轴向输入(平滑移动),按键检测区分“单次触发”(GetKeyDown)和“持续触发”(GetKey)。
根据项目需求选择合适的输入系统,配合 Collider、射线检测等功能,可实现绝大多数 PC 端交互逻辑。