| | #if MLA_INPUT_SYSTEM |
| | using System; |
| | using System.Collections.Generic; |
| | using Unity.Collections; |
| | using Unity.MLAgents.Actuators; |
| | using Unity.MLAgents.Policies; |
| | using UnityEngine; |
| | using UnityEngine.Assertions; |
| | using UnityEngine.InputSystem; |
| | using UnityEngine.InputSystem.Controls; |
| | using UnityEngine.InputSystem.LowLevel; |
| | using UnityEngine.InputSystem.Layouts; |
| | using UnityEngine.InputSystem.Utilities; |
| | #if UNITY_EDITOR |
| | using UnityEditor; |
| | #endif |
| |
|
| | namespace Unity.MLAgents.Extensions.Input |
| | { |
| | |
| | |
| | |
| | |
| | [RequireComponent(typeof(PlayerInput), typeof(IInputActionAssetProvider))] |
| | [AddComponentMenu("ML Agents/Input Actuator", (int)MenuGroup.Actuators)] |
| | public class InputActuatorComponent : ActuatorComponent |
| | { |
| | InputActionAsset m_InputAsset; |
| | IInputActionCollection2 m_AssetCollection; |
| | PlayerInput m_PlayerInput; |
| | BehaviorParameters m_BehaviorParameters; |
| | IActuator[] m_Actuators; |
| | InputDevice m_Device; |
| |
|
| | |
| | |
| | |
| | public static Dictionary<Type, Type> controlTypeToAdaptorType = new Dictionary<Type, Type> |
| | { |
| | { typeof(Vector2Control), typeof(Vector2InputActionAdaptor) }, |
| | { typeof(ButtonControl), typeof(ButtonInputActionAdaptor) }, |
| | { typeof(IntegerControl), typeof(IntegerInputActionAdaptor) }, |
| | { typeof(AxisControl), typeof(FloatInputActionAdaptor) }, |
| | { typeof(DoubleControl), typeof(DoubleInputActionAdaptor) } |
| | }; |
| |
|
| | string m_LayoutName; |
| | [SerializeField] |
| | ActionSpec m_ActionSpec; |
| | InputControlScheme m_ControlScheme; |
| |
|
| | public const string mlAgentsLayoutFormat = "MLAT"; |
| | public const string mlAgentsLayoutName = "MLAgentsLayout"; |
| | public const string mlAgentsControlSchemeName = "ml-agents"; |
| |
|
| | |
| | public override ActionSpec ActionSpec |
| | { |
| | get |
| | { |
| | #if UNITY_EDITOR |
| | if (!EditorApplication.isPlaying && m_ActionSpec.NumContinuousActions == 0 |
| | && m_ActionSpec.BranchSizes == null |
| | || m_ActionSpec.BranchSizes.Length == 0) |
| | { |
| | FindNeededComponents(); |
| | var actuators = CreateActuatorsFromMap(m_InputAsset.FindActionMap(m_PlayerInput.defaultActionMap), |
| | m_BehaviorParameters, |
| | null, |
| | InputActuatorEventContext.s_EditorContext); |
| | m_ActionSpec = CombineActuatorActionSpecs(actuators); |
| | } |
| | #endif |
| | return m_ActionSpec; |
| | } |
| | } |
| |
|
| | void OnDisable() |
| | { |
| | CleanupActionAsset(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public override IActuator[] CreateActuators() |
| | { |
| | FindNeededComponents(); |
| | var collection = m_AssetCollection ?? m_InputAsset; |
| | collection.Disable(); |
| | var inputActionMap = m_InputAsset.FindActionMap(m_PlayerInput.defaultActionMap); |
| |
|
| | RegisterLayoutBuilder(inputActionMap, m_LayoutName); |
| | m_Device = InputSystem.AddDevice(m_LayoutName); |
| |
|
| | var context = new InputActuatorEventContext(inputActionMap.actions.Count, m_Device); |
| | m_Actuators = CreateActuatorsFromMap(inputActionMap, m_BehaviorParameters, m_Device, context); |
| |
|
| | UpdateDeviceBinding(m_BehaviorParameters.IsInHeuristicMode()); |
| | inputActionMap.Enable(); |
| |
|
| | m_ActionSpec = CombineActuatorActionSpecs(m_Actuators); |
| | collection.Enable(); |
| | return m_Actuators; |
| | } |
| |
|
| | static ActionSpec CombineActuatorActionSpecs(IActuator[] actuators) |
| | { |
| | var specs = new ActionSpec[actuators.Length]; |
| | for (var i = 0; i < actuators.Length; i++) |
| | { |
| | specs[i] = actuators[i].ActionSpec; |
| | } |
| | return ActionSpec.Combine(specs); |
| | } |
| |
|
| | internal static IActuator[] CreateActuatorsFromMap(InputActionMap inputActionMap, |
| | BehaviorParameters behaviorParameters, |
| | InputDevice inputDevice, |
| | InputActuatorEventContext context) |
| | { |
| | var actuators = new IActuator[inputActionMap.actions.Count]; |
| | for (var i = 0; i < inputActionMap.actions.Count; i++) |
| | { |
| | var action = inputActionMap.actions[i]; |
| | var actionLayout = InputSystem.LoadLayout(action.expectedControlType); |
| | var adaptor = (IRLActionInputAdaptor)Activator.CreateInstance(controlTypeToAdaptorType[actionLayout.type]); |
| | actuators[i] = new InputActionActuator(inputDevice, behaviorParameters, action, adaptor, context); |
| |
|
| | |
| | |
| | var path = $"{inputDevice?.path}{InputControlPath.Separator}{action.name}"; |
| | action.AddBinding(path, |
| | action.interactions, |
| | action.processors, |
| | mlAgentsControlSchemeName); |
| | action.bindingMask = InputBinding.MaskByGroup(mlAgentsControlSchemeName); |
| | } |
| | return actuators; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | internal void UpdateDeviceBinding(bool isInHeuristicMode) |
| | { |
| | if (ReferenceEquals(m_Device, null)) |
| | { |
| | return; |
| | } |
| | var collection = m_AssetCollection ?? m_InputAsset; |
| | m_ControlScheme = CreateControlScheme(m_Device, isInHeuristicMode, m_InputAsset); |
| | if (m_InputAsset.FindControlSchemeIndex(m_ControlScheme.name) != -1) |
| | { |
| | m_InputAsset.RemoveControlScheme(m_ControlScheme.name); |
| | } |
| |
|
| | if (!isInHeuristicMode) |
| | { |
| | var inputActionMap = m_InputAsset.FindActionMap(m_PlayerInput.defaultActionMap); |
| | m_InputAsset.AddControlScheme(m_ControlScheme); |
| | collection.bindingMask = InputBinding.MaskByGroup(m_ControlScheme.bindingGroup); |
| | collection.devices = new ReadOnlyArray<InputDevice>(new[] { m_Device }); |
| | inputActionMap.bindingMask = collection.bindingMask; |
| | inputActionMap.devices = collection.devices; |
| | } |
| | else |
| | { |
| | var inputActionMap = m_InputAsset.FindActionMap(m_PlayerInput.defaultActionMap); |
| | collection.bindingMask = null; |
| | collection.devices = InputSystem.devices; |
| | inputActionMap.devices = InputSystem.devices; |
| | inputActionMap.bindingMask = null; |
| | } |
| | collection.Enable(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | internal static InputControlScheme CreateControlScheme(InputControl device, |
| | bool isInHeuristicMode, |
| | InputActionAsset asset) |
| | { |
| | var deviceRequirements = new List<InputControlScheme.DeviceRequirement> |
| | { |
| | new InputControlScheme.DeviceRequirement |
| | { |
| | controlPath = InputBinding.Separator + mlAgentsLayoutName |
| | } |
| | }; |
| |
|
| | if (isInHeuristicMode) |
| | { |
| | for (var i = 0; i < asset.controlSchemes.Count; i++) |
| | { |
| | var scheme = asset.controlSchemes[i]; |
| | for (var ii = 0; ii < scheme.deviceRequirements.Count; ii++) |
| | { |
| | deviceRequirements.Add(scheme.deviceRequirements[ii]); |
| | } |
| | } |
| | } |
| |
|
| | var inputControlScheme = new InputControlScheme( |
| | mlAgentsControlSchemeName, |
| | deviceRequirements); |
| |
|
| | return inputControlScheme; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | internal static void RegisterLayoutBuilder(InputActionMap defaultMap, string layoutName) |
| | { |
| | if (InputSystem.LoadLayout(layoutName) == null) |
| | { |
| | InputSystem.RegisterLayoutBuilder(() => |
| | { |
| | |
| | var builder = new InputControlLayout.Builder() |
| | .WithName(layoutName) |
| | .WithFormat(mlAgentsLayoutFormat); |
| | for (var i = 0; i < defaultMap.actions.Count; i++) |
| | { |
| | var action = defaultMap.actions[i]; |
| | builder.AddControl(action.name) |
| | .WithLayout(action.expectedControlType); |
| | } |
| | return builder.Build(); |
| | }, layoutName); |
| | } |
| | } |
| |
|
| | internal void FindNeededComponents() |
| | { |
| | if (m_InputAsset == null) |
| | { |
| | var assetProvider = GetComponent<IInputActionAssetProvider>(); |
| | Assert.IsNotNull(assetProvider); |
| | (m_InputAsset, m_AssetCollection) = assetProvider.GetInputActionAsset(); |
| | Assert.IsNotNull(m_InputAsset, "An InputActionAsset could not be found on IInputActionAssetProvider or PlayerInput."); |
| | } |
| | if (m_PlayerInput == null) |
| | { |
| | m_PlayerInput = GetComponent<PlayerInput>(); |
| | Assert.IsNotNull(m_PlayerInput, "PlayerInput component could not be found on this GameObject."); |
| | } |
| |
|
| | if (m_BehaviorParameters == null) |
| | { |
| | m_BehaviorParameters = GetComponent<BehaviorParameters>(); |
| | Assert.IsNotNull(m_BehaviorParameters, "BehaviorParameters were not on the current GameObject."); |
| | m_BehaviorParameters.OnPolicyUpdated += UpdateDeviceBinding; |
| | m_LayoutName = mlAgentsLayoutName + m_BehaviorParameters.BehaviorName; |
| | } |
| | } |
| |
|
| | internal void CleanupActionAsset() |
| | { |
| | InputSystem.RemoveLayout(mlAgentsLayoutName); |
| | if (!ReferenceEquals(m_Device, null)) |
| | { |
| | InputSystem.RemoveDevice(m_Device); |
| | } |
| |
|
| | if (!ReferenceEquals(m_InputAsset, null) |
| | && m_InputAsset.FindControlSchemeIndex(mlAgentsControlSchemeName) != -1) |
| | { |
| | m_InputAsset.RemoveControlScheme(mlAgentsControlSchemeName); |
| | } |
| |
|
| | if (m_Actuators != null) |
| | { |
| | Array.Clear(m_Actuators, 0, m_Actuators.Length); |
| | } |
| |
|
| | if (!ReferenceEquals(m_BehaviorParameters, null)) |
| | { |
| | m_BehaviorParameters.OnPolicyUpdated -= UpdateDeviceBinding; |
| | } |
| |
|
| | m_InputAsset = null; |
| | m_PlayerInput = null; |
| | m_BehaviorParameters = null; |
| | m_Device = null; |
| | } |
| |
|
| | int m_ActuatorsWrittenToEvent; |
| | NativeArray<byte> m_InputBufferForFrame; |
| | InputEventPtr m_InputEventPtrForFrame; |
| | public InputEventPtr GetEventForFrame() |
| | { |
| | #if UNITY_EDITOR |
| | if (!EditorApplication.isPlaying) |
| | { |
| | return new InputEventPtr(); |
| | } |
| | #endif |
| | if (m_ActuatorsWrittenToEvent % m_Actuators.Length == 0 || !m_InputEventPtrForFrame.valid) |
| | { |
| | m_ActuatorsWrittenToEvent = 0; |
| | m_InputEventPtrForFrame = new InputEventPtr(); |
| | m_InputBufferForFrame = StateEvent.From(m_Device, out m_InputEventPtrForFrame); |
| | } |
| |
|
| | return m_InputEventPtrForFrame; |
| | } |
| |
|
| | public void EventProcessedInFrame() |
| | { |
| | #if UNITY_EDITOR |
| | if (!EditorApplication.isPlaying) |
| | { |
| | return; |
| | } |
| | #endif |
| | m_ActuatorsWrittenToEvent++; |
| | if (m_ActuatorsWrittenToEvent == m_Actuators.Length && m_InputEventPtrForFrame.valid) |
| | { |
| | InputSystem.QueueEvent(m_InputEventPtrForFrame); |
| | m_InputBufferForFrame.Dispose(); |
| | } |
| | } |
| | } |
| | } |
| | #endif // MLA_INPUT_SYSTEM |
| |
|