Гид по технологиям

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

9 min read Game Development Обновлено 05 Jan 2026
Unity: физический контроллер персонажа на C#
Unity: физический контроллер персонажа на C#

Геймпад с наложенным фрагментом кода

Введение

Физический контроллер персонажа (rigidbody-based) использует физику Unity для управления передвижением и столкновениями. Это удобно для классических игр с физикой, скользящими и реалистичными реакциями на сталкивания. Краткое определение: Rigidbody — компонент Unity, который позволяет объекту двигаться под управлением физического движка.

Важно: этот подход даёт естественное поведение тела в столкновениях, но усложняет точное управление (например, точную платформерную механику). Для точного контроля используйте компонент CharacterController или кастомную кинематическую логику.

Что вы получите в конце

  • Рабочий прототип куба-персонажа с движением, поворотом и прыжком.
  • Чек-листы для разработчика, QA и дизайнера.
  • Советы по отладке и улучшению контроля.

Шаг 1: Создание сцены с игроком и террейном

Вид сцены Unity с кубом-персонажем и плоскостью

  1. Откройте Unity и создайте новый проект (3D).
  2. В иерархии (Hierarchy) добавьте плоскость: правый клик → 3D Object → Plane. Это будет ваш уровень/террейн для тестов.
  3. Добавьте куб: правый клик → 3D Object → Cube. Разместите его над Plane так, чтобы он падал на поверхность.
  4. Выделите Cube, в инспекторе нажмите Add Component → Rigidbody. Оставьте Use Gravity включённым.
  5. Установите основную камеру как дочернюю от 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: Тестирование и отладка

  1. Сохраните скрипт и перетащите его на объект Cube в иерархии.
  2. В инспекторе назначьте Rigidbody (если не назначился автоматически) и задайте speed и torque.
  3. Нажмите 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 (фиксированная физика).

Тестовые случаи и сценарии приёма

  1. Нормальный прыжок: нажать Space → куб отлетает вверх и возвращается через ожидаемое время; повторный прыжок недоступен до завершения интервала.
  2. Движение и столкновения: движение вперёд к стене → персонаж корректно сталкивается и не проходит сквозь стену.
  3. Поворот при движении: поворот прописан и не вызывает чрезмерного вращения по нефиксированным осям.
  4. Различная частота кадров: запустить игру при низком 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;
}

Это надёжнее, чем таймеры для управления прыжком, особенно если уровень содержит лестницы или платформы разной высоты.

Мини-методология: как развивать контроллер дальше

  1. Начните с Rigidbody-прототипа (как описано).
  2. Добавьте проверку isGrounded через Raycast или OnCollision.
  3. Перенесите физические вызовы в FixedUpdate.
  4. Реализуйте плавную анимацию (Animator) и синхронизацию с физикой.
  5. При необходимости замените на 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.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Скачать Microsoft Office бесплатно — инструкция
Программное обеспечение

Скачать Microsoft Office бесплатно — инструкция

Накладки PS5: цвета, цены и предзаказ
Игры

Накладки PS5: цвета, цены и предзаказ

Скрапинг изображений на Python: руководство
Веб-скрапинг

Скрапинг изображений на Python: руководство

История буфера обмена на Android: просмотр и управление
Android.

История буфера обмена на Android: просмотр и управление

Изменить ключевой кадр Live Photo на iPhone
Фото

Изменить ключевой кадр Live Photo на iPhone

Как обойти заблокированные сайты — PHProxy
Сеть

Как обойти заблокированные сайты — PHProxy