Скорость приседания и прыжка в Unity CharacterController
Как правильно реализовать логику скорости движения при crouch, прыжке и беге в Unity с CharacterController. Учет isGrounded, сохранение prevSpeed для избежания замедления в воздухе. Полный скрипт unity движения персонажа.
Как в Unity с CharacterController правильно реализовать логику скорости движения при приседании (crouch), прыжке и беге?
Проблема
Скорость приседания (_crouchSpeed) применяется всегда при нажатии Ctrl, даже в воздухе во время прыжка после бега или ходьбы. Это приводит к нежелательному замедлению.
Требуемая логика
- При приседании на земле: использовать _crouchSpeed.
- При прыжке с приседания с земли: сохранять _crouchSpeed в воздухе.
- При беге/ходьбе + прыжок + приседание в воздухе: сохранять скорость бега (_runSpeed) или ходьбы (_walkSpeed), игнорируя _crouchSpeed.
Ключевые методы из кода
Run(): устанавливает скорость на земле (ходьба/бег).Crouch(): всегда при ctrlPressed устанавливает _movementSpeed = _crouchSpeed и меняет масштаб.Gravity()иJumpHandler(): стандартная обработка.
Как доработать код, чтобы скорость при приседании учитывала состояние isGrounded и предыдущую скорость движения?
В Unity с CharacterController правильная логика скорости при приседании требует проверки isGrounded и сохранения предыдущей скорости в отдельной переменной, например prevSpeed. При crouch на земле используйте _crouchSpeed, но в воздухе после прыжка из бега или ходьбы игнорируйте Ctrl и держите runSpeed или walkSpeed; только прыжок именно из приседания сохранит низкую скорость. Это решает замедление в полёте и интегрируется в Run(), Crouch() и JumpHandler() через FixedUpdate.
Содержание
- Введение в unity character controller и проблемы скорости при приседании
- Базовая логика unity движения персонажа на земле
- Реализация прыжка unity с CharacterController
- Правильный crouch в unity скрипт движения с учетом isGrounded
- Сохранение предыдущей скорости при прыжке из бега или приседания
- Полный скрипт контроллер персонажа unity с доработками
- Тестирование и распространенные ошибки в charactercontroller unity
- Источники
- Заключение
Введение в unity character controller и проблемы скорости при приседании
Представьте: ваш персонаж в Unity бежит на полной скорости, жмёт прыжок, а в воздухе случайно тыкает Ctrl — и бац, скорость падает до черепашьей. Классическая засада с CharacterController. Почему так? Потому что в простом Crouch() вы всегда ставите _movementSpeed = _crouchSpeed, не проверяя, на земле ли герой или в полёте.
Это ломает unity движения персонажа: игрок ожидает, что в воздухе сохранится импульс от бега (_runSpeed), а не сработает приседание. Решение? Флаги вроде isGrounded из самого контроллера плюс переменная для предыдущей скорости. Sharp Coder Blog показывает похожий подход для FPS, где приседание влияет только на земле или при старте прыжка.
А что насчёт unity character controller в целом? Он не физический как Rigidbody, а kinematic — скорость задаётся вручную в controller.Move(). Значит, вся логика на вас: состояния (бег, ходьба, crouch, прыжок) + проверки. Давайте разберём по шагам, чтобы ваш unity скрипт движения стал идеальным.
Базовая логика unity движения персонажа на земле
Сначала базис. В FixedUpdate() вычисляете вектор движения: Vector3 move = new Vector3(inputX, 0, inputZ).normalized * _movementSpeed * Time.fixedDeltaTime. Это умножается на трансформ и подаётся в controller.Move().
На земле всё просто:
- Ходьба:
_movementSpeed = _walkSpeed(скажем, 3f). - Бег: если Shift, то
_runSpeed(6f). - Crouch: Ctrl меняет масштаб Y персонажа (с 2 на 1) и скорость на
_crouchSpeed(2f).
Проблема в Run() и Crouch() — они не синхронизированы. Вызовите Run() перед Crouch(), и последняя перезапишет скорость. Решение: объединить в один метод UpdateMovementSpeed(), где приоритет: если isGrounded && ctrlPressed — crouch, иначе бег/ходьба.
Код-пример базовый:
private void UpdateMovementSpeed() {
if (controller.isGrounded) {
if (Input.GetKey(KeyCode.LeftControl)) {
_movementSpeed = _crouchSpeed;
transform.localScale = new Vector3(1, 0.5f, 1); // Приседание
} else if (Input.GetKey(KeyCode.LeftShift)) {
_movementSpeed = _runSpeed;
} else {
_movementSpeed = _walkSpeed;
}
}
// Пока без воздуха — об этом позже
}
Это основа контроллер персонажа unity. Но в воздухе? Продолжим.
Реализация прыжка unity с CharacterController
Прыжок в JumpHandler() — стандарт: если isGrounded && Space, то verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity). Затем в Gravity() добавляем verticalVelocity += gravity * Time.fixedDeltaTime.
Но вот засада: прыжок запускает Crouch()? Нет, если не интегрировать. В вашем случае при прыжок unity из бега скорость должна сохраниться.
Добавьте в JumpHandler():
private void JumpHandler() {
if (controller.isGrounded && Input.GetKeyDown(KeyCode.Space)) {
// Сохраняем текущую горизонтальную скорость перед прыжком
prevSpeed = _movementSpeed;
wasCrouchingBeforeJump = Input.GetKey(KeyCode.LeftControl);
verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
}
Почему prevSpeed? В воздухе controller.Move() продолжит использовать _movementSpeed из земли. Если после прыжка нажать Ctrl — не дадим ей измениться. Stack Overflow советует именно _airSpeed для этого — аналогично.
Коротко: прыжок “замораживает” горизонтальную скорость.
Правильный crouch в unity скрипт движения с учетом isGrounded
Теперь сердце проблемы. В Crouch() не всегда ставьте _crouchSpeed. Проверяйте землю!
Доработайте:
private void Crouch() {
bool ctrlPressed = Input.GetKey(KeyCode.LeftControl);
if (controller.isGrounded) {
if (ctrlPressed) {
_movementSpeed = _crouchSpeed;
transform.localScale = new Vector3(1, crouchHeight, 1);
isCrouching = true;
} else {
transform.localScale = new Vector3(1, standHeight, 1);
isCrouching = false;
}
} else {
// В воздухе: если прыгнули из crouch — держим низкую скорость
if (wasCrouchingBeforeJump) {
_movementSpeed = _crouchSpeed;
} else {
// Игнорим Ctrl, возвращаем prevSpeed от бега/ходьбы
_movementSpeed = prevSpeed;
}
}
}
Видишь? isGrounded — ключ. В воздухе Ctrl не меняет ничего, кроме случая прыжка из приседания. Это делает unity скрипт движения предсказуемым.
Ещё трюк: lerp для плавного изменения масштаба, чтоб не дёргало:
transform.localScale = Vector3.Lerp(transform.localScale, targetScale, crouchSpeed * Time.deltaTime);
Сохранение предыдущей скорости при прыжке из бега или приседания
А если бег + прыжок + Ctrl в полёте? Держим _runSpeed. Вводим:
private float prevSpeed;private bool wasCrouchingBeforeJump;
В FixedUpdate() после Gravity() и перед Move():
if (!controller.isGrounded) {
if (wasCrouchingBeforeJump) {
_movementSpeed = _crouchSpeed; // Прыжок из crouch
} else {
_movementSpeed = prevSpeed; // Сохранили из Run()
}
}
Сброс флагов при приземлении:
if (controller.isGrounded && verticalVelocity < 0) {
wasCrouchingBeforeJump = false;
}
Идеально для прыжок unity 3d. Теперь импульс бега не теряется — игрок чувствует контроль.
Что если двойной прыжок? Добавьте таймер или счётчик, но для базового хватит.
Полный скрипт контроллер персонажа unity с доработками
Вот готовый класс. Прикрепите к персонажу с CharacterController. Настройки в инспекторе.
using UnityEngine;
public class PlayerMovement : MonoBehaviour {
[Header("Скорости")]
public float walkSpeed = 3f;
public float runSpeed = 6f;
public float crouchSpeed = 2f;
[Header("Прыжок и гравитация")]
public float jumpHeight = 2f;
public float gravity = -20f;
[Header("Размеры")]
public float standHeight = 2f;
public float crouchHeight = 1f;
private CharacterController controller;
private Vector3 velocity;
private float prevSpeed;
private bool wasCrouchingBeforeJump;
private bool isCrouching;
void Start() {
controller = GetComponent<CharacterController>();
transform.localScale = new Vector3(1, standHeight, 1);
}
void Update() {
Crouch();
JumpHandler();
}
void FixedUpdate() {
Gravity();
UpdateMovementSpeed();
Move();
ResetAirFlags();
}
private void UpdateMovementSpeed() {
float horizontalSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed;
if (controller.isGrounded && Input.GetKey(KeyCode.LeftControl)) {
horizontalSpeed = crouchSpeed;
}
prevSpeed = horizontalSpeed; // Обновляем для следующего прыжка
}
private void Move() {
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * prevSpeed * Time.fixedDeltaTime);
controller.Move(velocity * Time.fixedDeltaTime);
}
private void Crouch() {
bool ctrl = Input.GetKey(KeyCode.LeftControl);
Vector3 targetScale = ctrl && controller.isGrounded ?
new Vector3(1, crouchHeight, 1) : new Vector3(1, standHeight, 1);
transform.localScale = Vector3.Lerp(transform.localScale, targetScale, 5f * Time.deltaTime);
}
private void JumpHandler() {
if (controller.isGrounded && Input.GetKeyDown(KeyCode.Space)) {
wasCrouchingBeforeJump = Input.GetKey(KeyCode.LeftControl);
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
}
}
private void Gravity() {
if (controller.isGrounded && velocity.y < 0) {
velocity.y = -2f;
} else {
velocity.y += gravity * Time.fixedDeltaTime;
}
}
private void ResetAirFlags() {
if (controller.isGrounded && velocity.y < 0) {
wasCrouchingBeforeJump = false;
}
}
}
Тестируйте! Бег — прыжок — Ctrl: скорость бега держится. Crouch на земле — прыжок: низкая скорость в воздухе.
Тестирование и распространенные ошибки в charactercontroller unity
Тестируйте в Play Mode:
- Бег + прыжок + Ctrl в воздухе — скорость не меняется?
- Crouch на земле + прыжок — низкая скорость в полёте?
- Приземление — флаги сбрасываются?
Ошибки:
- Забыли
Time.fixedDeltaTime— рывки. Update()вместоFixedUpdate()для Move — нестабильно.- Нет lerp для scale — дёргается модель.
verticalVelocityне сбрасывается — бесконечный полёт.
Если камера — добавьте Cinemachine или follow-скрипт. Для unity 3d character controller с мышью — смотрите GameDev.ru примеры.
Готово. Ваш контроллер теперь как в AAA-играх.
Источники
- Sharp Coder Blog — Руководство по приседанию в FPS с CharacterController в Unity: https://ru.sharpcoderblog.com/blog/adding-crouching-to-fps-player-in-unity
- Stack Overflow на русском — Правильная реализация движения персонажа с учетом воздуха и приседания: https://ru.stackoverflow.com/questions/936026/Правильная-реализация-передвижения-персонажа
- GameDev.ru — Обсуждение скриптов движения 3D персонажа в Unity с контроллером: https://gamedev.ru/code/forum/?id=263774
Заключение
Ключ — в проверках isGrounded, prevSpeed и флаге прыжка из crouch: это делает unity character controller отзывчивым без багов замедления. Полный скрипт выше готов к использованию, протестируйте и донастройте под проект. Теперь ваши unity движения персонажа будут радовать игроков — без сюрпризов в воздухе. Удачи в геймдеве!
Для правильной реализации приседания (crouch) в unity character controller используйте флаг isCrouching и выбирайте скорость: при isCrouching — crouchSpeed, иначе walkingSpeed или runningSpeed.
Вычисляйте curSpeedX и curSpeedY с учетом isRunning и isCrouching в методе движения.
Чтобы избежать замедления в воздухе при прыжок unity, проверяйте isGrounded перед изменением isCrouching и сохраняйте предыдущую скорость в prevSpeed для восстановления после приземления.
Это интегрируется в unity скрипт движения персонажа плавно с Lerp для камеры:
- На земле: применяйте
crouchSpeedтолько еслиisGrounded. - В воздухе: сохраняйте скорость до прыжка.
В CharacterController обновляйте _movementSpeed в FixedUpdate: если isGrounded и Ctrl, то _movementSpeed = _crouchSpeed.
При прыжке из приседания с земли сохраняйте _crouchSpeed в _airSpeed для unity движения персонажа в воздухе.
Если прыжок из бега — игнорируйте crouch и держите _runSpeed.
Перемещайте через controller.Move с Time.fixedDeltaTime, чтобы скорость сохранялась при прыжок unity и не менялась в полете:
if (isGrounded && ctrlPressed) {
_movementSpeed = _crouchSpeed;
} else if (!isGrounded) {
// Сохранить air speed
}

