1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900 |
- using System;
- using UnityEngine.InputSystem.Layouts;
- using UnityEngine.InputSystem.Utilities;
-
- ////TODO: Rename all the xxxSyntax structs to xxxAccessor
-
- ////TODO: Replace all 'WithXXX' in the accessors with just 'SetXXX'; the 'WithXXX' reads too awkwardly
-
- namespace UnityEngine.InputSystem
- {
- /// <summary>
- /// Methods to change the setup of <see cref="InputAction"/>, <see cref="InputActionMap"/>,
- /// and <see cref="InputActionAsset"/> objects.
- /// </summary>
- /// <remarks>
- /// Unlike the methods in <see cref="InputActionRebindingExtensions"/>, the methods here are
- /// generally destructive, i.e. they will rearrange the data for actions.
- /// </remarks>
- public static class InputActionSetupExtensions
- {
- /// <summary>
- /// Create an action map with the given name and add it to the asset.
- /// </summary>
- /// <param name="asset">Asset to add the action map to</param>
- /// <param name="name">Name to assign to the </param>
- /// <returns>The newly added action map.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c> or
- /// <exception cref="InvalidOperationException">An action map with the given <paramref name="name"/>
- /// already exists in <paramref name="asset"/>.</exception>
- /// <paramref name="name"/> is <c>null</c> or empty.</exception>
- public static InputActionMap AddActionMap(this InputActionAsset asset, string name)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name));
- if (asset.FindActionMap(name) != null)
- throw new InvalidOperationException(
- $"An action map called '{name}' already exists in the asset");
-
- var map = new InputActionMap(name);
- map.GenerateId();
- asset.AddActionMap(map);
- return map;
- }
-
- /// <summary>
- /// Add an action map to the asset.
- /// </summary>
- /// <param name="asset">Asset to add the map to.</param>
- /// <param name="map">A named action map.</param>
- /// <exception cref="ArgumentNullException"><paramref name="map"/> or <paramref name="asset"/> is <c>null</c>.</exception>
- /// <exception cref="InvalidOperationException"><paramref name="map"/> has no name or asset already contains a
- /// map with the same name -or- <paramref name="map"/> is currently enabled -or- <paramref name="map"/> is part of
- /// an <see cref="InputActionAsset"/> that has <see cref="InputActionMap"/>s that are enabled.</exception>
- /// <seealso cref="InputActionAsset.actionMaps"/>
- public static void AddActionMap(this InputActionAsset asset, InputActionMap map)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (map == null)
- throw new ArgumentNullException(nameof(map));
- if (string.IsNullOrEmpty(map.name))
- throw new InvalidOperationException("Maps added to an input action asset must be named");
- if (map.asset != null)
- throw new InvalidOperationException(
- $"Cannot add map '{map}' to asset '{asset}' as it has already been added to asset '{map.asset}'");
- ////REVIEW: some of the rules here seem stupid; just replace?
- if (asset.FindActionMap(map.name) != null)
- throw new InvalidOperationException(
- $"An action map called '{map.name}' already exists in the asset");
-
- map.OnWantToChangeSetup();
- asset.OnWantToChangeSetup();
-
- ArrayHelpers.Append(ref asset.m_ActionMaps, map);
- map.m_Asset = asset;
- asset.OnSetupChanged();
- }
-
- /// <summary>
- /// Remove the given action map from the asset.
- /// </summary>
- /// <param name="asset">Asset to add the action map to.</param>
- /// <param name="map">An action map. If the given map is not part of the asset, the method
- /// does nothing.</param>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> or <paramref name="map"/> is <c>null</c>.</exception>
- /// <exception cref="InvalidOperationException"><paramref name="map"/> is currently enabled (see <see
- /// cref="InputActionMap.enabled"/>) or is part of an <see cref="InputActionAsset"/> that has <see cref="InputActionMap"/>s
- /// that are currently enabled.</exception>
- /// <seealso cref="RemoveActionMap(InputActionAsset,string)"/>
- /// <seealso cref="InputActionAsset.actionMaps"/>
- public static void RemoveActionMap(this InputActionAsset asset, InputActionMap map)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (map == null)
- throw new ArgumentNullException(nameof(map));
-
- map.OnWantToChangeSetup();
- asset.OnWantToChangeSetup();
-
- // Ignore if not part of this asset.
- if (map.m_Asset != asset)
- return;
-
- ArrayHelpers.Erase(ref asset.m_ActionMaps, map);
- map.m_Asset = null;
- asset.OnSetupChanged();
- }
-
- /// <summary>
- /// Remove the action map with the given name or ID from the asset.
- /// </summary>
- /// <param name="asset">Asset to remove the action map from.</param>
- /// <param name="nameOrId">The name or ID (see <see cref="InputActionMap.id"/>) of a map in the
- /// asset. Note that lookup is case-insensitive. If no map with the given name or ID is found,
- /// the method does nothing.</param>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> or <paramref name="nameOrId"/> is <c>null</c>.</exception>
- /// <exception cref="InvalidOperationException">The map referenced by <paramref name="nameOrId"/> is currently enabled
- /// (see <see cref="InputActionMap.enabled"/>).</exception>
- /// <seealso cref="RemoveActionMap(InputActionAsset,string)"/>
- /// <seealso cref="InputActionAsset.actionMaps"/>
- public static void RemoveActionMap(this InputActionAsset asset, string nameOrId)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (nameOrId == null)
- throw new ArgumentNullException(nameof(nameOrId));
- var map = asset.FindActionMap(nameOrId);
- if (map != null)
- asset.RemoveActionMap(map);
- }
-
- ////TODO: add method to add an existing InputAction to a map
-
- /// <summary>
- /// Add a new <see cref="InputAction"/> to the given <paramref name="map"/>.
- /// </summary>
- /// <param name="map">Action map to add the action to. The action will be appended to
- /// <see cref="InputActionMap.actions"/> of the map. The map must be disabled (see
- /// <see cref="InputActionMap.enabled"/>).</param>
- /// <param name="name">Name to give to the action. Must not be <c>null</c> or empty. Also,
- /// no other action that already exists in <paramref name="map"/> must have this name already.</param>
- /// <param name="type">Action type. See <see cref="InputAction.type"/>.</param>
- /// <param name="binding">If not <c>null</c>, a binding is automatically added to the newly created action
- /// with the value of this parameter being used as the binding's <see cref="InputBinding.path"/>.</param>
- /// <param name="interactions">If <paramref name="binding"/> is not <c>null</c>, this string is used for
- /// <see cref="InputBinding.interactions"/> of the binding that is automatically added for the action.</param>
- /// <param name="processors">If <paramref name="binding"/> is not <c>null</c>, this string is used for
- /// <see cref="InputBinding.processors"/> of the binding that is automatically added for the action.</param>
- /// <param name="groups">If <paramref name="binding"/> is not <c>null</c>, this string is used for
- /// <see cref="InputBinding.groups"/> of the binding that is automatically added for the action.</param>
- /// <param name="expectedControlLayout">Value for <see cref="InputAction.expectedControlType"/>; <c>null</c>
- /// by default.</param>
- /// <returns>The newly added input action.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="map"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException"><paramref name="name"/> is <c>null</c> or empty.</exception>
- /// <exception cref="InvalidOperationException"><paramref name="map"/> is enabled (see <see cref="InputActionMap.enabled"/>)
- /// or is part of an <see cref="InputActionAsset"/> that has <see cref="InputActionMap"/>s that are <see cref="InputActionMap.enabled"/>
- /// -or- <paramref name="map"/> already contains an action called <paramref name="name"/> (case-insensitive).</exception>
- public static InputAction AddAction(this InputActionMap map, string name, InputActionType type = default, string binding = null,
- string interactions = null, string processors = null, string groups = null, string expectedControlLayout = null)
- {
- if (map == null)
- throw new ArgumentNullException(nameof(map));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentException("Action must have name", nameof(name));
- map.OnWantToChangeSetup();
- if (map.FindAction(name) != null)
- throw new InvalidOperationException(
- $"Cannot add action with duplicate name '{name}' to set '{map.name}'");
-
- // Append action to array.
- var action = new InputAction(name, type)
- {
- expectedControlType = expectedControlLayout
- };
- action.GenerateId();
- ArrayHelpers.Append(ref map.m_Actions, action);
- action.m_ActionMap = map;
-
- // Add binding, if supplied.
- if (!string.IsNullOrEmpty(binding))
- {
- // Will trigger OnSetupChanged.
- action.AddBinding(binding, interactions: interactions, processors: processors, groups: groups);
- }
- else
- {
- if (!string.IsNullOrEmpty(groups))
- throw new ArgumentException(
- $"No binding path was specified for action '{action}' but groups was specified ('{groups}'); cannot apply groups without binding",
- nameof(groups));
-
- // If no binding has been supplied but there are interactions and processors, they go on the action itself.
- action.m_Interactions = interactions;
- action.m_Processors = processors;
-
- map.OnSetupChanged();
- }
-
- return action;
- }
-
- /// <summary>
- /// Remove the given action from its <see cref="InputActionMap"/>.
- /// </summary>
- /// <param name="action">An input action that is part of an <see cref="InputActionMap"/>.</param>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException"><paramref name="action"/> is a standalone action
- /// that is not part of an <see cref="InputActionMap"/> and thus cannot be removed from anything.</exception>
- /// <exception cref="InvalidOperationException"><paramref name="action"/> is part of an <see cref="InputActionMap"/>
- /// or <see cref="InputActionAsset"/> that has at least one enabled action.</exception>
- /// <remarks>
- /// After removal, the action's <see cref="InputAction.actionMap"/> will be set to <c>null</c>
- /// and the action will effectively become a standalone action that is not associated with
- /// any action map. Bindings on the action will be preserved. On the action map, the bindings
- /// for the action will be removed.
- /// </remarks>
- /// <seealso cref="AddAction"/>
- public static void RemoveAction(this InputAction action)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
-
- var actionMap = action.actionMap;
- if (actionMap == null)
- throw new ArgumentException(
- $"Action '{action}' does not belong to an action map; nowhere to remove from", nameof(action));
- actionMap.OnWantToChangeSetup();
-
- var bindingsForAction = action.bindings.ToArray();
-
- var index = actionMap.m_Actions.IndexOfReference(action);
- Debug.Assert(index != -1, "Could not find action in map");
- ArrayHelpers.EraseAt(ref actionMap.m_Actions, index);
-
- action.m_ActionMap = null;
- action.m_SingletonActionBindings = bindingsForAction;
-
- // Remove bindings to action from map.
- var newActionMapBindingCount = actionMap.m_Bindings.Length - bindingsForAction.Length;
- if (newActionMapBindingCount == 0)
- {
- actionMap.m_Bindings = null;
- }
- else
- {
- var newActionMapBindings = new InputBinding[newActionMapBindingCount];
- var oldActionMapBindings = actionMap.m_Bindings;
- var bindingIndex = 0;
- for (var i = 0; i < oldActionMapBindings.Length; ++i)
- {
- var binding = oldActionMapBindings[i];
- if (bindingsForAction.IndexOf(b => b == binding) == -1)
- newActionMapBindings[bindingIndex++] = binding;
- }
- actionMap.m_Bindings = newActionMapBindings;
- }
-
- actionMap.OnSetupChanged();
- }
-
- /// <summary>
- /// Remove the action with the given name from the asset.
- /// </summary>
- /// <param name="asset">Asset to remove the action from.</param>
- /// <param name="nameOrId">Name or ID of the action. See <see cref="InputActionAsset.FindAction(string,bool)"/> for
- /// details.</param>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c> -or- <paramref name="nameOrId"/>
- /// is <c>null</c> or empty.</exception>
- /// <seealso cref="RemoveAction(InputAction)"/>
- public static void RemoveAction(this InputActionAsset asset, string nameOrId)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (nameOrId == null)
- throw new ArgumentNullException(nameof(nameOrId));
- var action = asset.FindAction(nameOrId);
- action?.RemoveAction();
- }
-
- /// <summary>
- /// Add a new binding to the given action.
- /// </summary>
- /// <param name="action">Action to add the binding to. If the action is part of an <see cref="InputActionMap"/>,
- /// the newly added binding will be visible on <see cref="InputActionMap.bindings"/>.</param>
- /// <param name="path">Binding path string. See <see cref="InputBinding.path"/> for details.</param>
- /// <param name="interactions">Optional list of interactions to apply to the binding. See <see
- /// cref="InputBinding.interactions"/> for details.</param>
- /// <param name="processors">Optional list of processors to apply to the binding. See <see
- /// cref="InputBinding.processors"/> for details.</param>
- /// <param name="groups">Optional list of binding groups that should be assigned to the binding. See
- /// <see cref="InputBinding.groups"/> for details.</param>
- /// <returns>Fluent-style syntax to further configure the binding.</returns>
- public static BindingSyntax AddBinding(this InputAction action, string path, string interactions = null,
- string processors = null, string groups = null)
- {
- return AddBinding(action, new InputBinding
- {
- path = path,
- interactions = interactions,
- processors = processors,
- groups = groups
- });
- }
-
- /// <summary>
- /// Add a binding that references the given <paramref name="control"/> and triggers
- /// the given <paramref cref="action"/>.
- /// </summary>
- /// <param name="action">Action to trigger.</param>
- /// <param name="control">Control to bind to. The full <see cref="InputControl.path"/> of the control will
- /// be used in the resulting <see cref="InputBinding">binding</see>.</param>
- /// <returns>Syntax to configure the binding further.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is null or <paramref name="control"/> is null.</exception>
- /// <seealso cref="InputAction.bindings"/>
- public static BindingSyntax AddBinding(this InputAction action, InputControl control)
- {
- if (control == null)
- throw new ArgumentNullException(nameof(control));
- return AddBinding(action, control.path);
- }
-
- /// <summary>
- /// Add a new binding to the action.
- /// </summary>
- /// <param name="action">An action to add the binding to.</param>
- /// <param name="binding">Binding to add to the action or default. Binding can be further configured via
- /// the struct returned by the method.</param>
- /// <returns>
- /// Returns a fluent-style syntax structure that allows performing additional modifications
- /// based on the new binding.
- /// </returns>
- /// <remarks>
- /// This works both with actions that are part of an action set as well as with actions that aren't.
- ///
- /// Note that actions must be disabled while altering their binding sets. Also, if the action belongs
- /// to a set, all actions in the set must be disabled.
- ///
- /// <example>
- /// <code>
- /// fireAction.AddBinding()
- /// .WithPath("<Gamepad>/buttonSouth")
- /// .WithGroup("Gamepad");
- /// </code>
- /// </example>
- /// </remarks>
- public static BindingSyntax AddBinding(this InputAction action, InputBinding binding = default)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
-
- ////REVIEW: should this reference actions by ID?
- Debug.Assert(action.m_Name != null || action.isSingletonAction);
- binding.action = action.name;
-
- var actionMap = action.GetOrCreateActionMap();
- var bindingIndex = AddBindingInternal(actionMap, binding);
- return new BindingSyntax(actionMap, bindingIndex);
- }
-
- /// <summary>
- /// Add a new binding to the given action map.
- /// </summary>
- /// <param name="actionMap">Action map to add the binding to.</param>
- /// <param name="path">Path of the control(s) to bind to. See <see cref="InputControlPath"/> and
- /// <see cref="InputBinding.path"/>.</param>
- /// <param name="interactions">Names and parameters for interactions to apply to the
- /// binding. See <see cref="InputBinding.interactions"/>.</param>
- /// <param name="groups">Optional list of groups to apply to the binding. See <see cref="InputBinding.groups"/>.</param>
- /// <param name="action">Action to trigger from the binding. See <see cref="InputBinding.action"/>.</param>
- /// <param name="processors">Optional list of processors to apply to the binding. See <see cref="InputBinding.processors"/>.</param>
- /// <returns>A write-accessor to the newly added binding.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Add a binding for the A button the gamepad and make it trigger
- /// // the "fire" action.
- /// var gameplayActions = playerInput.actions.FindActionMap("gameplay");
- /// gameplayActions.AddBinding("<Gamepad>/buttonSouth", action: "fire");
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="InputBinding"/>
- /// <seealso cref="InputActionMap.bindings"/>
- public static BindingSyntax AddBinding(this InputActionMap actionMap, string path,
- string interactions = null, string groups = null, string action = null, string processors = null)
- {
- if (path == null)
- throw new ArgumentNullException(nameof(path), "Binding path cannot be null");
-
- return AddBinding(actionMap,
- new InputBinding
- {
- path = path,
- interactions = interactions,
- groups = groups,
- action = action,
- processors = processors,
- });
- }
-
- /// <summary>
- /// Add a new binding that triggers the given action to the given action map.
- /// </summary>
- /// <param name="actionMap">Action map to add the binding to.</param>
- /// <param name="action">Action to trigger from the binding. See <see cref="InputBinding.action"/>.
- /// Must be part of <paramref name="actionMap"/>.</param>
- /// <param name="path">Path of the control(s) to bind to. See <see cref="InputControlPath"/> and
- /// <see cref="InputBinding.path"/>.</param>
- /// <param name="interactions">Names and parameters for interactions to apply to the
- /// binding. See <see cref="InputBinding.interactions"/>.</param>
- /// <param name="groups">Binding groups to apply to the binding. See <see cref="InputBinding.groups"/>.</param>
- /// <returns>A write-accessor to the newly added binding.</returns>
- /// <exception cref="ArgumentException"><paramref name="action"/> is not part of <paramref name="actionMap"/>.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception>
- /// <seealso cref="InputBinding"/>
- /// <seealso cref="InputActionMap.bindings"/>
- public static BindingSyntax AddBinding(this InputActionMap actionMap, string path, InputAction action,
- string interactions = null, string groups = null)
- {
- if (action != null && action.actionMap != actionMap)
- throw new ArgumentException(
- $"Action '{action}' is not part of action map '{actionMap}'", nameof(action));
-
- if (action == null)
- return AddBinding(actionMap, path: path, interactions: interactions, groups: groups);
-
- return AddBinding(actionMap, path: path, interactions: interactions, groups: groups,
- action: action.id);
- }
-
- /// <summary>
- /// Add a new binding that triggers the action given by GUID <paramref name="action"/>.
- /// </summary>
- /// <param name="actionMap">Action map to add the binding to.</param>
- /// <param name="path">Path of the control(s) to bind to. See <see cref="InputControlPath"/> and <see cref="InputBinding.path"/>.</param>
- /// <param name="action">ID of the action as per <see cref="InputAction.id"/>.</param>
- /// <param name="interactions">Optional list of names and parameters for interactions to apply to the
- /// binding. See <see cref="InputBinding.interactions"/>.</param>
- /// <param name="groups">Optional list of groups to apply to the binding. See <see cref="InputBinding.groups"/>.</param>
- /// <returns>A write-accessor to the newly added binding.</returns>
- /// <exception cref="ArgumentException"><paramref name="action"/> is not part of <paramref name="actionMap"/>.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception>
- /// <seealso cref="InputBinding"/>
- /// <seealso cref="InputActionMap.bindings"/>
- /// <remarks>
- /// Example of adding a binding to an action map that binds to a Gamepad device "leftStick" control and associates it with an action:
- /// <example>
- /// <code>
- /// var map = new InputActionMap();
- /// var action = map.AddAction("action");
- /// map.AddBinding("<Gamepad>/leftStick", action: action.id);
- /// </code>
- /// </example>
- /// </remarks>
- public static BindingSyntax AddBinding(this InputActionMap actionMap, string path, Guid action,
- string interactions = null, string groups = null)
- {
- if (action == Guid.Empty)
- return AddBinding(actionMap, path: path, interactions: interactions, groups: groups);
- return AddBinding(actionMap, path: path, interactions: interactions, groups: groups,
- action: action.ToString());
- }
-
- /// <summary>
- /// Add a binding to the given action map.
- /// </summary>
- /// <param name="actionMap">Action map to add the binding to.</param>
- /// <param name="binding">Binding to add to the action map.</param>
- /// <returns>A write-accessor to the newly added binding.</returns>
- /// <exception cref="ArgumentException"><paramref name="action"/> is not part of <paramref name="actionMap"/>.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c>.</exception>
- /// <seealso cref="InputBinding"/>
- /// <seealso cref="InputActionMap.bindings"/>
- public static BindingSyntax AddBinding(this InputActionMap actionMap, InputBinding binding)
- {
- if (actionMap == null)
- throw new ArgumentNullException(nameof(actionMap));
- if (binding.path == null)
- throw new ArgumentException("Binding path cannot be null", nameof(binding));
-
- var bindingIndex = AddBindingInternal(actionMap, binding);
- return new BindingSyntax(actionMap, bindingIndex);
- }
-
- /// <summary>
- /// Add a composite binding to the <see cref="InputAction.bindings"/> of <paramref name="action"/>.
- /// </summary>
- /// <param name="action">Action to add the binding to.</param>
- /// <param name="composite">Type of composite to add. This needs to be the name the composite
- /// has been registered under using <see cref="InputSystem.RegisterBindingComposite{T}"/>. Case-insensitive.</param>
- /// <param name="interactions">Interactions to add to the binding. See <see cref="InputBinding.interactions"/>.</param>
- /// <param name="processors">Processors to add to the binding. See <see cref="InputBinding.processors"/>.</param>
- /// <returns>A write accessor to the newly added composite binding.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException"><paramref name="composite"/> is <c>null</c> or empty.</exception>
- public static CompositeSyntax AddCompositeBinding(this InputAction action, string composite,
- string interactions = null, string processors = null)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (string.IsNullOrEmpty(composite))
- throw new ArgumentException("Composite name cannot be null or empty", nameof(composite));
-
- var actionMap = action.GetOrCreateActionMap();
-
- var binding = new InputBinding
- {
- name = NameAndParameters.ParseName(composite),
- path = composite,
- interactions = interactions,
- processors = processors,
- isComposite = true,
- action = action.name
- };
-
- var bindingIndex = AddBindingInternal(actionMap, binding);
- return new CompositeSyntax(actionMap, action, bindingIndex);
- }
-
- ////TODO: AddCompositeBinding<T>
-
- private static int AddBindingInternal(InputActionMap map, InputBinding binding, int bindingIndex = -1)
- {
- Debug.Assert(map != null);
-
- // Make sure the binding has an ID.
- if (string.IsNullOrEmpty(binding.m_Id))
- binding.GenerateId();
-
- // Append to bindings in set.
- if (bindingIndex < 0)
- bindingIndex = ArrayHelpers.Append(ref map.m_Bindings, binding);
- else
- ArrayHelpers.InsertAt(ref map.m_Bindings, bindingIndex, binding);
-
- // Make sure this asset is reloaded from disk when exiting play mode so it isn't inadvertently
- // changed between play sessions. Only applies when running in the editor.
- if (map.asset != null)
- map.asset.MarkAsDirty();
-
- // If we're looking at a singleton action, make sure m_Bindings is up to date just
- // in case the action gets serialized.
- if (map.m_SingletonAction != null)
- map.m_SingletonAction.m_SingletonActionBindings = map.m_Bindings;
-
- // NOTE: We treat this as a mere binding modification, even though we have added something.
- // InputAction.RestoreActionStatesAfterReResolvingBindings() can deal with bindings
- // having been removed or added.
- map.OnBindingModified();
-
- return bindingIndex;
- }
-
- /// <summary>
- /// Get write access to the binding in <see cref="InputAction.bindings"/> of <paramref name="action"/>
- /// at the given <paramref name="index"/>.
- /// </summary>
- /// <param name="action">Action whose bindings to change.</param>
- /// <param name="index">Index in <paramref name="action"/>'s <see cref="InputAction.bindings"/> of the binding to be changed.</param>
- /// <returns>A write accessor to the given binding.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Grab "fire" action from PlayerInput.
- /// var fireAction = playerInput.actions["fire"];
- ///
- /// // Change its second binding to go to the left mouse button.
- /// fireAction.ChangeBinding(1)
- /// .WithPath("<Mouse>/leftButton");
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range (as per <see cref="InputAction.bindings"/>
- /// of <paramref name="action"/>).</exception>
- public static BindingSyntax ChangeBinding(this InputAction action, int index)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
-
- var indexOnMap = action.BindingIndexOnActionToBindingIndexOnMap(index);
- return new BindingSyntax(action.GetOrCreateActionMap(), indexOnMap, action);
- }
-
- /// <summary>
- /// Get write access to the binding in <see cref="InputAction.bindings"/> of <paramref name="action"/>
- /// with the given <paramref name="name"/>.
- /// </summary>
- /// <param name="action">Action whose bindings to change.</param>
- /// <param name="name">Name of the binding to be changed <see cref="InputAction.bindings"/>.</param>
- /// <returns>A write accessor to the given binding.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Grab "fire" action from PlayerInput.
- /// var fireAction = playerInput.actions["fire"];
- ///
- /// // Change its second binding to go to the left mouse button.
- /// fireAction.ChangeBinding("fire")
- /// .WithPath("<Mouse>/leftButton");
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range (as per <see cref="InputAction.bindings"/>
- /// of <paramref name="action"/>).</exception>
- public static BindingSyntax ChangeBinding(this InputAction action, string name)
- {
- return action.ChangeBinding(new InputBinding { name = name });
- }
-
- /// <summary>
- /// Get write access to the binding in <see cref="InputActionMap.bindings"/> of <paramref name="actionMap"/>
- /// at the given <paramref name="index"/>.
- /// </summary>
- /// <param name="actionMap">Action map whose bindings to change.</param>
- /// <param name="index">Index in <paramref name="actionMap"/>'s <see cref="InputActionMap.bindings"/> of the binding to be changed.</param>
- /// <returns>A write accessor to the given binding.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Grab "gameplay" actions from PlayerInput.
- /// var gameplayActions = playerInput.actions.FindActionMap("gameplay");
- ///
- /// // Change its second binding to go to the left mouse button.
- /// gameplayActions.ChangeBinding(1)
- /// .WithPath("<Mouse>/leftButton");
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="actionMap"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range (as per <see cref="InputActionMap.bindings"/>
- /// of <paramref name="actionMap"/>).</exception>
- public static BindingSyntax ChangeBinding(this InputActionMap actionMap, int index)
- {
- if (actionMap == null)
- throw new ArgumentNullException(nameof(actionMap));
- if (index < 0 || index >= actionMap.m_Bindings.LengthSafe())
- throw new ArgumentOutOfRangeException(nameof(index));
-
- return new BindingSyntax(actionMap, index);
- }
-
- /// <summary>
- /// Get write access to the binding in <see cref="InputAction.bindings"/> of <paramref name="action"/>
- /// that has the given <paramref name="id"/>.
- /// </summary>
- /// <param name="action">Action whose bindings to change.</param>
- /// <param name="id">ID of the binding as per <see cref="InputBinding.id"/>.</param>
- /// <returns>A write accessor to the binding with the given ID.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Grab "fire" action from PlayerInput.
- /// var fireAction = playerInput.actions["fire"];
- ///
- /// // Change the binding with the given ID to go to the left mouse button.
- /// fireAction.ChangeBindingWithId("c3de9215-31c3-4654-8562-854bf2f7864f")
- /// .WithPath("<Mouse>/leftButton");
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException">No binding with the given <paramref name="id"/> exists
- /// on <paramref name="action"/>.</exception>
- public static BindingSyntax ChangeBindingWithId(this InputAction action, string id)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
-
- return action.ChangeBinding(new InputBinding {m_Id = id});
- }
-
- /// <summary>
- /// Get write access to the binding in <see cref="InputAction.bindings"/> of <paramref name="action"/>
- /// that has the given <paramref name="id"/>.
- /// </summary>
- /// <param name="action">Action whose bindings to change.</param>
- /// <param name="id">ID of the binding as per <see cref="InputBinding.id"/>.</param>
- /// <returns>A write accessor to the binding with the given ID.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Grab "fire" action from PlayerInput.
- /// var fireAction = playerInput.actions["fire"];
- ///
- /// // Change the binding with the given ID to go to the left mouse button.
- /// fireAction.ChangeBindingWithId(new Guid("c3de9215-31c3-4654-8562-854bf2f7864f"))
- /// .WithPath("<Mouse>/leftButton");
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException">No binding with the given <paramref name="id"/> exists
- /// on <paramref name="action"/>.</exception>
- public static BindingSyntax ChangeBindingWithId(this InputAction action, Guid id)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
-
- return action.ChangeBinding(new InputBinding {id = id});
- }
-
- /// <summary>
- /// Get write access to the first binding in <see cref="InputAction.bindings"/> of <paramref name="action"/>
- /// that is assigned to the given binding <paramref name="group"/>.
- /// </summary>
- /// <param name="action">Action whose bindings to change.</param>
- /// <param name="group">Name of the binding group as per <see cref="InputBinding.groups"/>.</param>
- /// <returns>A write accessor to the first binding on <paramref name="action"/> that is assigned to the
- /// given binding <paramref name="group"/>.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Grab "fire" action from PlayerInput.
- /// var fireAction = playerInput.actions["fire"];
- ///
- /// // Change the binding in the "Keyboard&Mouse" group to go to the left mouse button.
- /// fireAction.ChangeBindingWithGroup("Keyboard&Mouse")
- /// .WithPath("<Mouse>/leftButton");
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException">No binding on the <paramref name="action"/> is assigned
- /// to the given binding <paramref name="group"/>.</exception>
- public static BindingSyntax ChangeBindingWithGroup(this InputAction action, string group)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
-
- return action.ChangeBinding(new InputBinding {groups = group});
- }
-
- /// <summary>
- /// Get write access to the binding in <see cref="InputAction.bindings"/> of <paramref name="action"/>
- /// that is bound to the given <paramref name="path"/>.
- /// </summary>
- /// <param name="action">Action whose bindings to change.</param>
- /// <param name="path">Path of the binding as per <see cref="InputBinding.path"/>.</param>
- /// <returns>A write accessor to the binding on <paramref name="action"/> that is assigned the
- /// given <paramref name="path"/>.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Grab "fire" action from PlayerInput.
- /// var fireAction = playerInput.actions["fire"];
- ///
- /// // Change the binding to the right mouse button to go to the left mouse button instead.
- /// fireAction.ChangeBindingWithPath("<Mouse>/rightButton")
- /// .WithPath("<Mouse>/leftButton");
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- /// <exception cref="ArgumentException">No binding on the <paramref name="action"/> is assigned
- /// the given <paramref name="path"/>.</exception>
- public static BindingSyntax ChangeBindingWithPath(this InputAction action, string path)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
-
- return action.ChangeBinding(new InputBinding {path = path});
- }
-
- /// <summary>
- /// Get write access to the binding on <paramref name="action"/> that matches the given
- /// <paramref name="match"/>.
- /// </summary>
- /// <param name="action">Action whose bindings to match against.</param>
- /// <param name="match">A binding mask. See <see cref="InputBinding.Matches"/> for
- /// details.</param>
- /// <returns>A write-accessor to the first binding matching <paramref name="match"/> or
- /// an invalid accessor (see <see cref="BindingSyntax.valid"/>) if no binding was found to
- /// match the mask.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
- public static BindingSyntax ChangeBinding(this InputAction action, InputBinding match)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
-
- var actionMap = action.GetOrCreateActionMap();
-
- int bindingIndexInMap = -1;
- var id = action.idDontGenerate;
- if (id != null)
- {
- // Prio1: Attempt to match action id (stronger)
- match.action = action.id.ToString();
- bindingIndexInMap = actionMap.FindBindingRelativeToMap(match);
- }
- if (bindingIndexInMap == -1)
- {
- // Prio2: Attempt to match action name (weaker)
- match.action = action.name;
- bindingIndexInMap = actionMap.FindBindingRelativeToMap(match);
- }
- if (bindingIndexInMap == -1)
- return default;
-
- return new BindingSyntax(actionMap, bindingIndexInMap, action);
- }
-
- /// <summary>
- /// Get a write accessor to the binding of <paramref name="action"/> that is both a composite
- /// (see <see cref="InputBinding.isComposite"/>) and has the given binding name or composite
- /// type.
- /// </summary>
- /// <param name="action">Action to look up the binding on. All bindings in the action's
- /// <see cref="InputAction.bindings"/> property will be considered.</param>
- /// <param name="compositeName">Either the name of the composite binding (see <see cref="InputBinding.name"/>)
- /// to look for or the name of the composite type used in the binding (such as "1DAxis"). Case-insensitive.</param>
- /// <returns>A write accessor to the given composite binding or an invalid accessor if no composite
- /// matching <paramref name="compositeName"/> could be found on <paramref name="action"/>.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Add arrow keys as alternatives to the WASD Vector2 composite.
- /// playerInput.actions["move"]
- /// .ChangeCompositeBinding("WASD")
- /// .InsertPartBinding("Up", "<Keyboard>/upArrow")
- /// .InsertPartBinding("Down", "<Keyboard>/downArrow")
- /// .InsertPartBinding("Left", "<Keyboard>/leftArrow")
- /// .InsertPartBinding("Right", "<Keyboard>/rightArrow");
- /// </code>
- /// </example>
- /// </remarks>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c> -or- <paramref name="compositeName"/>
- /// is <c>null</c> or empty.</exception>
- /// <seealso cref="InputBinding.isComposite"/>
- /// <seealso cref="InputBindingComposite"/>
- public static BindingSyntax ChangeCompositeBinding(this InputAction action, string compositeName)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (string.IsNullOrEmpty(compositeName))
- throw new ArgumentNullException(nameof(compositeName));
-
- var actionMap = action.GetOrCreateActionMap();
- var bindings = actionMap.m_Bindings;
- var numBindings = bindings.LengthSafe();
-
- for (var i = 0; i < numBindings; ++i)
- {
- ref var binding = ref bindings[i];
- if (!binding.isComposite || !binding.TriggersAction(action))
- continue;
-
- ////REVIEW: should this do a registration lookup to deal with aliases?
- if (compositeName.Equals(binding.name, StringComparison.InvariantCultureIgnoreCase)
- || compositeName.Equals(NameAndParameters.ParseName(binding.path),
- StringComparison.InvariantCultureIgnoreCase))
- return new BindingSyntax(actionMap, i, action);
- }
-
- return default;
- }
-
- ////TODO: update binding mask if necessary
- /// <summary>
- /// Rename an existing action.
- /// </summary>
- /// <param name="action">Action to assign a new name to. Can be singleton action or action that
- /// is part of a map.</param>
- /// <param name="newName">New name to assign to action. Cannot be empty.</param>
- /// <exception cref="ArgumentNullException"><paramref name="action"/> is null or <paramref name="newName"/> is
- /// null or empty.</exception>
- /// <exception cref="InvalidOperationException"><see cref="InputAction.actionMap"/> of <paramref name="action"/>
- /// already contains an action called <paramref name="newName"/>.</exception>
- /// <remarks>
- /// Renaming an action will also update the bindings that refer to the action.
- /// </remarks>
- public static void Rename(this InputAction action, string newName)
- {
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (string.IsNullOrEmpty(newName))
- throw new ArgumentNullException(nameof(newName));
-
- if (action.name == newName)
- return;
-
- // Make sure name isn't already taken in map.
- var actionMap = action.actionMap;
- if (actionMap?.FindAction(newName) != null)
- throw new InvalidOperationException(
- $"Cannot rename '{action}' to '{newName}' in map '{actionMap}' as the map already contains an action with that name");
-
- var oldName = action.m_Name;
- action.m_Name = newName;
- actionMap?.ClearActionLookupTable();
-
- if (actionMap?.asset != null)
- actionMap?.asset.MarkAsDirty();
-
- // Update bindings.
- var bindings = action.GetOrCreateActionMap().m_Bindings;
- var bindingCount = bindings.LengthSafe();
- for (var i = 0; i < bindingCount; ++i)
- if (string.Compare(bindings[i].action, oldName, StringComparison.InvariantCultureIgnoreCase) == 0)
- bindings[i].action = newName;
- }
-
- /// <summary>
- /// Add a new control scheme to the asset.
- /// </summary>
- /// <param name="asset">Asset to add the control scheme to.</param>
- /// <param name="controlScheme">Control scheme to add.</param>
- /// <exception cref="ArgumentException"><paramref name="controlScheme"/> has no name.</exception>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c>.</exception>
- /// <exception cref="InvalidOperationException">A control scheme with the same name as <paramref name="controlScheme"/>
- /// already exists in the asset.</exception>
- /// <remarks>
- /// </remarks>
- public static void AddControlScheme(this InputActionAsset asset, InputControlScheme controlScheme)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (string.IsNullOrEmpty(controlScheme.name))
- throw new ArgumentException("Cannot add control scheme without name to asset " + asset.name, nameof(controlScheme));
- if (asset.FindControlScheme(controlScheme.name) != null)
- throw new InvalidOperationException(
- $"Asset '{asset.name}' already contains a control scheme called '{controlScheme.name}'");
-
- ArrayHelpers.Append(ref asset.m_ControlSchemes, controlScheme);
-
- asset.MarkAsDirty();
- }
-
- /// <summary>
- /// Add a new control scheme to the given <paramref name="asset"/>.
- /// </summary>
- /// <param name="asset">Asset to add the control scheme to.</param>
- /// <param name="name">Name to give to the control scheme. Must be unique within the control schemes of the
- /// asset. Also used as default name of <see cref="InputControlScheme.bindingGroup">binding group</see> associated
- /// with the control scheme.</param>
- /// <returns>Syntax to allow providing additional configuration for the newly added control scheme.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c> -or- <paramref name="name"/>
- /// is <c>null</c> or empty.</exception>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Create an .inputactions asset.
- /// var asset = ScriptableObject.CreateInstance<InputActionAsset>();
- ///
- /// // Add an action map to it.
- /// var actionMap = asset.AddActionMap("actions");
- ///
- /// // Add an action to it and bind it to the A button on the gamepad.
- /// // Also, associate that binding with the "Gamepad" control scheme.
- /// var action = actionMap.AddAction("action");
- /// action.AddBinding("<Gamepad>/buttonSouth", groups: "Gamepad");
- ///
- /// // Add a control scheme called "Gamepad" that requires a Gamepad device.
- /// asset.AddControlScheme("Gamepad")
- /// .WithRequiredDevice<Gamepad>();
- /// </code>
- /// </example>
- /// </remarks>
- public static ControlSchemeSyntax AddControlScheme(this InputActionAsset asset, string name)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name));
-
- var index = asset.controlSchemes.Count;
- asset.AddControlScheme(new InputControlScheme(name));
-
- return new ControlSchemeSyntax(asset, index);
- }
-
- /// <summary>
- /// Remove the control scheme with the given name from the asset.
- /// </summary>
- /// <param name="asset">Asset to remove the control scheme from.</param>
- /// <param name="name">Name of the control scheme. Matching is case-insensitive.</param>
- /// <exception cref="ArgumentNullException"><paramref name="asset"/> is null -or- <paramref name="name"/>
- /// is <c>null</c> or empty.</exception>
- /// <remarks>
- /// If no control scheme with the given name can be found, the method does nothing.
- /// </remarks>
- public static void RemoveControlScheme(this InputActionAsset asset, string name)
- {
- if (asset == null)
- throw new ArgumentNullException(nameof(asset));
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name));
-
- var index = asset.FindControlSchemeIndex(name);
- if (index != -1)
- ArrayHelpers.EraseAt(ref asset.m_ControlSchemes, index);
-
- asset.MarkAsDirty();
- }
-
- /// <summary>
- /// Associates the control scheme given by <paramref name="scheme"/> with the binding group given by <paramref name="bindingGroup"/>.
- /// </summary>
- /// <param name="scheme">The control scheme to modify.</param>
- /// <param name="bindingGroup">The binding group to be associated with the control scheme.</param>
- /// <returns><paramref name="scheme"/></returns>
- public static InputControlScheme WithBindingGroup(this InputControlScheme scheme, string bindingGroup)
- {
- return new ControlSchemeSyntax(scheme).WithBindingGroup(bindingGroup).Done();
- }
-
- public static InputControlScheme WithDevice(this InputControlScheme scheme, string controlPath, bool required)
- {
- if (required)
- return new ControlSchemeSyntax(scheme).WithRequiredDevice(controlPath).Done();
- return new ControlSchemeSyntax(scheme).WithOptionalDevice(controlPath).Done();
- }
-
- public static InputControlScheme WithRequiredDevice(this InputControlScheme scheme, string controlPath)
- {
- return new ControlSchemeSyntax(scheme).WithRequiredDevice(controlPath).Done();
- }
-
- public static InputControlScheme WithOptionalDevice(this InputControlScheme scheme, string controlPath)
- {
- return new ControlSchemeSyntax(scheme).WithOptionalDevice(controlPath).Done();
- }
-
- public static InputControlScheme OrWithRequiredDevice(this InputControlScheme scheme, string controlPath)
- {
- return new ControlSchemeSyntax(scheme).OrWithRequiredDevice(controlPath).Done();
- }
-
- public static InputControlScheme OrWithOptionalDevice(this InputControlScheme scheme, string controlPath)
- {
- return new ControlSchemeSyntax(scheme).OrWithOptionalDevice(controlPath).Done();
- }
-
- /// <summary>
- /// Write accessor to a binding on either an <see cref="InputAction"/> or an
- /// <see cref="InputActionMap"/>.
- /// </summary>
- /// <remarks>
- /// Both <see cref="InputAction.bindings"/> and <see cref="InputActionMap.bindings"/> are
- /// read-only. To modify bindings (other than setting overrides which you can do
- /// through <see cref="InputActionRebindingExtensions.ApplyBindingOverride(InputAction,int,InputBinding)"/>),
- /// it is necessary to gain indirect write access through this structure.
- ///
- /// <example>
- /// <code>
- /// playerInput.actions["fire"]
- /// .ChangeBinding(0)
- /// .WithPath("<Keyboard>/space");
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="AddBinding(InputAction,InputBinding)"/>
- /// <seealso cref="ChangeBinding(InputAction,int)"/>
- public struct BindingSyntax
- {
- private readonly InputActionMap m_ActionMap;
- private readonly InputAction m_Action;
- internal readonly int m_BindingIndexInMap;
-
- /// <summary>
- /// True if the if binding accessor is valid.
- /// </summary>
- public bool valid => m_ActionMap != null && m_BindingIndexInMap >= 0 && m_BindingIndexInMap < m_ActionMap.m_Bindings.LengthSafe();
-
- /// <summary>
- /// Index of the binding that the accessor refers to.
- /// </summary>
- /// <remarks>
- /// When accessing bindings on an <see cref="InputAction"/>, this is the index in
- /// <see cref="InputAction.bindings"/> of the action. When accessing bindings on an
- /// <see cref="InputActionMap"/>, it is the index <see cref="InputActionMap.bindings"/>
- /// of the map.
- /// </remarks>
- public int bindingIndex
- {
- get
- {
- if (!valid)
- return -1;
- if (m_Action != null)
- return m_Action.BindingIndexOnMapToBindingIndexOnAction(m_BindingIndexInMap);
- return m_BindingIndexInMap;
- }
- }
-
- /// <summary>
- /// The current binding in entirety.
- /// </summary>
- /// <exception cref="InvalidOperationException">The accessor is not <see cref="valid"/>.</exception>
- public InputBinding binding
- {
- get
- {
- if (!valid)
- throw new InvalidOperationException("BindingSyntax accessor is not valid");
- return m_ActionMap.m_Bindings[m_BindingIndexInMap];
- }
- }
-
- internal BindingSyntax(InputActionMap map, int bindingIndexInMap, InputAction action = null)
- {
- m_ActionMap = map;
- m_BindingIndexInMap = bindingIndexInMap;
- m_Action = action;
- }
-
- /// <summary>
- /// Set the <see cref="InputBinding.name"/> of the binding.
- /// </summary>
- /// <param name="name">Name for the binding.</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is not <see cref="valid"/>.</exception>
- /// <seealso cref="InputBinding.name"/>
- public BindingSyntax WithName(string name)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- m_ActionMap.m_Bindings[m_BindingIndexInMap].name = name;
- m_ActionMap.OnBindingModified();
- return this;
- }
-
- /// <summary>
- /// Set the <see cref="InputBinding.path"/> of the binding.
- /// </summary>
- /// <param name="path">Path for the binding.</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is not <see cref="valid"/>.</exception>
- /// <seealso cref="InputBinding.path"/>
- public BindingSyntax WithPath(string path)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- m_ActionMap.m_Bindings[m_BindingIndexInMap].path = path;
- m_ActionMap.OnBindingModified();
- return this;
- }
-
- /// <summary>
- /// Add <paramref name="group"/> to the list of <see cref="InputBinding.groups"/> of the binding.
- /// </summary>
- /// <param name="group">Name of the binding group (such as "Gamepad").</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="ArgumentException"><paramref name="group"/> is <c>null</c> or empty -or- it contains
- /// a <see cref="InputBinding.Separator"/> character.</exception>
- public BindingSyntax WithGroup(string group)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- if (string.IsNullOrEmpty(group))
- throw new ArgumentException("Group name cannot be null or empty", nameof(group));
- if (group.IndexOf(InputBinding.Separator) != -1)
- throw new ArgumentException(
- $"Group name cannot contain separator character '{InputBinding.Separator}'", nameof(group));
-
- return WithGroups(group);
- }
-
- /// <summary>
- /// Add all the groups specified in <paramref name="groups"/> to the list of <see cref="InputBinding.groups"/> of the binding.
- /// </summary>
- /// <param name="groups">A semi-colon separated list of group names.</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid.</exception>
- public BindingSyntax WithGroups(string groups)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- if (string.IsNullOrEmpty(groups))
- return this;
-
- // Join with existing group, if any.
- var currentGroups = m_ActionMap.m_Bindings[m_BindingIndexInMap].groups;
- if (!string.IsNullOrEmpty(currentGroups))
- groups = string.Join(InputBinding.kSeparatorString, currentGroups, groups);
-
- // Set groups on binding.
- m_ActionMap.m_Bindings[m_BindingIndexInMap].groups = groups;
- m_ActionMap.OnBindingModified();
-
- return this;
- }
-
- /// <summary>
- /// Add an interaction via specified name in <paramref name="interaction"/> to the list of interactions.
- /// </summary>
- /// <param name="interaction">Interaction class name</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid.</exception>
- /// <exception cref="ArgumentException">If interaction name is null or empty.</exception>
- public BindingSyntax WithInteraction(string interaction)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- if (string.IsNullOrEmpty(interaction))
- throw new ArgumentException("Interaction cannot be null or empty", nameof(interaction));
- if (interaction.IndexOf(InputBinding.Separator) != -1)
- throw new ArgumentException(
- $"Interaction string cannot contain separator character '{InputBinding.Separator}'", nameof(interaction));
-
- return WithInteractions(interaction);
- }
-
- /// <summary>
- /// Add the set of interactions specified in <paramref name="interactions"/> to the list of interactions.
- /// </summary>
- /// <param name="interactions">A semi-colon separated list of interaction names.</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid.</exception>
- public BindingSyntax WithInteractions(string interactions)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- if (string.IsNullOrEmpty(interactions))
- return this;
-
- // Join with existing interaction string, if any.
- var currentInteractions = m_ActionMap.m_Bindings[m_BindingIndexInMap].interactions;
- if (!string.IsNullOrEmpty(currentInteractions))
- interactions = string.Join(InputBinding.kSeparatorString, currentInteractions, interactions);
-
- // Set interactions on binding.
- m_ActionMap.m_Bindings[m_BindingIndexInMap].interactions = interactions;
- m_ActionMap.OnBindingModified();
-
- return this;
- }
-
- /// <summary>
- /// Add an interaction of type specified in <typeparamref name="TInteraction"/> to the list of interactions.
- /// </summary>
- /// <typeparam name="TInteraction"></typeparam>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid.</exception>
- /// <exception cref="NotSupportedException">Interaction type has not been registered.</exception>
- public BindingSyntax WithInteraction<TInteraction>()
- where TInteraction : IInputInteraction
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
-
- var interactionName = InputInteraction.s_Interactions.FindNameForType(typeof(TInteraction));
- if (interactionName.IsEmpty())
- throw new NotSupportedException($"Type '{typeof(TInteraction)}' has not been registered as a interaction");
-
- return WithInteraction(interactionName);
- }
-
- /// <summary>
- /// Add a processor to the list of <see cref="InputBinding.processors"/> of the binding.
- /// </summary>
- /// <param name="processor">Name of the processor, such as "Scale".</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid.</exception>
- /// <exception cref="ArgumentException">The processor name is null or empty.</exception>
- /// <exception cref="ArgumentException">The processor name contains a semi-colon.</exception>
- public BindingSyntax WithProcessor(string processor)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- if (string.IsNullOrEmpty(processor))
- throw new ArgumentException("Processor cannot be null or empty", nameof(processor));
- if (processor.IndexOf(InputBinding.Separator) != -1)
- throw new ArgumentException(
- $"Processor string cannot contain separator character '{InputBinding.Separator}'", nameof(processor));
-
- return WithProcessors(processor);
- }
-
- /// <summary>
- /// Add processors to the list of <see cref="InputBinding.processors"/> of the binding.
- /// </summary>
- /// <param name="processors">A semi-colon separated list of processor names.</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid.</exception>
- public BindingSyntax WithProcessors(string processors)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- if (string.IsNullOrEmpty(processors))
- return this;
-
- // Join with existing processor string, if any.
- var currentProcessors = m_ActionMap.m_Bindings[m_BindingIndexInMap].processors;
- if (!string.IsNullOrEmpty(currentProcessors))
- processors = string.Join(InputBinding.kSeparatorString, currentProcessors, processors);
-
- // Set processors on binding.
- m_ActionMap.m_Bindings[m_BindingIndexInMap].processors = processors;
- m_ActionMap.OnBindingModified();
-
- return this;
- }
-
- /// <summary>
- /// Add a processor to the list of <see cref="InputBinding.processors"/> of the binding.
- /// </summary>
- /// <typeparam name="TProcessor">Type of processor.</typeparam>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid.</exception>
- /// <exception cref="NotSupportedException">Processor type has not been registered.</exception>
- public BindingSyntax WithProcessor<TProcessor>()
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
-
- var processorName = InputProcessor.s_Processors.FindNameForType(typeof(TProcessor));
- if (processorName.IsEmpty())
- throw new NotSupportedException($"Type '{typeof(TProcessor)}' has not been registered as a processor");
-
- return WithProcessor(processorName);
- }
-
- /// <summary>
- /// Specify which action to trigger.
- /// </summary>
- /// <param name="action">Action to trigger.</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid.</exception>
- /// <exception cref="ArgumentNullException">Provided action is null.</exception>
- /// <exception cref="ArgumentException">Provided action is a singleton action (not part of any action maps).</exception>
- public BindingSyntax Triggering(InputAction action)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
- if (action == null)
- throw new ArgumentNullException(nameof(action));
- if (action.isSingletonAction)
- throw new ArgumentException(
- $"Cannot change the action a binding triggers on singleton action '{action}'", nameof(action));
- m_ActionMap.m_Bindings[m_BindingIndexInMap].action = action.name;
- m_ActionMap.OnBindingModified();
- return this;
- }
-
- /// <summary>
- /// Replace the current binding with the given one.
- /// </summary>
- /// <param name="binding">An input binding.</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <remarks>
- /// This method replaces the current binding wholesale, i.e. it will overwrite all fields.
- /// Be aware that this has the potential of corrupting the binding data in case the given
- /// binding is a composite.
- /// </remarks>
- public BindingSyntax To(InputBinding binding)
- {
- if (!valid)
- throw new InvalidOperationException("Accessor is not valid");
-
- m_ActionMap.m_Bindings[m_BindingIndexInMap] = binding;
-
- // If it's a singleton action, we force the binding to stay with the action.
- if (m_ActionMap.m_SingletonAction != null)
- m_ActionMap.m_Bindings[m_BindingIndexInMap].action = m_ActionMap.m_SingletonAction.name;
-
- m_ActionMap.OnBindingModified();
-
- return this;
- }
-
- /// <summary>
- /// Switch to configuring the next binding.
- /// </summary>
- /// <returns>An instance configured to edit the next binding or an invalid (see <see cref="valid"/>) instance if
- /// there is no next binding.</returns>
- /// <remarks>If the BindingSyntax is restricted to a single action, the result will be invalid (see <see cref="valid"/>)
- /// if there is no next binding on the action. If the BindingSyntax is restricted to an <see cref="InputActionMap"/>, the result will
- /// be be invalid if there is no next binding in the map.</remarks>
- public BindingSyntax NextBinding()
- {
- return Iterate(true);
- }
-
- /// <summary>
- /// Switch to configuring the previous binding.
- /// </summary>
- /// <returns>An instance configured to edit the previous binding or an invalid (see <see cref="valid"/>) instance if
- /// there is no previous binding.</returns>
- /// <remarks>If the BindingSyntax is restricted to a single action, the result will be invalid (see <see cref="valid"/>)
- /// if there is no previous binding on the action. If the BindingSyntax is restricted to an <see cref="InputActionMap"/>, the result will
- /// be be invalid if there is no previous binding in the map.</remarks>
- public BindingSyntax PreviousBinding()
- {
- return Iterate(false);
- }
-
- /// <summary>
- /// Iterate to the next part binding of the current composite with the given part name.
- /// </summary>
- /// <param name="partName">Name of the part of the binding, such as <c>"Positive"</c>.</param>
- /// <returns>An accessor to the next part binding with the given name or an invalid (see <see cref="valid"/>)
- /// accessor if there is no such binding.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="partName"/> is <c>null</c> or empty.</exception>
- /// <remarks>
- /// Each binding that is part of a composite is marked with <see cref="InputBinding.isPartOfComposite"/>
- /// set to true. The name of the part is determined by <see cref="InputBinding.name"/> (comparison is
- /// case-insensitive). Which parts are relevant to a specific composite is determined by the type of
- /// composite. An <see cref="Composites.AxisComposite"/>, for example, has <c>"Negative"</c> and a
- /// <c>"Positive"</c> part.
- ///
- /// <example>
- /// <code>
- /// // Delete first "Positive" part of "Axis" composite.
- /// action.ChangeCompositeBinding("Axis")
- /// .NextPartBinding("Positive").Erase();
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="InputBinding.isPartOfComposite"/>
- /// <seealso cref="InputBinding.isComposite"/>
- /// <seealso cref="InputBindingComposite"/>
- public BindingSyntax NextPartBinding(string partName)
- {
- if (string.IsNullOrEmpty(partName))
- throw new ArgumentNullException(nameof(partName));
- return IteratePartBinding(true, partName);
- }
-
- /// <summary>
- /// Iterate to the previous part binding of the current composite with the given part name.
- /// </summary>
- /// <param name="partName">Name of the part of the binding, such as <c>"Positive"</c>.</param>
- /// <returns>An accessor to the previous part binding with the given name or an invalid (see <see cref="valid"/>)
- /// accessor if there is no such binding.</returns>
- /// <exception cref="ArgumentNullException"><paramref name="partName"/> is <c>null</c> or empty.</exception>
- /// <remarks>
- /// Each binding that is part of a composite is marked with <see cref="InputBinding.isPartOfComposite"/>
- /// set to true. The name of the part is determined by <see cref="InputBinding.name"/> (comparison is
- /// case-insensitive). Which parts are relevant to a specific composite is determined by the type of
- /// composite. An <see cref="Composites.AxisComposite"/>, for example, has <c>"Negative"</c> and a
- /// <c>"Positive"</c> part.
- /// </remarks>
- /// <seealso cref="InputBinding.isPartOfComposite"/>
- /// <seealso cref="InputBinding.isComposite"/>
- /// <seealso cref="InputBindingComposite"/>
- public BindingSyntax PreviousPartBinding(string partName)
- {
- if (string.IsNullOrEmpty(partName))
- throw new ArgumentNullException(nameof(partName));
- return IteratePartBinding(false, partName);
- }
-
- /// <summary>
- /// Iterate to the next composite binding.
- /// </summary>
- /// <param name="compositeName">If <c>null</c> (default), an accessor to the next composite binding,
- /// regardless of name or type, is returned. If it is not <c>null</c>, can be either the name of
- /// the binding (see <see cref="InputBinding.name"/>) or the name of the composite used in the
- /// binding (see <see cref="InputSystem.RegisterBindingComposite"/></param>).
- /// <returns>A write accessor to the next composite binding or an invalid accessor (see
- /// <see cref="valid"/>) if no such binding was found.</returns>
- public BindingSyntax NextCompositeBinding(string compositeName = null)
- {
- return IterateCompositeBinding(true, compositeName);
- }
-
- /// <summary>
- /// Iterate to the previous composite binding.
- /// </summary>
- /// <param name="compositeName">If <c>null</c> (default), an accessor to the previous composite binding,
- /// regardless of name or type, is returned. If it is not <c>null</c>, can be either the name of
- /// the binding (see <see cref="InputBinding.name"/>) or the name of the composite used in the
- /// binding (see <see cref="InputSystem.RegisterBindingComposite"/>).</param>
- /// <returns>A write accessor to the previous composite binding or an invalid accessor (see
- /// <see cref="valid"/>) if no such binding was found.</returns>
- public BindingSyntax PreviousCompositeBinding(string compositeName = null)
- {
- return IterateCompositeBinding(false, compositeName);
- }
-
- private BindingSyntax Iterate(bool next)
- {
- if (m_ActionMap == null)
- return default;
-
- var bindings = m_ActionMap.m_Bindings;
- if (bindings == null)
- return default;
-
- // To find the next binding for a specific action, we may have to jump
- // over unrelated bindings in-between.
- var index = m_BindingIndexInMap;
- while (true)
- {
- index += next ? 1 : -1;
- if (index < 0 || index >= bindings.Length)
- return default;
-
- if (m_Action == null || bindings[index].TriggersAction(m_Action))
- break;
- }
-
- return new BindingSyntax(m_ActionMap, index, m_Action);
- }
-
- private BindingSyntax IterateCompositeBinding(bool next, string compositeName)
- {
- for (var accessor = Iterate(next); accessor.valid; accessor = accessor.Iterate(next))
- {
- if (!accessor.binding.isComposite)
- continue;
-
- if (compositeName == null)
- return accessor;
-
- // Try name of binding.
- if (compositeName.Equals(accessor.binding.name, StringComparison.InvariantCultureIgnoreCase))
- return accessor;
-
- // Try composite type name.
- var name = NameAndParameters.ParseName(accessor.binding.path);
- if (compositeName.Equals(name, StringComparison.InvariantCultureIgnoreCase))
- return accessor;
- }
-
- return default;
- }
-
- private BindingSyntax IteratePartBinding(bool next, string partName)
- {
- if (!valid)
- return default;
-
- if (binding.isComposite)
- {
- // If we're at the composite, only proceed if we're iterating down
- // instead of up.
- if (!next)
- return default;
- }
- else if (!binding.isPartOfComposite)
- return default;
-
- for (var accessor = Iterate(next); accessor.valid; accessor = accessor.Iterate(next))
- {
- if (!accessor.binding.isPartOfComposite)
- return default;
-
- if (partName.Equals(accessor.binding.name, StringComparison.InvariantCultureIgnoreCase))
- return accessor;
- }
-
- return default;
- }
-
- ////TODO: allow setting overrides through this accessor
-
- /// <summary>
- /// Remove the binding.
- /// </summary>
- /// <remarks>
- /// If the binding is a composite (see <see cref="InputBinding.isComposite"/>), part bindings of the
- /// composite will be removed as well.
- ///
- /// Note that the accessor will not necessarily be invalidated. Instead, it will point to what used
- /// to be the next binding in the array (though that means the accessor will be invalid if the binding
- /// that got erased was the last one in the array).
- /// </remarks>
- /// <exception cref="InvalidOperationException">The instance is not <see cref="valid"/>.</exception>
- public void Erase()
- {
- if (!valid)
- throw new InvalidOperationException("Instance not valid");
-
- var isComposite = m_ActionMap.m_Bindings[m_BindingIndexInMap].isComposite;
- ArrayHelpers.EraseAt(ref m_ActionMap.m_Bindings, m_BindingIndexInMap);
-
- // If it's a composite, also erase part bindings.
- if (isComposite)
- {
- while (m_BindingIndexInMap < m_ActionMap.m_Bindings.LengthSafe() && m_ActionMap.m_Bindings[m_BindingIndexInMap].isPartOfComposite)
- {
- ArrayHelpers.EraseAt(ref m_ActionMap.m_Bindings, m_BindingIndexInMap);
- }
- }
-
- m_Action.m_BindingsCount = m_ActionMap.m_Bindings.LengthSafe();
- m_ActionMap.OnBindingModified();
-
- // We have switched to a different binding array. For singleton actions, we need to
- // sync up the reference that the action itself has.
- if (m_ActionMap.m_SingletonAction != null)
- m_ActionMap.m_SingletonAction.m_SingletonActionBindings = m_ActionMap.m_Bindings;
- }
-
- /// <summary>
- /// Insert a composite part into a composite binding.
- /// </summary>
- /// <param name="partName">Name of the part in composite binding.</param>
- /// <param name="path">Control path to bind to.</param>
- /// <returns>The same binding syntax for further configuration.</returns>
- /// <exception cref="ArgumentNullException">Part name is null or empty.</exception>
- /// <exception cref="InvalidOperationException">The binding accessor is invalid or the binding accessor is not pointing to composite or part binding.</exception>
- public BindingSyntax InsertPartBinding(string partName, string path)
- {
- if (string.IsNullOrEmpty(partName))
- throw new ArgumentNullException(nameof(partName));
- if (!valid)
- throw new InvalidOperationException("Binding accessor is not valid");
- var binding = this.binding;
- if (!binding.isPartOfComposite && !binding.isComposite)
- throw new InvalidOperationException("Binding accessor must point to composite or part binding");
-
- AddBindingInternal(m_ActionMap,
- new InputBinding { path = path, isPartOfComposite = true, name = partName },
- m_BindingIndexInMap + 1);
-
- return new BindingSyntax(m_ActionMap, m_BindingIndexInMap + 1, m_Action);
- }
- }
-
- ////TODO: remove this and merge it into BindingSyntax
- /// <summary>
- /// Write accessor to a composite binding.
- /// </summary>
- /// <remarks>
- /// To add a composite binding to an action, you must first call <see cref="InputActionSetupExtensions.AddCompositeBinding"/>
- /// and then use the CompositeSyntax struct to add composite parts.
- /// <example>
- /// <code>
- /// playerInput.actions["fire"]
- /// .ChangeBinding(0)
- /// .AddCompositeBinding("2DVector")
- /// .With("Up", "<Keyboard>/w")
- /// .With("Down", "<Keyboard>/s")
- /// .With("Left", "<Keyboard>/a")
- /// .With("Right", "<Keyboard>/d");
- /// </code>
- /// </example>
- /// </remarks>
- /// <seealso cref="AddBinding(InputAction,InputBinding)"/>
- /// <seealso cref="ChangeBinding(InputAction,int)"/>
- public struct CompositeSyntax
- {
- private readonly InputAction m_Action;
- private readonly InputActionMap m_ActionMap;
- private int m_BindingIndexInMap;
-
- /// <summary>
- /// Index of the binding that the accessor refers to.
- /// </summary>
- /// <remarks>
- /// When accessing bindings on an <see cref="InputAction"/>, this is the index in
- /// <see cref="InputAction.bindings"/> of the action. When accessing bindings on an
- /// <see cref="InputActionMap"/>, it is the index <see cref="InputActionMap.bindings"/>
- /// of the map.
- /// </remarks>
- public int bindingIndex
- {
- get
- {
- if (m_ActionMap == null)
- return -1;
- if (m_Action != null)
- return m_Action.BindingIndexOnMapToBindingIndexOnAction(m_BindingIndexInMap);
- return m_BindingIndexInMap;
- }
- }
-
- internal CompositeSyntax(InputActionMap map, InputAction action, int compositeIndex)
- {
- m_Action = action;
- m_ActionMap = map;
- m_BindingIndexInMap = compositeIndex;
- }
-
- /// <summary>
- /// Add a part binding to the composite.
- /// </summary>
- /// <param name="name">Name of the part. This is dependent on the type of composite. For
- /// <see cref="Composites.Vector2Composite"/>, for example, the valid parts are <c>"Up"</c>, <c>"Down"</c>,
- /// <c>"Left"</c>, and <c>"Right"</c>.</param>
- /// <param name="binding">Control path to binding to. See <see cref="InputBinding.path"/>.</param>
- /// <param name="groups">Binding groups to assign to the part binding. See <see cref="InputBinding.groups"/>.</param>
- /// <param name="processors">Optional list of processors to apply to the binding. See <see cref="InputBinding.processors"/>.</param>
- /// <returns>The same composite syntax for further configuration.</returns>
- public CompositeSyntax With(string name, string binding, string groups = null, string processors = null)
- {
- ////TODO: check whether non-composite bindings have been added in-between
-
- using (InputActionRebindingExtensions.DeferBindingResolution())
- {
- int bindingIndex;
- if (m_Action != null)
- bindingIndex = m_Action.AddBinding(path: binding, groups: groups, processors: processors)
- .m_BindingIndexInMap;
- else
- bindingIndex = m_ActionMap.AddBinding(path: binding, groups: groups, processors: processors)
- .m_BindingIndexInMap;
-
- m_ActionMap.m_Bindings[bindingIndex].name = name;
- m_ActionMap.m_Bindings[bindingIndex].isPartOfComposite = true;
- }
-
- return this;
- }
- }
-
- /// <summary>
- /// Write accessor to a control scheme.
- /// </summary>
- public struct ControlSchemeSyntax
- {
- // REVIEW: Consider removing this for major revision, e.g. v2 or let functionality used by it be internal to reduce API surface.
-
- private readonly InputActionAsset m_Asset;
- private readonly int m_ControlSchemeIndex;
- private InputControlScheme m_ControlScheme;
-
- internal ControlSchemeSyntax(InputActionAsset asset, int index)
- {
- m_Asset = asset;
- m_ControlSchemeIndex = index;
- m_ControlScheme = new InputControlScheme();
- }
-
- internal ControlSchemeSyntax(InputControlScheme controlScheme)
- {
- m_Asset = null;
- m_ControlSchemeIndex = -1;
- m_ControlScheme = controlScheme;
- }
-
- /// <summary>
- /// Sets or overwrite the binding group for control scheme.
- /// </summary>
- /// <param name="bindingGroup">A binding group. See <see cref="InputBinding.groups"/>.</param>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- /// <exception cref="ArgumentNullException">If provided name is null or empty.</exception>
- public ControlSchemeSyntax WithBindingGroup(string bindingGroup)
- {
- if (string.IsNullOrEmpty(bindingGroup))
- throw new ArgumentNullException(nameof(bindingGroup));
-
- if (m_Asset == null)
- m_ControlScheme.m_BindingGroup = bindingGroup;
- else
- m_Asset.m_ControlSchemes[m_ControlSchemeIndex].bindingGroup = bindingGroup;
-
- return this;
- }
-
- /// <summary>
- /// Adds a required device to control scheme.
- /// </summary>
- /// <typeparam name="TDevice">Type of device.</typeparam>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- public ControlSchemeSyntax WithRequiredDevice<TDevice>()
- where TDevice : InputDevice
- {
- return WithRequiredDevice(DeviceTypeToControlPath<TDevice>());
- }
-
- /// <summary>
- /// Adds an optional device to control scheme.
- /// </summary>
- /// <typeparam name="TDevice">Type of device.</typeparam>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- /// <see cref="InputControlScheme.DeviceRequirement.isOptional"/>
- public ControlSchemeSyntax WithOptionalDevice<TDevice>()
- where TDevice : InputDevice
- {
- return WithOptionalDevice(DeviceTypeToControlPath<TDevice>());
- }
-
- /// <summary>
- /// Combines another required device with the previous device using boolean OR
- /// logic such that either the previous device or this device are required to be present.
- /// </summary>
- /// <typeparam name="TDevice">Type of device.</typeparam>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- /// <remarks>
- /// <example>
- /// <code>
- /// // Create an .inputactions asset.
- /// var asset = ScriptableObject.CreateInstance<InputActionAsset>();
- ///
- /// asset.AddControlScheme("KeyboardAndMouseOrPen")
- /// .WithRequiredDevice("<Keyboard>")
- /// .WithRequiredDevice("<Mouse>")
- /// .OrWithRequiredDevice("<Pen>")
- /// </code>
- /// </example>
- /// </remarks>
- /// <see cref="InputControlScheme.DeviceRequirement.isOR"/>
- public ControlSchemeSyntax OrWithRequiredDevice<TDevice>()
- where TDevice : InputDevice
- {
- return OrWithRequiredDevice(DeviceTypeToControlPath<TDevice>());
- }
-
- /// <summary>
- /// Combines another optional device with the previous device using boolean OR
- /// logic such that either the previous device or this device are required to be present.
- /// If this is the last device in a chain of OR'd devices, the entire chain of
- /// devices becomes optional.
- /// </summary>
- /// <typeparam name="TDevice">Type of device.</typeparam>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- /// <see cref="InputControlScheme.DeviceRequirement.isOR"/>
- public ControlSchemeSyntax OrWithOptionalDevice<TDevice>()
- where TDevice : InputDevice
- {
- return OrWithOptionalDevice(DeviceTypeToControlPath<TDevice>());
- }
-
- /// <summary>
- /// Adds a required device to control scheme.
- /// </summary>
- /// <param name="controlPath">Device path, like <Gamepad>.</param>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- public ControlSchemeSyntax WithRequiredDevice(string controlPath)
- {
- AddDeviceEntry(controlPath, InputControlScheme.DeviceRequirement.Flags.None);
- return this;
- }
-
- /// <summary>
- /// Add an optional device to the control scheme.
- /// </summary>
- /// <param name="controlPath">The device path, like <Gamepad>.</param>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- public ControlSchemeSyntax WithOptionalDevice(string controlPath)
- {
- AddDeviceEntry(controlPath, InputControlScheme.DeviceRequirement.Flags.Optional);
- return this;
- }
-
- /// <summary>
- /// Adds another possible required device to the control scheme.
- /// </summary>
- /// <param name="controlPath">Device path, like <Gamepad>.</param>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- public ControlSchemeSyntax OrWithRequiredDevice(string controlPath)
- {
- AddDeviceEntry(controlPath, InputControlScheme.DeviceRequirement.Flags.Or);
- return this;
- }
-
- /// <summary>
- /// Adds another possible optional device to control scheme.
- /// </summary>
- /// <param name="controlPath">Device path, like <Gamepad>.</param>
- /// <returns>The same control scheme syntax for further configuration.</returns>
- public ControlSchemeSyntax OrWithOptionalDevice(string controlPath)
- {
- AddDeviceEntry(controlPath,
- InputControlScheme.DeviceRequirement.Flags.Optional |
- InputControlScheme.DeviceRequirement.Flags.Or);
- return this;
- }
-
- private string DeviceTypeToControlPath<TDevice>()
- where TDevice : InputDevice
- {
- var layoutName = InputControlLayout.s_Layouts.TryFindLayoutForType(typeof(TDevice)).ToString();
- if (string.IsNullOrEmpty(layoutName))
- layoutName = typeof(TDevice).Name;
- return $"<{layoutName}>";
- }
-
- /// <summary>
- /// Call this method when done building out the control scheme to return the associated instance.
- /// </summary>
- /// <returns>The associated control scheme</returns>
- public InputControlScheme Done()
- {
- if (m_Asset != null)
- return m_Asset.m_ControlSchemes[m_ControlSchemeIndex];
- return m_ControlScheme;
- }
-
- private void AddDeviceEntry(string controlPath, InputControlScheme.DeviceRequirement.Flags flags)
- {
- if (string.IsNullOrEmpty(controlPath))
- throw new ArgumentNullException(nameof(controlPath));
-
- var scheme = m_Asset != null ? m_Asset.m_ControlSchemes[m_ControlSchemeIndex] : m_ControlScheme;
- ArrayHelpers.Append(ref scheme.m_DeviceRequirements,
- new InputControlScheme.DeviceRequirement
- {
- m_ControlPath = controlPath,
- m_Flags = flags,
- });
-
- if (m_Asset == null)
- m_ControlScheme = scheme;
- else
- m_Asset.m_ControlSchemes[m_ControlSchemeIndex] = scheme;
- }
- }
- }
- }
|