Configuration

21 min read

The ScyllaInputConfiguration ScriptableObject and its companion JSON config file let you tune every Input module knob without a rebuild, from scheme-detection thresholds to rebind timeouts to binding storage backends.

Summary

This page documents ScyllaInputConfiguration, the ScriptableObject asset that holds every knob the Input module exposes, plus the JSON config file that lets you override those knobs at runtime without a rebuild. It is the same dual-layer model the rest of the framework uses – the Core Configuration page covers the mechanics in full; this page focuses on the Input-specific fields and the conventions that fit Input’s surface.

The settings are organised into five exported groups that map one-to-one with the runtime managers documented on the other Input pages: Control Scheme Detection (five thresholds for the auto-detection covered in Control Schemes and Hints), Input Buffering (three knobs for the ring buffer covered in Buffering), Binding Storage (five settings for the persistence layer covered in Rebinding), Rebind Settings (four defaults for interactive rebinding), and Input Logging (three per-category log toggles). Top-level fields outside the groups carry the references that cannot live in JSON: the InputActionAsset and the five built-in InputIconSet slots (Keyboard, Xbox, PlayStation, Nintendo, Fallback).

A companion asset, ScyllaInputContextConfiguration, owns the default context set – six InputContextDefinition entries that produce Gameplay, Free Camera, Strategy Camera, Menu, Console, and Global contexts on first load. It is a separate asset rather than a section of ScyllaInputConfiguration because the context set tends to grow project-specific and benefits from its own data model and its own “Reset to Defaults” affordance. You assign both assets to the ScyllaInput component independently.

There is always a moment in QA when a tester wants to retry a level with the gamepad stick deadzone halved, or an accessibility audit asks for the rebind timeout doubled to ten seconds, or a streamer reports that the auto-scheme-detection thrashes on their setup and asks you to lock it to keyboard prompts. The dual-layer model exists for those cases: you or a tester drop a JSON file under User Documents/{ProductName}/Config/ and the module’s ApplyConfigFileOverrides reads each key the file provides and writes it into the live settings. Keys the file omits keep their asset defaults, so a one-line override file is the right shape for “I only want to change the rebind timeout”.

Use this page for:

  • Creating the configuration asset and assigning it to ScyllaInput.
  • Looking up which knob controls which behaviour and what its default is.
  • Authoring a JSON override file for a player, a tester, or a per-build environment.
  • Switching binding storage backends from the Inspector (JSON file vs PlayerPrefs).
  • Tuning auto-detection thresholds without code edits.
  • Disabling per-category log noise for production builds.
  • Authoring the project’s ScyllaInputContextConfiguration set.

Features

  • Five exported setting groups plus top-level references. Five grouped sections cover the runtime knobs (Control Scheme Detection, Input Buffering, Binding Storage, Rebind Settings, Input Logging). Top-level fields carry the InputActionAsset reference and five icon-set slots that cannot be serialised as JSON paths.
  • JSON config file overlay. Every numeric, boolean, and string knob can be overridden at runtime via the Scylla config file system. Asset references are excluded from JSON overrides; their slots are filled at editor time on the asset itself.
  • Apply() runs during module initialization. The module reads the assigned configuration during Initialize; the JSON overrides are layered on top of the asset values and the resulting settings are pushed into the sub-managers. There is no per-frame settings sync; tuning live requires re-applying.
  • LoadDefault() Resources fallback. When no configuration is assigned to ScyllaInput, the module loads Resources/Configurations/Scylla Input Configuration (and a sibling Resources path for the context configuration). The shipped framework asset becomes the default until you supply your own.
  • ExportToConfigFile() round-trip. The asset can export itself as a ConfigFile template for players or server operators to edit. The Inspector “Export Config File” button drops the JSON on disk in the canonical four-tier path. Round-trips cleanly with ApplyConfigFileOverrides.
  • Three logging toggle pairs. Binding changes, context changes, and scheme changes each have their own boolean. Default is on for all three, which is helpful in development; turn them off individually for shipping builds where log volume matters.
  • Companion ScyllaInputContextConfiguration asset. Holds the project’s InputContextDefinition set. Ships with six defaults (Gameplay, Free Camera, Strategy Camera, Menu, Console, Global) and a “Reset to Defaults” button. Edit, remove, or extend per project.
  • Four-tier fallback per the Core configuration model. User Documents -> Application -> Resources -> compiled defaults. The Input module does not add or remove tiers; see Configuration for the full mechanics.

