AgentSkillsCN

input-system-new

基于 ScriptableObject 事件的 Unity 新输入系统封装,轻松应对多设备输入需求。

SKILL.md
--- frontmatter
name: input-system-new
description: "Unity New Input System wrapper with ScriptableObject events for multi-device input handling."
version: 1.0.0
tags: ["input", "gamepad", "keyboard", "touch", "New-Input-System"]
argument-hint: "action='Jump' OR device='Gamepad' scheme='Controller'"
disable-model-invocation: false
user-invocable: true
allowed-tools:
  - run_command
  - list_dir
  - write_to_file

New Input System

Overview

Unity New Input System abstraction layer. Wraps input actions into ScriptableObject-based event channels for decoupled, multi-device input handling.

When to Use

  • Use for multi-platform input (keyboard, gamepad, touch)
  • Use for rebindable controls
  • Use for local multiplayer
  • Use for UI navigation input
  • Use for action-based input (not polling)

Architecture

code
┌─────────────────────────────────────────────────────────────┐
│                  INPUT SYSTEM ARCHITECTURE                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  INPUT ACTIONS ASSET     INPUT READER (SO)    CONSUMERS    │
│  ┌──────────────────┐   ┌──────────────┐    ┌──────────┐  │
│  │ .inputactions    │──▶│ InputReader  │───▶│ Player   │  │
│  │ ┌──────────────┐ │   │              │    │ UI       │  │
│  │ │ Move         │ │   │ OnMove       │    │ Camera   │  │
│  │ │ Jump         │ │   │ OnJump       │    │ Vehicle  │  │
│  │ │ Attack       │ │   │ OnAttack     │    │ etc.     │  │
│  │ └──────────────┘ │   └──────────────┘    └──────────┘  │
│  └──────────────────┘                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Input Action Types

TypeUseC# Type
ButtonJump, Attackbool
ValueMove, LookVector2
PassThroughContinuous inputAny

Control Schemes

SchemeDevices
Keyboard&MouseKeyboard + Mouse
GamepadXbox, PlayStation, Switch
TouchTouchscreen

Best Practices

  • ✅ Create InputReader ScriptableObject
  • ✅ Use C# events, not direct polling
  • ✅ Generate C# class from .inputactions
  • ✅ Use PlayerInput component for multiplayer
  • ✅ Implement rebinding system
  • NEVER poll Input directly (Input.GetKey)
  • NEVER reference InputActions in consumers
  • NEVER forget to enable action maps

Few-Shot Examples

Example 1: Input Reader ScriptableObject

User: "Create an input reader."

Agent:

csharp
[CreateAssetMenu(fileName = "InputReader", menuName = "Input/Input Reader")]
public class InputReader : ScriptableObject, GameInput.IPlayerActions
{
    private GameInput _gameInput;
    
    public event Action<Vector2> OnMoveEvent;
    public event Action OnJumpEvent;
    public event Action OnAttackEvent;
    public event Action<bool> OnSprintEvent;
    
    public Vector2 MoveInput { get; private set; }
    
    private void OnEnable()
    {
        _gameInput ??= new GameInput();
        _gameInput.Player.SetCallbacks(this);
        _gameInput.Player.Enable();
    }
    
    private void OnDisable()
    {
        _gameInput.Player.Disable();
    }
    
    public void OnMove(InputAction.CallbackContext context)
    {
        MoveInput = context.ReadValue<Vector2>();
        OnMoveEvent?.Invoke(MoveInput);
    }
    
    public void OnJump(InputAction.CallbackContext context)
    {
        if (context.performed)
            OnJumpEvent?.Invoke();
    }
    
    public void OnAttack(InputAction.CallbackContext context)
    {
        if (context.performed)
            OnAttackEvent?.Invoke();
    }
    
    public void OnSprint(InputAction.CallbackContext context)
    {
        OnSprintEvent?.Invoke(context.performed);
    }
}

Example 2: Consumer (PlayerController)

User: "Connect input to player."

Agent:

csharp
public class PlayerController : MonoBehaviour
{
    [SerializeField] private InputReader _inputReader;
    
    private void OnEnable()
    {
        _inputReader.OnJumpEvent += HandleJump;
        _inputReader.OnAttackEvent += HandleAttack;
    }
    
    private void OnDisable()
    {
        _inputReader.OnJumpEvent -= HandleJump;
        _inputReader.OnAttackEvent -= HandleAttack;
    }
    
    private void Update()
    {
        Vector2 move = _inputReader.MoveInput;
        // Use move for continuous input
    }
    
    private void HandleJump() => _jumpRequested = true;
    private void HandleAttack() => _attackRequested = true;
}

Example 3: UI Navigation

User: "Enable gamepad UI navigation."

Agent:

csharp
private void SwitchToUI()
{
    _gameInput.Player.Disable();
    _gameInput.UI.Enable();
    
    // UI Toolkit focus
    _uiDocument.rootVisualElement.Q<Button>().Focus();
}

private void SwitchToGameplay()
{
    _gameInput.UI.Disable();
    _gameInput.Player.Enable();
}

Rebinding Flow

  1. Start rebinding: action.PerformInteractiveRebinding()
  2. Wait for input
  3. Save to PlayerPrefs: action.SaveBindingOverridesAsJson()
  4. Load on startup: action.LoadBindingOverridesFromJson()

Related Skills

  • @menu-navigation-flow - UI with gamepad
  • @ui-toolkit-modern - Focusable elements
  • @advanced-character-controller - Movement input