Пиксельный персонаж-маскот прыгает между двумя платформами в 2D-игре на Unity

Как сделать движение и прыжок персонажа в Unity 2D

·  10 мин чтения Для начинающих Unity 2D C# Физика Туториал

Коротко. Чтобы сделать движение и прыжок персонажа в Unity 2D, добавьте ему Rigidbody2D и Collider2D, в скрипте читайте ввод в Update(), а скорость задавайте в FixedUpdate(): rb.linearVelocity = new Vector2(moveX * speed, rb.linearVelocity.y);. Прыжок — это вертикальная скорость по кнопке, но только когда персонаж стоит на земле: землю проверяем маленьким кругом Physics2D.OverlapCircle(groundCheck.position, 0.15f, groundLayer).

Ниже — готовый скрипт PlayerController2D под Unity 6, настройка компонентов по шагам, разбор «почему персонаж вращается, проваливается и прыгает в воздухе» и пара приёмов, от которых управление начинает ощущаться приятно.

Движение и прыжок — первая механика, которую делает почти каждый, кто открыл Unity. И почти у каждого она ломается одинаково: персонаж кувыркается, залипает на стенах, прыгает по десять раз в воздухе или вообще проходит сквозь пол. Хорошая новость: все эти проблемы решаются одним аккуратным скриптом и тремя галочками в инспекторе. За 20 минут мы соберём контроллер 2D-персонажа, который можно брать в любой платформер, а в конце разберём, как сделать управление «вкусным».

Мой первый платформер на Unity двигался через transform.Translate — и герой лихо пролетал сквозь стены, если разогнаться. С тех пор я собрал десятки контроллеров для учебных проектов и своих игр, и рецепт устоялся: только физика, ввод отдельно от движения, земля — отдельной проверкой. Именно его и разложим.

Как работает движение персонажа в Unity 2D

Движение в 2D-платформере — это изменение скорости компонента Rigidbody2D, а не «сдвиг координат». Rigidbody2D отдаёт объект физическому движку: тот сам считает гравитацию, останавливает персонажа у стены и не даёт провалиться в пол. Мы лишь говорим ему, какую горизонтальную скорость держать в этом кадре и когда добавить вертикальный импульс для прыжка.

Отсюда три правила, которые сэкономят вам часы:

Шаг 1. Настраиваем компоненты персонажа и пола

Персонажу нужны два компонента: Rigidbody2D (физика) и коллайдер (форма для столкновений). Выделите спрайт героя и через Add Component добавьте:

Инспектор Unity: компонент Rigidbody2D с включённым Freeze Rotation Z и Capsule Collider 2D на персонаже
Freeze Rotation Z — самая важная галочка в этой статье.

Полу и платформам нужен только Box Collider 2D — без Rigidbody2D (статичные объекты и так неподвижны). И ещё одна вещь, без которой не заработает прыжок: слой Ground. В правом верхнем углу инспектора откройте выпадающий список Layer → Add Layer…, впишите в свободную строку Ground и назначьте этот слой всем платформам.

Инспектор Unity: платформа с Box Collider 2D и назначенным слоем Ground
Платформа: Box Collider 2D и слой Ground. Rigidbody ей не нужен.

Частая ошибка: персонаж проваливается сквозь пол. Проверьте три вещи: у пола есть Collider2D, у персонажа коллайдер без галочки Is Trigger, и оба объекта в одной плоскости по Z (в 2D это не влияет на физику, но сбивает, когда спрайт «за» полом).

Шаг 2. Скрипт движения: WASD и стрелки

Создайте C#-скрипт PlayerController2D и повесьте на персонажа. Ввод читаем через новый Input System — в Unity 6 он включён по умолчанию, поэтому обращаемся к клавиатуре напрямую через Keyboard.current. Стрелки и WASD работают одновременно.