Concepts

The vocabulary used through the page. The Core Configuration page covers the dual-layer mechanics in full; this table is the Input-specific subset.

ConceptType / shapeNotes
ScyllaInputConfiguration[CreateAssetMenu] ScriptableObjectThe one asset that bundles every Input module knob. Assigned to ScyllaInput in the Inspector. Created via Assets > Create > Scylla > Input > Input Configuration.
ScyllaInputContextConfigurationsibling [CreateAssetMenu] ScriptableObjectHolds the InputContextDefinition set. Separate from ScyllaInputConfiguration because contexts tend to grow project-specific.
Setting groupnamed sub-section, mapped to a nested class for the three “complex” groupsFive groups are exported to JSON: Control Scheme Detection, Input Buffering, Binding Storage, Rebind Settings, Input Logging. The first two are flat fields on the root asset; the last three live on nested classes (BindingStorageSettings, etc.).
ConfigFileScylla Core JSON modelThe serialisable shape the JSON override file uses. Each group is a JSON object; each key is a tuning knob. TryGetInt/TryGetFloat/TryGetBool/TryGetString read individual keys with defaults preserved when absent.
Four-tier fallbackUser Documents -> Application -> Resources -> code defaultsThe order in which ConfigFileManager looks for an override file. Higher tiers win when present. Documented in detail in Configuration.
Asset reference fieldsInputActionAsset + five InputIconSet slotsTop-level fields outside any group. Excluded from JSON overrides because object references cannot be serialised as JSON paths; assign in the Inspector only.
Per-build gateIsConfigFileEnabledForCurrentBuild() overrideControls whether the JSON override layer is active for a given build target. Defaults to always enabled; override on a configuration subclass to restrict to specific build types (e.g. only development builds).

Recipes

These recipes cover the full configuration workflow. Each one is self-contained – jump to the one you need. The first recipe, creating and assigning the asset, is the setup step every other recipe assumes is already done.

Create the asset and assign it to ScyllaInput

Problem. You need the module to find a configuration on startup. The framework ships a default asset under Resources/Configurations/Scylla Input Configuration that the module loads when no explicit assignment exists, so a fresh project works without any configuration ceremony. But the moment you want to supply your own InputActionAsset or tune any knob, you need a project-specific asset assigned in the Inspector.

Solution. Create the asset once at project setup time, fill in the asset-reference fields, and drop it into the ScyllaInput component.

Editor steps:

1. Right-click in the Project window:
     Create > Scylla > Input > Input Configuration
   Save the asset as Assets/Game/Configs/Input Configuration.asset (or
   wherever your project keeps configurations).

2. With the asset selected, fill in the top-level reference fields:
   - Input Action Asset:   your project's primary InputActionAsset
                           (e.g. Assets/Game/Input/GameInputActions.inputactions)
   - Keyboard Icon Set:    your keyboard prompt sprites
   - Xbox Icon Set:        your Xbox prompt sprites
   - PlayStation Icon Set: your PlayStation prompt sprites
   - Nintendo Icon Set:    optional; falls back to FallbackIconSet when null
   - Fallback Icon Set:    your generic gamepad prompt sprites

3. In the Scene Hierarchy, select the ScyllaBootstrap GameObject's
   ScyllaInput child, find the Configuration slot, and drop the asset in.

