【Unity基础详解】(11)Unity核心:输入系统

目录

[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);
        }
    }
}

挂载方式

  1. 将脚本挂载到摄像机对象上
  2. 如果使用第一人称角色,通常摄像机是玩家对象的子物体

父物体 Player:空对象,挂载角色控制器等
子物体 Main Camera:挂载MouseLook脚本,playerBody指向Player

参数说明

  1. mouseSensitivity:鼠标灵敏度,值越大旋转越快
  2. 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 安装:

  1. 打开 Unity → Window → Package Manager;
  2. 在 Package Manager 窗口的顶部,确保选择 Packages:Unity Registry
  3. 搜索 "Input System"(需勾选 "Show preview packages",部分版本默认显示);
  4. 点击 "Install",安装完成后会提示 "重启 Unity"(因需切换输入后端);
  5. 重启后,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
  1. 右键 Project 窗口 → Create → Input Actions,命名为 PlayerInputActions
  2. 双击打开配置窗口,开始编辑动作组和动作。
  3. 双击 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 动作绑定键盘按键:

  1. 点击 Move 动作旁的 + 按钮,选择 Add Binding
  2. 在新绑定行的 Path 字段旁点击小图标
  3. 在弹出的 Control picker 窗口中按下 W 键,系统会识别为 Keyboard/Key W,点击 Save 确认
  4. 重复以上步骤,依次为 A、S、D 键添加绑定

更高效的绑定方法(推荐):

  1. 先删除已添加的四个绑定
  2. 点击 Move 动作旁的 +,选择 Add Composite -> 2D Vector
  3. 这会创建包含 Up、Down、Left、Right 的复合绑定
  4. 直接在对应 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

  1. 选中Jump动作。在 Properties 面板中,将 Action Type设置为 Button。因为跳跃是一个瞬时的触发动作。
  2. 点击 Jump动作旁边的 + 按钮,选择 Add Binding
  3. 点击 Path 旁的图标,在Control picker中按下 Space 键,然后Save。

(4)保存并关闭

完成所有绑定后,点击 Input Action Editor 窗口右上角的 Save asset按钮。关闭该窗口。

2.3.2 搭建场景

  1. 在 Hierarchy 窗口中右键点击,选择【3D Object】->【Plane】创建地面
  2. 选中 Plane 对象,在 Inspector 窗口中将 Transform 的 Scale 属性设置为 (10, 1, 10) 以扩大面积
  3. 再次右键点击 Hierarchy 窗口,选择【3D Object】->【Cube】创建玩家对象
  4. 将新建的 Cube 重命名为 "Player"
  5. 选中 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 属性可判断动作是否完成

配置玩家

  1. 在 Hierarchy 窗口中选择 Player GameObject
  2. 点击 Inspector 窗口中的 Add Component 按钮
  3. 搜索并添加 Character Controller 组件
  4. 再次点击 Add Component 按钮
  5. 搜索并添加之前创建的 Player Controller 脚本

Player Controller 组件中标记为 [SerializeField] 的参数(Move Speed、Jump Force 和 Gravity)可直接在 Inspector 窗口中进行可视化调整。

2.3.4 方式2:PlayerInput组件(推荐)

PlayerInput 充当了一个 "输入代理" 的角色:

核心功能:

  1. 实时监听 Input Action Asset 中定义的所有操作(如移动、跳跃)
  2. 当检测到输入触发(比如按下空格键)时,会根据预设的 "行为模式"(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; // 松开按键时重置输入
}
相关推荐
世洋Blog3 小时前
Unity开发微信小游戏-减少WASM包体大小
unity·游戏引擎·wasm·微信小游戏
TO_ZRG4 小时前
Unity 通过 NativePlugin 接入Android SDK 指南
android·unity·游戏引擎
jtymyxmz6 小时前
《Unity Shader》10.2.1 镜子效果
unity·游戏引擎
ellis19706 小时前
Unity打开新项目Package相关报错处理记录
unity
微:xsooop8 小时前
iOS 上架4.3a 审核4.3a 被拒4.3a 【灾难来袭】
flutter·unity·ios·uniapp
微光守望者8 小时前
Unity ScriptableObject详解:优化游戏架构的强大工具
unity·游戏引擎
jtymyxmz9 小时前
《Unity Shader》10.2.2 玻璃效果
unity·游戏引擎
zxc24460393412 小时前
gpu instancer crowd 动画使用方式
unity
C MIKE13 小时前
unity资源下载
unity