using UnityEngine; using UnityEngine.AI; using UnityEngine.InputSystem; public enum MovementType { PointAndClick, DirectionalInput } [RequireComponent(typeof(NavMeshAgent))] public class PlayerMovement : MonoBehaviour { [Header("Movement Settings")] [SerializeField] private float lookSpeed = 10f; [SerializeField] private float gamepadRotationSpeed = 300f; [SerializeField] private float minimumInputThreshold = 0.1f; [SerializeField] private MovementType movementType = MovementType.DirectionalInput; [SerializeField] private BoolSharedField directionalInputMode; public BoolSharedField DirectionalInputMode => directionalInputMode; [Header("Controller Settings")] [SerializeField] private float gamepadDeadzone = 0.2f; [Header("Debug Visualization")] [SerializeField] private bool showDebugVisuals = false; // Input System variables private PlayerInput playerInput; private Vector2 moveInput; private Vector2 aimInput; private bool isMouseAiming; private Transform target; private NavMeshAgent agent; private Vector3 direction = Vector3.zero; private Quaternion lookRotation = Quaternion.identity; private Camera mainCamera; private Plane groundPlane; private Vector3 lastAimDirection; private bool isUsingGamepad; private Vector3 currentMoveDirection; private bool isMoving; private ProjectileSpawnLocationController aimController; private Vector3 currentAimPoint; public Vector2 currentMovementDirection = new Vector2(); public Vector2 currentAimDirection = new Vector2(); public Vector2 lastAimDirection2 = new Vector2(); public Vector2 relativeMovementDirection = new Vector2(); private Vector3 mouseAimPoint; private bool hasMouseAimPoint; private float startingAgentSpeed; public float StartingAgentSpeed => startingAgentSpeed; private float startingAgentAngularSpeed; public float StartingAgentAngularSpeed => startingAgentAngularSpeed; private float startingAgentAcceleration; public float StartingAgentAcceleration => startingAgentAcceleration; private void Awake() { agent = GetComponent(); mainCamera = Camera.main; groundPlane = new Plane(Vector3.up, Vector3.zero); aimController = GetComponentInChildren(); lastAimDirection = transform.forward; directionalInputMode.Value = true; movementType = MovementType.DirectionalInput; startingAgentSpeed = agent.speed; startingAgentAngularSpeed = agent.angularSpeed; startingAgentAcceleration = agent.acceleration; // Initialize Input System playerInput = GetComponent(); if (playerInput == null) { Debug.LogWarning("PlayerInput component not found. Add PlayerInput component to use Input System."); } } void Start() { SetupAgentForDirectionalMovement(); } // Input callbacks - these will be called by Unity's SendMessage system public void OnMove(InputValue value) { moveInput = value.Get(); currentMovementDirection = moveInput; } public void OnStick(InputValue value) { aimInput = value.Get(); currentAimDirection = aimInput; // Detect gamepad usage if (aimInput.magnitude > gamepadDeadzone) { isUsingGamepad = true; hasMouseAimPoint = false; } } public void OnLook(InputValue value) { // This gets called when mouse moves, indicating mouse usage isUsingGamepad = false; } private void Update() { SetMovementType(directionalInputMode.Value ? MovementType.DirectionalInput : MovementType.PointAndClick); if (movementType == MovementType.DirectionalInput) { // Handle movement HandleDirectionalMovement(); // Handle aiming based on input method if (!isUsingGamepad) { HandleMouseAiming(); } else { HandleGamepadAiming(); } HandleRelativeForAnimation(); } else if (target != null) { HandlePointAndClickMovement(); } // Handle channeled ability rotation if (CastBarHandler.Instance.currentAbility is ChanneledAbility && CastBarHandler.Instance.castBar.activeSelf) { var channelAbility = (ChanneledAbility)CastBarHandler.Instance.currentAbility; if (channelAbility.allowAiming) { InstantFaceCast(aimController.GetLookat()); } } } private void HandleDirectionalMovement() { if (agent.isStopped) return; Vector3 cameraForward = mainCamera.transform.forward; Vector3 cameraRight = mainCamera.transform.right; cameraForward.y = 0f; cameraRight.y = 0f; cameraForward.Normalize(); cameraRight.Normalize(); currentMoveDirection = (cameraForward * currentMovementDirection.y + cameraRight * currentMovementDirection.x).normalized; isMoving = currentMoveDirection.magnitude >= minimumInputThreshold; if (isMoving) { agent.SetDestination(transform.position + currentMoveDirection); } else { agent.velocity = Vector3.zero; agent.SetDestination(transform.position); } // If we're using mouse aim, maintain character rotation even while moving if (!isUsingGamepad && hasMouseAimPoint) { Vector3 aimDirection = (mouseAimPoint - transform.position).normalized; if (aimDirection != Vector3.zero) { transform.rotation = Quaternion.LookRotation(aimDirection); } } } private void HandleGamepadAiming() { if (currentAimDirection.magnitude > gamepadDeadzone) { Vector3 cameraForward = mainCamera.transform.forward; Vector3 cameraRight = mainCamera.transform.right; cameraForward.y = 0f; cameraRight.y = 0f; Vector3 aimDirection = (cameraForward * currentAimDirection.y + cameraRight * currentAimDirection.x).normalized; lastAimDirection = aimDirection; lastAimDirection2.x = aimDirection.x; lastAimDirection2.y = aimDirection.z; Quaternion targetRotation = Quaternion.LookRotation(aimDirection); transform.rotation = Quaternion.RotateTowards( transform.rotation, targetRotation, gamepadRotationSpeed * Time.deltaTime ); } else if (lastAimDirection != Vector3.zero) { Quaternion targetRotation = Quaternion.LookRotation(lastAimDirection); transform.rotation = Quaternion.RotateTowards( transform.rotation, targetRotation, gamepadRotationSpeed * Time.deltaTime ); } } private void HandleRelativeForAnimation() { // Calculate forward/backward movement relative to aim direction relativeMovementDirection.y = Vector2.Dot(lastAimDirection2, currentMovementDirection); // Calculate strafe movement relative to aim direction relativeMovementDirection.x = lastAimDirection2.x * currentMovementDirection.y - lastAimDirection2.y * currentMovementDirection.x; } private void HandleMouseAiming() { // Get the current mouse position directly from the Input System Vector3 screenMousePosition = Mouse.current.position.ReadValue(); Ray ray = mainCamera.ScreenPointToRay(screenMousePosition); if (groundPlane.Raycast(ray, out float distance)) { mouseAimPoint = ray.GetPoint(distance); mouseAimPoint.y = transform.position.y; hasMouseAimPoint = true; Vector3 aimDirection = (mouseAimPoint - transform.position).normalized; if (aimDirection != Vector3.zero) { lastAimDirection = aimDirection; lastAimDirection2.x = aimDirection.x; lastAimDirection2.y = aimDirection.z; transform.rotation = Quaternion.LookRotation(aimDirection); } } } private void HandlePointAndClickMovement() { agent.SetDestination(target.position); FacePoint(agent.destination); } private void SetupAgentForDirectionalMovement() { agent.updateRotation = false; agent.stoppingDistance = 0f; agent.acceleration = startingAgentAcceleration; agent.angularSpeed = startingAgentAngularSpeed; } #region Public Methods public void SetMovementType(MovementType type) { movementType = type; if (movementType == MovementType.DirectionalInput) { SetupAgentForDirectionalMovement(); } else { agent.updateRotation = true; agent.stoppingDistance = 0.25f; agent.acceleration = startingAgentAcceleration; agent.angularSpeed = startingAgentAngularSpeed; } } public void ToggleAgentMoving(bool busy) { agent.isStopped = busy; if (busy) { agent.SetDestination(transform.position); } } public void MoveToPoint(Vector3 point) { agent.SetDestination(point); FacePoint(point); } public void FollowTarget(Interactable newTarget) { agent.stoppingDistance = newTarget.radius * 0.8f; agent.updateRotation = false; target = newTarget.interactionTransform; FaceTarget(); } public void StopFollowingTarget() { agent.stoppingDistance = 0f; agent.updateRotation = true; target = null; } public void FaceAttack(int index) { if (target != null) FaceTarget(); } public void FacePoint(Vector3 point) { direction = (point - transform.position).normalized; direction.y = 0f; lookRotation = Quaternion.LookRotation(direction); transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * lookSpeed); } public void FaceTarget() { if (target == null) return; direction = (target.position - transform.position).normalized; direction.y = 0f; lookRotation = Quaternion.LookRotation(direction); transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * lookSpeed); } public void InstantFaceTarget() { if (target != null) { direction = (target.position - transform.position).normalized; direction.y = 0f; lookRotation = Quaternion.LookRotation(direction); transform.rotation = lookRotation; } } public void InstantFaceCast(Vector3 lookat) { transform.forward = lookat; } public void StopLocomotion(int index) { StopFollowingTarget(); agent.SetDestination(transform.position); } #endregion #region Debug private void OnDrawGizmos() { if (!showDebugVisuals) return; // Draw movement direction Gizmos.color = Color.blue; Gizmos.DrawRay(transform.position, currentMoveDirection * 2f); // Draw aim direction Gizmos.color = Color.red; Gizmos.DrawRay(transform.position, lastAimDirection * 2f); // Draw aim point if using mouse if (!isUsingGamepad) { Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(currentAimPoint, 0.2f); } } #endregion }