You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity新Input System实现2D玩家水平移动速度归零问题

Unity新Input System实现2D玩家水平速度控制问题解决

问题背景

用Unity新Input System编写2D玩家水平移动脚本时,需要通过Rigidbody2D.velocity实现平滑移动,而非直接修改position,但遇到了核心问题:

  • 仅监听performed输入事件时,松开按键/摇杆回中后,系统不会主动触发输入归零逻辑,导致玩家速度保持不变,无法减速到0
  • 若手动强制归零输入值,又会出现速度刚提升就被重置的异常情况
  • 本质是复合输入(键盘/摇杆)在无输入状态下不会主动返回Vector2.zero,缺少旧Input Manager中OnButtonUp()的触发逻辑

解决方案

方法1:监听performed+canceled双事件

Input System的canceled事件会在输入结束时触发(比如按键松开、摇杆回到中立位),通过监听该事件可在输入停止时将速度目标设为0,完美匹配需求。

修改后的完整代码:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerHorizontalMovement : MonoBehaviour
{
    [Tooltip("[units/s]")]
    [SerializeField] private float horizontalSpeed = 3f;

    [Tooltip("[units/s^2]")]
    [SerializeField] private float horizontalAccel = 60f;

    private PlayerControls controls;
    private Rigidbody2D myRigidbody;
    private float velocityGoal;

    private void Awake()
    {
        controls = new PlayerControls();
        // 输入触发时,将输入值乘以最大速度作为目标速度
        controls.Player.Move.performed += context => 
            velocityGoal = context.ReadValue<Vector2>().x * horizontalSpeed;
        // 输入取消时,直接将目标速度归零
        controls.Player.Move.canceled += _ => velocityGoal = 0f;
    }

    private void Start()
    {
        myRigidbody = GetComponent<Rigidbody2D>();
        velocityGoal = 0f;
    }

    private void FixedUpdate()
    {
        Vector2 newVelocity = myRigidbody.velocity;
        // 平滑向目标速度过渡,实现加速度控制
        newVelocity.x = Mathf.MoveTowards(newVelocity.x, velocityGoal, horizontalAccel * Time.fixedDeltaTime);
        myRigidbody.velocity = newVelocity;
    }

    private void OnEnable() => controls.Player.Enable();
    private void OnDisable() => controls.Player.Disable();
    private void OnDestroy() => controls.Dispose(); // 释放Input资源,避免内存泄漏
}

方法2:FixedUpdate周期主动读取输入值

如果不想依赖事件驱动,可在物理更新周期FixedUpdate中主动读取当前输入状态,无论有无输入,系统都会返回正确的数值(无输入时为0)。

修改后的完整代码:

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerHorizontalMovement : MonoBehaviour
{
    [Tooltip("[units/s]")]
    [SerializeField] private float horizontalSpeed = 3f;

    [Tooltip("[units/s^2]")]
    [SerializeField] private float horizontalAccel = 60f;

    private PlayerControls controls;
    private Rigidbody2D myRigidbody;
    private InputAction moveAction;

    private void Awake()
    {
        controls = new PlayerControls();
        moveAction = controls.Player.Move;
    }

    private void Start()
    {
        myRigidbody = GetComponent<Rigidbody2D>();
    }

    private void FixedUpdate()
    {
        // 主动读取当前输入值,无输入时自动返回0
        float inputX = moveAction.ReadValue<Vector2>().x;
        float velocityGoal = inputX * horizontalSpeed;

        Vector2 newVelocity = myRigidbody.velocity;
        newVelocity.x = Mathf.MoveTowards(newVelocity.x, velocityGoal, horizontalAccel * Time.fixedDeltaTime);
        myRigidbody.velocity = newVelocity;
    }

    private void OnEnable() => controls.Player.Enable();
    private void OnDisable() => controls.Player.Disable();
    private void OnDestroy() => controls.Dispose();
}

关键说明

  • 方法1适合需要在输入开始/结束时执行额外逻辑的场景(比如播放移动音效),事件驱动更高效
  • 方法2逻辑更直观,接近旧Input Manager的写法,适合简单移动控制需求
  • 必须将读取到的输入值(范围[-1,1])乘以horizontalSpeed,否则移动速度会远低于预期
  • 务必在OnDestroy中调用controls.Dispose(),释放Input System资源避免内存泄漏

内容的提问来源于stack exchange,提问作者Lurosset

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 03:12:18