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