C#
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController2D : MonoBehaviour
{
    [Header("Движение")]
    [SerializeField] private float speed = 7f;

    private Rigidbody2D rb;
    private float moveX; // -1 влево, 0 стоим, 1 вправо

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    // Ввод читаем каждый кадр
    private void Update()
    {
        var kb = Keyboard.current;
        if (kb == null) return;

        moveX = 0f;
        if (kb.aKey.isPressed || kb.leftArrowKey.isPressed)  moveX = -1f;
        if (kb.dKey.isPressed || kb.rightArrowKey.isPressed) moveX =  1f;

        // Разворачиваем спрайт по направлению движения
        if (moveX != 0f)
            transform.localScale = new Vector3(Mathf.Sign(moveX), 1f, 1f);
    }

    // Физику двигаем с фиксированным шагом
    private void FixedUpdate()
    {
        // В Unity до версии 6 вместо linearVelocity пишите velocity
        rb.linearVelocity = new Vector2(moveX * speed, rb.linearVelocity.y);
    }
}

Нажмите Play — персонаж уже бегает влево-вправо, упирается в стены и падает на платформы. Обратите внимание на строку с linearVelocity: по X мы задаём скорость сами, а по Y оставляем текущую — иначе каждый физический шаг мы бы обнуляли гравитацию, и герой зависал бы в воздухе.

Заметка: если Unity ругается на Keyboard — у вас старая система ввода. Либо переключите Project Settings → Player → Active Input Handling на Input System Package, либо замените чтение клавиш одной строкой: moveX = Input.GetAxisRaw("Horizontal"); (и уберите using UnityEngine.InputSystem;). Логика движения от этого не меняется.

Окно Project Settings в Unity: параметр Active Input Handling со значением Input System Package
Где проверить, какая система ввода включена в проекте.

Шаг 3. Прыжок и проверка «на земле»

Прыжок — это вертикальная скорость, которую мы задаём один раз по нажатию кнопки. Единственная сложность — понять, стоит ли персонаж на земле, иначе он будет прыгать бесконечно. Самый надёжный способ для новичка — невидимый круг под ногами: если он пересекается с чем-то на слое Ground, значит, мы стоим.

Создайте внутри персонажа пустой дочерний объект (ПКМ по персонажу → Create Empty), назовите GroundCheck и опустите его к самым ступням — чуть ниже нижнего края коллайдера. Затем дополните скрипт:

Сцена Unity: дочерний объект GroundCheck у ступней персонажа с жёлтым кругом-гизмо проверки земли
Круг должен чуть «утопать» в пол — тогда проверка сработает надёжно.
C#
using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Rigidbody2D))]
public class PlayerController2D : MonoBehaviour
{
    [Header("Движение")]
    [SerializeField] private float speed = 7f;

    [Header("Прыжок")]
    [SerializeField] private float jumpForce = 14f;
    [SerializeField] private Transform groundCheck;      // пустой объект у ступней
    [SerializeField] private float groundRadius = 0.15f;
    [SerializeField] private LayerMask groundLayer;      // выбрать слой Ground

    private Rigidbody2D rb;
    private float moveX;
    private bool jumpPressed;
    private bool isGrounded;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        var kb = Keyboard.current;
        if (kb == null) return;

        moveX = 0f;
        if (kb.aKey.isPressed || kb.leftArrowKey.isPressed)  moveX = -1f;
        if (kb.dKey.isPressed || kb.rightArrowKey.isPressed) moveX =  1f;

        if (moveX != 0f)
            transform.localScale = new Vector3(Mathf.Sign(moveX), 1f, 1f);

        // Запоминаем нажатие, чтобы не потерять его между кадрами физики
        if (kb.spaceKey.wasPressedThisFrame || kb.wKey.wasPressedThisFrame || kb.upArrowKey.wasPressedThisFrame)
            jumpPressed = true;
    }

    private void FixedUpdate()
    {
        // 1. Стоим ли на земле?
        isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, groundLayer);

        // 2. Горизонтальное движение
        rb.linearVelocity = new Vector2(moveX * speed, rb.linearVelocity.y);

        // 3. Прыжок — только с земли
        if (jumpPressed && isGrounded)
            rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);

        jumpPressed = false;
    }

    // Рисуем круг проверки в редакторе, чтобы видеть, где он
    private void OnDrawGizmosSelected()
    {
        if (groundCheck == null) return;
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(groundCheck.position, groundRadius);
    }
}

