using Photon.Pun; using UnityEngine; using UnityEngine.AI; 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; 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 PhotonView photonView; 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(); photonView = 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; } void Start() { if (!photonView.IsMine) { this.enabled = false; return; } SetupAgentForDirectionalMovement(); } private void Update() { SetMovementType(directionalInputMode.Value ? MovementType.DirectionalInput : MovementType.PointAndClick); if (movementType == MovementType.DirectionalInput) { DetectInputMethod(); // Handle movement first HandleDirectionalMovement(); // Then handle aiming, with mouse taking priority if (!isUsingGamepad) { HandleMouseAiming(); } else { HandleGamepadAiming(); hasMouseAimPoint = false; // Reset mouse aim when using gamepad } 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 DetectInputMethod() { Vector2 gamepadMove = new Vector2( Input.GetAxisRaw(GameConstants.Input.HorizontalAxis), Input.GetAxisRaw(GameConstants.Input.VerticalAxis)); Vector2 gamepadLook = new Vector2( Input.GetAxisRaw(GameConstants.Input.AimHorizontalAxis), Input.GetAxisRaw(GameConstants.Input.AimVerticalAxis)); // Check for mouse movement if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0) { isUsingGamepad = false; return; } // Check for gamepad input if (gamepadMove.magnitude > gamepadDeadzone || gamepadLook.magnitude > gamepadDeadzone) { isUsingGamepad = true; hasMouseAimPoint = false; // Reset mouse aim when using gamepad } } private void HandleDirectionalMovement() { if (agent.isStopped) return; currentMovementDirection = new Vector2( Input.GetAxisRaw(GameConstants.Input.HorizontalAxis), Input.GetAxisRaw(GameConstants.Input.VerticalAxis)); 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 HandleAimingRotation() { if (isUsingGamepad) { HandleGamepadAiming(); } else { HandleMouseAiming(); } } private void HandleGamepadAiming() { currentAimDirection = new Vector2( Input.GetAxisRaw(GameConstants.Input.AimHorizontalAxis), Input.GetAxisRaw(GameConstants.Input.AimVerticalAxis) ); 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 ); } // Remove the else { isAiming = false; } - we want to maintain the last aim direction // Instead, maintain the last valid aim direction 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 // Cross product for left/right movement relativeMovementDirection.x = lastAimDirection2.x * currentMovementDirection.y - lastAimDirection2.y * currentMovementDirection.x; } private void HandleMouseAiming() { Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); 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; // Always update rotation when using mouse 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 }