Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

InputActionReference.cs 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using System;
  2. using System.Linq;
  3. ////REVIEW: Can we somehow make this a simple struct? The one problem we have is that we can't put struct instances as sub-assets into
  4. //// the import (i.e. InputActionImporter can't do AddObjectToAsset with them). However, maybe there's a way around that. The thing
  5. //// is that we really want to store the asset reference plus the action GUID on the *user* side, i.e. the referencing side. Right
  6. //// now, what happens is that InputActionImporter puts these objects along with the reference and GUID they contain in the
  7. //// *imported* object, i.e. right with the asset. This partially defeats the whole purpose of having these objects and it means
  8. //// that now the GUID doesn't really matter anymore. Rather, it's the file ID that now has to be stable.
  9. ////
  10. //// If we always store the GUID and asset reference on the user side, we can put the serialized data *anywhere* and it'll remain
  11. //// save and proper no matter what we do in InputActionImporter.
  12. ////REVIEW: should this throw if you try to assign an action that is not a singleton?
  13. ////REVIEW: akin to this, also have an InputActionMapReference?
  14. namespace UnityEngine.InputSystem
  15. {
  16. /// <summary>
  17. /// References a specific <see cref="InputAction"/> in an <see cref="InputActionMap"/>
  18. /// stored inside an <see cref="InputActionAsset"/>.
  19. /// </summary>
  20. /// <remarks>
  21. /// The difference to a plain reference directly to an <see cref="InputAction"/> object is
  22. /// that an InputActionReference can be serialized without causing the referenced <see cref="InputAction"/>
  23. /// to be serialized as well. The reference will remain intact even if the action or the map
  24. /// that contains the action is renamed.
  25. ///
  26. /// References can be set up graphically in the editor by dropping individual actions from the project
  27. /// browser onto a reference field.
  28. /// </remarks>
  29. /// <seealso cref="InputActionProperty"/>
  30. /// <seealso cref="InputAction"/>
  31. /// <seealso cref="InputActionAsset"/>
  32. public class InputActionReference : ScriptableObject
  33. {
  34. /// <summary>
  35. /// The asset that the referenced action is part of. Null if the reference
  36. /// is not initialized or if the asset has been deleted.
  37. /// </summary>
  38. /// <value>InputActionAsset of the referenced action.</value>
  39. public InputActionAsset asset => m_Asset;
  40. /// <summary>
  41. /// The action that the reference resolves to. Null if the action
  42. /// cannot be found.
  43. /// </summary>
  44. /// <value>The action that reference points to.</value>
  45. /// <remarks>
  46. /// Actions are resolved on demand based on their internally stored IDs.
  47. /// </remarks>
  48. public InputAction action
  49. {
  50. get
  51. {
  52. if (m_Action == null)
  53. {
  54. if (m_Asset == null)
  55. return null;
  56. m_Action = m_Asset.FindAction(new Guid(m_ActionId));
  57. }
  58. return m_Action;
  59. }
  60. }
  61. /// <summary>
  62. /// Initialize the reference to refer to the given action.
  63. /// </summary>
  64. /// <param name="action">An input action. Must be contained in an <see cref="InputActionMap"/>
  65. /// that is itself contained in an <see cref="InputActionAsset"/>. Can be <c>null</c> in which
  66. /// case the reference is reset to its default state which does not reference an action.</param>
  67. /// <exception cref="InvalidOperationException"><paramref name="action"/> is not contained in an
  68. /// <see cref="InputActionMap"/> that is itself contained in an <see cref="InputActionAsset"/>.</exception>
  69. public void Set(InputAction action)
  70. {
  71. if (action == null)
  72. {
  73. m_Asset = default;
  74. m_ActionId = default;
  75. return;
  76. }
  77. var map = action.actionMap;
  78. if (map == null || map.asset == null)
  79. throw new InvalidOperationException(
  80. $"Action '{action}' must be part of an InputActionAsset in order to be able to create an InputActionReference for it");
  81. SetInternal(map.asset, action);
  82. }
  83. /// <summary>
  84. /// Look up an action in the given asset and initialize the reference to
  85. /// point to it.
  86. /// </summary>
  87. /// <param name="asset">An .inputactions asset.</param>
  88. /// <param name="mapName">Name of the <see cref="InputActionMap"/> in <paramref name="asset"/>
  89. /// (see <see cref="InputActionAsset.actionMaps"/>). Case-insensitive.</param>
  90. /// <param name="actionName">Name of the action in <paramref name="mapName"/>. Case-insensitive.</param>
  91. /// <exception cref="ArgumentNullException"><paramref name="asset"/> is <c>null</c> -or-
  92. /// <paramref name="mapName"/> is <c>null</c> or empty -or- <paramref name="actionName"/>
  93. /// is <c>null</c> or empty.</exception>
  94. /// <exception cref="ArgumentException">No action map called <paramref name="mapName"/> could
  95. /// be found in <paramref name="asset"/> -or- no action called <paramref name="actionName"/>
  96. /// could be found in the action map called <paramref name="mapName"/> in <paramref name="asset"/>.</exception>
  97. public void Set(InputActionAsset asset, string mapName, string actionName)
  98. {
  99. if (asset == null)
  100. throw new ArgumentNullException(nameof(asset));
  101. if (string.IsNullOrEmpty(mapName))
  102. throw new ArgumentNullException(nameof(mapName));
  103. if (string.IsNullOrEmpty(actionName))
  104. throw new ArgumentNullException(nameof(actionName));
  105. var actionMap = asset.FindActionMap(mapName);
  106. if (actionMap == null)
  107. throw new ArgumentException($"No action map '{mapName}' in '{asset}'", nameof(mapName));
  108. var action = actionMap.FindAction(actionName);
  109. if (action == null)
  110. throw new ArgumentException($"No action '{actionName}' in map '{mapName}' of asset '{asset}'",
  111. nameof(actionName));
  112. SetInternal(asset, action);
  113. }
  114. private void SetInternal(InputActionAsset asset, InputAction action)
  115. {
  116. var actionMap = action.actionMap;
  117. if (!asset.actionMaps.Contains(actionMap))
  118. throw new ArgumentException(
  119. $"Action '{action}' is not contained in asset '{asset}'", nameof(action));
  120. m_Asset = asset;
  121. m_ActionId = action.id.ToString();
  122. name = GetDisplayName(action);
  123. ////REVIEW: should this dirty the asset if IDs had not been generated yet?
  124. }
  125. /// <summary>
  126. /// Return a string representation of the reference useful for debugging.
  127. /// </summary>
  128. /// <returns>A string representation of the reference.</returns>
  129. public override string ToString()
  130. {
  131. try
  132. {
  133. var action = this.action;
  134. return $"{m_Asset.name}:{action.actionMap.name}/{action.name}";
  135. }
  136. catch
  137. {
  138. if (m_Asset != null)
  139. return $"{m_Asset.name}:{m_ActionId}";
  140. }
  141. return base.ToString();
  142. }
  143. internal static string GetDisplayName(InputAction action)
  144. {
  145. return !string.IsNullOrEmpty(action?.actionMap?.name) ? $"{action.actionMap?.name}/{action.name}" : action?.name;
  146. }
  147. /// <summary>
  148. /// Return a string representation useful for showing in UI.
  149. /// </summary>
  150. internal string ToDisplayName()
  151. {
  152. return string.IsNullOrEmpty(name) ? GetDisplayName(action) : name;
  153. }
  154. /// <summary>
  155. /// Convert an InputActionReference to the InputAction it points to.
  156. /// </summary>
  157. /// <param name="reference">An InputActionReference object. Can be null.</param>
  158. /// <returns>The value of <see cref="action"/> from <paramref name="reference"/>. Can be null.</returns>
  159. public static implicit operator InputAction(InputActionReference reference)
  160. {
  161. return reference?.action;
  162. }
  163. /// <summary>
  164. /// Create a new InputActionReference object that references the given action.
  165. /// </summary>
  166. /// <param name="action">An input action. Must be contained in an <see cref="InputActionMap"/>
  167. /// that is itself contained in an <see cref="InputActionAsset"/>. Can be <c>null</c> in which
  168. /// case the reference is reset to its default state which does not reference an action.</param>
  169. /// <returns>A new InputActionReference referencing <paramref name="action"/>.</returns>
  170. public static InputActionReference Create(InputAction action)
  171. {
  172. if (action == null)
  173. return null;
  174. var reference = CreateInstance<InputActionReference>();
  175. reference.Set(action);
  176. return reference;
  177. }
  178. [SerializeField] internal InputActionAsset m_Asset;
  179. // Can't serialize System.Guid and Unity's GUID is editor only so these
  180. // go out as strings.
  181. [SerializeField] internal string m_ActionId;
  182. /// <summary>
  183. /// The resolved, cached input action.
  184. /// </summary>
  185. [NonSerialized] private InputAction m_Action;
  186. // Make annoying Microsoft code analyzer happy.
  187. public InputAction ToInputAction()
  188. {
  189. return action;
  190. }
  191. }
  192. }