Unity: простой физический контроллер персонажа на C#

Введение
Физический контроллер персонажа (rigidbody-based) использует физику Unity для управления передвижением и столкновениями. Это удобно для классических игр с физикой, скользящими и реалистичными реакциями на сталкивания. Краткое определение: Rigidbody — компонент Unity, который позволяет объекту двигаться под управлением физического движка.
Важно: этот подход даёт естественное поведение тела в столкновениях, но усложняет точное управление (например, точную платформерную механику). Для точного контроля используйте компонент CharacterController или кастомную кинематическую логику.
Что вы получите в конце
- Рабочий прототип куба-персонажа с движением, поворотом и прыжком.
- Чек-листы для разработчика, QA и дизайнера.
- Советы по отладке и улучшению контроля.
Шаг 1: Создание сцены с игроком и террейном
- Откройте Unity и создайте новый проект (3D).
- В иерархии (Hierarchy) добавьте плоскость: правый клик → 3D Object → Plane. Это будет ваш уровень/террейн для тестов.
- Добавьте куб: правый клик → 3D Object → Cube. Разместите его над Plane так, чтобы он падал на поверхность.
- Выделите Cube, в инспекторе нажмите Add Component → Rigidbody. Оставьте Use Gravity включённым.
- Установите основную камеру как дочернюю от Cube (перетяните камеру на Cube в иерархии). Камера будет следовать за кубом, создавая простую third-person перспективу.
Простой тест: нажмите кнопку Play. Квадрат должен упасть на Plane, и камера будет следовать за ним.
Совет: можно подгрузить бесплатные ассеты из Asset Store, чтобы заменить куб моделью персонажа и плоскость на готовый уровень.
Шаг 2: Создаём C# файл
Создайте папку Scripts в Project (правый клик → Create → Folder → назовите Scripts). Затем в этой папке правый клик → Create → C# Script и назовите файл, например, CharacterControl.
Откройте скрипт и начните с базовой структуры:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Character_Control : MonoBehaviour {
void Start() {
}
void Update() {
}
}Пояснение: MonoBehaviour — базовый класс для компонентов Unity. Start() вызывается при инициализации, Update() — каждый кадр.
Шаг 3: Движение вперёд и назад через AddForce
Добавим публичные переменные для Rigidbody и скорости в тело класса (сделаем их public, чтобы редактировать из инспектора):
public Rigidbody rigidbody;
public float speed;В Start() получим компонент Rigidbody объекта:
void Start() {
rigidbody = GetComponent();
} Реализуем движение вперёд/назад в Update(). В примере используется Input.GetKey, реагирующий на клавиши “w” и “s”. Метод AddForce добавляет силу, направленную вдоль transform.forward:
void Update() {
if (Input.GetKey("w")) {
rigidbody.AddForce(transform.forward * speed);
}
if (Input.GetKey("s")) {
rigidbody.AddForce((transform.forward * -1) * speed);
}
}Примечание по хорошей практике: переменные Rigidbody обычно называют “rb” или “_rb” во избежание совпадения с устаревшими свойствами. В небольшом примере допустимо оставить имя rigidbody, но для больших проектов лучше избегать имён, одинаковых со встроенными свойствами.
Важно: сила AddForce зависит от mass и drag в Rigidbody. Подбирайте speed и массу, чтобы получить желаемое ускорение.
Проблема: куб может катиться/переворачиваться при движении. Чтобы этого избежать, в компоненте Rigidbody отметьте Freeze Rotation по осям X и Z (Inspector → Rigidbody → Constraints → Freeze Rotation X, Z).
Шаг 4: Поворот влево и вправо через AddTorque
Добавим переменную torque:
public float torque;Поворот реализуем с использованием AddTorque. В примере применяется Input.GetAxis(“Horizontal”), который выдаёт -1..1 в зависимости от направления. Можно также реагировать на клавиши A/D напрямую.
void Update() {
// другие проверки движения...
if (Input.GetKey("d")) {
float turn = Input.GetAxis("Horizontal");
rigidbody.AddTorque(transform.up * torque * turn);
}
if (Input.GetKey("a")) {
float turn = Input.GetAxis("Horizontal");
rigidbody.AddTorque(transform.up * torque * turn);
}
}Совет: параметры mass и drag в Rigidbody, а также значение torque влияют на “чувство” поворота. В простом примере можно начать с mass = 1, drag = 1, torque = 2 и подбирать дальше.
Шаг 5: Прыжок с защитой от двойного прыжка
Чтобы прыжок имел управляемую высоту, добавим переменную-флаг isJumping:
private bool isJumping = false;Блокируем движение во время прыжка, обернув вызовы движения в if (!isJumping) { … }.
Реализация прыжка с проверкой на нажатие пробела и временной блокировкой:
void Update() {
if (!isJumping) {
if (Input.GetKeyDown("space")) {
isJumping = true;
rigidbody.AddForce(transform.up * speed * 120);
rigidbody.angularVelocity = Vector3.zero;
Invoke("Move_Setter", 0.8f);
}
// движения только если не прыгаем
if (Input.GetKey("w")) {
rigidbody.AddForce(transform.forward * speed);
}
if (Input.GetKey("s")) {
rigidbody.AddForce((transform.forward * -1) * speed);
}
}
// поворот можно разрешить или запретить во время прыжка — решайте по механике
}
void Move_Setter() {
isJumping = false;
}Комментарий: Invoke вызывает метод по имени через заданную задержку. В примере после 0.8 секунды флаг isJumping сбрасывается.
Альтернатива: вместо Invoke можно отслеживать время с помощью корутины или переменной-таймера в Update() для более точного контроля.
Шаг 6: Тестирование и отладка
- Сохраните скрипт и перетащите его на объект Cube в иерархии.
- В инспекторе назначьте Rigidbody (если не назначился автоматически) и задайте speed и torque.
- Нажмите Play и проверьте:
- При нажатии W/S куб продвигается вперёд/назад.
- При нажатии A/D куб поворачивается.
- При нажатии Space куб подпрыгивает и в течение заданного времени нельзя прыгать снова.
Отладка:
- Откройте консоль (Window → General → Console) для поиска ошибок и предупреждений.
- Если движение слишком медленное/быстрое — измените speed, mass, drag.
- Если куб «скользит» и не останавливается — увеличьте drag или применяйте силу не каждый кадр, а импульсно (ForceMode.Impulse).
Важно: для контролей, чувствительных к кадровой частоте, используйте FixedUpdate() для физики и применяйте силы там, а не в Update(). FixedUpdate вызывается с фиксированным шагом (по умолчанию 0.02s) и синхронизирован с физическим движком.
Пример переноса логики в FixedUpdate():
void FixedUpdate() {
// использование Input.GetAxis в Update() и применение силы в FixedUpdate() — распространённый паттерн
}Альтернативы и когда применять другой подход
- CharacterController (встроенный компонент) — лучше для точного платформерного управления, т.к. даёт детерминированное перемещение и удобные функции Move/CollisionFlags.
- Кинематическая логика (движение через transform.Translate/MovePosition) — подходит, когда важна абсолютная управляемость и неожиданные взаимодействия с физикой не нужны.
- Новая система ввода Input System — заменяет старую Input.* для кроссплатформенных контролей и переназначаемых схем.
Когда физический подход не подходит:
- Нужна точная привязка к пиксельной сетке или платформер с «точным» прыжком.
- Нужно избегать непредсказуемых реакций на столкновения.
Практические советы и хорошие практики
- Используйте FixedUpdate для применения сил (AddForce/AddTorque).
- Отделяйте чтение ввода (в Update) и применение сил (в FixedUpdate) через буферные переменные.
- Не полагайтесь на Invoke для критичной логики — используйте корутины или таймеры для предсказуемости.
- Для лучшего контроля поворота можно ограничить угловую скорость (rigidbody.maxAngularVelocity) или применять силу не во время прыжка.
- Для стабильности физики следите за масштабом объектов: слишком маленькие или большие объекты могут дать численные артефакты.
Чек-листы (рольные)
Разработчик:
- Скрипт прикреплён к игровому объекту.
- Rigidbody назначен и настроен (mass/drag/constraints).
- Speed и torque отрегулированы.
- Логика прыжка работает и защищает от двойного прыжка.
- Физика применяется в FixedUpdate или через контролируемные временные шаги.
QA:
- Тестирование W/S/A/D/Space на разных скоростях кадров.
- Проверка поведения при столкновениях с играемыми объектами.
- Тестирование на разных платформах (PC, мобильные при наличии виртуальных кнопок).
Дизайнер/Художник:
- Камера настроена удобно для геймплея.
- Модель персонажа заменена на финальную при необходимости.
- Коллайдеры и визуальные привязки совпадают.
Критерии приёмки
- Персонаж перемещается вперёд/назад при нажатии W/S.
- Персонаж поворачивается при A/D и не опрокидывается по X/Z осям.
- Прыжок происходит при нажатии Space и блокируется на заданное время.
- Поведение воспроизводимо при 30 и 60 FPS (фиксированная физика).
Тестовые случаи и сценарии приёма
- Нормальный прыжок: нажать Space → куб отлетает вверх и возвращается через ожидаемое время; повторный прыжок недоступен до завершения интервала.
- Движение и столкновения: движение вперёд к стене → персонаж корректно сталкивается и не проходит сквозь стену.
- Поворот при движении: поворот прописан и не вызывает чрезмерного вращения по нефиксированным осям.
- Различная частота кадров: запустить игру при низком FPS — поведение не должно «ускользать» или становиться непредсказуемым.
Отладочные приёмы и распространённые проблемы
- Проблема: куб крутится как мяч. Решение: зафиксировать Rotation X/Z и/или сбросить angularVelocity в нужные моменты.
- Проблема: прыжок слишком сильный. Решение: уменьшить множитель прыжковой силы (speed * 120), использовать ForceMode.Impulse для одноразовых толчков.
- Проблема: двойной прыжок. Решение: использовать флаг isJumping и/или проверять контакт с землёй через OnCollisionEnter/OnCollisionStay и проверять нормаль контакта.
Пример проверки соприкосновения с землёй:
private bool isGrounded = false;
void OnCollisionEnter(Collision collision) {
// примитивная проверка: если столкновение снизу — персонаж на земле
foreach (ContactPoint contact in collision.contacts) {
if (Vector3.Dot(contact.normal, Vector3.up) > 0.5f) {
isGrounded = true;
isJumping = false;
}
}
}
void OnCollisionExit(Collision collision) {
isGrounded = false;
}Это надёжнее, чем таймеры для управления прыжком, особенно если уровень содержит лестницы или платформы разной высоты.
Мини-методология: как развивать контроллер дальше
- Начните с Rigidbody-прототипа (как описано).
- Добавьте проверку isGrounded через Raycast или OnCollision.
- Перенесите физические вызовы в FixedUpdate.
- Реализуйте плавную анимацию (Animator) и синхронизацию с физикой.
- При необходимости замените на CharacterController для более точных платформерных механик.
Сравнение подходов (кратко)
- Rigidbody (физика): естественные столкновения, легко интегрируется с физикой, менее точное управление.
- CharacterController: предсказуемость и контроль, не использует физический движок для перемещения.
- Кинематическое перемещение (transform): простота, но конфликт с физикой при взаимодействиях.
Примеры конфигураций (чек-лист параметров Rigidbody для начала)
- Mass: 1
- Drag: 1
- Angular Drag: 0.05
- Use Gravity: true
- Constraints: Freeze Rotation X, Z
Эти настройки можно считать стартовой точкой для прототипа.
Mermaid: простая логика решения, какой контроллер выбрать
flowchart TD
A[Нужна точная платформа?] -->|Да| B[CharacterController]
A -->|Нет| C[Нужна физика столкновений?]
C -->|Да| D[Rigidbody]
C -->|Нет| E[Kinematic / transform]Безопасность и производительность
- Не вызывать тяжёлые операции в Update/FixedUpdate без необходимости.
- Группировать физические проверки и использовать слои и маски (LayerMask) для оптимизации столкновений.
- Проверять расчётные стоимости при большом количестве персонажей — оптимизируйте через LOD и отключение физики вне области видимости.
Совместимость и миграция
- Поддерживаемые версии: описанные API актуальны для Unity 2019+; API Input.* есть в старой системе ввода. Для проектов на новой Input System адаптируйте чтение ввода.
- При миграции на CharacterController потребуется переписать логику движения под Move() и обработку столкновений вручную.
Локальные замечания для русскоязычных разработчиков
- Учитывайте локальные клавиатурные раскладки (например, в локализованных версиях клавиша “w” может быть недоступна) — лучше использовать Input.GetAxis и настраиваемые схемы.
- Для мобильных версий добавьте виртуальные джойстики/кнопки или адаптивную схему управления.
Краткое резюме
- Rigidbody-основанный контроллер обеспечивает естественную физику, но требует аккуратной настройки mass/drag/torque.
- Для стабильности используйте FixedUpdate, проверку isGrounded и ограничение вращений.
- Рассмотрите альтернативы (CharacterController) при необходимости более точного управления.
Важно: тестируйте поведение при разных настройках физики и на целевых платформах.
Полезные шаблоны и сниппеты
Быстрый шаблон чтения ввода и применения силы в FixedUpdate:
private float moveInput = 0f;
private float turnInput = 0f;
void Update() {
moveInput = Input.GetAxis("Vertical"); // -1..1
turnInput = Input.GetAxis("Horizontal");
if (Input.GetKeyDown("space") && isGrounded) {
Jump();
}
}
void FixedUpdate() {
rb.AddForce(transform.forward * moveInput * speed);
rb.AddTorque(transform.up * turnInput * torque);
}
void Jump() {
rb.AddForce(transform.up * jumpForce, ForceMode.Impulse);
isGrounded = false;
}Социальная предпросмотрка (рекомендация)
OG title: Unity: физический контроллер персонажа на C# OG description: Пошаговое руководство по созданию Rigidbody-контроллера: движение, поворот, прыжок, тесты и чек-листы.
Итог
Физический контроллер на Rigidbody — отличный способ быстро получить играбельный прототип с естественной физикой. Он прост в реализации, но требует внимания к стабильности: используйте FixedUpdate, проверяйте isGrounded, корректно настраивайте массу и сопротивление. Если нужен строгий контроль над перемещением, рассматривайте CharacterController.
Похожие материалы
Скачать Microsoft Office бесплатно — инструкция
Накладки PS5: цвета, цены и предзаказ
Скрапинг изображений на Python: руководство