Files
MMORPG/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorState.cs
cooney ce83f21c93 UI 자동화를 위해 바인딩 기능 구현
- 유니티 에셋 인증 오류로 meta 재생성
2026-01-25 01:31:34 +09:00

392 lines
18 KiB
C#

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
namespace UnityEngine.InputSystem.Editor
{
[System.Serializable]
internal class CutElement
{
private Guid id;
internal Type type;
public CutElement(Guid id, Type type)
{
this.id = id;
this.type = type;
}
public int GetIndexOfProperty(InputActionsEditorState state)
{
if (type == typeof(InputActionMap))
{
var actionMap = state.serializedObject
?.FindProperty(nameof(InputActionAsset.m_ActionMaps))
?.FirstOrDefault(s => InputActionSerializationHelpers.GetId(s).Equals(id));
return actionMap.GetIndexOfArrayElement();
}
if (type == typeof(InputAction))
{
var action = Selectors.GetActionMapAtIndex(state, actionMapIndex(state))?.wrappedProperty.FindPropertyRelative("m_Actions").FirstOrDefault(a => InputActionSerializationHelpers.GetId(a).Equals(id));
return action.GetIndexOfArrayElement();
}
if (type == typeof(InputBinding))
{
var binding = Selectors.GetBindingForId(state, id.ToString(),
out _);
return binding.GetIndexOfArrayElement();
}
return -1;
}
public int actionMapIndex(InputActionsEditorState state) => type == typeof(InputActionMap) ? GetIndexOfProperty(state) : GetActionMapIndex(state);
private int GetActionMapIndex(InputActionsEditorState state)
{
var actionMaps = state.serializedObject?.FindProperty(nameof(InputActionAsset.m_ActionMaps));
var cutActionMapIndex = state.serializedObject
?.FindProperty(nameof(InputActionAsset.m_ActionMaps))
?.FirstOrDefault(s => s.FindPropertyRelative("m_Id").stringValue.Equals(id)).GetIndexOfArrayElement();
if (type == typeof(InputBinding))
cutActionMapIndex = actionMaps.FirstOrDefault(map => map.FindPropertyRelative("m_Bindings").Select(InputActionSerializationHelpers.GetId).Contains(id)).GetIndexOfArrayElement();
else if (type == typeof(InputAction))
cutActionMapIndex = actionMaps.FirstOrDefault(map => map.FindPropertyRelative("m_Actions").Select(InputActionSerializationHelpers.GetId).Contains(id)).GetIndexOfArrayElement();
return cutActionMapIndex ?? -1;
}
}
internal struct InputActionsEditorState
{
public int selectedActionMapIndex { get {return m_selectedActionMapIndex; } }
public int selectedActionIndex { get {return m_selectedActionIndex; } }
public int selectedBindingIndex { get {return m_selectedBindingIndex; } }
public SelectionType selectionType { get {return m_selectionType; } }
public SerializedObject serializedObject { get; } // Note that state doesn't own this disposable object
private readonly List<CutElement> cutElements => m_CutElements;
// Control schemes
public int selectedControlSchemeIndex { get { return m_selectedControlSchemeIndex; } }
public int selectedDeviceRequirementIndex { get {return m_selectedDeviceRequirementIndex; } }
public InputControlScheme selectedControlScheme => m_ControlScheme; // TODO Bad this either po
internal InputActionsEditorSessionAnalytic m_Analytics;
[SerializeField] int m_selectedActionMapIndex;
[SerializeField] int m_selectedActionIndex;
[SerializeField] int m_selectedBindingIndex;
[SerializeField] SelectionType m_selectionType;
[SerializeField] int m_selectedControlSchemeIndex;
[SerializeField] int m_selectedDeviceRequirementIndex;
private List<CutElement> m_CutElements;
internal bool hasCutElements => m_CutElements != null && m_CutElements.Count > 0;
public InputActionsEditorState(
InputActionsEditorSessionAnalytic analytics,
SerializedObject inputActionAsset,
int selectedActionMapIndex = 0,
int selectedActionIndex = 0,
int selectedBindingIndex = 0,
SelectionType selectionType = SelectionType.Action,
InputControlScheme selectedControlScheme = default,
int selectedControlSchemeIndex = -1,
int selectedDeviceRequirementIndex = -1,
List<CutElement> cutElements = null)
{
Debug.Assert(inputActionAsset != null);
m_Analytics = analytics;
serializedObject = inputActionAsset;
m_selectedActionMapIndex = selectedActionMapIndex;
m_selectedActionIndex = selectedActionIndex;
m_selectedBindingIndex = selectedBindingIndex;
m_selectionType = selectionType;
m_ControlScheme = selectedControlScheme;
m_selectedControlSchemeIndex = selectedControlSchemeIndex;
m_selectedDeviceRequirementIndex = selectedDeviceRequirementIndex;
m_CutElements = cutElements;
}
public InputActionsEditorState(InputActionsEditorState other, SerializedObject asset)
{
m_Analytics = other.m_Analytics;
// Assign serialized object, not that this might be equal to other.serializedObject,
// a slight variation of it with any kind of changes or a completely different one.
// Hence, we do our best here to keep any selections consistent by remapping objects
// based on GUIDs (IDs) and when it fails, attempt to select first object and if that
// fails revert to not having a selection. This would even be true for domain reloads
// if the asset would be modified during domain reload.
serializedObject = asset;
if (other.Equals(default(InputActionsEditorState)))
{
// This instance was created by default constructor and thus is missing some appropriate defaults:
other.m_selectionType = SelectionType.Action;
other.m_selectedControlSchemeIndex = -1;
other.m_selectedDeviceRequirementIndex = -1;
}
// Attempt to preserve action map selection by GUID, otherwise select first or last resort none
var otherSelectedActionMap = other.GetSelectedActionMap();
var actionMapCount = Selectors.GetActionMapCount(asset);
m_selectedActionMapIndex = otherSelectedActionMap != null
? Selectors.GetActionMapIndexFromId(asset,
InputActionSerializationHelpers.GetId(otherSelectedActionMap))
: actionMapCount > 0 ? 0 : -1;
var selectedActionMap = m_selectedActionMapIndex >= 0
? Selectors.GetActionMapAtIndex(asset, m_selectedActionMapIndex)?.wrappedProperty : null;
// Attempt to preserve action selection by GUID, otherwise select first or last resort none
var otherSelectedAction = m_selectedActionMapIndex >= 0 ?
Selectors.GetSelectedAction(other) : null;
m_selectedActionIndex = selectedActionMap != null && otherSelectedAction.HasValue
? Selectors.GetActionIndexFromId(selectedActionMap,
InputActionSerializationHelpers.GetId(otherSelectedAction.Value.wrappedProperty))
: Selectors.GetActionCount(selectedActionMap) > 0 ? 0 : -1;
// Attempt to preserve binding selection by GUID, otherwise select first or none
m_selectedBindingIndex = -1;
if (m_selectedActionMapIndex >= 0)
{
var otherSelectedBinding = Selectors.GetSelectedBinding(other);
if (otherSelectedBinding != null)
{
var otherSelectedBindingId =
InputActionSerializationHelpers.GetId(otherSelectedBinding.Value.wrappedProperty);
var binding = Selectors.GetBindingForId(asset, otherSelectedBindingId.ToString(), out _);
if (binding != null)
m_selectedBindingIndex = binding.GetIndexOfArrayElement();
}
}
// Sanity check selection type and override any previous selection if not valid given indices
// since we have remapped GUIDs to selection indices for another asset (SerializedObject)
if (other.m_selectionType == SelectionType.Binding && m_selectedBindingIndex < 0)
m_selectionType = SelectionType.Action;
else
m_selectionType = other.m_selectionType;
m_selectedControlSchemeIndex = other.m_selectedControlSchemeIndex;
m_selectedDeviceRequirementIndex = other.m_selectedDeviceRequirementIndex;
// Selected ControlScheme index is serialized but we have to recreated actual object after domain reload.
// In case asset is different from from others asset the index might not even be valid range so we need
// to reattempt to preserve selection but range adapt.
// Note that control schemes and device requirements currently lack any GUID/ID to be uniquely identified.
var controlSchemesArrayProperty = serializedObject.FindProperty(nameof(InputActionAsset.m_ControlSchemes));
if (m_selectedControlSchemeIndex >= 0 && controlSchemesArrayProperty.arraySize > 0)
{
if (m_selectedControlSchemeIndex >= controlSchemesArrayProperty.arraySize)
m_selectedControlSchemeIndex = 0;
m_ControlScheme = new InputControlScheme(
controlSchemesArrayProperty.GetArrayElementAtIndex(other.m_selectedControlSchemeIndex));
// TODO Preserve device requirement index
}
else
{
m_selectedControlSchemeIndex = -1;
m_selectedDeviceRequirementIndex = -1;
m_ControlScheme = new InputControlScheme();
}
m_CutElements = other.cutElements;
}
public InputActionsEditorState With(
int? selectedActionMapIndex = null,
int? selectedActionIndex = null,
int? selectedBindingIndex = null,
SelectionType? selectionType = null,
InputControlScheme? selectedControlScheme = null,
int? selectedControlSchemeIndex = null,
int? selectedDeviceRequirementIndex = null,
List<CutElement> cutElements = null)
{
return new InputActionsEditorState(
m_Analytics,
serializedObject,
selectedActionMapIndex ?? this.selectedActionMapIndex,
selectedActionIndex ?? this.selectedActionIndex,
selectedBindingIndex ?? this.selectedBindingIndex,
selectionType ?? this.selectionType,
// Control schemes
selectedControlScheme ?? this.selectedControlScheme,
selectedControlSchemeIndex ?? this.selectedControlSchemeIndex,
selectedDeviceRequirementIndex ?? this.selectedDeviceRequirementIndex,
cutElements ?? m_CutElements
);
}
public InputActionsEditorState ClearCutElements()
{
return new InputActionsEditorState(
m_Analytics,
serializedObject,
selectedActionMapIndex,
selectedActionIndex,
selectedBindingIndex,
selectionType,
selectedControlScheme,
selectedControlSchemeIndex,
selectedDeviceRequirementIndex,
cutElements: null);
}
public SerializedProperty GetActionMapByName(string actionMapName)
{
return serializedObject
.FindProperty(nameof(InputActionAsset.m_ActionMaps))
.FirstOrDefault(p => p.FindPropertyRelative(nameof(InputActionMap.m_Name)).stringValue == actionMapName);
}
public InputActionsEditorState SelectAction(string actionName)
{
var actionMap = GetSelectedActionMap();
var actions = actionMap.FindPropertyRelative(nameof(InputActionMap.m_Actions));
for (var i = 0; i < actions.arraySize; i++)
{
if (actions.GetArrayElementAtIndex(i)
.FindPropertyRelative(nameof(InputAction.m_Name)).stringValue != actionName) continue;
return With(selectedActionIndex: i, selectionType: SelectionType.Action);
}
// If we cannot find the desired map we should return invalid index
return With(selectedActionIndex: -1, selectionType: SelectionType.Action);
}
public InputActionsEditorState SelectAction(SerializedProperty state)
{
var index = state.GetIndexOfArrayElement();
return With(selectedActionIndex: index, selectionType: SelectionType.Action);
}
public InputActionsEditorState SelectActionMap(SerializedProperty actionMap)
{
var index = actionMap.GetIndexOfArrayElement();
return With(selectedBindingIndex: 0, selectedActionMapIndex: index, selectedActionIndex: 0);
}
public InputActionsEditorState SelectActionMap(string actionMapName)
{
var actionMap = GetActionMapByName(actionMapName);
return With(selectedBindingIndex: 0,
selectedActionMapIndex: actionMap.GetIndexOfArrayElement(),
selectedActionIndex: 0, selectionType: SelectionType.Action);
}
public InputActionsEditorState SelectBinding(int index)
{
//if no binding selected (due to no bindings in list) set selection type to action
if (index == -1)
return With(selectedBindingIndex: index, selectionType: SelectionType.Action);
return With(selectedBindingIndex: index, selectionType: SelectionType.Binding);
}
public InputActionsEditorState SelectAction(int index)
{
//if no action selected (no actions available) set selection type to none
if (index == -1)
return With(selectedActionIndex: index, selectionType: SelectionType.None);
return With(selectedActionIndex: index, selectionType: SelectionType.Action);
}
public InputActionsEditorState SelectActionMap(int index)
{
if (index == -1)
return With(selectedActionMapIndex: index, selectionType: SelectionType.None);
return With(selectedBindingIndex: 0,
selectedActionMapIndex: index,
selectedActionIndex: 0, selectionType: SelectionType.Action);
}
public InputActionsEditorState CutActionOrBinding()
{
m_CutElements = new List<CutElement>();
var type = selectionType == SelectionType.Action ? typeof(InputAction) : typeof(InputBinding);
var property = selectionType == SelectionType.Action ? Selectors.GetSelectedAction(this)?.wrappedProperty : Selectors.GetSelectedBinding(this)?.wrappedProperty;
cutElements.Add(new CutElement(InputActionSerializationHelpers.GetId(property), type));
return With(cutElements: cutElements);
}
public InputActionsEditorState CutActionMaps()
{
m_CutElements = new List<CutElement> { new(InputActionSerializationHelpers.GetId(Selectors.GetSelectedActionMap(this)?.wrappedProperty), typeof(InputActionMap)) };
return With(cutElements: cutElements);
}
public IEnumerable<string> GetDisabledActionMaps(List<string> allActionMaps)
{
if (cutElements == null || cutElements == null)
return Enumerable.Empty<string>();
var cutActionMaps = cutElements.Where(cut => cut.type == typeof(InputActionMap));
var state = this;
return allActionMaps.Where(actionMapName =>
{
return cutActionMaps.Any(am => am.GetIndexOfProperty(state) == allActionMaps.IndexOf(actionMapName));
});
}
public readonly bool IsBindingCut(int actionMapIndex, int bindingIndex)
{
if (cutElements == null)
return false;
var state = this;
return cutElements.Any(cutElement => cutElement.actionMapIndex(state) == actionMapIndex &&
cutElement.GetIndexOfProperty(state) == bindingIndex &&
cutElement.type == typeof(InputBinding));
}
public readonly bool IsActionCut(int actionMapIndex, int actionIndex)
{
if (cutElements == null)
return false;
var state = this;
return cutElements.Any(cutElement => cutElement.actionMapIndex(state) == actionMapIndex &&
cutElement.GetIndexOfProperty(state) == actionIndex &&
cutElement.type == typeof(InputAction));
}
public readonly bool IsActionMapCut(int actionMapIndex)
{
if (cutElements == null)
return false;
var state = this;
return cutElements.Any(cutElement => cutElement.GetIndexOfProperty(state) == actionMapIndex && cutElement.type == typeof(InputActionMap));
}
public readonly List<CutElement> GetCutElements()
{
return m_CutElements;
}
private SerializedProperty GetSelectedActionMap()
{
return Selectors.GetActionMapAtIndex(serializedObject, selectedActionMapIndex)?.wrappedProperty;
}
private readonly InputControlScheme m_ControlScheme;
}
internal enum SelectionType
{
None,
Action,
Binding
}
}
#endif