Maze / Library /PackageCache /com.unity.inputsystem@1.6.1 /Samples~ /Visualizers /InputActionVisualizer.cs
| using System; | |
| using System.Collections.Generic; | |
| ////TODO: support ProcessEventsManually | |
| ////TODO: add way to pick by player index | |
| // Some fields assigned through only through serialization. | |
| namespace UnityEngine.InputSystem.Samples | |
| { | |
| /// <summary> | |
| /// A component for debugging purposes that adds an on-screen display which shows | |
| /// activity on an input action over time (<see cref="InputActionVisualizer.Visualization.Interaction"/>) | |
| /// or an action's current value (<see cref="InputActionVisualizer.Visualization.Value"/>). | |
| /// </summary> | |
| /// <seealso cref="InputControlVisualizer"/> | |
| [] | |
| [] | |
| public class InputActionVisualizer : InputVisualizer | |
| { | |
| /// <summary> | |
| /// The action that is being visualized. May be null. | |
| /// </summary> | |
| public InputAction action => m_Action; | |
| protected void FixedUpdate() | |
| { | |
| if (m_Visualization != Visualization.Value || m_Action == null || m_Visualizer == null) | |
| return; | |
| if (InputSystem.settings.updateMode != InputSettings.UpdateMode.ProcessEventsInFixedUpdate) | |
| return; | |
| RecordValue(Time.fixedTime); | |
| } | |
| protected void Update() | |
| { | |
| if (m_Visualization != Visualization.Value || m_Action == null || m_Visualizer == null) | |
| return; | |
| if (InputSystem.settings.updateMode != InputSettings.UpdateMode.ProcessEventsInDynamicUpdate) | |
| return; | |
| RecordValue(Time.time); | |
| } | |
| protected new void OnEnable() | |
| { | |
| if (m_Visualization == Visualization.None) | |
| return; | |
| base.OnEnable(); | |
| ResolveAction(); | |
| SetupVisualizer(); | |
| if (s_EnabledInstances == null) | |
| s_EnabledInstances = new List<InputActionVisualizer>(); | |
| if (s_EnabledInstances.Count == 0) | |
| InputSystem.onActionChange += OnActionChange; | |
| s_EnabledInstances.Add(this); | |
| } | |
| protected new void OnDisable() | |
| { | |
| base.OnDisable(); | |
| s_EnabledInstances.Remove(this); | |
| if (s_EnabledInstances.Count == 0) | |
| InputSystem.onActionChange -= OnActionChange; | |
| if (m_Visualization == Visualization.Interaction && m_Action != null) | |
| { | |
| m_Action.started -= OnActionTriggered; | |
| m_Action.performed -= OnActionTriggered; | |
| m_Action.canceled -= OnActionTriggered; | |
| } | |
| } | |
| protected new void OnGUI() | |
| { | |
| if (m_Visualization == Visualization.None) | |
| return; | |
| if (Event.current.type != EventType.Repaint) | |
| return; | |
| base.OnGUI(); | |
| if (m_ShowControlName && m_ActiveControlName != null) | |
| VisualizationHelpers.DrawText(m_ActiveControlName, new Vector2(m_Rect.x, m_Rect.yMax), | |
| VisualizationHelpers.ValueTextStyle); | |
| } | |
| private void RecordValue(double time) | |
| { | |
| Debug.Assert(m_Action != null); | |
| Debug.Assert(m_Visualizer != null); | |
| var value = m_Action.ReadValueAsObject(); | |
| m_Visualizer.AddSample(value, time); | |
| if (m_ShowControlName) | |
| RecordControlName(); | |
| } | |
| private void RecordControlName() | |
| { | |
| var control = m_Action.activeControl; | |
| if (control == m_ActiveControl) | |
| return; | |
| m_ActiveControl = control; | |
| m_ActiveControlName = control != null ? new GUIContent(control.path) : null; | |
| } | |
| private void ResolveAction() | |
| { | |
| // If we have a reference to an action, try that first. | |
| if (m_ActionReference != null) | |
| m_Action = m_ActionReference.action; | |
| // If we didn't get an action from that but we have an action name, | |
| // just search through the currently enabled actions for one that | |
| // matches by name. | |
| if (m_Action == null && !string.IsNullOrEmpty(m_ActionName)) | |
| { | |
| var slashIndex = m_ActionName.IndexOf('/'); | |
| var mapName = slashIndex != -1 ? m_ActionName.Substring(0, slashIndex) : null; | |
| var actionName = slashIndex != -1 ? m_ActionName.Substring(slashIndex + 1) : m_ActionName; | |
| var enabledActions = InputSystem.ListEnabledActions(); | |
| foreach (var action in enabledActions) | |
| { | |
| if (string.Compare(actionName, action.name, StringComparison.InvariantCultureIgnoreCase) != 0) | |
| continue; | |
| if (mapName != null && action.actionMap != null && string.Compare(mapName, action.actionMap.name, | |
| StringComparison.InvariantCultureIgnoreCase) != 0) | |
| continue; | |
| m_Action = action; | |
| break; | |
| } | |
| } | |
| // If we still don't have an action, there's nothing much for us to do. | |
| // The action may show up at a later point. | |
| if (m_Action == null) | |
| return; | |
| if (m_Visualization == Visualization.Interaction) | |
| { | |
| m_Action.performed += OnActionTriggered; | |
| m_Action.started += OnActionTriggered; | |
| m_Action.canceled += OnActionTriggered; | |
| } | |
| } | |
| private void SetupVisualizer() | |
| { | |
| m_Visualizer = null; | |
| if (m_Action == null) | |
| return; | |
| switch (m_Visualization) | |
| { | |
| case Visualization.Value: | |
| switch (m_Action.type) | |
| { | |
| case InputActionType.Button: | |
| m_Visualizer = new VisualizationHelpers.ScalarVisualizer<float> | |
| { | |
| limitMax = 1 | |
| }; | |
| break; | |
| case InputActionType.Value: | |
| case InputActionType.PassThrough: | |
| if (!string.IsNullOrEmpty(m_Action.expectedControlType)) | |
| { | |
| var layout = InputSystem.LoadLayout(m_Action.expectedControlType); | |
| if (layout != null) | |
| { | |
| var valueType = layout.GetValueType(); | |
| if (valueType == typeof(float)) | |
| m_Visualizer = new VisualizationHelpers.ScalarVisualizer<float> | |
| { | |
| limitMax = 1 | |
| }; | |
| else if (valueType == typeof(int)) | |
| m_Visualizer = new VisualizationHelpers.ScalarVisualizer<int> | |
| { | |
| limitMax = 1 | |
| }; | |
| else if (valueType == typeof(Vector2)) | |
| m_Visualizer = new VisualizationHelpers.Vector2Visualizer(); | |
| } | |
| } | |
| break; | |
| } | |
| break; | |
| case Visualization.Interaction: | |
| // We don't really know which interactions are sitting on the action and its bindings | |
| // and while we could do and perform work to find out, it's simpler to just wait until | |
| // we get input and then whatever interactions we encounter as we go along. Also keeps | |
| // the visualization a little less cluttered. | |
| m_Visualizer = new VisualizationHelpers.TimelineVisualizer(); | |
| break; | |
| } | |
| } | |
| private void OnActionDisabled() | |
| { | |
| } | |
| private void OnActionTriggered(InputAction.CallbackContext context) | |
| { | |
| Debug.Assert(m_Visualization == Visualization.Interaction); | |
| var timelineName = "Default"; | |
| var interaction = context.interaction; | |
| if (interaction != null) | |
| { | |
| timelineName = interaction.GetType().Name; | |
| if (timelineName.EndsWith("Interaction")) | |
| timelineName = timelineName.Substring(0, timelineName.Length - "Interaction".Length); | |
| } | |
| var visualizer = (VisualizationHelpers.TimelineVisualizer)m_Visualizer; | |
| var timelineIndex = visualizer.GetTimeline(timelineName); | |
| if (timelineIndex == -1) | |
| { | |
| Color color; | |
| timelineIndex = visualizer.timelineCount; | |
| if (timelineIndex < s_InteractionColors.Length) | |
| color = s_InteractionColors[timelineIndex]; | |
| else | |
| color = new Color(Random.value, Random.value, Random.value, 1); | |
| visualizer.AddTimeline(timelineName, color); | |
| if (timelineIndex > 0) | |
| visualizer.showLegend = true; | |
| } | |
| var time = (float)context.time; | |
| switch (context.phase) | |
| { | |
| case InputActionPhase.Canceled: | |
| visualizer.AddSample(timelineIndex, 0f, time); | |
| break; | |
| case InputActionPhase.Performed: | |
| visualizer.AddSample(timelineIndex, 1f, time); | |
| visualizer.AddSample(timelineIndex, 0f, time); | |
| break; | |
| case InputActionPhase.Started: | |
| visualizer.AddSample(timelineIndex, 0.5f, time); | |
| break; | |
| } | |
| if (m_ShowControlName) | |
| RecordControlName(); | |
| } | |
| private static void OnActionChange(object actionOrMap, InputActionChange change) | |
| { | |
| switch (change) | |
| { | |
| case InputActionChange.ActionEnabled: | |
| case InputActionChange.ActionMapEnabled: | |
| for (var i = 0; i < s_EnabledInstances.Count; ++i) | |
| if (s_EnabledInstances[i].m_Action == null) | |
| { | |
| s_EnabledInstances[i].ResolveAction(); | |
| if (s_EnabledInstances[i].m_Action != null) | |
| s_EnabledInstances[i].SetupVisualizer(); | |
| } | |
| break; | |
| case InputActionChange.ActionDisabled: | |
| for (var i = 0; i < s_EnabledInstances.Count; ++i) | |
| if (actionOrMap == s_EnabledInstances[i].m_Action) | |
| s_EnabledInstances[i].OnActionDisabled(); | |
| break; | |
| case InputActionChange.ActionMapDisabled: | |
| for (var i = 0; i < s_EnabledInstances.Count; ++i) | |
| if (s_EnabledInstances[i].m_Action?.actionMap == actionOrMap) | |
| s_EnabledInstances[i].OnActionDisabled(); | |
| break; | |
| } | |
| } | |
| [] private Visualization m_Visualization; | |
| [] private InputActionReference m_ActionReference; | |
| [] private string m_ActionName; | |
| [] private bool m_ShowControlName; | |
| [] private InputAction m_Action; | |
| [] private InputControl m_ActiveControl; | |
| [] private GUIContent m_ActiveControlName; | |
| private static List<InputActionVisualizer> s_EnabledInstances; | |
| private static readonly Color[] s_InteractionColors = | |
| { | |
| new Color(1, 0, 0, 1), | |
| new Color(0, 0, 1, 1), | |
| new Color(1, 1, 0, 1), | |
| new Color(1, 0, 1, 1), | |
| new Color(0, 1, 1, 1), | |
| new Color(0, 1, 0, 1), | |
| }; | |
| public enum Visualization | |
| { | |
| None, | |
| Value, | |
| Interaction, | |
| } | |
| } | |
| } | |