using UnityEngine; namespace UnityAgent { /// /// Third-person character controller with WASD movement, jump and a /// rigidbody-based physics response. Designed to be paired with the /// ThirdPersonCamera component. /// [RequireComponent(typeof(Rigidbody))] [RequireComponent(typeof(CapsuleCollider))] public class PlayerController : MonoBehaviour { [Header("Movement")] public float moveSpeed = 6f; public float sprintMultiplier = 1.6f; public float rotationSmooth = 12f; public float jumpForce = 7.5f; [Header("Ground Check")] public float groundCheckDistance = 0.2f; public LayerMask groundMask = ~0; [Header("Camera")] public Transform cameraTransform; private Rigidbody _rb; private CapsuleCollider _col; private Vector2 _moveInput; private bool _jumpRequested; private bool _isGrounded; private float _targetRotation; private void Awake() { _rb = GetComponent(); _col = GetComponent(); _rb.freezeRotation = true; _rb.interpolation = RigidbodyInterpolation.Interpolate; if (cameraTransform == null && Camera.main != null) cameraTransform = Camera.main.transform; } private void Update() { _moveInput.x = Input.GetAxisRaw("Horizontal"); _moveInput.y = Input.GetAxisRaw("Vertical"); _moveInput = Vector2.ClampMagnitude(_moveInput, 1f); if (Input.GetButtonDown("Jump") && _isGrounded) _jumpRequested = true; } private void FixedUpdate() { GroundCheck(); Move(); if (_jumpRequested) { _rb.AddForce(Vector3.up * jumpForce, ForceMode.VelocityChange); _jumpRequested = false; } } private void GroundCheck() { Vector3 origin = transform.position + Vector3.up * (_col.height * 0.5f - _col.radius); _isGrounded = Physics.SphereCast( origin, _col.radius * 0.9f, Vector3.down, out _, groundCheckDistance, groundMask, QueryTriggerInteraction.Ignore); } private void Move() { float speed = moveSpeed; if (Input.GetKey(KeyCode.LeftShift)) speed *= sprintMultiplier; if (_moveInput.sqrMagnitude < 0.01f) { // Apply horizontal damping so the character stops cleanly. Vector3 v = _rb.velocity; v.x *= 0.85f; v.z *= 0.85f; _rb.velocity = v; return; } Vector3 camForward = cameraTransform ? cameraTransform.forward : transform.forward; Vector3 camRight = cameraTransform ? cameraTransform.right : transform.right; camForward.y = 0f; camForward.Normalize(); camRight.y = 0f; camRight.Normalize(); Vector3 desired = (camForward * _moveInput.y + camRight * _moveInput.x) * speed; Vector3 velocity = _rb.velocity; velocity.x = desired.x; velocity.z = desired.z; _rb.velocity = velocity; if (desired.sqrMagnitude > 0.01f) { _targetRotation = Mathf.Atan2(desired.x, desired.z) * Mathf.Rad2Deg; float smoothed = Mathf.LerpAngle( transform.rotation.eulerAngles.y, _targetRotation, Time.fixedDeltaTime * rotationSmooth); _rb.MoveRotation(Quaternion.Euler(0f, smoothed, 0f)); } } } }