目录
[1 Input Manager](#1 Input Manager)
[1.1 核心概念与配置](#1.1 核心概念与配置)
[1.2 常用方法](#1.2 常用方法)
[1.2.1 轴输入方法](#1.2.1 轴输入方法)
[1.2.1.1 Input.GetAxisRaw(String AxisName)](#1.2.1.1 Input.GetAxisRaw(String AxisName))
[1.2.1.2 Input.GetAxisRaw(String AxisName)](#1.2.1.2 Input.GetAxisRaw(String AxisName))
[1.2.2 按键输入方法](#1.2.2 按键输入方法)
[1.2.3 鼠标输入方法](#1.2.3 鼠标输入方法)
[1.2.3.1 鼠标按钮输入](#1.2.3.1 鼠标按钮输入)
[1.2.3.2 鼠标移动输入](#1.2.3.2 鼠标移动输入)
[1.2.3.3 鼠标滚轮输入](#1.2.3.3 鼠标滚轮输入)
[1.2.4 虚拟按钮方法](#1.2.4 虚拟按钮方法)
[1.2.5 综合示例:第一人称角色控制](#1.2.5 综合示例:第一人称角色控制)
[2 Input System](#2 Input System)
[2.1 核心概念](#2.1 核心概念)
[2.2 安装和设置](#2.2 安装和设置)
[2.2.1 安装流程](#2.2.1 安装流程)
[2.2.2 基础设置](#2.2.2 基础设置)
[2.3 案例](#2.3 案例)
[2.3.1 可视化配置](#2.3.1 可视化配置)
[2.3.1.1 创建 Input Action Asset](#2.3.1.1 创建 Input Action Asset)
[2.3.1.2 Input Action Editor 中配置](#2.3.1.2 Input Action Editor 中配置)
[2.3.2 搭建场景](#2.3.2 搭建场景)
[2.3.3 方式1:手动注册回调](#2.3.3 方式1:手动注册回调)
[2.3.4 方式2:PlayerInput组件(推荐)](#2.3.4 方式2:PlayerInput组件(推荐))
Unity的输入系统主要有两套:传统的Input Manager和较新的Input System。下面将详细介绍这两者,包括它们的特性、使用方法以及区别。
1 Input Manager
这是Unity内置的、历史悠久的输入系统,通过预定义的"虚拟轴(Virtual Axes)"和"按钮(Buttons)"来工作。
1.1 核心概念与配置
虚拟轴(Virtual Axes):一个抽象的概念,它将多个物理输入(如键盘的A/D键、手柄摇杆)映射到一个统一的数值范围(例如 -1 到 1)。这让你无需在代码中关心玩家具体使用了哪个键。
**配置位置:**Edit > Project Settings > Input Manager
配置内容:在这里可以看到预定义的轴(如Horizontal,Vertical, Fire1)并可以创建新的。每个轴可以绑定多个正向(Positive Button)和负向(Negative Button)按键,甚至可以绑定鼠标移动和手柄摇杆。


每个轴包含以下重要属性:
Name: 轴的标识名称(在代码中使用)
Descriptive Name/ Negative Name: 在输入设置中显示的名称
Positive Button/Negative Button: 正/负方向的按键绑定
Alt Positive/Alt Negative Button: 备用按键
Gravity: 松开按键后轴值归零的速度
Dead: 摇杆死区值
Sensitivity: 轴值变化的速度
Snap: 启用时,当按下相反方向键时立即归零
Type: 输入类型(按键/鼠标/摇杆)
1.2 常用方法
1.2.1 轴输入方法
1.2.1.1 Input.GetAxisRaw(String AxisName)
解释:获取平滑滤波后的轴值,返回-1到1之间的浮点数。
代码示例:
cs
void Update()
{
// 水平移动 (A/D 或 左右箭头)
float horizontal = Input.GetAxis("Horizontal");
// 垂直移动 (W/S 或 上下箭头)
float vertical = Input.GetAxis("Vertical");
// 鼠标水平移动
float mouseX = Input.GetAxis("Mouse X");
// 鼠标垂直移动
float mouseY = Input.GetAxis("Mouse Y");
// 使用示例:角色移动
Vector3 movement = new Vector3(horizontal, 0, vertical);
transform.Translate(movement * moveSpeed * Time.deltaTime);
// 使用示例:相机旋转
transform.Rotate(0, mouseX * rotationSpeed, 0);
}
1.2.1.2 Input.GetAxisRaw(String AxisName)
解释:获取未经平滑处理的原始轴值,只返回-1、0或1。
代码示例:
cs
void Update()
{
// 立即响应,没有平滑过渡
float horizontalRaw = Input.GetAxisRaw("Horizontal");
float verticalRaw = Input.GetAxisRaw("Vertical");
// 适合需要即时响应的场景,如格斗游戏
if (horizontalRaw != 0)
{
// 立即转向
}
}
GetAxis和GetAxisRaw区别
GetAxis:有平滑过渡,适合角色移动
GetAxisRaw:无平滑,立即响应,适合菜单导航或即时转向
1.2.2 按键输入方法
- Input.GetKey(KeyCode.指定键):当指定按键被按住时返回true
- Input.GetKeyDown(KeyCode.指定键):在按键按下的第一帧返回true
- Input.GetKeyUp(KeyCode.指定键):在按键释放的第一帧返回true
1.2.3 鼠标输入方法
Input Manager 提供了全面的鼠标输入处理功能,包括按钮点击、移动、滚轮和屏幕坐标等。
1.2.3.1 鼠标按钮输入
cs
void Update()
{
// 方法一:使用 GetMouseButtonDown - 按下瞬间触发
if (Input.GetMouseButtonDown(0)) // 左键
{
Debug.Log("鼠标左键按下");
}
if (Input.GetMouseButtonDown(1)) // 右键
{
Debug.Log("鼠标右键按下");
}
if (Input.GetMouseButtonDown(2)) // 中键
{
Debug.Log("鼠标中键按下");
}
// 方法二:使用 GetMouseButton - 持续按住时触发
if (Input.GetMouseButton(0))
{
// 每帧都会执行,适合拖拽、持续射击等
Debug.Log("鼠标左键持续触发");
}
// 方法三:使用 GetMouseButtonUp - 释放瞬间触发
if (Input.GetMouseButtonUp(0))
{
Debug.Log("鼠标左键释放");
}
}
鼠标按钮编号对应关系
0 = 左键 (Left Button)
1 = 右键 (Right Button)
2 = 中键 (Middle Button)
3+ = 额外按钮(某些游戏鼠标的侧键等)
1.2.3.2 鼠标移动输入
获取鼠标水平移动(左右移动):float mouseX = Input.GetAxis("Mouse X");
获取鼠标垂直移动(上下移动):float mouseY = Input.GetAxis("Mouse Y");
代码示例:
cs
public class CameraRotate : MonoBehaviour
{
public float mouseSensitivity = 100f;
public Transform playerBody;
private float xRotation = 0f;
void Start()
{
// 锁定光标到屏幕中心并隐藏
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
// 获取鼠标输入
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity * Time.deltaTime;
// 处理垂直方向旋转(上下看)
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f); // 限制上下视角范围
// 应用垂直旋转到摄像机
transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
// 处理水平方向旋转(左右看)
if (playerBody != null)
{
playerBody.Rotate(Vector3.up * mouseX);
}
else
{
// 如果没有指定玩家身体,直接旋转摄像机
transform.Rotate(Vector3.up * mouseX);
}
}
}
挂载方式:
- 将脚本挂载到摄像机对象上
- 如果使用第一人称角色,通常摄像机是玩家对象的子物体
父物体 Player:空对象,挂载角色控制器等
子物体 Main Camera:挂载MouseLook脚本,playerBody指向Player参数说明:
- mouseSensitivity:鼠标灵敏度,值越大旋转越快
- playerBody:可选的玩家身体Transform,用于水平旋转整个玩家对象
如果需要退出光标锁定模式,可以按ESC键,或者在代码中添加:
cs
// 在Update方法中添加
if (Input.GetKeyDown(KeyCode.Escape))
{
Cursor.lockState = CursorLockMode.None;
}
1.2.3.3 鼠标滚轮输入
获取鼠标滚轮输入:float scrollWheel = Input.GetAxis("Mouse ScrollWheel");
返回值说明:
- > 0 : 向前滚动(远离用户)
- < 0 : 向后滚动(靠近用户)
- = 0 : 没有滚动
1.2.4 虚拟按钮方法
解释:通过Input Manager中配置的按钮名称来检测,而不是直接使用KeyCode。
Input.GetButton("Fire1"):按住Fire1(默认鼠标左键或左Ctrl)
Input.GetButtonDown("Jump"): 按下Jump按钮(默认空格键)
Input.GetButtonUp("Fire2"):释放Fire2按钮(默认鼠标右键或左Alt)
优点简单易用:几行代码就能实现基本输入
无需安装:Unity内置功能
快速原型:适合快速验证想法
学习成本低:概念简单,上手快
局限
轮询开销:每帧检查输入状态
配置复杂:复杂输入配置繁琐
难以重绑定:玩家自定义按键困难
代码耦合:输入逻辑与游戏逻辑紧密绑定
Input Manager虽然功能相对基础,但对于小型项目、原型开发或简单的移动端游戏来说,仍然是一个可靠且高效的选择。
1.2.5 综合示例:第一人称角色控制
创建游戏物体,配置结构:
Player (空物体,挂载CharacterController 组件和FirstPersonController 脚本)
├── Main Camera (摄像机)
├── PlayerModel (角色模型)
cs
public class FirstPersonController : MonoBehaviour
{
[Header("移动设置")]
public float walkSpeed = 5f; // 行走速度
public float runSpeed = 10f; // 奔跑速度
public float jumpForce = 8f; // 跳跃力量
public float gravity = 20f; // 重力强度
[Header("视角设置")]
public float mouseSensitivity = 2f; // 鼠标灵敏度
public float lookUpLimit = 80f; // 向上看的角度限制
[Header("蹲下设置")]
public float crouchHeight = 1f; // 蹲下时的高度
public float standHeight = 2f; // 站立时的高度
public float crouchSpeed = 2.5f; // 蹲下时的移动速度
private CharacterController characterController;
private Camera playerCamera;
private Vector3 moveDirection = Vector3.zero;
private float rotationX = 0;
private bool isCrouching = false;
private float currentSpeed;
void Start()
{
// 获取组件引用
characterController = GetComponent<CharacterController>();
playerCamera = GetComponentInChildren<Camera>();
// 锁定鼠标到屏幕中心并隐藏
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
// 设置初始高度
characterController.height = standHeight;
currentSpeed = walkSpeed;
}
void Update()
{
HandleMouseLook(); // 处理鼠标视角
HandleMovement(); // 处理移动
HandleJump(); // 处理跳跃
HandleCrouch(); // 处理蹲下
HandleRun(); // 处理奔跑
}
// 处理鼠标视角旋转
void HandleMouseLook()
{
// 获取鼠标输入
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
// 左右旋转(Y轴)- 旋转整个游戏物体
transform.Rotate(0, mouseX, 0);
// 上下旋转(X轴)- 只旋转摄像机
rotationX -= mouseY;
rotationX = Mathf.Clamp(rotationX, -lookUpLimit, lookUpLimit); // 限制上下视角范围
playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
}
// 处理角色移动
void HandleMovement()
{
// 检查角色是否在地面上
bool isGrounded = characterController.isGrounded;
// 获取键盘输入(WASD)
float horizontal = Input.GetAxis("Horizontal"); // A/D 键
float vertical = Input.GetAxis("Vertical"); // W/S 键
// 将输入方向转换为世界空间方向
Vector3 forward = transform.forward * vertical;
Vector3 right = transform.right * horizontal;
// 计算水平移动方向
Vector3 targetDirection = (forward + right).normalized;
targetDirection *= currentSpeed;
// 应用水平移动
moveDirection.x = targetDirection.x;
moveDirection.z = targetDirection.z;
// 应用重力
if (isGrounded)
{
// 在地面上时,保持一个向下的微小力量以确保接地检测准确
if (moveDirection.y < 0)
{
moveDirection.y = -0.5f;
}
}
else
{
// 在空中时应用重力
moveDirection.y -= gravity * Time.deltaTime;
}
// 使用Character Controller移动
characterController.Move(moveDirection * Time.deltaTime);
}
// 处理跳跃
void HandleJump()
{
// 如果在地面上且按下跳跃键
if (characterController.isGrounded && Input.GetButtonDown("Jump"))
{
// 只有当站立时才能跳跃
if (!isCrouching)
{
moveDirection.y = jumpForce;
}
}
}
// 处理蹲下
void HandleCrouch()
{
// 按下C键切换蹲下状态
if (Input.GetKeyDown(KeyCode.C))
{
isCrouching = !isCrouching;
if (isCrouching)
{
// 蹲下:降低高度和速度
characterController.height = crouchHeight;
currentSpeed = crouchSpeed;
// 调整摄像机位置,避免下沉到地面以下
Vector3 cameraPos = playerCamera.transform.localPosition;
cameraPos.y = crouchHeight - 0.1f; // 稍微抬高避免穿地
playerCamera.transform.localPosition = cameraPos;
}
else
{
// 站立:恢复高度和速度
characterController.height = standHeight;
currentSpeed = walkSpeed;
// 恢复摄像机位置
Vector3 cameraPos = playerCamera.transform.localPosition;
cameraPos.y = standHeight / 2; // 摄像机在角色中间高度
playerCamera.transform.localPosition = cameraPos;
}
}
}
// 处理奔跑
void HandleRun()
{
// 按下左Shift键奔跑,松开恢复行走
// 只有在站立状态下才能奔跑
if (Input.GetKey(KeyCode.LeftShift) && !isCrouching)
{
currentSpeed = runSpeed;
}
else if (!isCrouching)
{
currentSpeed = walkSpeed;
}
}
}
控制方式:
移动:WASD 键
视角:鼠标移动
跳跃:空格键
奔跑:按住左Shift键
蹲下:C键(切换)
Inspector窗口中可以调整:
移动设置:行走/奔跑速度、跳跃高度、重力
视角设置:鼠标灵敏度、视角限制
蹲下设置:蹲下高度和速度
Character Controller设置:
- 确保Slope Limit设置为45-60度
- Step Offset设置为0.3-0.5以便上小台阶
摄像机位置:确保摄像机在角色眼睛高度(约1.6-1.8单位)
碰撞检测:Character Controller会自动处理碰撞,但需要确保场景中的物体有碰撞器
2 Input System
2.1 核心概念
Unity Input System 是 Unity 2019.1 版本后推出的新一代输入系统(官方全称:Input System Package),用于替代传统的Input Manager(旧输入系统)。它解决了旧系统在多设备支持、灵活性、可配置性等方面的短板,专为现代游戏开发(如多平台、本地多人、复杂输入逻辑)设计,是目前 Unity 官方推荐的输入解决方案。
与 Input Manager 的对比
| 特性 | 旧 Input Manager(Input 类) | 新 Input System |
|---|---|---|
| 设备支持 | 仅支持常见设备(键盘、鼠标、手柄),扩展困难 | 原生支持键盘、鼠标、触摸、手柄、VR/AR 设备、自定义设备,可扩展 |
| 输入逻辑 | 状态驱动(需轮询 Input.GetKey) |
事件驱动(回调)+ 状态查询(可选),逻辑更清晰 |
| 可配置性 | 仅在 Inspector 简单配置轴和按钮 | 可视化配置(Input Action Asset)、代码动态配置,支持多方案切换 |
| 本地多人 | 需手动处理设备分配,难度大 | 原生支持多玩家、多设备绑定,一键切换玩家输入源 |
| 输入处理 | 无内置交互逻辑(长按、双击需手动实现) | 内置 Interaction(按压、长按、双击等)和 Processor(死区、归一化等) |
| 平台适配 | 需手动适配不同平台输入映射 | 自动适配多平台,可针对平台定制输入绑定 |
核心优势
- 多设备与多平台兼容:一次配置,自动适配 PC、主机、移动、VR/AR 等平台,无需重复编写适配代码。
- 事件驱动架构:输入触发时通过回调通知(如 "跳跃按下""移动轴变化"),避免轮询,性能更优,逻辑更解耦。
- 可视化配置:通过 Input Action Asset 可视化管理所有输入动作(如 "移动""跳跃""攻击"),无需硬编码键位映射。
- 强大的交互扩展:内置长按、双击、按住重复等交互逻辑,支持自定义输入处理流程(如死区过滤、灵敏度调整)。
- 原生支持本地多人:轻松实现多玩家共用一台设备(如分屏游戏),支持设备动态分配与切换。
| 概念 | 含义与作用 |
|---|---|
| Input Action(输入动作) | 抽象的 "游戏行为"(如 "移动""跳跃""开火"),与具体设备无关(比如 "跳跃" 可绑定键盘空格或手柄 A 键) |
| Input Action Map(动作映射表) | 动作的分组容器(如 "Player" 组包含移动 / 跳跃,"UI" 组包含导航 / 确认),可按需启用 / 禁用(如暂停时禁用 Player 组) |
| Binding(绑定) | 动作与设备控件的关联(如 "跳跃" 动作绑定到键盘 Space 键、手柄 South 键),支持多绑定(同一动作多键触发) |
| Control(控件) | 设备的具体输入单元(如键盘的 Space 键、鼠标的 LeftButton、手柄的 LeftStick(左摇杆)) |
| Device(设备) | 输入设备的抽象(如 Keyboard、Mouse、Gamepad、Touchscreen),系统自动检测已连接设备 |
| Interaction(交互) | 动作的触发规则(如 "Press"(按下触发)、"Hold"(长按触发)、"Tap"(轻触)、"DoubleTap"(双击)) |
| Processor(处理器) | 输入值的预处理(如 "Deadzone"(死区过滤)、"Normalize"(归一化)、"Invert"(反转)) |
| Input Action Asset | 存储所有动作、动作组、绑定的资源文件(.inputactions),可视化配置的核心载体 |
2.2 安装和设置
2.2.1 安装流程
Input System 是独立 Package,需通过 Package Manager 安装:
- 打开 Unity → Window → Package Manager;
- 在 Package Manager 窗口的顶部,确保选择 Packages
:Unity Registry- 搜索 "Input System"(需勾选 "Show preview packages",部分版本默认显示);
- 点击 "Install",安装完成后会提示 "重启 Unity"(因需切换输入后端);
- 重启后,Unity 会自动启用新输入系统

2.2.2 基础设置
Active Input Handling:可在 Player Settings → Other Settings → Active Input Handling 确认。若项目需同时使用新旧输入系统,选择 "Both";若仅用新系统,选择 "Input System Package"(性能更优)。
设备检测 :系统会自动检测已连接的设备(如手柄、键盘),可通过 InputSystem
.devices 查看所有可用设备。输入后端:默认自动适配平台,无需手动配置(PC 端默认支持 DirectInput/XInput,主机端自动适配对应输入协议)。
2.3 案例
Input System 有两种核心使用方式:可视化配置(推荐,适合大部分场景) 和 代码动态配置(适合灵活场景)。
2.3.1 可视化配置
2.3.1.1 创建 Input Action Asset
- 右键 Project 窗口 → Create → Input Actions,命名为 PlayerInputActions
- 双击打开配置窗口,开始编辑动作组和动作。
- 双击 PlayerInputActions文件,会打开 Input Action Editor 窗口。

配置窗口分为 3 个面板:
- Action Maps:左侧面板,添加动作组;
- Actions:中间面板,添加动作;
- Bindings:右侧面板,为动作绑定设备控件。
2.3.1.2Input Action Editor 中配置
(1)配置Action Maps
默认会生成一个名为"New Action Map"的条目,建议将其重命名为"Player"。操作映射用于对不同类型的输入进行分组管理。
(2)配置Adding Actions
在Player Action Map 下,点击 + 按钮添加第一个Action,命名为"Move"
再次点击 + 按钮添加第二个Action,命名为"Jump"
(3)配置 "Move" Action
选择 Move 动作。在右侧 Properties 面板中,Action Type 默认为 Value 类型,这正是我们需要的,因为移动输入(如 WASD 或摇杆)是持续性的数值输入。
接下来为 Move 动作绑定键盘按键:
- 点击 Move 动作旁的 + 按钮,选择 Add Binding
- 在新绑定行的 Path 字段旁点击小图标
- 在弹出的 Control picker 窗口中按下 W 键,系统会识别为 Keyboard/Key W,点击 Save 确认
- 重复以上步骤,依次为 A、S、D 键添加绑定
更高效的绑定方法(推荐):
- 先删除已添加的四个绑定
- 点击 Move 动作旁的 +,选择 Add Composite -> 2D Vector
- 这会创建包含 Up、Down、Left、Right 的复合绑定
- 直接在对应 Path 中分别设置 W、S、A、D 键
使用 2D Vector Composite 的优势:
- 输入系统会自动将四个方向键组合成 Vector2 类型(x 表示左右,y 表示前后)
- 结构更清晰,推荐采用此方法
完成后的 Move 动作设置应如下:
- Up: Keyboard/Key W
- Down: Keyboard/Key S
- Left: Keyboard/Key A
- Right: Keyboard/Key D
(4)配置 "Jump" Actio
- 选中Jump动作。在 Properties 面板中,将 Action Type设置为 Button。因为跳跃是一个瞬时的触发动作。
- 点击 Jump动作旁边的 + 按钮,选择 Add Binding
- 点击 Path 旁的图标,在Control picker中按下 Space 键,然后Save。
(4)保存并关闭
完成所有绑定后,点击 Input Action Editor 窗口右上角的 Save asset按钮。关闭该窗口。
2.3.2 搭建场景
- 在 Hierarchy 窗口中右键点击,选择【3D Object】->【Plane】创建地面
- 选中 Plane 对象,在 Inspector 窗口中将 Transform 的 Scale 属性设置为 (10, 1, 10) 以扩大面积
- 再次右键点击 Hierarchy 窗口,选择【3D Object】->【Cube】创建玩家对象
- 将新建的 Cube 重命名为 "Player"
- 选中 Player 对象,在 Inspector 窗口中将 Transform 的 Position 设置为 (0, 1, 0),使其正好位于地面上方
2.3.3 方式1:手动注册回调
cs
using UnityEngine.InputSystem; // 引入 Input System 命名空间
// 这是我们的玩家控制器脚本
public class PlayerController : MonoBehaviour
{
[Header("移动参数")]
[Tooltip("玩家移动速度")]
[SerializeField] private float moveSpeed = 5f; // 序列化字段,可在 Inspector 中调整
[Header("跳跃参数")]
[Tooltip("跳跃力度")]
[SerializeField] private float jumpForce = 7f;
[Tooltip("重力加速度")]
[SerializeField] private float gravity = 9.8f;
// 引用 CharacterController 组件,用于处理移动和碰撞
private CharacterController characterController;
// 存储从 Input System 接收到的移动输入
private Vector2 moveInput;
// 存储当前的速度向量,特别是 Y 轴用于跳跃和重力
private Vector3 velocity;
// 用于检查玩家是否在地面上
private bool isGrounded;
// 引用我们创建的 Input Action Asset
private PlayerInputActions playerInputActions;
private void Awake()
{
// 获取玩家身上的 CharacterController 组件
characterController = GetComponent<CharacterController>();
// 实例化 Input Action Asset
playerInputActions = new PlayerInputActions();
}
// 当脚本启用时调用
private void OnEnable()
{
// 注册输入回调函数
// 当 "Move" 动作有输入时,调用 OnMove 方法
playerInputActions.Player.Move.performed += OnMovePerformed;
// 当 "Move" 动作输入结束时(如松开按键),调用 OnMoveCanceled 方法
playerInputActions.Player.Move.canceled += OnMoveCanceled;
// 当 "Jump" 动作被触发时(如按下空格),调用 OnJump 方法
playerInputActions.Player.Jump.performed += OnJumpPerformed;
}
// 当脚本禁用时调用
private void OnDisable()
{
// 取消注册输入回调,防止内存泄漏
playerInputActions.Player.Move.performed -= OnMovePerformed;
playerInputActions.Player.Move.canceled -= OnMoveCanceled;
playerInputActions.Player.Jump.performed -= OnJumpPerformed;
}
// "Move" 动作输入时调用
private void OnMovePerformed(InputAction.CallbackContext context)
{
// 将输入值(Vector2)存储到 moveInput 变量中
moveInput = context.ReadValue<Vector2>();
Debug.Log("移动输入: " + moveInput);
}
// "Move" 动作输入结束时调用
private void OnMoveCanceled(InputAction.CallbackContext context)
{
// 将 moveInput 重置为零向量
moveInput = Vector2.zero;
}
// "Jump" 动作触发时调用
private void OnJumpPerformed(InputAction.CallbackContext context)
{
// 只有当玩家在地面上时才允许跳跃
if (isGrounded)
{
// 给 velocity.y 一个向上的力
velocity.y = jumpForce;
Debug.Log("跳跃!");
}
}
private void Update()
{
// 检查玩家是否在地面上
isGrounded = characterController.isGrounded;
// --- 移动逻辑 ---
// 根据输入的 X (左右) 和 Y (前后) 轴,计算移动方向
// transform.right 是世界空间中的右方向,transform.forward 是前方向
Vector3 moveDirection = transform.right * moveInput.x + transform.forward * moveInput.y;
moveDirection.Normalize(); // 归一化向量,确保斜向移动速度与正前/正右移动速度相同
// --- 重力逻辑 ---
// 如果玩家不在地面上,就应用重力
if (!isGrounded)
{
velocity.y -= gravity * Time.deltaTime;
}
// 如果玩家在地面上且有向下的速度(比如刚落地),则将速度设置为一个很小的负值,防止悬空
else if (velocity.y < 0)
{
velocity.y = -2f;
}
// 将移动方向乘以速度
moveDirection *= moveSpeed;
// 将 Y 轴速度(跳跃和重力)赋值给 moveDirection
moveDirection.y = velocity.y;
// 使用 CharacterController.Move 方法移动玩家
// 注意:这个方法需要一个基于时间的位移向量 (Vector3)
characterController.Move(moveDirection * Time.deltaTime);
}
}
回调上下文说明:当为 Action 的 performed、canceled 或 started 事件注册回调函数时,系统会传入一个 InputAction.CallbackContext 参数。该参数包含输入事件的所有相关信息,例如:
- 使用 context.ReadValue<T>() 方法可读取输入值
- 通过 context.performed 属性可判断动作是否完成
配置玩家
- 在 Hierarchy 窗口中选择 Player GameObject
- 点击 Inspector 窗口中的 Add Component 按钮
- 搜索并添加 Character Controller 组件
- 再次点击 Add Component 按钮
- 搜索并添加之前创建的 Player Controller 脚本
Player Controller 组件中标记为 [SerializeField] 的参数(Move Speed、Jump Force 和 Gravity)可直接在 Inspector 窗口中进行可视化调整。
2.3.4 方式2:PlayerInput组件(推荐)
PlayerInput 充当了一个 "输入代理" 的角色:
核心功能:
- 实时监听 Input Action Asset 中定义的所有操作(如移动、跳跃)
- 当检测到输入触发(比如按下空格键)时,会根据预设的 "行为模式"(Behavior) 自动通知游戏脚本(如 PlayerController)执行对应逻辑
优势在于:
- 无需手动编写事件注册/注销代码(OnEnable/OnDisable)
- 自动处理资源管理,避免内存泄漏问题
在 Hierarchy 中选择Player 对象 并添加PlayerInput组件后,我们接下来详细解析该组件的各项配置参数。
| 配置项 | 作用说明 |
|---|---|
| Actions | 关联你创建的 Input Action Asset(如 PlayerInputActions)。这是组件的 "输入源",告诉它要监听哪些动作。 |
| Behavior | 核心配置!指定当输入动作触发时,组件如何通知你的脚本(4 种模式,重点讲前 3 种)。 |
| Default Action Map | 默认启用的 "动作映射"(如 Player)。游戏启动时,组件会自动启用这个映射。 |
| UI Input Module | 仅当需要处理 UI 输入时(如菜单导航)配置,通常留空(默认自动适配)。 |
| Enable Input | 勾选表示启用输入监听(默认勾选)。可通过代码 playerInput.enabled = false 临时禁用输入。 |
| Events | 当 Behavior选择 Invoke Unity Events 时,会显示此面板,用于绑定动作到脚本方法。 |
推荐优先采用Invoke Unity Events模式:具备类型安全特性、支持可视化绑定、无需手动管理事件,是团队协作和项目长期维护的理想选择。
操作步骤:
1.关联 Actions 资产:
- 在 PlayerInput 组件的 Actions 字段右侧点击圆圈图标
- 选择已创建的 PlayerInputActions 资产
2.设置 Behavior 为 Invoke Unity Events:
- 组件下方将显示 Events 折叠面板
- 展开后可看到 PlayerInputActions 中定义的所有动作(如 Move、Jump)
3.绑定动作到脚本方法(以 Jump 为例):
- 点击 Jump 事件右侧的 + 按钮添加回调
- 将 Hierarchy 中的 Player 对象(或挂载 PlayerController 的对象)拖入对象引用框
- 在方法选择框(默认显示"No Function")中选择:
- PlayerController → OnJump(接收 Jump 输入的脚本方法)
4.绑定其他动作:
- 重复上述步骤,将 Move 动作绑定到 PlayerController 的 OnMove 方法
编写接收方法的注意事项:
- 方法访问修饰符需为 public(编辑器绑定仅支持 public)
- 方法参数类型必须为 InputAction.CallbackContext(用于获取输入状态和值)
5.编写Player Controller 脚本,赋给Player
代码如下:
cs
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
[SerializeField] private float moveSpeed = 5f;
[SerializeField] private float jumpForce = 7f;
[SerializeField] private float gravity = 9.8f;
private CharacterController characterController;
private Vector2 moveInput; // 存储移动输入
private Vector3 velocity;
private bool isGrounded;
private void Awake()
{
characterController = GetComponent<CharacterController>();
}
private void Update()
{
isGrounded = characterController.isGrounded;
// 移动逻辑
Vector3 moveDirection = transform.right * moveInput.x + transform.forward * moveInput.y;
moveDirection.Normalize();
moveDirection *= moveSpeed;
// 重力逻辑
if (!isGrounded)
velocity.y -= gravity * Time.deltaTime;
else if (velocity.y < 0)
velocity.y = -2f; // 防止悬空
moveDirection.y = velocity.y;
characterController.Move(moveDirection * Time.deltaTime);
}
// 接收 Move 动作的方法(必须带 InputAction.CallbackContext 参数)
public void OnMove(InputAction.CallbackContext context)
{
// context.ReadValue<Vector2>() 获取移动的 x/y 值(WASD 对应 ±1)
moveInput = context.ReadValue<Vector2>();
Debug.Log($"移动输入:{moveInput},状态:{context.phase}");
}
// 接收 Jump 动作的方法
public void OnJump(InputAction.CallbackContext context)
{
// context.performed 表示动作"完成触发"(如按下空格的瞬间)
if (context.performed && isGrounded)
{
velocity.y = jumpForce;
Debug.Log("跳跃触发!");
}
}
}
动作生命周期(context.phase):InputAction.CallbackContext 的 phase 属性指示动作的当前状态,主要包含以下状态:
Started:动作起始阶段(例如按键刚被按下但未完全触发时) Performed:动作完成阶段(例如按键完全按下或摇杆进入有效区域时)
示例:Move 动作的 Canceled 状态可用于重置 moveInput 参数
cs
public void OnMove(InputAction.CallbackContext context)
{
if (context.performed)
moveInput = context.ReadValue<Vector2>();
else if (context.canceled)
moveInput = Vector2.zero; // 松开按键时重置输入
}