UI 자동화를 위해 바인딩 기능 구현
- 유니티 에셋 인증 오류로 meta 재생성
This commit is contained in:
@@ -0,0 +1,726 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
////TODO: resolving bindings to actions needs to take "{id}" form into account
|
||||
|
||||
namespace UnityEngine.InputSystem.Editor
|
||||
{
|
||||
// Helpers for doctoring around in InputActions using SerializedProperties.
|
||||
internal static class InputActionSerializationHelpers
|
||||
{
|
||||
public static string GetName(SerializedProperty element)
|
||||
{
|
||||
using (var nameProperty = element.FindPropertyRelative("m_Name"))
|
||||
{
|
||||
Debug.Assert(nameProperty != null, $"Cannot find m_Name property in {element.propertyPath}");
|
||||
return nameProperty.stringValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static Guid GetId(SerializedProperty element)
|
||||
{
|
||||
using (var idProperty = element.FindPropertyRelative("m_Id"))
|
||||
{
|
||||
Debug.Assert(idProperty != null, $"Cannot find m_Id property in {element.propertyPath}");
|
||||
return new Guid(idProperty.stringValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetIndex(SerializedProperty arrayProperty, Guid id)
|
||||
{
|
||||
Debug.Assert(arrayProperty.isArray, $"Property {arrayProperty.propertyPath} is not an array");
|
||||
for (var i = 0; i < arrayProperty.arraySize; ++i)
|
||||
{
|
||||
using (var element = arrayProperty.GetArrayElementAtIndex(i))
|
||||
if (GetId(element) == id)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int GetIndex(SerializedProperty arrayProperty, SerializedProperty arrayElement)
|
||||
{
|
||||
return GetIndex(arrayProperty, GetId(arrayElement));
|
||||
}
|
||||
|
||||
public static int GetIndex(SerializedProperty arrayElement)
|
||||
{
|
||||
var arrayProperty = arrayElement.GetArrayPropertyFromElement();
|
||||
return GetIndex(arrayProperty, arrayElement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starting with the given binding, find the composite that the binding belongs to. The given binding
|
||||
/// must either be the composite or be part of a composite.
|
||||
/// </summary>
|
||||
public static int GetCompositeStartIndex(SerializedProperty bindingArrayProperty, int bindingIndex)
|
||||
{
|
||||
for (var i = bindingIndex; i >= 0; --i)
|
||||
{
|
||||
var bindingProperty = bindingArrayProperty.GetArrayElementAtIndex(i);
|
||||
var bindingFlags = (InputBinding.Flags)bindingProperty.FindPropertyRelative("m_Flags").intValue;
|
||||
if ((bindingFlags & InputBinding.Flags.Composite) != 0)
|
||||
return i;
|
||||
Debug.Assert((bindingFlags & InputBinding.Flags.PartOfComposite) != 0,
|
||||
"Binding is neither a composite nor part of a composite");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int GetCompositePartCount(SerializedProperty bindingArrayProperty, int bindingIndex)
|
||||
{
|
||||
var compositeStartIndex = GetCompositeStartIndex(bindingArrayProperty, bindingIndex);
|
||||
if (compositeStartIndex == -1)
|
||||
return 0;
|
||||
|
||||
var numParts = 0;
|
||||
for (var i = compositeStartIndex + 1; i < bindingArrayProperty.arraySize; ++i, ++numParts)
|
||||
{
|
||||
var bindingProperty = bindingArrayProperty.GetArrayElementAtIndex(i);
|
||||
var bindingFlags = (InputBinding.Flags)bindingProperty.FindPropertyRelative("m_Flags").intValue;
|
||||
if ((bindingFlags & InputBinding.Flags.PartOfComposite) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return numParts;
|
||||
}
|
||||
|
||||
public static int ConvertBindingIndexOnActionToBindingIndexInArray(SerializedProperty bindingArrayProperty, string actionName,
|
||||
int bindingIndexOnAction)
|
||||
{
|
||||
var bindingCount = bindingArrayProperty.arraySize;
|
||||
var indexOnAction = -1;
|
||||
var indexInArray = 0;
|
||||
for (; indexInArray < bindingCount; ++indexInArray)
|
||||
{
|
||||
var bindingActionName = bindingArrayProperty.GetArrayElementAtIndex(indexInArray).FindPropertyRelative("m_Action")
|
||||
.stringValue;
|
||||
if (actionName.Equals(bindingActionName, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
++indexOnAction;
|
||||
if (indexOnAction == bindingIndexOnAction)
|
||||
return indexInArray;
|
||||
}
|
||||
}
|
||||
return indexInArray;
|
||||
}
|
||||
|
||||
public static void AddActionMaps(SerializedObject asset, SerializedObject sourceAsset)
|
||||
{
|
||||
Debug.Assert(asset.targetObject is InputActionAsset);
|
||||
Debug.Assert(sourceAsset.targetObject is InputActionAsset);
|
||||
|
||||
var mapArrayPropertySrc = sourceAsset.FindProperty(nameof(InputActionAsset.m_ActionMaps));
|
||||
var mapArrayPropertyDst = asset.FindProperty(nameof(InputActionAsset.m_ActionMaps));
|
||||
|
||||
// Copy each action map from source and paste at the end of destination
|
||||
var buffer = new StringBuilder();
|
||||
for (var i = 0; i < mapArrayPropertySrc.arraySize; ++i)
|
||||
{
|
||||
buffer.Clear();
|
||||
var mapProperty = mapArrayPropertySrc.GetArrayElementAtIndex(i);
|
||||
CopyPasteHelper.CopyItems(new List<SerializedProperty> {mapProperty}, buffer, typeof(InputActionMap), mapProperty);
|
||||
CopyPasteHelper.PasteItems(buffer.ToString(), new[] { mapArrayPropertyDst.arraySize - 1 }, mapArrayPropertyDst);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddControlSchemes(SerializedObject asset, SerializedObject sourceAsset)
|
||||
{
|
||||
Debug.Assert((asset.targetObject is InputActionAsset));
|
||||
Debug.Assert((sourceAsset.targetObject is InputActionAsset));
|
||||
|
||||
var src = sourceAsset.FindProperty(nameof(InputActionAsset.m_ControlSchemes));
|
||||
var dst = asset.FindProperty(nameof(InputActionAsset.m_ControlSchemes));
|
||||
|
||||
var buffer = new StringBuilder();
|
||||
src.CopyToJson(buffer, ignoreObjectReferences: true);
|
||||
dst.RestoreFromJson(buffer.ToString());
|
||||
}
|
||||
|
||||
public static SerializedProperty AddActionMap(SerializedObject asset, int index = -1)
|
||||
{
|
||||
if (!(asset.targetObject is InputActionAsset))
|
||||
throw new InvalidOperationException(
|
||||
$"Can only add action maps to InputActionAsset objects (actual object is {asset.targetObject}");
|
||||
|
||||
var mapArrayProperty = asset.FindProperty("m_ActionMaps");
|
||||
var name = FindUniqueName(mapArrayProperty, "New action map");
|
||||
if (index < 0)
|
||||
index = mapArrayProperty.arraySize;
|
||||
|
||||
mapArrayProperty.InsertArrayElementAtIndex(index);
|
||||
var mapProperty = mapArrayProperty.GetArrayElementAtIndex(index);
|
||||
|
||||
mapProperty.FindPropertyRelative("m_Name").stringValue = name;
|
||||
mapProperty.FindPropertyRelative("m_Id").stringValue = Guid.NewGuid().ToString();
|
||||
mapProperty.FindPropertyRelative("m_Actions").ClearArray();
|
||||
mapProperty.FindPropertyRelative("m_Bindings").ClearArray();
|
||||
// NB: This isn't always required: If there's already values in the mapArrayProperty, then inserting a new
|
||||
// element will duplicate the values from the adjacent element to the new element.
|
||||
// However, if the array has been emptied - i.e. if all action maps have been deleted -
|
||||
// then the m_Asset property is null, and needs setting here.
|
||||
if (mapProperty.FindPropertyRelative("m_Asset").objectReferenceValue == null)
|
||||
mapProperty.FindPropertyRelative("m_Asset").objectReferenceValue = asset.targetObject;
|
||||
|
||||
return mapProperty;
|
||||
}
|
||||
|
||||
public static void DeleteActionMap(SerializedObject asset, Guid id)
|
||||
{
|
||||
var mapArrayProperty = asset.FindProperty("m_ActionMaps");
|
||||
var mapIndex = GetIndex(mapArrayProperty, id);
|
||||
if (mapIndex == -1)
|
||||
throw new ArgumentException($"No map with id {id} in {asset}", nameof(id));
|
||||
mapArrayProperty.DeleteArrayElementAtIndex(mapIndex);
|
||||
}
|
||||
|
||||
public static void DeleteAllActionMaps(SerializedObject asset)
|
||||
{
|
||||
Debug.Assert(asset.targetObject is InputActionAsset);
|
||||
|
||||
var mapArrayProperty = asset.FindProperty("m_ActionMaps");
|
||||
while (mapArrayProperty.arraySize > 0)
|
||||
mapArrayProperty.DeleteArrayElementAtIndex(0);
|
||||
}
|
||||
|
||||
public static void MoveActionMap(SerializedObject asset, int fromIndex, int toIndex)
|
||||
{
|
||||
var mapArrayProperty = asset.FindProperty("m_ActionMaps");
|
||||
mapArrayProperty.MoveArrayElement(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
public static void MoveAction(SerializedProperty actionMap, int fromIndex, int toIndex)
|
||||
{
|
||||
var actionArrayProperty = actionMap.FindPropertyRelative(nameof(InputActionMap.m_Actions));
|
||||
actionArrayProperty.MoveArrayElement(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
public static void MoveBinding(SerializedProperty actionMap, int fromIndex, int toIndex)
|
||||
{
|
||||
var arrayProperty = actionMap.FindPropertyRelative(nameof(InputActionMap.m_Bindings));
|
||||
arrayProperty.MoveArrayElement(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
// Append a new action to the end of the set.
|
||||
public static SerializedProperty AddAction(SerializedProperty actionMap, int index = -1)
|
||||
{
|
||||
var actionsArrayProperty = actionMap.FindPropertyRelative("m_Actions");
|
||||
if (index < 0)
|
||||
index = actionsArrayProperty.arraySize;
|
||||
|
||||
var actionName = FindUniqueName(actionsArrayProperty, "New action");
|
||||
|
||||
actionsArrayProperty.InsertArrayElementAtIndex(index);
|
||||
var actionProperty = actionsArrayProperty.GetArrayElementAtIndex(index);
|
||||
|
||||
actionProperty.FindPropertyRelative("m_Name").stringValue = actionName;
|
||||
actionProperty.FindPropertyRelative("m_Type").intValue = (int)InputActionType.Button; // Default to creating button actions.
|
||||
actionProperty.FindPropertyRelative("m_Id").stringValue = Guid.NewGuid().ToString();
|
||||
actionProperty.FindPropertyRelative("m_ExpectedControlType").stringValue = "Button";
|
||||
actionProperty.FindPropertyRelative("m_Flags").intValue = 0;
|
||||
actionProperty.FindPropertyRelative("m_Interactions").stringValue = "";
|
||||
actionProperty.FindPropertyRelative("m_Processors").stringValue = "";
|
||||
|
||||
return actionProperty;
|
||||
}
|
||||
|
||||
public static void DeleteActionAndBindings(SerializedProperty actionMap, Guid actionId)
|
||||
{
|
||||
using (var actionsArrayProperty = actionMap.FindPropertyRelative("m_Actions"))
|
||||
using (var bindingsArrayProperty = actionMap.FindPropertyRelative("m_Bindings"))
|
||||
{
|
||||
// Find index of action.
|
||||
var actionIndex = GetIndex(actionsArrayProperty, actionId);
|
||||
if (actionIndex == -1)
|
||||
throw new ArgumentException($"No action with ID {actionId} in {actionMap.propertyPath}",
|
||||
nameof(actionId));
|
||||
|
||||
using (var actionsProperty = actionsArrayProperty.GetArrayElementAtIndex(actionIndex))
|
||||
{
|
||||
var actionName = GetName(actionsProperty);
|
||||
var actionIdString = actionId.ToString();
|
||||
|
||||
// Delete all bindings that refer to the action by ID or name.
|
||||
for (var i = 0; i < bindingsArrayProperty.arraySize; ++i)
|
||||
{
|
||||
using (var bindingProperty = bindingsArrayProperty.GetArrayElementAtIndex(i))
|
||||
using (var bindingActionProperty = bindingProperty.FindPropertyRelative("m_Action"))
|
||||
{
|
||||
var targetAction = bindingActionProperty.stringValue;
|
||||
if (targetAction.Equals(actionName, StringComparison.InvariantCultureIgnoreCase) ||
|
||||
targetAction == actionIdString)
|
||||
{
|
||||
bindingsArrayProperty.DeleteArrayElementAtIndex(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actionsArrayProperty.DeleteArrayElementAtIndex(actionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Equivalent to InputAction.AddBinding().
|
||||
public static SerializedProperty AddBinding(SerializedProperty actionProperty,
|
||||
SerializedProperty actionMapProperty = null, SerializedProperty afterBinding = null,
|
||||
string groups = "", string path = "", string name = "",
|
||||
string interactions = "", string processors = "",
|
||||
InputBinding.Flags flags = InputBinding.Flags.None)
|
||||
{
|
||||
var bindingsArrayProperty = actionMapProperty != null
|
||||
? actionMapProperty.FindPropertyRelative("m_Bindings")
|
||||
: actionProperty.FindPropertyRelative("m_SingletonActionBindings");
|
||||
var bindingsCount = bindingsArrayProperty.arraySize;
|
||||
var actionName = actionProperty.FindPropertyRelative("m_Name").stringValue;
|
||||
|
||||
int bindingIndex;
|
||||
if (afterBinding != null)
|
||||
{
|
||||
// If we're supposed to put the binding right after another binding, find the
|
||||
// binding's index. Also, if it's a composite, skip past all its parts.
|
||||
bindingIndex = GetIndex(bindingsArrayProperty, afterBinding);
|
||||
if (IsCompositeBinding(afterBinding))
|
||||
bindingIndex += GetCompositePartCount(bindingsArrayProperty, bindingIndex);
|
||||
++bindingIndex; // Put it *after* the binding.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the index of the last binding for the action in the array.
|
||||
var indexOfLastBindingForAction = -1;
|
||||
for (var i = 0; i < bindingsCount; ++i)
|
||||
{
|
||||
var bindingProperty = bindingsArrayProperty.GetArrayElementAtIndex(i);
|
||||
var bindingActionName = bindingProperty.FindPropertyRelative("m_Action").stringValue;
|
||||
if (actionName.Equals(bindingActionName, StringComparison.InvariantCultureIgnoreCase))
|
||||
indexOfLastBindingForAction = i;
|
||||
}
|
||||
|
||||
// Insert after last binding or at end of array.
|
||||
bindingIndex = indexOfLastBindingForAction != -1 ? indexOfLastBindingForAction + 1 : bindingsCount;
|
||||
}
|
||||
|
||||
////TODO: bind using {id} rather than action name
|
||||
return AddBindingToBindingArray(bindingsArrayProperty,
|
||||
bindingIndex: bindingIndex,
|
||||
actionName: actionName,
|
||||
groups: groups,
|
||||
path: path,
|
||||
name: name,
|
||||
interactions: interactions,
|
||||
processors: processors,
|
||||
flags: flags);
|
||||
}
|
||||
|
||||
public static SerializedProperty AddBindingToBindingArray(SerializedProperty bindingsArrayProperty, int bindingIndex = -1,
|
||||
string actionName = "", string groups = "", string path = "", string name = "", string interactions = "", string processors = "",
|
||||
InputBinding.Flags flags = InputBinding.Flags.None)
|
||||
{
|
||||
Debug.Assert(bindingsArrayProperty != null);
|
||||
Debug.Assert(bindingsArrayProperty.isArray, "SerializedProperty is not an array of bindings");
|
||||
Debug.Assert(bindingIndex == -1 || (bindingIndex >= 0 && bindingIndex <= bindingsArrayProperty.arraySize));
|
||||
|
||||
if (bindingIndex == -1)
|
||||
bindingIndex = bindingsArrayProperty.arraySize;
|
||||
|
||||
bindingsArrayProperty.InsertArrayElementAtIndex(bindingIndex);
|
||||
|
||||
var newBindingProperty = bindingsArrayProperty.GetArrayElementAtIndex(bindingIndex);
|
||||
newBindingProperty.FindPropertyRelative("m_Path").stringValue = path;
|
||||
newBindingProperty.FindPropertyRelative("m_Groups").stringValue = groups;
|
||||
newBindingProperty.FindPropertyRelative("m_Interactions").stringValue = interactions;
|
||||
newBindingProperty.FindPropertyRelative("m_Processors").stringValue = processors;
|
||||
newBindingProperty.FindPropertyRelative("m_Flags").intValue = (int)flags;
|
||||
newBindingProperty.FindPropertyRelative("m_Action").stringValue = actionName;
|
||||
newBindingProperty.FindPropertyRelative("m_Name").stringValue = name;
|
||||
newBindingProperty.FindPropertyRelative("m_Id").stringValue = Guid.NewGuid().ToString();
|
||||
|
||||
////FIXME: this likely leaves m_Bindings in the map for singleton actions unsync'd in some cases
|
||||
|
||||
return newBindingProperty;
|
||||
}
|
||||
|
||||
public static void SetBindingPartName(SerializedProperty bindingProperty, string partName)
|
||||
{
|
||||
//expects beautified partName
|
||||
bindingProperty.FindPropertyRelative("m_Name").stringValue = partName;
|
||||
}
|
||||
|
||||
public static void ChangeBinding(SerializedProperty bindingProperty, string path = null, string groups = null,
|
||||
string interactions = null, string processors = null, string action = null)
|
||||
{
|
||||
// Path.
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
var pathProperty = bindingProperty.FindPropertyRelative("m_Path");
|
||||
pathProperty.stringValue = path;
|
||||
}
|
||||
|
||||
// Groups.
|
||||
if (!string.IsNullOrEmpty(groups))
|
||||
{
|
||||
var groupsProperty = bindingProperty.FindPropertyRelative("m_Groups");
|
||||
groupsProperty.stringValue = groups;
|
||||
}
|
||||
|
||||
// Interactions.
|
||||
if (!string.IsNullOrEmpty(interactions))
|
||||
{
|
||||
var interactionsProperty = bindingProperty.FindPropertyRelative("m_Interactions");
|
||||
interactionsProperty.stringValue = interactions;
|
||||
}
|
||||
|
||||
// Processors.
|
||||
if (!string.IsNullOrEmpty(processors))
|
||||
{
|
||||
var processorsProperty = bindingProperty.FindPropertyRelative("m_Processors");
|
||||
processorsProperty.stringValue = processors;
|
||||
}
|
||||
|
||||
// Action.
|
||||
if (!string.IsNullOrEmpty(action))
|
||||
{
|
||||
var actionProperty = bindingProperty.FindPropertyRelative("m_Action");
|
||||
actionProperty.stringValue = action;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteBinding(SerializedProperty binding, SerializedProperty actionMap)
|
||||
{
|
||||
var bindingsProperty = actionMap.FindPropertyRelative("m_Bindings");
|
||||
DeleteBinding(binding, bindingsProperty, binding.GetIndexOfArrayElement());
|
||||
}
|
||||
|
||||
private static void DeleteBinding(SerializedProperty bindingProperty, SerializedProperty bindingArrayProperty, int bindingIndex)
|
||||
{
|
||||
var bindingFlags = (InputBinding.Flags)bindingProperty.FindPropertyRelative("m_Flags").intValue;
|
||||
var isComposite = (bindingFlags & InputBinding.Flags.Composite) != 0;
|
||||
// If it's a composite, delete all its parts first.
|
||||
if (isComposite)
|
||||
{
|
||||
for (var partIndex = bindingIndex + 1; partIndex < bindingArrayProperty.arraySize;)
|
||||
{
|
||||
var part = bindingArrayProperty.GetArrayElementAtIndex(partIndex);
|
||||
var flags = (InputBinding.Flags)part.FindPropertyRelative("m_Flags").intValue;
|
||||
if ((flags & InputBinding.Flags.PartOfComposite) == 0)
|
||||
break;
|
||||
bindingArrayProperty.DeleteArrayElementAtIndex(partIndex);
|
||||
}
|
||||
}
|
||||
|
||||
bindingArrayProperty.DeleteArrayElementAtIndex(bindingIndex);
|
||||
}
|
||||
|
||||
public static void DeleteBinding(SerializedProperty bindingArrayProperty, Guid id)
|
||||
{
|
||||
var bindingIndex = GetIndex(bindingArrayProperty, id);
|
||||
var bindingProperty = bindingArrayProperty.GetArrayElementAtIndex(bindingIndex);
|
||||
DeleteBinding(bindingProperty, bindingArrayProperty, bindingIndex);
|
||||
}
|
||||
|
||||
public static void EnsureUniqueName(SerializedProperty arrayElement)
|
||||
{
|
||||
var arrayProperty = arrayElement.GetArrayPropertyFromElement();
|
||||
var arrayIndexOfElement = arrayElement.GetIndexOfArrayElement();
|
||||
var nameProperty = arrayElement.FindPropertyRelative("m_Name");
|
||||
var baseName = nameProperty.stringValue;
|
||||
nameProperty.stringValue = FindUniqueName(arrayProperty, baseName, ignoreIndex: arrayIndexOfElement);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "False positive (possibly caused by lambda expression?).")]
|
||||
public static string FindUniqueName(SerializedProperty arrayProperty, string baseName, int ignoreIndex = -1)
|
||||
{
|
||||
return StringHelpers.MakeUniqueName(baseName,
|
||||
Enumerable.Range(0, arrayProperty.arraySize),
|
||||
index =>
|
||||
{
|
||||
if (index == ignoreIndex)
|
||||
return string.Empty;
|
||||
var elementProperty = arrayProperty.GetArrayElementAtIndex(index);
|
||||
var nameProperty = elementProperty.FindPropertyRelative("m_Name");
|
||||
if (nameProperty == null)
|
||||
throw new ArgumentException($"Cannot find m_Name property in elements of array",
|
||||
nameof(arrayProperty));
|
||||
return nameProperty.stringValue;
|
||||
});
|
||||
}
|
||||
|
||||
public static void AssignUniqueIDs(SerializedProperty element)
|
||||
{
|
||||
AssignUniqueID(element);
|
||||
foreach (var child in element.GetChildren())
|
||||
{
|
||||
if (!child.isArray)
|
||||
continue;
|
||||
|
||||
var fieldType = child.GetFieldType();
|
||||
if (fieldType == typeof(InputBinding[]) || fieldType == typeof(InputAction[]) ||
|
||||
fieldType == typeof(InputActionMap))
|
||||
{
|
||||
for (var i = 0; i < child.arraySize; ++i)
|
||||
using (var childElement = child.GetArrayElementAtIndex(i))
|
||||
AssignUniqueIDs(childElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssignUniqueID(SerializedProperty property)
|
||||
{
|
||||
var idProperty = property.FindPropertyRelative("m_Id");
|
||||
idProperty.stringValue = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
public static void RenameAction(SerializedProperty actionProperty, SerializedProperty actionMapProperty, string newName)
|
||||
{
|
||||
// Make sure name is unique.
|
||||
var actionsArrayProperty = actionMapProperty.FindPropertyRelative("m_Actions");
|
||||
var uniqueName = FindUniqueName(actionsArrayProperty, newName, actionProperty.GetIndexOfArrayElement());
|
||||
|
||||
// Update all bindings that refer to the action.
|
||||
var nameProperty = actionProperty.FindPropertyRelative("m_Name");
|
||||
var oldName = nameProperty.stringValue;
|
||||
var bindingsProperty = actionMapProperty.FindPropertyRelative("m_Bindings");
|
||||
for (var i = 0; i < bindingsProperty.arraySize; i++)
|
||||
{
|
||||
var element = bindingsProperty.GetArrayElementAtIndex(i);
|
||||
var actionNameProperty = element.FindPropertyRelative("m_Action");
|
||||
if (actionNameProperty.stringValue.Equals(oldName, StringComparison.InvariantCultureIgnoreCase))
|
||||
actionNameProperty.stringValue = uniqueName;
|
||||
}
|
||||
|
||||
// Update name.
|
||||
nameProperty.stringValue = uniqueName;
|
||||
}
|
||||
|
||||
public static void RenameActionMap(SerializedProperty actionMapProperty, string newName)
|
||||
{
|
||||
// Make sure name is unique in InputActionAsset.
|
||||
var assetObject = actionMapProperty.serializedObject;
|
||||
var mapsArrayProperty = assetObject.FindProperty("m_ActionMaps");
|
||||
var uniqueName = FindUniqueName(mapsArrayProperty, newName, actionMapProperty.GetIndexOfArrayElement());
|
||||
|
||||
// Assign to map.
|
||||
var nameProperty = actionMapProperty.FindPropertyRelative("m_Name");
|
||||
nameProperty.stringValue = uniqueName;
|
||||
}
|
||||
|
||||
public static void RenameComposite(SerializedProperty compositeGroupProperty, string newName)
|
||||
{
|
||||
var nameProperty = compositeGroupProperty.FindPropertyRelative("m_Name");
|
||||
nameProperty.stringValue = newName;
|
||||
}
|
||||
|
||||
public static SerializedProperty AddCompositeBinding(SerializedProperty actionProperty, SerializedProperty actionMapProperty,
|
||||
string compositeName, Type compositeType = null, string groups = "", bool addPartBindings = true)
|
||||
{
|
||||
var newProperty = AddBinding(actionProperty, actionMapProperty);
|
||||
newProperty.FindPropertyRelative("m_Name").stringValue = ObjectNames.NicifyVariableName(compositeName);
|
||||
newProperty.FindPropertyRelative("m_Path").stringValue = compositeName;
|
||||
newProperty.FindPropertyRelative("m_Flags").intValue = (int)InputBinding.Flags.Composite;
|
||||
|
||||
if (addPartBindings)
|
||||
{
|
||||
var fields = compositeType.GetFields(BindingFlags.GetField | BindingFlags.Public | BindingFlags.Instance);
|
||||
foreach (var field in fields)
|
||||
{
|
||||
// Skip fields that aren't marked with [InputControl] attribute.
|
||||
if (field.GetCustomAttribute<InputControlAttribute>(false) == null)
|
||||
continue;
|
||||
|
||||
var partProperty = AddBinding(actionProperty, actionMapProperty, groups: groups);
|
||||
partProperty.FindPropertyRelative("m_Name").stringValue = field.Name;
|
||||
partProperty.FindPropertyRelative("m_Flags").intValue = (int)InputBinding.Flags.PartOfComposite;
|
||||
}
|
||||
}
|
||||
|
||||
return newProperty;
|
||||
}
|
||||
|
||||
public static bool IsCompositeBinding(SerializedProperty bindingProperty)
|
||||
{
|
||||
using (var flagsProperty = bindingProperty.FindPropertyRelative("m_Flags"))
|
||||
{
|
||||
var flags = (InputBinding.Flags)flagsProperty.intValue;
|
||||
return (flags & InputBinding.Flags.Composite) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static SerializedProperty ChangeCompositeBindingType(SerializedProperty bindingProperty,
|
||||
NameAndParameters nameAndParameters)
|
||||
{
|
||||
var bindingsArrayProperty = bindingProperty.GetArrayPropertyFromElement();
|
||||
Debug.Assert(bindingsArrayProperty != null, "SerializedProperty is not an array of bindings");
|
||||
var bindingIndex = bindingProperty.GetIndexOfArrayElement();
|
||||
|
||||
Debug.Assert(IsCompositeBinding(bindingProperty),
|
||||
$"Binding {bindingProperty.propertyPath} is not a composite");
|
||||
|
||||
// If the composite still has the default name, change it to the default
|
||||
// one for the new composite type.
|
||||
var pathProperty = bindingProperty.FindPropertyRelative("m_Path");
|
||||
var nameProperty = bindingProperty.FindPropertyRelative("m_Name");
|
||||
if (nameProperty.stringValue ==
|
||||
ObjectNames.NicifyVariableName(NameAndParameters.Parse(pathProperty.stringValue).name))
|
||||
nameProperty.stringValue = ObjectNames.NicifyVariableName(nameAndParameters.name);
|
||||
|
||||
pathProperty.stringValue = nameAndParameters.ToString();
|
||||
|
||||
// Adjust part bindings if we have information on the registered composite. If we don't have
|
||||
// a type, we don't know about the parts. In that case, leave part bindings untouched.
|
||||
var compositeType = InputBindingComposite.s_Composites.LookupTypeRegistration(nameAndParameters.name);
|
||||
if (compositeType != null)
|
||||
{
|
||||
var actionName = bindingProperty.FindPropertyRelative("m_Action").stringValue;
|
||||
|
||||
// Repurpose existing part bindings for the new composite or add any part bindings that
|
||||
// we're missing.
|
||||
var fields = compositeType.GetFields(BindingFlags.GetField | BindingFlags.Public | BindingFlags.Instance);
|
||||
var partIndex = 0;
|
||||
var partBindingsStartIndex = bindingIndex + 1;
|
||||
foreach (var field in fields)
|
||||
{
|
||||
// Skip fields that aren't marked with [InputControl] attribute.
|
||||
if (field.GetCustomAttribute<InputControlAttribute>(false) == null)
|
||||
continue;
|
||||
|
||||
// See if we can reuse an existing part binding.
|
||||
SerializedProperty partProperty = null;
|
||||
if (partBindingsStartIndex + partIndex < bindingsArrayProperty.arraySize)
|
||||
{
|
||||
////REVIEW: this should probably look up part bindings by name rather than going sequentially
|
||||
var element = bindingsArrayProperty.GetArrayElementAtIndex(partBindingsStartIndex + partIndex);
|
||||
if (((InputBinding.Flags)element.FindPropertyRelative("m_Flags").intValue & InputBinding.Flags.PartOfComposite) != 0)
|
||||
partProperty = element;
|
||||
}
|
||||
|
||||
// If not, insert a new binding.
|
||||
if (partProperty == null)
|
||||
{
|
||||
partProperty = AddBindingToBindingArray(bindingsArrayProperty, partBindingsStartIndex + partIndex,
|
||||
flags: InputBinding.Flags.PartOfComposite);
|
||||
}
|
||||
|
||||
// Initialize.
|
||||
partProperty.FindPropertyRelative("m_Name").stringValue = ObjectNames.NicifyVariableName(field.Name);
|
||||
partProperty.FindPropertyRelative("m_Action").stringValue = actionName;
|
||||
++partIndex;
|
||||
}
|
||||
|
||||
////REVIEW: when we allow adding the same part multiple times, we may want to do something smarter here
|
||||
// Delete extraneous part bindings.
|
||||
while (partBindingsStartIndex + partIndex < bindingsArrayProperty.arraySize)
|
||||
{
|
||||
var element = bindingsArrayProperty.GetArrayElementAtIndex(partBindingsStartIndex + partIndex);
|
||||
if (((InputBinding.Flags)element.FindPropertyRelative("m_Flags").intValue & InputBinding.Flags.PartOfComposite) == 0)
|
||||
break;
|
||||
|
||||
bindingsArrayProperty.DeleteArrayElementAtIndex(partBindingsStartIndex + partIndex);
|
||||
// No incrementing of partIndex.
|
||||
}
|
||||
}
|
||||
|
||||
return bindingProperty;
|
||||
}
|
||||
|
||||
public static void ReplaceBindingGroup(SerializedObject asset, string oldBindingGroup, string newBindingGroup, bool deleteOrphanedBindings = false)
|
||||
{
|
||||
var mapArrayProperty = asset.FindProperty("m_ActionMaps");
|
||||
var mapCount = mapArrayProperty.arraySize;
|
||||
|
||||
for (var k = 0; k < mapCount; ++k)
|
||||
{
|
||||
var actionMapProperty = mapArrayProperty.GetArrayElementAtIndex(k);
|
||||
var bindingsArrayProperty = actionMapProperty.FindPropertyRelative("m_Bindings");
|
||||
var bindingsCount = bindingsArrayProperty.arraySize;
|
||||
|
||||
for (var i = 0; i < bindingsCount; ++i)
|
||||
{
|
||||
var bindingProperty = bindingsArrayProperty.GetArrayElementAtIndex(i);
|
||||
var groupsProperty = bindingProperty.FindPropertyRelative("m_Groups");
|
||||
var groups = groupsProperty.stringValue;
|
||||
|
||||
// Ignore bindings not belonging to any control scheme.
|
||||
if (string.IsNullOrEmpty(groups))
|
||||
continue;
|
||||
|
||||
var groupsArray = groups.Split(InputBinding.Separator);
|
||||
var numGroups = groupsArray.LengthSafe();
|
||||
var didRename = false;
|
||||
for (var n = 0; n < numGroups; ++n)
|
||||
{
|
||||
if (string.Compare(groupsArray[n], oldBindingGroup, StringComparison.InvariantCultureIgnoreCase) != 0)
|
||||
continue;
|
||||
if (string.IsNullOrEmpty(newBindingGroup))
|
||||
{
|
||||
ArrayHelpers.EraseAt(ref groupsArray, n);
|
||||
--n;
|
||||
--numGroups;
|
||||
}
|
||||
else
|
||||
groupsArray[n] = newBindingGroup;
|
||||
didRename = true;
|
||||
}
|
||||
if (!didRename)
|
||||
continue;
|
||||
|
||||
if (groupsArray != null)
|
||||
groupsProperty.stringValue = string.Join(InputBinding.kSeparatorString, groupsArray);
|
||||
else
|
||||
{
|
||||
if (deleteOrphanedBindings)
|
||||
{
|
||||
// Binding no long belongs to any binding group. Delete it.
|
||||
bindingsArrayProperty.DeleteArrayElementAtIndex(i);
|
||||
--i;
|
||||
--bindingsCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
groupsProperty.stringValue = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveUnusedBindingGroups(SerializedProperty binding, ReadOnlyArray<InputControlScheme> controlSchemes)
|
||||
{
|
||||
var groupsProperty = binding.FindPropertyRelative(nameof(InputBinding.m_Groups));
|
||||
groupsProperty.stringValue = string.Join(InputBinding.kSeparatorString,
|
||||
groupsProperty.stringValue
|
||||
.Split(InputBinding.Separator)
|
||||
.Where(g => controlSchemes.Any(c => c.bindingGroup.Equals(g, StringComparison.InvariantCultureIgnoreCase))));
|
||||
}
|
||||
|
||||
#region Control Schemes
|
||||
|
||||
public static void DeleteAllControlSchemes(SerializedObject asset)
|
||||
{
|
||||
var schemes = GetControlSchemesArray(asset);
|
||||
while (schemes.arraySize > 0)
|
||||
schemes.DeleteArrayElementAtIndex(0);
|
||||
}
|
||||
|
||||
public static int IndexOfControlScheme(SerializedProperty controlSchemeArray, string controlSchemeName)
|
||||
{
|
||||
var serializedControlScheme = controlSchemeArray.FirstOrDefault(sp =>
|
||||
sp.FindPropertyRelative(nameof(InputControlScheme.m_Name)).stringValue == controlSchemeName);
|
||||
return serializedControlScheme?.GetIndexOfArrayElement() ?? -1;
|
||||
}
|
||||
|
||||
public static SerializedProperty GetControlSchemesArray(SerializedObject asset)
|
||||
{
|
||||
return asset.FindProperty(nameof(InputActionAsset.m_ControlSchemes));
|
||||
}
|
||||
|
||||
#endregion // Control Schemes
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
Reference in New Issue
Block a user