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