Getting Started

7 min read

Add the ScyllaInput module to your scene, point it at an action asset, and start reading and reacting to player input in a few minutes.

This page takes you from a Scylla scene with no input to a working input layer: add the component, point it at an action asset, poll an action from Update, and subscribe to an action event from the Scylla Event eXchange (SEX) bus. Expect about five minutes if you already have the framework running.

Installation

Scylla Input ships inside the Scylla package. There is no separate download or import step – when you have Scylla Core, you already have Scylla Input. If you have not yet installed the framework or wired up a ScyllaBootstrap in your scene, start at Scylla Core Getting Started and come back here once the framework is running.

Setup and Configuration

Add the component

Add the ScyllaInput component to a GameObject that is a direct child of the ScyllaBootstrap root. This is the only structural requirement: the framework’s discovery pass walks one level of children and registers every ScyllaModule it finds. A module nested any deeper is invisible to the framework and will not initialize.

Scylla          (GameObject with ScyllaBootstrap)
|-- ScyllaInput     <-- add the component here, as a direct child
|-- ScyllaConsole
\-- ...

Press Play. The Console should show a line from the Input module confirming the number of connected devices. That is your signal the module is up.

Action asset

Scylla Input needs an InputActionAsset to expose named actions. If you do not assign one, the module automatically loads the default asset from Resources/Input Actions/Scylla Input Actions. That default ships with the framework and contains a starter set of action maps, so a fresh scene already has working actions the moment the component is added.

When your project needs its own bindings, you have two options:

  • Assign via configuration (recommended). Create a ScyllaInputConfiguration asset (Assets > Create > Scylla > Input > Input Configuration) and drag your InputActionAsset into its Input Action Asset slot. Then drop the configuration asset into the ScyllaInput component’s Configuration slot in the Inspector. The module reads the asset during initialization and enables all of its action maps.

  • Register at runtime. Call ScyllaInput.Instance.RegisterActionAsset(myAsset) from inside a ScyllaCore.Instance.RunWhenReady(...) callback. The default Scylla asset stays active alongside any you add, and action maps are disambiguated by name.

Configuration knobs

The ScyllaInputConfiguration asset exposes six groups of settings – control scheme detection thresholds, input buffering window, binding persistence, rebind defaults, multi-bridge query mode, and log verbosity. For the full list see Input configuration. The defaults are reasonable for most projects; you do not need to touch them to get started.

Usage

Poll an action each frame

For movement, aiming, and anything that feeds a per-frame calculation, retrieve the ScyllaInputAction once and read it in Update. Cache the reference after the framework is ready:

using Scylla.Core;
using Scylla.Input;
using UnityEngine;

public sealed class PlayerController : MonoBehaviour
{
    /* Cache the action references once after the framework is ready.
       ActionManager.GetAction is a dictionary lookup - cheap but not free.
       The reference stays valid for the lifetime of the registered asset. */
    private ScyllaInputAction _moveAction;
    private ScyllaInputAction _jumpAction;

    private void Start()
    {
        ScyllaCore.Instance.RunWhenReady(() =>
        {
            /* Action IDs match the names you gave them in the InputActionAsset editor.
               GetAction returns null when the ID is not found - check before caching. */
            _moveAction = ScyllaInput.Instance.ActionManager.GetAction("Move");
            _jumpAction = ScyllaInput.Instance.ActionManager.GetAction("Jump");
        });
    }

    private void Update()
    {
        if (_moveAction == null) return;

        /* ReadVector2 returns the current stick or WASD composite value.
           Zero when no binding is active. Allocates nothing. */
        Vector2 move = _moveAction.ReadVector2();

        /* WasPerformedThisFrame is true on exactly one frame - the rising edge.
           Prefer this over IsPressed for jump/attack/interact to avoid repeated
           triggers while the button is held. */
        if (_jumpAction != null && _jumpAction.WasPerformedThisFrame)
        {
            Jump();
        }

        ApplyMovement(move);
    }

    private void Jump() { /* ... */ }
    private void ApplyMovement(Vector2 v) { /* ... */ }
}

You can also query the facade directly without caching an action reference when you only need a yes/no answer:

/* IsActionPressed is true every frame the binding is held.
   WasActionPerformedThisFrame is true on the one frame it was triggered.
   Both return false when the action ID is not found. */
if (ScyllaInput.Instance.IsActionPressed("Sprint"))
{
    ApplySprint();
}

if (ScyllaInput.Instance.WasActionPerformedThisFrame("Interact"))
{
    Interact();
}

Subscribe to action events

When a system only needs to react once per trigger – playing a sound, incrementing a counter, scheduling an animation – subscribing to the SEX bus is cleaner than polling. The module publishes InputActionPerformedEvent every time an action reaches its performed phase:

using Scylla.Core;
using Scylla.Core.Events;
using Scylla.Input;
using UnityEngine;

public sealed class JumpAudio : MonoBehaviour
{
    /* Store the subscription token so you can dispose it later.
       Discarding the token makes the listener effectively permanent and leaks
       it across scene reloads until the bus cleanup pass notices the destroyed owner. */
    private ScyllaEventSubscription _jumpSubscription;

    private void Start()
    {
        ScyllaCore.Instance.RunWhenReady(() =>
        {
            _jumpSubscription = ScyllaEvents.Listen<InputActionPerformedEvent>(
                subscriber: this,
                handler:    OnInputPerformed
            );
        });
    }

    private void OnDestroy()
    {
        /* Always dispose. Disposing an already-disposed token is safe. */
        _jumpSubscription?.Dispose();
    }

    private void OnInputPerformed(InputActionPerformedEvent evt)
    {
        /* InputActionPerformedEvent fires for every action that performs.
           Filter on ActionID to handle only what this listener cares about. */
        if (evt.ActionID == "Jump")
        {
            PlayJumpSound();
        }
    }

    private void PlayJumpSound() { /* ... */ }
}

Further features

The module has more surface than the day-one path above. Deeper topics are covered on their own pages:

  • Actions and Bindings – action map registration, modifier disambiguation, the ScyllaKey enum for Input-System-free callers.
  • Contexts – push and pop InputContext objects to switch active action maps (for example, swapping gameplay bindings for menu bindings).
  • Buffering – record actions in a time window so a jump pressed one frame before landing still registers.
  • Rebinding – interactive runtime rebinding, persistence to JSON or PlayerPrefs, conflict detection.
  • Control Schemes and Hints – auto-detect keyboard/mouse vs. gamepad, resolve button-prompt sprites from icon sets.

Troubleshooting