Class PlayerInputCreator
Generates (or updates) the PlayerInput aggregate's asset chain: a UnityPlayerInputSettingsDefinition and, in Multiplayer, the three settings assets it can reference — Serenity.InputGateway.Infrastructure.Settings.UnityPlayerInputRoutingSettings, Serenity.InputGateway.Infrastructure.Settings.UnityPlayerInputJoinGateSettings, and Serenity.InputGateway.Infrastructure.Settings.UnityPlayerInputDeviceFilterSettings. This is the most complex creator in the project (four inter-referencing assets); a dedicated wizard window over this engine ships separately.
Resolution is by-Id-in-folder for all four types, mirroring
Serenity.GameMode.Installation.Editor.Builder.GameModeSettingsCreator: nothing at runtime
resolves any of these four assets by Id — UnitySerenityInstaller assigns the
UnityPlayerInputSettingsDefinition directly on the composition root, and the Definition in
turn either carries a direct routing reference or the installer falls back to an
Addressables-label lookup (see below) — so more than one instance of any of the four types can
legitimately exist per project (e.g. per build configuration), exactly like Logging's profiles.
The routing fallback is NOT null-tolerant end to end — verified by reading both call sites.
UnityPlayerInputInstaller.InstallInputRoutingSettingsFromDefinition uses the Definition's
own routing field when assigned; otherwise it calls
UnityAssetUtils.LoadAllFromAnyBlocking<UnityPlayerInputRoutingSettings> over the
PLAYER_INPUT label and takes the first match. If THAT also finds nothing, the
resolved routing stays null, and Install() unconditionally calls
InstallUiInputRouter → UnityPlayerInputUiInputRouterFactory.Create →
UiInputRouterFactoryInput.EnsureValid(), which throws ArgumentNullException when
RoutingSettings is null — a hard boot crash, not a graceful degrade. This is why every asset this
creator produces is retagged with PLAYER_INPUT (not just for inventory
consistency, unlike GameMode's settings — here the label is load-bearing for the fallback), and why
SinglePlayer — which deliberately leaves the Definition's routing
reference untouched — validates (warns, never blocks) whether a labeled
Serenity.InputGateway.Infrastructure.Settings.UnityPlayerInputRoutingSettings already exists anywhere the fallback could find it. By
contrast, InstallJoinBehavior and UnityPrimaryInputGate.IsDeviceAllowedForPrimary both
null-check routing and degrade gracefully (join disabled, any device allowed as primary) — only the UI
router path is a hard crash.
Player Prefab is authored as a UnityEngine.GameObject, not the strongly-typed
UnityEngine.InputSystem.PlayerInput field the Definition actually stores. A designer drags a prefab asset; this
engine resolves its UnityEngine.InputSystem.PlayerInput component (mirroring
UnityPlayerInputInstaller.InstallJoinBehavior's own GetComponent<PlayerInput>() check)
and warns — never blocks, matching the installer's own severity — when the prefab has none.
onPrimaryJoinTask is a plain reference, not authored. It is a UnityReflectionCallTaskDefinition — a generic reflection-call task asset with its own authoring surface entirely out of scope here; this engine only assigns whatever reference is handed to it (or preserves the existing one on update), the same way it treats InputAsset.
A wizard window collecting input and rendering results ships in a separate change; every asset mutation lives here.
Inherited Members
Namespace: Serenity.InputGateway.Installation.Editor.Builder
Assembly: Serenity.UnityPlayerInput.Installation.Editor.dll
Syntax
public static class PlayerInputCreator
Fields
DefaultOutputFolder
Folder a brand-new asset chain is created under when the caller names none.
Declaration
public const string DefaultOutputFolder = "Assets/Game/Media/PlayerInput"
Field Value
| Type | Description |
|---|---|
| string |
Methods
Create(Input)
Validates the request, resolves whichever assets already exist by Id to seed a scratch draft from each — so a field this run does not touch survives exactly as the real asset already had it, not a blank template default — applies the flat DTO's fields onto those drafts, then commits them through CreateFromDrafts(UnityPlayerInputSettingsDefinition, UnityPlayerInputRoutingSettings, UnityPlayerInputJoinGateSettings, UnityPlayerInputDeviceFilterSettings, CreationMode, string, string, string, string, string, bool), the single write path shared with CreatePlayerInputWindow's own long-lived per-asset drafts. Kept for headless/test/recipe callers that only have this flat PlayerInputCreator.Input shape — the window itself seeds and edits its own drafts directly and calls CreateFromDrafts(UnityPlayerInputSettingsDefinition, UnityPlayerInputRoutingSettings, UnityPlayerInputJoinGateSettings, UnityPlayerInputDeviceFilterSettings, CreationMode, string, string, string, string, string, bool) without ever going through this DTO.
Declaration
public static PlayerInputCreator.Result Create(PlayerInputCreator.Input input)
Parameters
| Type | Name | Description |
|---|---|---|
| PlayerInputCreator.Input | input |
Returns
| Type | Description |
|---|---|
| PlayerInputCreator.Result |
CreateFromDrafts(UnityPlayerInputSettingsDefinition, UnityPlayerInputRoutingSettings, UnityPlayerInputJoinGateSettings, UnityPlayerInputDeviceFilterSettings, CreationMode, string, string, string, string, string, bool)
The single write path: in Multiplayer, resolves-or-creates the Device
Filter and Join Gate bottom-up, then Routing (wiring both onto routingDraft before
committing it), then always resolves-or-creates the Definition (wiring Routing onto
definitionDraft only in Multiplayer mode) — each commit going through
ResolveAndCommit<TSettings>(TSettings, string, string, string, SerenityAssetCreationTransaction), so serialization decides what
gets written onto each real asset, not a hand-maintained field list. On any failure every mutation
across every touched asset is rolled back: created assets/folders are deleted, their Addressables
entries removed, and any already-existing asset that was being edited has its pre-call field values
restored. The four CALLER-OWNED draft parameters are protected the same way: each is captured via
Serenity.Global.Infrastructure.Editor.SerenityExistingAssetRestore before this method writes an Id trim or a cross-reference
onto it, and restored to that captured state on failure — so a caller's long-lived draft (the
wizard window's own bound scratch instance) never keeps a reference to an asset the rollback just
deleted.
Declaration
public static PlayerInputCreator.Result CreateFromDrafts(UnityPlayerInputSettingsDefinition definitionDraft, UnityPlayerInputRoutingSettings routingDraft, UnityPlayerInputJoinGateSettings joinGateDraft, UnityPlayerInputDeviceFilterSettings deviceFilterDraft, PlayerInputCreator.CreationMode mode, string outputFolderPath, string definitionId, string routingId, string joinGateId, string deviceFilterId, bool registerAsAddressables)
Parameters
| Type | Name | Description |
|---|---|---|
| UnityPlayerInputSettingsDefinition | definitionDraft | Every field to write onto the Definition. Required. |
| UnityPlayerInputRoutingSettings | routingDraft | Every field to write onto Routing — required in Multiplayer mode, ignored
in SinglePlayer mode (the Definition's own |
| UnityPlayerInputJoinGateSettings | joinGateDraft | Every field to write onto the Join Gate — Multiplayer mode only. |
| UnityPlayerInputDeviceFilterSettings | deviceFilterDraft | Every field to write onto the Device Filter — Multiplayer mode only. |
| PlayerInputCreator.CreationMode | mode | |
| string | outputFolderPath | |
| string | definitionId | |
| string | routingId | |
| string | joinGateId | |
| string | deviceFilterId | |
| bool | registerAsAddressables |
Returns
| Type | Description |
|---|---|
| PlayerInputCreator.Result |
Validate(Input)
Validates the request: a non-empty Id, a well-formed output folder, every timing throw UnityPlayerInputInstaller.ValidateSettings would raise, a required Input Asset (the installer throws when it is null), the UI Action Map name resolving against it (an error), the Navigate/Submit/Cancel action names resolving within that map (warnings), the Player Prefab carrying a UnityEngine.InputSystem.PlayerInput component when assigned (a warning, mirroring the installer's own severity), and the same resolve-or-edit duplicate-Id/unlink warnings Create(Input) would act on — including, in SinglePlayer, whether the installer's Routing label fallback has anything to find at all. The full rule set lives in Serenity.InputGateway.Installation.Editor.Builder.PlayerInputCreatorValidator, extracted under the file-size ratchet — this stays the one public entry point every caller uses.
Declaration
public static SerenityCreationValidationResult Validate(PlayerInputCreator.Input input)
Parameters
| Type | Name | Description |
|---|---|---|
| PlayerInputCreator.Input | input |
Returns
| Type | Description |
|---|---|
| SerenityCreationValidationResult |