Осталось два действия в инспекторе: перетащите объект GroundCheck в поле Ground Check и в выпадающем списке Ground Layer отметьте слой Ground. Нажмите Play — персонаж бегает, прыгает по пробелу и не умеет прыгать в воздухе.

Совет: прыжок кажется «ватным»? Не трогайте jumpForce первым делом — поднимите Gravity Scale у Rigidbody2D до 3–4 и уже под него подберите силу прыжка. Быстрое падение делает прыжок резким и «отзывчивым», как в Celeste или Hollow Knight.

Бонус: два приёма, чтобы управление ощущалось приятно

Базовый контроллер работает, но в хороших платформерах игроку немного «подыгрывают». Два самых дешёвых приёма, которые я добавляю почти в каждый проект:

Оба приёма — по 5–6 строк, но именно они отличают «какую-то демку» от игры, в которую хочется играть. Когда движение готово, следующий естественный шаг — переход на следующий уровень через финишную зону: там мы вешаем на персонажа тег Player и загружаем сцену из триггера.

Частые ошибки и как их починить

Вывод

Рабочий контроллер 2D-персонажа — это Rigidbody2D с замороженным вращением, капсульный коллайдер, ввод в Update(), скорость в FixedUpdate() и маленький круг под ногами для проверки земли. Скрипт из статьи можно копировать в любой платформер и постепенно наращивать: coyote time, переменный прыжок, рывок, анимации.

Если вы только устанавливаете движок — начните с гайда как установить Unity и создать первый проект, а сомневаетесь между 2D и 3D — загляните в разбор, что выбрать новичку. Хочется собрать по такому же принципу целую игру с поддержкой и проверкой заданий — это делаем на бесплатном курсе Unity Старт и дальше по полной программе.

Частые вопросы

Как сделать движение персонажа в Unity 2D?

Добавьте персонажу Rigidbody2D и Collider2D, считайте ввод в Update() и в FixedUpdate() задайте скорость: rb.linearVelocity = new Vector2(moveX * speed, rb.linearVelocity.y). Так персонаж двигается по физике и корректно сталкивается со стенами и платформами.

Как сделать прыжок в Unity 2D?

По нажатию кнопки задайте вертикальную скорость: rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce). Перед этим проверьте, что персонаж стоит на земле, иначе он будет прыгать в воздухе бесконечно.

Как проверить, что персонаж стоит на земле (is grounded)?

Самый надёжный способ — Physics2D.OverlapCircle(groundCheck.position, 0.15f, groundLayer): маленький круг под ногами проверяет, касается ли он объектов на слое Ground. Способ с OnCollisionEnter2D хуже: он считает землёй и стену, и потолок.

Чем transform.Translate отличается от Rigidbody2D для движения?

transform.Translate просто телепортирует объект на новую позицию каждый кадр, минуя физику — персонаж проходит сквозь стены и дёргается на коллайдерах. Rigidbody2D двигает объект через физический движок: столкновения, гравитация и прыжок работают сами. Для платформера всегда используйте Rigidbody2D.

Почему персонаж падает или вращается при движении?

Персонаж вращается, если у Rigidbody2D не включена галочка Freeze Rotation Z в разделе Constraints. Проваливается сквозь пол — если у пола нет Collider2D или у самого персонажа коллайдер стоит с галочкой Is Trigger.

Хочешь собрать свой первый платформер целиком?

На бесплатном курсе Unity Старт мы ставим движок, делаем первый проект и первый скрипт — а дальше по программе собираем полноценные игры с проверкой заданий.

Начать бесплатно