using Unity.MLAgents.Extensions.Input; using UnityEngine; using UnityEngine.InputSystem; /// /// This class handles the input for the PushBlock Cube character in the PushBlock scene. /// Note that the only ML-Agents code here is the implementation of the . /// The looks for a component that implements that interface in order to /// rebind actions to virtual controllers when training agents or running inference. This means that you can /// keep your input handling code separate from ML-Agents, and have your agent's action space defined by the /// actions defined in your project's . /// /// If you don't implement the will /// look for a component on the GameObject it live on. It will rebind the actions of that /// instance of the asset. /// /// It is important to note that if you have multiple components on the same GameObject handling input, you will /// need to share the instance of the generated C# (named /// here) in order to ensure that all of your actions are bound correctly for ml-agents training and inference. /// public class PushBlockWithInputPlayerController : MonoBehaviour, IInputActionAssetProvider { PushBlockWithInputSettings m_PushBlockSettings; public float JumpTime = 0.5f; float m_JumpTimeRemaining; Rigidbody m_PlayerRb; //cached on initialization PushBlockActions m_PushBlockActions; float m_JumpCoolDownStart; void Awake() { m_PushBlockSettings = FindObjectOfType(); LazyInitializeActions(); // Cache the agent rigidbody m_PlayerRb = GetComponent(); } void LazyInitializeActions() { if (m_PushBlockActions != null) { return; } m_PushBlockActions = new PushBlockActions(); m_PushBlockActions.Enable(); // You can listen to C# events. m_PushBlockActions.Movement.jump.performed += JumpOnperformed; } void JumpOnperformed(InputAction.CallbackContext callbackContext) { InnerJump(gameObject.transform); } void FixedUpdate() { // Or you can poll the action itself like we do here. InnerMove(gameObject.transform, m_PushBlockActions.Movement.movement.ReadValue()); if (m_JumpTimeRemaining < 0) { m_PlayerRb.AddForce(-transform.up * (m_PushBlockSettings.agentJumpForce * 3), ForceMode.Acceleration); } m_JumpTimeRemaining -= Time.fixedDeltaTime; } void InnerJump(Transform t) { if (Time.realtimeSinceStartup - m_JumpCoolDownStart > m_PushBlockSettings.agentJumpCoolDown) { m_JumpTimeRemaining = JumpTime; m_PlayerRb.AddForce(t.up * m_PushBlockSettings.agentJumpForce, ForceMode.VelocityChange); m_JumpCoolDownStart = Time.realtimeSinceStartup; } } void InnerMove(Transform t, Vector2 v) { var forward = CreateForwardVector(v); var up = CreateUpVector(v); var dirToGo = t.forward * forward; var rotateDir = t.up * up; t.Rotate(rotateDir, Time.deltaTime * 200f); m_PlayerRb.AddForce(dirToGo * m_PushBlockSettings.agentRunSpeed, ForceMode.VelocityChange); } static float CreateUpVector(Vector2 move) { return Mathf.Abs(move.x) > Mathf.Abs(move.y) ? move.x : 0f; } static float CreateForwardVector(Vector2 move) { return Mathf.Abs(move.y) > Mathf.Abs(move.x) ? move.y : 0f; } /// /// This is the implementation of the for this class. We need /// both the and the if you are /// listening to C# events, Unity Events, or receiving Messages from the Input System Package as those callbacks /// are set up through the generated . /// /// public (InputActionAsset, IInputActionCollection2) GetInputActionAsset() { LazyInitializeActions(); return (m_PushBlockActions.asset, m_PushBlockActions); } }