【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; // 松开按键时重置输入
}
相关推荐
晓13131 小时前
【Cocos Creator 3.x】篇——第二章 入门
前端·javascript·游戏引擎
玖玥拾3 小时前
Cocos学习笔记:粒子系统与对象层批量处理
游戏引擎·cocos2d
是果果呀儿4 小时前
Vuforia实现物体旋转、移动、缩放
unity·增强现实
不知名的老吴8 小时前
Unity3D 2022安装教程及全流程下载步骤指南
unity·游戏引擎
Thomas_YXQ8 小时前
Unity3D Addressable 深度优化热更性能消耗
开发语言·3d·unity·微信
程序员也有头发8 小时前
如何使用AI工具开发Unity
unity·游戏引擎·ai编程
隔窗听雨眠8 小时前
从零开始的游戏开发入门指南
unity
sinat_384503118 小时前
【无标题】
unity·webgl
caimouse8 小时前
Godot 引擎官方常见问题(FAQ)整理
游戏引擎·godot
一锅炖出任易仙9 小时前
创梦汤锅学习日记day29
学习·ai·ue5·游戏引擎