4. Enter Play mode. The Console shows "ScyllaInputConfiguration applied"
   on successful initialisation. Missing fields log as named warnings
   (e.g. "Input Action Asset is null; attempting to load
   Resources/Input Actions/Scylla Input Actions").

Override knobs via JSON config file

Problem. A QA tester reports the gamepad stick deadzone is too tight on their controller and wants to test with 0.05. An accessibility audit asks for a ten-second rebind timeout. A streamer wants the auto-detection cooldown extended to one second. You need all three changes testable without a rebuild and without touching the asset that everyone else is using.

Solution. Drop a JSON override file under User Documents/{ProductName}/Config/ (the platform-specific path is documented in the Core Configuration page) and the next Apply reads and applies it. Only the keys you include are overridden; everything else keeps its asset default.

{
  "Control Scheme Detection": {
    "GamepadStickThreshold": 0.05,
    "SchemeSwitchCooldown":  1.0
  },
  "Rebind Settings": {
    "RebindSettings": {
      "Timeout": 10.0
    }
  },
  "Input Logging": {
    "InputLogging": {
      "LogContextChanges": false
    }
  }
}

A few things worth knowing about the file format:

  • Only changed keys appear. Every key the file omits keeps the asset’s default. The example above changes four settings out of the 17 the module exports; the other 13 stay at their asset values.
  • Groups are JSON objects keyed by the human-readable name. Group names match the constants on ScyllaInputConfiguration (GROUP_CONTROL_SCHEME_DETECTION, GROUP_REBIND_SETTINGS, etc.). The settings table below lists each group’s exact name.
  • Nested groups (Binding Storage, Rebind Settings, Input Logging) use a sub-object key. Each nested class becomes a JSON object keyed by its internal group name (e.g. "RebindSettings", "InputLogging"). The outer key is the human-readable group name; the inner key is the class name.
  • Types are JSON primitives. Floats are JSON numbers, bools are JSON booleans, strings are JSON strings. Enum-shaped settings (e.g. StorageType = JSONFile or PlayerPrefs) are written as strings matching the enum value name.

You can hand-edit the file, drop it via an installer, or export it from the asset itself: select the configuration asset, click “Export Config File” in the Inspector, and the framework writes the canonical JSON to the appropriate four-tier path. Edit from there; the next module initialisation picks up the changes.

Read the InputActionAsset and icon-set fields from code

Problem. You need to inspect at runtime which action asset or icon set the module is using – perhaps to log a diagnostic, to display the icon set name in a debug overlay, or to verify that a particular icon set was registered correctly.

Solution. The five object-reference fields live at the top of the asset and never participate in JSON overrides. Unity ScriptableObject references cannot be serialised as JSON paths in a way that survives builds, so they are assigned once in the Inspector and read from code when needed. The framework reads them once during module initialisation and forwards them to the appropriate sub-managers.

/* The framework reads these during Initialize and forwards to the hints manager.
 * Reading them from game code is uncommon; it is most useful for diagnostics
 * and for editor tooling that needs to inspect which assets are wired up. */
var config = ScyllaInput.Instance.Configuration;

/* Which action asset is loaded? Useful in an editor diagnostic window. */
UnityEngine.Debug.Log($"Action asset: {config.InputActionAsset.name}");

/* Which icon sets were registered? The hints manager already holds these after
 * Initialize, but you can inspect the configuration directly. */
UnityEngine.Debug.Log($"Keyboard icons: {config.KeyboardIconSet?.IconSetID}");
UnityEngine.Debug.Log($"Xbox icons: {config.XboxIconSet?.IconSetID}");
UnityEngine.Debug.Log($"Fallback icons: {config.FallbackIconSet?.IconSetID}");

Author the companion ScyllaInputContextConfiguration

Problem. Contexts (the LIFO stack covered in Contexts) define which action maps are active at a given moment and whether higher-priority contexts block input from lower-priority ones. You need to ship a default context set that works for your game’s structure without modifying the main ScyllaInputConfiguration asset.

Solution. The context set lives in a separate ScyllaInputContextConfiguration asset. A fresh asset created via the menu ships pre-populated with the six default contexts – Gameplay, Free Camera, Strategy Camera, Menu, Console, and Global – as InputContextDefinition entries. Edit, remove, or add definitions in the Inspector. The “Reset to Defaults” button restores the original six.

Editor steps:

1. Right-click in the Project window:
     Create > Scylla > Input > Context Configuration
   Save the asset as Assets/Game/Configs/Input Context Configuration.asset.

2. With the asset selected, the Inspector shows six default
   InputContextDefinition entries:
     - Gameplay        (priority 0, exclusion group "Movement", enables Gameplay maps)
     - Free Camera     (priority 0, exclusion group "Movement", enables Free Camera map)
     - Strategy Camera (priority 0, exclusion group "Movement", enables Strategy Camera map)
     - Menu            (priority 80, blocks lower)
     - Console         (priority 90, blocks lower)
     - Global          (priority 100, blocks nothing)

3. Edit, remove, or add definitions. Each definition carries:
     - Context ID           (the string identifier used by PushContext)
     - Display Name         (for debug UI)
     - Priority             (integer, drives the blocking decision)
     - Blocks Lower Pty     (boolean)
     - Consumes All Input   (boolean)
     - Exclusion Group      (optional shared string for mutually exclusive contexts)
     - Enabled Action Maps  (list of map names enabled on activation)
     - Disabled Action Maps (list of map names disabled on activation)

4. In the Scene Hierarchy, select the ScyllaInput component and drop
   the context configuration asset into the Context Configuration slot
   (separate from the main Input Configuration slot).

5. Enter Play mode. The Context Manager auto-registers every definition;
   PushContext("Menu") works from that point forward.

Each InputContextDefinition emits a runtime InputContext via CreateContext() during initialisation. The runtime objects are then registered with the context manager and become the targets for PushContext(string) calls. See the Contexts page for the runtime API surface.

Gate JSON overrides to development builds only

Problem. Your game is shipping to a certification or competitive environment where you cannot allow players to edit configuration files. You want JSON overrides active during development and QA but completely disabled in shipping builds.

Solution. Subclass ScyllaInputConfiguration and override IsConfigFileEnabledForCurrentBuild. The subclass becomes a new ScriptableObject type you assign to ScyllaInput in the Inspector exactly like the base type. The Apply call still runs on initialisation; the JSON layer simply does not contribute to the final values when the gate returns false.

using Scylla.Input;
using UnityEngine;

/* Project-specific subclass that gates JSON overrides to development
 * builds only. Shipped builds use the asset values as the only source
 * of truth; the JSON layer is not consulted. */
[CreateAssetMenu(menuName = "Game/Configs/Locked Input Configuration")]
public sealed class LockedInputConfiguration : ScyllaInputConfiguration
{
    public override bool IsConfigFileEnabledForCurrentBuild()
    {
#if UNITY_EDITOR || DEVELOPMENT_BUILD
        return true;
#else
        return false;
#endif
    }
}

Projects that want a finer-grained gate (for example, enable for QA, disable for cert builds) can branch on Application.installerName, a custom scripting define, or any project-specific condition.

API Sketch

The shape of the configuration asset, the four nested settings classes, and the companion context configuration.

namespace Scylla.Input
{
    [CreateAssetMenu(fileName = "Scylla Input Configuration",
                     menuName  = "Scylla/Input/Input Configuration")]
    public sealed class ScyllaInputConfiguration : ScyllaConfiguration
    {
        /* Asset references (top-level; excluded from JSON overrides). */
        public UnityEngine.InputSystem.InputActionAsset InputActionAsset { get; set; }
        public InputIconSet KeyboardIconSet     { get; set; }
        public InputIconSet XboxIconSet         { get; set; }
        public InputIconSet PlayStationIconSet  { get; set; }
        public InputIconSet NintendoIconSet     { get; set; }
        public InputIconSet FallbackIconSet     { get; set; }

        /* Control Scheme Detection group (5 keys). */
        public bool  AutoDetectionEnabled     { get; set; }   /* default true */
        public float SchemeSwitchCooldown     { get; set; }   /* default 0.3 s, range 0.1-2.0 */
        public float GamepadStickThreshold    { get; set; }   /* default 0.2, range 0.05-0.5 */
        public float GamepadTriggerThreshold  { get; set; }   /* default 0.1, range 0.05-0.5 */
        public float MouseDeltaThreshold      { get; set; }   /* default 5 px, range 1-20 */

        /* Input Buffering group (3 keys). */
        public bool  BufferingEnabled         { get; set; }   /* default false */
        public float DefaultBufferWindow      { get; set; }   /* default 0.15 s, range 0.05-1.0 */
        public int   MaxBufferedInputs        { get; set; }   /* default 10, range 1-20 */

        /* Nested settings groups. */
        public BindingStorageSettings BindingStorage { get; set; }
        public RebindSettings         Rebind         { get; set; }
        public InputLoggingSettings   Logging        { get; set; }

        /* Apply settings to the running module (called automatically on
         * Initialize; can be re-invoked at runtime after changes). */
        public void Apply();

        /* Inherited from ScyllaConfiguration. */
        public override string     GetConfigFileBaseName();
        public override int        ApplyConfigFileOverrides(ConfigFile configFile);
        public override ConfigFile ExportToConfigFile();
        public virtual  bool       IsConfigFileEnabledForCurrentBuild();

        /* Resources fallback loader. */
        public static ScyllaInputConfiguration LoadDefault();

        [Serializable] public class BindingStorageSettings
        {
            public BindingStorageType StorageType        { get; set; }  /* default JSONFile */
            public string             JSONStorageFolder  { get; set; }  /* default "Scylla/Input" */
            public string             DefaultProfileID   { get; set; }  /* default "default" */
            public bool               AutoLoadBindings   { get; set; }  /* default true */
            public bool               AutoSaveBindings   { get; set; }  /* default true */
        }

        [Serializable] public class RebindSettings
        {
            public float  Timeout              { get; set; }  /* default 5 s, range 1-30 */
            public string DefaultCancelPath    { get; set; }  /* default "<Keyboard>/escape" */
            public bool   ExcludeMousePosition { get; set; }  /* default true */
            public bool   ExcludeMouseDelta    { get; set; }  /* default true */
        }

        [Serializable] public class InputLoggingSettings
        {
            public bool LogBindingChanges { get; set; }  /* default true */
            public bool LogContextChanges { get; set; }  /* default true */
            public bool LogSchemeChanges  { get; set; }  /* default true */
        }
    }

    [CreateAssetMenu(fileName = "Scylla Input Context Configuration",
                     menuName  = "Scylla/Input/Context Configuration")]
    public sealed class ScyllaInputContextConfiguration : ScyllaConfiguration
    {
        public List<InputContextDefinition> ContextDefinitions { get; set; }

        public static ScyllaInputContextConfiguration LoadDefault();
        public void ResetToDefaults();
    }
}

See the API reference for the full surface, including the constants that define the JSON key names and the framework-internal Apply plumbing.

Settings reference

Every knob, organised by group, with the JSON group name and the default.

Control Scheme Detection

JSON group name: Control Scheme Detection.

KeyTypeDefaultRange / accepted valuesEffect
AutoDetectionEnabledbooltruetrue or falseWhen true, the control scheme manager samples device input every frame and switches schemes automatically. When false, only programmatic switches work.
SchemeSwitchCooldownfloat0.30.1 to 2.0 secondsMinimum gap between auto-switches. Prevents thrashing when both devices register input in quick succession.
GamepadStickThresholdfloat0.20.05 to 0.5Minimum analog stick magnitude that counts as gamepad activity. Filters analog drift.
GamepadTriggerThresholdfloat0.10.05 to 0.5Minimum trigger pressure that counts as gamepad activity. Filters resting-finger contact.
MouseDeltaThresholdfloat51 to 20 pixelsMinimum mouse movement per frame that counts as keyboard/mouse activity. Filters optical sensor noise.

Input Buffering

JSON group name: Input Buffering.

KeyTypeDefaultRange / accepted valuesEffect
BufferingEnabledboolfalsetrue or falseWhen true, action presses recorded via BufferAction are queued in the ring buffer. When false, all buffer operations are no-ops.
DefaultBufferWindowfloat0.150.05 to 1.0 secondsDefault age limit for buffered entries. Overridden per-action via SetActionBufferWindow.
MaxBufferedInputsint101 to 20Ring buffer capacity. When full, the oldest entry is overwritten on the next BufferAction call.

Binding Storage

JSON group name: Binding Storage. Settings live on the nested BindingStorageSettings class.

KeyTypeDefaultAccepted valuesEffect
StorageTypeenumJSONFileJSONFile, PlayerPrefsSelects the active IBindingStorage backend. JSON writes files under JSONStorageFolder; PlayerPrefs uses Unity’s registry.
JSONStorageFolderstringScylla/InputAny non-empty relative pathSubdirectory under Application.persistentDataPath where binding JSON files are written. Only used when StorageType = JSONFile.
DefaultProfileIDstringdefaultAny non-empty stringProfile ID used as the implicit target for SaveBindings / LoadBindings when none is specified.
AutoLoadBindingsbooltruetrue or falseWhen true, the default profile is loaded automatically during module initialisation.
AutoSaveBindingsbooltruetrue or falseWhen true, bindings are saved automatically after every successful rebind operation.

Rebind Settings

JSON group name: Rebind Settings. Settings live on the nested RebindSettings class.

KeyTypeDefaultRange / accepted valuesEffect
Timeoutfloat51 to 30 secondsMaximum seconds a rebind operation waits for input before auto-cancelling.
DefaultCancelPathstring<Keyboard>/escapeAny Input System path or emptyControl path that immediately cancels an active rebind. An empty string disables the cancel path (timeout only).
ExcludeMousePositionbooltruetrue or falseWhen true, <Mouse>/position is rejected as a rebind target so players cannot accidentally bind to cursor location.
ExcludeMouseDeltabooltruetrue or falseWhen true, <Mouse>/delta is rejected so players cannot accidentally bind to mouse motion.

Input Logging

JSON group name: Input Logging. Settings live on the nested InputLoggingSettings class.

KeyTypeDefaultEffect
LogBindingChangesbooltrueWhen true, logs each binding override applied, reset, loaded, or saved. Useful in development; noisy in shipped builds.
LogContextChangesbooltrueWhen true, logs every push and pop on the context stack. Useful for tracing context flow when debugging input priority issues.
LogSchemeChangesbooltrueWhen true, logs every active control scheme switch. Useful for verifying auto-detection thresholds during development.

Best Practices

  • Keep one configuration asset per project. A single shared asset is easier to maintain than per-scene variants. Per-build differences belong in the JSON override layer or in a subclass that overrides IsConfigFileEnabledForCurrentBuild.
  • Author the asset early; tune via JSON. Set the asset to defaults that match your game’s general feel; let QA, accessibility, and platform-specific overrides land via the JSON file. This avoids per-build asset duplication.
  • Use the framework defaults as a starting point. The default Scylla Input Configuration asset that ships under Resources/Configurations carries the conservative values documented in this page. Copy it (or create fresh and copy the defaults) when starting a new project, then tune from there.
  • Export from the Inspector to seed the JSON file. Click “Export Config File” to produce a canonical JSON document that matches the current asset values; ship that file as the “if you want to override, start here” seed for player-facing tuning options.
  • Keep AutoLoadBindings and AutoSaveBindings on by default. Manually managing save/load through code is the right shape for advanced settings UI (a confirm-changes pattern that should not auto-save mid-rebind), but most projects benefit from the default “the player rebinds; the change persists immediately” flow.
  • Disable the three logging toggles in shipping builds. Each is on by default for development. For cert and shipping builds, turn off the categories you do not need to investigate. Shipping titles typically keep LogBindingChanges = true (useful for support tickets) and disable the other two.
  • Choose BindingStorage.StorageType per platform. Default JSONFile for desktop (modding-friendly, easy for support), PlayerPrefs for mobile and console (no file-system access required, certification-friendly). A platform-specific subclass override or a JSON file shipped per build is the right shape for the switch.
  • Treat BufferingEnabled as the global on/off switch. When your project uses buffering at all, leave it on and tune via per-action windows from code. When your project does not use buffering, leave it off so the buffer manager’s per-frame work is skipped.
  • Subclass for project-specific gates. The IsConfigFileEnabledForCurrentBuild override is the cleanest path for “JSON overrides only in development”. Subclassing produces a new asset type that ships alongside the framework default without modifying the framework code.

Pitfalls