using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.InputSystem.Utilities;
////TODO: control schemes, like actions and maps, should have stable IDs so that they can be renamed
////REVIEW: have some way of expressing 'contracts' on action maps? I.e. something like
//// "I expect a 'look' and a 'move' action in here"
////REVIEW: rename this from "InputActionAsset" to something else that emphasizes the asset aspect less
//// and instead emphasizes the map collection aspect more?
namespace UnityEngine.InputSystem
{
///
/// An asset containing action maps and control schemes.
///
///
/// InputActionAssets can be created in code but are usually stored in JSON format on
/// disk with the ".inputactions" extension and are imported by Unity using a custom
/// importer.
///
/// To create an InputActionAsset in code, use the Singleton API and populate the
/// asset with the methods found in . Alternatively,
/// you can load an InputActionAsset directly from a string in JSON format using .
///
///
///
/// // Create and configure an asset in code.
/// var asset1 = ScriptableObject.CreateInstance<InputActionAsset>();
/// var actionMap1 = asset1.CreateActionMap("map1");
/// action1Map.AddAction("action1", binding: "<Keyboard>/space");
///
///
///
/// Each asset can contain arbitrary many action maps that can be enabled and disabled individually
/// (see and ) or in bulk
/// (see and ). The name of each action map must be unique.
/// The list of action maps can be queried from .
///
/// InputActionAssets can only define s. They can be added to
/// an asset with
/// and can be queried from .
///
/// Be aware that input action assets do not separate between static (configuration) data and dynamic
/// (instance) data. For audio, for example, AudioClip represents the static,
/// shared data portion of audio playback whereas AudioSource" represents the
/// dynamic, per-instance audio playback portion (referencing the clip through AudioSource.clip).
///
/// For input, such a split is less beneficial as the same input is generally not exercised
/// multiple times in parallel. Keeping both static and dynamic data together simplifies
/// using the system.
///
/// However, there are scenarios where you indeed want to take the same input action and
/// exercise it multiple times in parallel. A prominent example of such a use case is
/// local multiplayer where each player gets the same set of actions but is controlling
/// them with a different device (or devices) each. This is easily achieved by simply
/// using UnityEngine.Object.Instantiate to instantiate the input action
/// asset multiple times. will automatically do so in its
/// internals.
///
/// Note also that all action maps in an asset share binding state. This means that if
/// one map in an asset has to resolve its bindings, all maps in the asset have to.
///
public class InputActionAsset : ScriptableObject, IInputActionCollection
{
///
/// File extension (without the dot) for InputActionAssets in JSON format.
///
/// File extension for InputActionAsset source files.
///
/// Files with this extension will automatically be imported by Unity as
/// InputActionAssets.
///
public const string Extension = "inputactions";
///
/// True if any action in the asset is currently enabled.
///
///
///
///
///
///
public bool enabled
{
get
{
foreach (var actionMap in actionMaps)
if (actionMap.enabled)
return true;
return false;
}
}
///
/// List of action maps defined in the asset.
///
/// Action maps contained in the asset.
///
///
///
public ReadOnlyArray actionMaps => new ReadOnlyArray(m_ActionMaps);
///
/// List of control schemes defined in the asset.
///
/// Control schemes defined for the asset.
///
///
public ReadOnlyArray controlSchemes => new ReadOnlyArray(m_ControlSchemes);
///
/// Binding mask to apply to all action maps and actions in the asset.
///
/// Optional mask that determines which bindings in the asset to enable.
///
/// Binding masks can be applied at three different levels: for an entire asset through
/// this property, for a specific map through ,
/// and for single actions through . By default,
/// none of the masks will be set (i.e. they will be null).
///
/// When an action is enabled, all the binding masks that apply to it are taken into
/// account. Specifically, this means that any given binding on the action will be
/// enabled only if it matches the mask applied to the asset, the mask applied
/// to the map that contains the action, and the mask applied to the action itself.
/// All the masks are individually optional.
///
/// Masks are matched against bindings using .
///
/// Note that if you modify the masks applicable to an action while it is
/// enabled, the action's will get updated immediately to
/// respect the mask. To avoid repeated binding resolution, it is most efficient
/// to apply binding masks before enabling actions.
///
/// Binding masks are non-destructive. All the bindings on the action are left
/// in place. Setting a mask will not affect the value of the
/// and properties.
///
///
///
///
public InputBinding? bindingMask
{
get => m_BindingMask;
set
{
if (m_BindingMask == value)
return;
m_BindingMask = value;
ReResolveIfNecessary();
}
}
///
/// Set of devices that bindings in the asset can bind to.
///
/// Optional set of devices to use by bindings in the asset.
///
/// By default (with this property being null), bindings will bind to any of the
/// controls available through , i.e. controls from all
/// devices in the system will be used.
///
/// By setting this property, binding resolution can instead be restricted to just specific
/// devices. This restriction can either be applied to an entire asset using this property
/// or to specific action maps by using . Note that if
/// both this property and is set for a specific action
/// map, the list of devices on the action map will take precedence and the list on the
/// asset will be ignored for bindings in that action map.
///
///
///
/// // Create an asset with a single action map and a single action with a
/// // gamepad binding.
/// var asset = ScriptableObject.CreateInstance<InputActionAsset>();
/// var actionMap = new InputActionMap();
/// var fireAction = actionMap.AddAction("Fire", binding: "<Gamepad>/buttonSouth");
/// asset.AddActionMap(actionMap);
///
/// // Let's assume we have two gamepads connected. If we enable the
/// // action map now, the 'Fire' action will bind to both.
/// actionMap.Enable();
///
/// // This will print two controls.
/// Debug.Log(string.Join("\n", fireAction.controls));
///
/// // To restrict the setup to just the first gamepad, we can assign
/// // to the 'devices' property (in this case, we could do so on either
/// // the action map or on the asset; we choose the latter here).
/// asset.devices = new InputDevice[] { Gamepad.all[0] };
///
/// // Now this will print only one control.
/// Debug.Log(string.Join("\n", fireAction.controls));
///
///
///
///
public ReadOnlyArray? devices
{
get
{
if (m_DevicesCount < 0)
return null;
return new ReadOnlyArray(m_DevicesArray, 0, m_DevicesCount);
}
set
{
if (value == null)
{
if (m_DevicesCount < 0)
return; // No change.
if (m_DevicesArray != null & m_DevicesCount > 0)
Array.Clear(m_DevicesArray, 0, m_DevicesCount);
m_DevicesCount = -1;
}
else
{
// See if the array actually changes content. Avoids re-resolving when there
// is no need to.
if (m_DevicesCount == value.Value.Count)
{
var noChange = true;
for (var i = 0; i < m_DevicesCount; ++i)
{
if (!ReferenceEquals(m_DevicesArray[i], value.Value[i]))
{
noChange = false;
break;
}
}
if (noChange)
return;
}
if (m_DevicesCount > 0)
m_DevicesArray.Clear(ref m_DevicesCount);
m_DevicesCount = 0;
ArrayHelpers.AppendListWithCapacity(ref m_DevicesArray, ref m_DevicesCount, value.Value);
}
ReResolveIfNecessary();
}
}
///
/// Look up an action by name or ID.
///
/// Name of the action as either a "map/action" combination (e.g. "gameplay/fire") or
/// a simple name. In the former case, the name is split at the '/' slash and the first part is used to find
/// a map with that name and the second part is used to find an action with that name inside the map. In the
/// latter case, all maps are searched in order and the first action that has the given name in any of the maps
/// is returned. Note that name comparisons are case-insensitive.
///
/// Alternatively, the given string can be a GUID as given by .
/// The action with the corresponding name or null if no matching action could be found.
///
/// This method is equivalent to except that it throws
/// if no action with the given name or ID
/// could be found.
///
/// No action was found matching .
/// is null or empty.
///
public InputAction this[string actionNameOrId]
{
get
{
var action = FindAction(actionNameOrId);
if (action == null)
throw new KeyNotFoundException($"Cannot find action '{actionNameOrId}' in '{this}'");
return action;
}
}
///
/// Return a JSON representation of the asset.
///
/// A string in JSON format that represents the static/configuration data present
/// in the asset.
///
/// This will not save dynamic execution state such as callbacks installed on
/// actions or enabled/disabled states of individual
/// maps and actions.
///
/// Use to deserialize the JSON data back into an InputActionAsset.
///
/// Be aware that the format used by this method is different than what you
/// get if you call JsonUtility.ToJson on an InputActionAsset instance. In other
/// words, the JSON format is not identical to the Unity serialized object representation
/// of the asset.
///
///
public string ToJson()
{
var fileJson = new WriteFileJson
{
name = name,
maps = InputActionMap.WriteFileJson.FromMaps(m_ActionMaps).maps,
controlSchemes = InputControlScheme.SchemeJson.ToJson(m_ControlSchemes),
};
return JsonUtility.ToJson(fileJson, true);
}
///
/// Replace the contents of the asset with the data in the given JSON string.
///
/// JSON contents of an .inputactions asset.
///
/// .inputactions assets are stored in JSON format. This method allows reading
/// the JSON source text of such an asset into an existing InputActionMap instance.
///
///
///
/// var asset = ScriptableObject.CreateInstance<InputActionAsset>();
/// asset.LoadFromJson(@"
/// {
/// ""maps"" : [
/// {
/// ""name"" : ""gameplay"",
/// ""actions"" : [
/// { ""name"" : ""fire"", ""type"" : ""button"" },
/// { ""name"" : ""look"", ""type"" : ""value"" },
/// { ""name"" : ""move"", ""type"" : ""value"" }
/// ],
/// ""bindings"" : [
/// { ""path"" : ""<Gamepad>/buttonSouth"", ""action"" : ""fire"", ""groups"" : ""Gamepad"" },
/// { ""path"" : ""<Gamepad>/leftTrigger"", ""action"" : ""fire"", ""groups"" : ""Gamepad"" },
/// { ""path"" : ""<Gamepad>/leftStick"", ""action"" : ""move"", ""groups"" : ""Gamepad"" },
/// { ""path"" : ""<Gamepad>/rightStick"", ""action"" : ""look"", ""groups"" : ""Gamepad"" },
/// { ""path"" : ""dpad"", ""action"" : ""move"", ""groups"" : ""Gamepad"", ""isComposite"" : true },
/// { ""path"" : ""<Keyboard>/a"", ""name"" : ""left"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
/// { ""path"" : ""<Keyboard>/d"", ""name"" : ""right"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
/// { ""path"" : ""<Keyboard>/w"", ""name"" : ""up"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
/// { ""path"" : ""<Keyboard>/s"", ""name"" : ""down"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
/// { ""path"" : ""<Mouse>/delta"", ""action"" : ""look"", ""groups"" : ""Keyboard&Mouse"" },
/// { ""path"" : ""<Mouse>/leftButton"", ""action"" : ""fire"", ""groups"" : ""Keyboard&Mouse"" }
/// ]
/// },
/// {
/// ""name"" : ""ui"",
/// ""actions"" : [
/// { ""name"" : ""navigate"" }
/// ],
/// ""bindings"" : [
/// { ""path"" : ""<Gamepad>/dpad"", ""action"" : ""navigate"", ""groups"" : ""Gamepad"" }
/// ]
/// }
/// ],
/// ""controlSchemes"" : [
/// {
/// ""name"" : ""Gamepad"",
/// ""bindingGroup"" : ""Gamepad"",
/// ""devices"" : [
/// { ""devicePath"" : ""<Gamepad>"" }
/// ]
/// },
/// {
/// ""name"" : ""Keyboard&Mouse"",
/// ""bindingGroup"" : ""Keyboard&Mouse"",
/// ""devices"" : [
/// { ""devicePath"" : ""<Keyboard>"" },
/// { ""devicePath"" : ""<Mouse>"" }
/// ]
/// }
/// ]
/// }");
///
///
///
/// is null or empty.
///
///
public void LoadFromJson(string json)
{
if (string.IsNullOrEmpty(json))
throw new ArgumentNullException(nameof(json));
var parsedJson = JsonUtility.FromJson(json);
parsedJson.ToAsset(this);
}
///
/// Replace the contents of the asset with the data in the given JSON string.
///
/// JSON contents of an .inputactions asset.
/// The InputActionAsset instance created from the given JSON string.
///
/// .inputactions assets are stored in JSON format. This method allows turning
/// the JSON source text of such an asset into a new InputActionMap instance.
///
/// Be aware that the format used by this method is different than what you
/// get if you call JsonUtility.ToJson on an InputActionAsset instance. In other
/// words, the JSON format is not identical to the Unity serialized object representation
/// of the asset.
///
///
///
/// var asset = InputActionAsset.FromJson(@"
/// {
/// ""maps"" : [
/// {
/// ""name"" : ""gameplay"",
/// ""actions"" : [
/// { ""name"" : ""fire"", ""type"" : ""button"" },
/// { ""name"" : ""look"", ""type"" : ""value"" },
/// { ""name"" : ""move"", ""type"" : ""value"" }
/// ],
/// ""bindings"" : [
/// { ""path"" : ""<Gamepad>/buttonSouth"", ""action"" : ""fire"", ""groups"" : ""Gamepad"" },
/// { ""path"" : ""<Gamepad>/leftTrigger"", ""action"" : ""fire"", ""groups"" : ""Gamepad"" },
/// { ""path"" : ""<Gamepad>/leftStick"", ""action"" : ""move"", ""groups"" : ""Gamepad"" },
/// { ""path"" : ""<Gamepad>/rightStick"", ""action"" : ""look"", ""groups"" : ""Gamepad"" },
/// { ""path"" : ""dpad"", ""action"" : ""move"", ""groups"" : ""Gamepad"", ""isComposite"" : true },
/// { ""path"" : ""<Keyboard>/a"", ""name"" : ""left"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
/// { ""path"" : ""<Keyboard>/d"", ""name"" : ""right"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
/// { ""path"" : ""<Keyboard>/w"", ""name"" : ""up"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
/// { ""path"" : ""<Keyboard>/s"", ""name"" : ""down"", ""action"" : ""move"", ""groups"" : ""Keyboard&Mouse"", ""isPartOfComposite"" : true },
/// { ""path"" : ""<Mouse>/delta"", ""action"" : ""look"", ""groups"" : ""Keyboard&Mouse"" },
/// { ""path"" : ""<Mouse>/leftButton"", ""action"" : ""fire"", ""groups"" : ""Keyboard&Mouse"" }
/// ]
/// },
/// {
/// ""name"" : ""ui"",
/// ""actions"" : [
/// { ""name"" : ""navigate"" }
/// ],
/// ""bindings"" : [
/// { ""path"" : ""<Gamepad>/dpad"", ""action"" : ""navigate"", ""groups"" : ""Gamepad"" }
/// ]
/// }
/// ],
/// ""controlSchemes"" : [
/// {
/// ""name"" : ""Gamepad"",
/// ""bindingGroup"" : ""Gamepad"",
/// ""devices"" : [
/// { ""devicePath"" : ""<Gamepad>"" }
/// ]
/// },
/// {
/// ""name"" : ""Keyboard&Mouse"",
/// ""bindingGroup"" : ""Keyboard&Mouse"",
/// ""devices"" : [
/// { ""devicePath"" : ""<Keyboard>"" },
/// { ""devicePath"" : ""<Mouse>"" }
/// ]
/// }
/// ]
/// }");
///
///
///
/// is null or empty.
///
///
public static InputActionAsset FromJson(string json)
{
if (string.IsNullOrEmpty(json))
throw new ArgumentNullException(nameof(json));
var asset = CreateInstance();
asset.LoadFromJson(json);
return asset;
}
///
/// Find an by its name in one of the s
/// in the asset.
///
/// Name of the action as either a "map/action" combination (e.g. "gameplay/fire") or
/// a simple name. In the former case, the name is split at the '/' slash and the first part is used to find
/// a map with that name and the second part is used to find an action with that name inside the map. In the
/// latter case, all maps are searched in order and the first action that has the given name in any of the maps
/// is returned. Note that name comparisons are case-insensitive.
///
/// Alternatively, the given string can be a GUID as given by .
/// If true, instead of returning null when the action
/// cannot be found, throw ArgumentException.
/// The action with the corresponding name or null if no matching action could be found.
///
///
///
/// var asset = ScriptableObject.CreateInstance<InputActionAsset>();
///
/// var map1 = new InputActionMap("map1");
/// var map2 = new InputActionMap("map2");
///
/// asset.AddActionMap(map1);
/// asset.AddActionMap(map2);
///
/// var action1 = map1.AddAction("action1");
/// var action2 = map1.AddAction("action2");
/// var action3 = map2.AddAction("action3");
///
/// // Search all maps in the asset for any action that has the given name.
/// asset.FindAction("action1") // Returns action1.
/// asset.FindAction("action2") // Returns action2
/// asset.FindAction("action3") // Returns action3.
///
/// // Search for a specific action in a specific map.
/// asset.FindAction("map1/action1") // Returns action1.
/// asset.FindAction("map2/action2") // Returns action2.
/// asset.FindAction("map3/action3") // Returns action3.
///
/// // Search by unique action ID.
/// asset.FindAction(action1.id.ToString()) // Returns action1.
/// asset.FindAction(action2.id.ToString()) // Returns action2.
/// asset.FindAction(action3.id.ToString()) // Returns action3.
///
///
///
/// is null.
/// Thrown if is true and the
/// action could not be found. -Or- If contains a slash but is missing
/// either the action or the map name.
public InputAction FindAction(string actionNameOrId, bool throwIfNotFound = false)
{
if (actionNameOrId == null)
throw new ArgumentNullException(nameof(actionNameOrId));
if (m_ActionMaps != null)
{
// Check if we have a "map/action" path.
var indexOfSlash = actionNameOrId.IndexOf('/');
if (indexOfSlash == -1)
{
// No slash so it's just a simple action name.
for (var i = 0; i < m_ActionMaps.Length; ++i)
{
var action = m_ActionMaps[i].FindAction(actionNameOrId);
if (action != null)
return action;
}
}
else
{
// Have a path. First search for the map, then for the action.
var mapName = new Substring(actionNameOrId, 0, indexOfSlash);
var actionName = new Substring(actionNameOrId, indexOfSlash + 1);
if (mapName.isEmpty || actionName.isEmpty)
throw new ArgumentException("Malformed action path: " + actionNameOrId, nameof(actionNameOrId));
for (var i = 0; i < m_ActionMaps.Length; ++i)
{
var map = m_ActionMaps[i];
if (Substring.Compare(map.name, mapName, StringComparison.InvariantCultureIgnoreCase) != 0)
continue;
var actions = map.m_Actions;
for (var n = 0; n < actions.Length; ++n)
{
var action = actions[n];
if (Substring.Compare(action.name, actionName,
StringComparison.InvariantCultureIgnoreCase) == 0)
return action;
}
break;
}
}
}
if (throwIfNotFound)
throw new ArgumentException($"No action '{actionNameOrId}' in '{this}'");
return null;
}
///
/// Find an in the asset by its name or ID.
///
/// Name or ID (see ) of the action map
/// to look for. Matching is case-insensitive.
/// If true, instead of returning null, throw ArgumentException.
/// The with a name or ID matching or
/// null if no matching map could be found.
/// is null.
/// If is true, thrown if
/// the action map cannot be found.
///
///
public InputActionMap FindActionMap(string nameOrId, bool throwIfNotFound = false)
{
if (nameOrId == null)
throw new ArgumentNullException(nameof(nameOrId));
if (m_ActionMaps == null)
return null;
// If the name contains a hyphen, it may be a GUID.
if (nameOrId.Contains('-') && Guid.TryParse(nameOrId, out var id))
{
for (var i = 0; i < m_ActionMaps.Length; ++i)
{
var map = m_ActionMaps[i];
if (map.idDontGenerate == id)
return map;
}
}
// Default lookup is by name (case-insensitive).
for (var i = 0; i < m_ActionMaps.Length; ++i)
{
var map = m_ActionMaps[i];
if (string.Compare(nameOrId, map.name, StringComparison.InvariantCultureIgnoreCase) == 0)
return map;
}
if (throwIfNotFound)
throw new ArgumentException($"Cannot find action map '{nameOrId}' in '{this}'");
return null;
}
///
/// Find an in the asset by its ID.
///
/// ID (see ) of the action map
/// to look for.
/// The with ID matching or
/// null if no map in the asset has the given ID.
///
///
public InputActionMap FindActionMap(Guid id)
{
if (m_ActionMaps == null)
return null;
for (var i = 0; i < m_ActionMaps.Length; ++i)
{
var map = m_ActionMaps[i];
if (map.idDontGenerate == id)
return map;
}
return null;
}
///
/// Find an action by its ID (see ).
///
/// ID of the action to look for.
/// The action in the asset with the given ID or null if no action
/// in the asset has the given ID.
public InputAction FindAction(Guid guid)
{
if (m_ActionMaps == null)
return null;
for (var i = 0; i < m_ActionMaps.Length; ++i)
{
var map = m_ActionMaps[i];
var action = map.FindAction(guid);
if (action != null)
return action;
}
return null;
}
///
/// Find the control scheme with the given name and return its index
/// in .
///
/// Name of the control scheme. Matching is case-insensitive.
/// The index of the given control scheme or -1 if no control scheme
/// with the given name could be found.
/// is null
/// or empty.
public int FindControlSchemeIndex(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
if (m_ControlSchemes == null)
return -1;
for (var i = 0; i < m_ControlSchemes.Length; ++i)
if (string.Compare(name, m_ControlSchemes[i].name, StringComparison.InvariantCultureIgnoreCase) == 0)
return i;
return -1;
}
///
/// Find the control scheme with the given name and return it.
///
/// Name of the control scheme. Matching is case-insensitive.
/// The control scheme with the given name or null if no scheme
/// with the given name could be found in the asset.
/// is null
/// or empty.
public InputControlScheme? FindControlScheme(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
var index = FindControlSchemeIndex(name);
if (index == -1)
return null;
return m_ControlSchemes[index];
}
///
/// Enable all action maps in the asset.
///
///
/// This method is equivalent to calling on
/// all maps in .
///
public void Enable()
{
foreach (var map in actionMaps)
map.Enable();
}
///
/// Disable all action maps in the asset.
///
///
/// This method is equivalent to calling on
/// all maps in .
///
public void Disable()
{
foreach (var map in actionMaps)
map.Disable();
}
///
/// Return true if the given action is part of the asset.
///
/// An action. Can be null.
/// True if the given action is part of the asset, false otherwise.
public bool Contains(InputAction action)
{
var map = action?.actionMap;
if (map == null)
return false;
return map.asset == this;
}
///
/// Enumerate all actions in the asset.
///
/// Enumerate over all actions in the asset.
///
/// Actions will be enumerated one action map in
/// after the other. The actions from each map will be yielded in turn.
///
/// This method will allocate GC heap memory.
///
public IEnumerator GetEnumerator()
{
if (m_ActionMaps == null)
yield break;
for (var i = 0; i < m_ActionMaps.Length; ++i)
{
var actions = m_ActionMaps[i].actions;
var actionCount = actions.Count;
for (var n = 0; n < actionCount; ++n)
yield return actions[n];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private void ReResolveIfNecessary()
{
if (m_SharedStateForAllMaps == null)
return;
Debug.Assert(m_ActionMaps != null && m_ActionMaps.Length > 0);
// State is share between all action maps in the asset. Resolving bindings for the
// first map will resolve them for all maps.
m_ActionMaps[0].LazyResolveBindings();
}
private void OnDestroy()
{
Disable();
if (m_SharedStateForAllMaps != null)
{
m_SharedStateForAllMaps.Dispose(); // Will clean up InputActionMap state.
m_SharedStateForAllMaps = null;
}
}
////TODO: ApplyBindingOverrides, RemoveBindingOverrides, RemoveAllBindingOverrides
[SerializeField] internal InputActionMap[] m_ActionMaps;
[SerializeField] internal InputControlScheme[] m_ControlSchemes;
////TODO: make this persistent across domain reloads
///
/// Shared state for all action maps in the asset.
///
[NonSerialized] internal InputActionState m_SharedStateForAllMaps;
[NonSerialized] internal InputBinding? m_BindingMask;
[NonSerialized] private int m_DevicesCount = -1;
[NonSerialized] private InputDevice[] m_DevicesArray;
[Serializable]
internal struct WriteFileJson
{
public string name;
public InputActionMap.WriteMapJson[] maps;
public InputControlScheme.SchemeJson[] controlSchemes;
}
[Serializable]
internal struct ReadFileJson
{
public string name;
public InputActionMap.ReadMapJson[] maps;
public InputControlScheme.SchemeJson[] controlSchemes;
public void ToAsset(InputActionAsset asset)
{
asset.name = name;
asset.m_ActionMaps = new InputActionMap.ReadFileJson {maps = maps}.ToMaps();
asset.m_ControlSchemes = InputControlScheme.SchemeJson.ToSchemes(controlSchemes);
// Link maps to their asset.
if (asset.m_ActionMaps != null)
foreach (var map in asset.m_ActionMaps)
map.m_Asset = asset;
}
}
}
}