U
章 03镜头与操作6 课 — 点击打开
04 / 06 · 11 分钟
镜头与操作

全新的 Input System

到目前为止的项目

Assets/

Scripts/

PlayerMovement.csCharacterSwitcher.csSkillCaster.csInput.GetAxis / GetKey→InputRouter.cs

Settings/

PlayerControls.inputactions

项目设置

Input System package

你已经有的这一课新增这一课替换

你写的三个脚本仍然直接调用 Input:PlayerMovement 读 GetAxisRaw,CharacterSwitcher 读数字键,SkillCaster 读 Q、E、R。三个都绑死在键盘上,而 tab 05 要把这套搬上手机,所以这就是问题。

用 Input System,一个动作只要定义一次,键盘、手柄、触屏都能绑上去。学完这节课,你的 gameplay 脚本压根不需要知道玩家手里拿的是什么设备。

动态图解

左边是各种设备,中间是一个 action 资源,右边是你的 gameplay 代码。

安装并切换

  1. 1

    在 Package Manager 里选择 Input System → Install。Unity 会提示重启,点确认即可。

  2. 2

    Edit▸Project Settings▸Player▸Active Input Handling → 迁移期间先选 Both。

  3. 3

    项目里:Create▸Input Actions,命名为 PlayerControls,双击打开编辑器。

  4. 4

    勾选 Generate C# Class 再点 Apply —— Unity 会自动帮你生成好包装 class。

搭建 action map

  1. 1

    新建一个名叫 Gameplay 的 Action Map。

  2. 2

    Action Move → Action Type 选 Value → Control Type 选 Vector2。加一个 2D Vector Composite 绑定:W/A/S/D。再加一个绑定:手柄左摇杆。

  3. 3

    Action Look → Value → Vector2 → 绑定 Mouse Delta 和手柄右摇杆。

  4. 4

    Jump 和 Attack 这两个 Action → Action Type 选 Button → 分别绑定 Space / South 键和鼠标左键 / West 键。

  5. 5

    Skill1 / Skill2 / Skill3 → Button → 绑定 Q、E、R。它们替代 SkillCaster 现在自己读的那几个按键。

  6. 6

    SwapCharacter 这个 Action → Value / Axis → 绑定 1、2、3 键。用来替代 CharacterSwitcher 现在的读取。

在代码里读取

InputRouter.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class InputRouter : MonoBehaviour
{
    // filled by the generated class
    private PlayerControls controls;

    public Vector2 Move { get; private set; }
    public Vector2 Look { get; private set; }
    public bool JumpPressed { get; private set; }

    void Awake()
    {
        controls = new PlayerControls();

        controls.Gameplay.Move.performed += ctx => Move = ctx.ReadValue<Vector2>();
        controls.Gameplay.Move.canceled  += ctx => Move = Vector2.zero;   // never forget this line

        controls.Gameplay.Look.performed += ctx => Look = ctx.ReadValue<Vector2>();
        controls.Gameplay.Look.canceled  += ctx => Look = Vector2.zero;

        controls.Gameplay.Jump.performed += ctx => JumpPressed = true;
        controls.Gameplay.Attack.performed += ctx => GetComponent<SkillCaster>().TryCast(0);
    }

    void OnEnable()  => controls.Enable();    // no Enable = no input at all
    void OnDisable() => controls.Disable();

    void LateUpdate() => JumpPressed = false; // one-frame flag
}
  • performed 在输入开始或变化时触发,canceled 在松开时触发。漏掉 canceled,角色就会一直走下去。

  • Move 是一个 Vector2 (x, y)。在 3D 里映射成 (x, 0, y) —— 摇杆的 y 变成世界的 Z 轴。

  • 还可以再建一个叫 UI 的 action map,在 Gameplay 关掉时把它打开 —— 暂停菜单拦住角色不让动就是靠这个。

退役那三处旧的读取代码

把这一步收拾干净,整个 tab 才真正值回票价。将项目里所有的 Input.GetAxis、Input.GetKey、Input.GetMouseButton 全部删掉,改成统一走 InputRouter。

the three edits
// 1 — PlayerMovement.cs
- float h = Input.GetAxisRaw("Horizontal");
- float v = Input.GetAxisRaw("Vertical");
- Vector3 input = new Vector3(h, 0f, v).normalized;
+ Vector3 input = new Vector3(router.Move.x, 0f, router.Move.y).normalized;

// 2 — CharacterSwitcher.cs
- for (int i = 0; i < characters.Count && i < 9; i++)
-     if (Input.GetKeyDown(KeyCode.Alpha1 + i)) Select(i);
+ // in InputRouter.Awake:
+ controls.Gameplay.SwapCharacter.performed += c => switcher.Select((int)c.ReadValue<float>() - 1);

// 3 — SkillCaster.cs
- for (int i = 0; i < skills.Count && i < keys.Length; i++)
-     if (Input.GetKeyDown(keys[i])) TryCast(i);
+ // in InputRouter.Awake:
+ controls.Gameplay.Skill1.performed += _ => caster.TryCast(0);
+ controls.Gameplay.Skill2.performed += _ => caster.TryCast(1);
+ controls.Gameplay.Skill3.performed += _ => caster.TryCast(2);

在移动脚本里怎么用

C# 脚本
[SerializeField] private InputRouter input;

void Update()
{
    Vector3 dir = new Vector3(input.Move.x, 0f, input.Move.y);
    // ... rest is identical to the old script
}

✓ PlayerInput component

可以用 Send Messages / Unity Events 模式。搭得最快,适合做原型。

✓ Generated C# class

调用写得明明白白,改名也安全,不用靠方法名字符串那种魔法。正式项目更适合这个。