1. 事件驱动编程与InputSystem基础

第一次接触Unity的InputSystem时,我被它的事件驱动机制深深吸引。相比传统的Input.GetKey方式,事件驱动让代码结构更清晰,逻辑更易于维护。想象一下,你的游戏角色移动不再需要每帧检测按键状态,而是按键按下时自动通知你,这种编程范式转变带来的便利性不言而喻。

InputSystem的核心是InputAction概念。你可以把它理解为一个抽象层,将具体的物理输入(键盘、手柄、触摸屏)与游戏逻辑解耦。我特别喜欢这种设计,因为它让输入设备切换变得异常简单。比如你为PC开发游戏时用键盘测试,后期想支持手柄,几乎不需要修改任何移动控制代码。

创建InputAction资源时,建议使用ScriptableObject形式。这样做有两个好处:一是可以在编辑器中进行可视化配置,二是方便在不同场景间复用。我通常会创建一个名为"PlayerControls"的资产,把所有角色相关的输入(移动、跳跃、攻击)都放在里面。

[CreateAssetMenu(menuName = "Input/PlayerControls")]
public class PlayerControls : ScriptableObject, IPlayerActions
{
    private InputActions inputActions;
    
    public event UnityAction<Vector2> OnMove = delegate { };
    
    private void OnEnable()
    {
        inputActions = new InputActions();
        inputActions.Player.SetCallbacks(this);
    }
}

2. InputActionPhase的实战应用

理解InputActionPhase的各个阶段是掌握InputSystem的关键。刚开始使用时,我经常混淆Started和Performed阶段,直到在实际项目中踩过几次坑才真正明白它们的区别。

Started阶段相当于传统Input.GetKeyDown,只在按键按下的第一帧触发。这个阶段特别适合处理需要精确时机判断的操作,比如跳跃。在我的平台游戏项目中,角色只有在按键按下瞬间才能起跳,按住不放不会连续跳跃,这时就必须使用Started阶段。

Performed阶段则更灵活,它既包含按键按下事件,也包含持续按住状态。对于角色移动这种需要持续输入的操作,Performed是最佳选择。这里有个实用技巧:通过context.ReadValue可以获取输入的强度值,这对实现模拟摇杆的渐变移动特别有用。

public void OnMove(InputAction.CallbackContext context)
{
    if(context.phase == InputActionPhase.Performed)
    {
        Vector2 input = context.ReadValue<Vector2>();
        OnMove?.Invoke(input);
    }
    else if(context.phase == InputActionPhase.Canceled)
    {
        OnMove?.Invoke(Vector2.zero);
    }
}

Canceled阶段对应Input.GetKeyUp,处理按键释放逻辑。比如角色停止移动时播放刹车动画,或者射击游戏的扳机复位。我建议在这个阶段一定要重置相关状态,避免出现"按键已释放但角色仍在移动"的bug。

3. 平滑移动控制的实现技巧

直接给刚体赋值的移动方式往往显得生硬,缺乏游戏应有的手感。经过多次尝试,我总结出一套平滑移动的实现方案,核心是使用协程进行插值过渡。

首先需要理解的是加速度概念。现实世界中物体不会瞬间达到最大速度,游戏角色移动也应该有加速和减速过程。我的做法是使用Lerp函数在当前位置和目标速度之间进行插值:

IEnumerator SmoothMove(Vector2 targetVelocity, float duration)
{
    float elapsed = 0f;
    Vector2 startVelocity = rigidbody.velocity;
    
    while(elapsed < duration)
    {
        rigidbody.velocity = Vector2.Lerp(
            startVelocity, 
            targetVelocity, 
            elapsed / duration
        );
        elapsed += Time.deltaTime;
        yield return null;
    }
    
    rigidbody.velocity = targetVelocity;
}

对于2D游戏,我还会加入惯性效果。当玩家突然改变移动方向时,角色会先减速再反向加速,这种物理反馈让操控感更加真实。实现方法是在移动协程中加入速度曲线控制:

[SerializeField] private AnimationCurve accelerationCurve;
[SerializeField] private AnimationCurve decelerationCurve;

IEnumerator SmoothMove(Vector2 direction, bool isAccelerating)
{
    float t = 0f;
    Vector2 startVel = rigidbody.velocity;
    Vector2 targetVel = direction * moveSpeed;
    
    while(t < 1f)
    {
        t += Time.deltaTime / (isAccelerating ? accelerationTime : decelerationTime);
        float curveValue = isAccelerating 
            ? accelerationCurve.Evaluate(t)
            : decelerationCurve.Evaluate(t);
            
        rigidbody.velocity = Vector2.Lerp(startVel, targetVel, curveValue);
        yield return null;
    }
}

4. 高级事件处理与架构设计

当游戏规模扩大时,简单的回调函数可能会变得难以维护。我在一个中型RPG项目中重构输入系统时,开发了一套基于观察者模式的事件总线架构。

核心思想是将输入事件转换为游戏内通用事件。例如,将OnMove事件转换为PlayerMoveEvent,这样任何需要响应移动的系统(动画、音效、任务)都可以订阅,而不需要直接依赖输入模块:

public class InputEventDispatcher : MonoBehaviour
{
    [SerializeField] private PlayerControls controls;
    
    private void OnEnable()
    {
        controls.OnMove += HandleMove;
    }
    
    private void HandleMove(Vector2 input)
    {
        EventBus.Publish(new PlayerMoveEvent(input));
    }
}

public class MovementSystem : MonoBehaviour
{
    private void OnEnable()
    {
        EventBus.Subscribe<PlayerMoveEvent>(OnPlayerMove);
    }
    
    private void OnPlayerMove(PlayerMoveEvent e)
    {
        // 处理移动逻辑
    }
}

对于需要区分输入设备的情况,可以在事件中添加设备信息。我通常会创建一个InputContext结构体,包含输入值、设备类型和时间戳等信息:

public struct InputContext
{
    public InputDeviceType DeviceType;
    public Vector2 InputValue;
    public float Timestamp;
}

public enum InputDeviceType
{
    Keyboard,
    Gamepad,
    Touch
}

这种架构最大的优势是解耦。当我们需要添加新的输入设备或修改控制方案时,只需要调整InputEventDispatcher,其他系统完全不受影响。在支持多平台的项目中,这种设计显著降低了维护成本。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