using UnityEngine; [RequireComponent(typeof(CharacterController))] public class PlayerMovement : MonoBehaviour { [Header("Movement Settings")] [SerializeField] private float moveSpeed = 4f; [SerializeField] private float crouchSpeed = 2f; [SerializeField] private float gravity = -9.81f; [Header("Crouch Settings")] [SerializeField] private KeyCode crouchKey = KeyCode.LeftControl; [SerializeField] private float standHeight = 2.0f; [SerializeField] private float crouchHeight = 1.0f; [SerializeField] private float crouchTransitionSpeed = 10f; [SerializeField] private Transform playerCamera; // Ссылка на камеру игрока [Header("Ground Check")] [SerializeField] private Transform groundCheck; [SerializeField] private float groundDistance = 0.4f; [SerializeField] private LayerMask groundMask; private CharacterController controller; private Vector3 velocity; private bool isGrounded; private float initialHeight; private Vector3 standCameraPos; private Vector3 crouchCameraPos; private Vector3 initialCenter; private bool isCrouching; private void Awake() { controller = GetComponent(); } private void Start() { // Запоминаем исходные значения из Инспектора initialHeight = controller.height; initialCenter = controller.center; if (playerCamera == null) { Camera cam = GetComponentInChildren(); if (cam != null) playerCamera = cam.transform; } if (playerCamera != null) { standCameraPos = playerCamera.localPosition; // Камера опускается ровно на разницу высоты crouchCameraPos = new Vector3( standCameraPos.x, standCameraPos.y - (standHeight - crouchHeight), standCameraPos.z ); } } private void Update() { HandleCrouch(); } private void HandleCrouch() { isCrouching = Input.GetKey(crouchKey); float targetHeight = isCrouching ? crouchHeight : standHeight; float currentDelta = initialHeight - targetHeight; // ГЛАВНАЯ ФОРМУЛА: дно капсулы остается на земле Vector3 targetCenter = new Vector3( initialCenter.x, initialCenter.y - (currentDelta * 0.5f), initialCenter.z ); controller.height = Mathf.MoveTowards(controller.height, targetHeight, Time.deltaTime * crouchTransitionSpeed); controller.center = Vector3.MoveTowards(controller.center, targetCenter, Time.deltaTime * crouchTransitionSpeed); if (playerCamera != null) { Vector3 targetCamPos = isCrouching ? crouchCameraPos : standCameraPos; playerCamera.localPosition = Vector3.MoveTowards(playerCamera.localPosition, targetCamPos, Time.deltaTime * crouchTransitionSpeed); } } public void ProcessMove(Vector2 input) { // Проверка касания земли (сфера на уровне ног) isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask); if (isGrounded && velocity.y < 0) { velocity.y = -2f; // Небольшое прижатие к земле для стабильного спуска по склонам } // Выбор текущей скорости в зависимости от состояния приседа float currentSpeed = isCrouching ? crouchSpeed : moveSpeed; // Движение относительно локальных координат игрока Vector3 move = transform.right * input.x + transform.forward * input.y; controller.Move(move * (currentSpeed * Time.deltaTime)); // Применение гравитации velocity.y += gravity * Time.deltaTime; controller.Move(velocity * Time.deltaTime); } }