Коротко. Чтобы сделать движение и прыжок персонажа в 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 отдаёт объект физическому движку: тот сам считает гравитацию, останавливает персонажа у стены и не даёт провалиться в пол. Мы лишь говорим ему, какую горизонтальную скорость держать в этом кадре и когда добавить вертикальный импульс для прыжка.
Отсюда три правила, которые сэкономят вам часы:
- Ввод — в
Update(), движение — вFixedUpdate(). Клавиши опрашиваются каждый кадр, а физика считается с фиксированным шагом (по умолчанию 50 раз в секунду). Если писать скорость вUpdate(), на разных FPS персонаж будет вести себя по-разному. - Меняем
linearVelocity, а неtransform.position. Так работают столкновения и гравитация. (В Unity до версии 6 свойство называлосьvelocity— код ниже отличается одним словом.) - Землю проверяем отдельно. Столкновение «с чем-то» — это ещё не земля: персонаж может касаться стены или потолка.
Шаг 1. Настраиваем компоненты персонажа и пола
Персонажу нужны два компонента: Rigidbody2D (физика) и коллайдер (форма для столкновений). Выделите спрайт героя и через Add Component добавьте:
Rigidbody2D— Body Type: Dynamic, Gravity Scale: 3 (с единицей персонаж падает «как на Луне»), Collision Detection: Continuous, а в разделе Constraints обязательно включите Freeze Rotation Z — иначе герой будет кувыркаться при каждом касании угла.Capsule Collider 2D— капсула лучше квадрата: скруглённые края не цепляются за стыки платформ. Подгоните размер под спрайт кнопкой Edit Collider.
Полу и платформам нужен только Box Collider 2D — без Rigidbody2D (статичные объекты и так неподвижны). И ещё одна вещь, без которой не заработает прыжок: слой Ground. В правом верхнем углу инспектора откройте выпадающий список Layer → Add Layer…, впишите в свободную строку Ground и назначьте этот слой всем платформам.
Частая ошибка: персонаж проваливается сквозь пол. Проверьте три вещи: у пола есть Collider2D, у персонажа коллайдер без галочки Is Trigger, и оба объекта в одной плоскости по Z (в 2D это не влияет на физику, но сбивает, когда спрайт «за» полом).
Шаг 2. Скрипт движения: WASD и стрелки
Создайте C#-скрипт PlayerController2D и повесьте на персонажа. Ввод читаем через новый Input System — в Unity 6 он включён по умолчанию, поэтому обращаемся к клавиатуре напрямую через Keyboard.current. Стрелки и WASD работают одновременно.
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;). Логика движения от этого не меняется.
Шаг 3. Прыжок и проверка «на земле»
Прыжок — это вертикальная скорость, которую мы задаём один раз по нажатию кнопки. Единственная сложность — понять, стоит ли персонаж на земле, иначе он будет прыгать бесконечно. Самый надёжный способ для новичка — невидимый круг под ногами: если он пересекается с чем-то на слое Ground, значит, мы стоим.
Создайте внутри персонажа пустой дочерний объект (ПКМ по персонажу → Create Empty), назовите GroundCheck и опустите его к самым ступням — чуть ниже нижнего края коллайдера. Затем дополните скрипт:
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.
Бонус: два приёма, чтобы управление ощущалось приятно
Базовый контроллер работает, но в хороших платформерах игроку немного «подыгрывают». Два самых дешёвых приёма, которые я добавляю почти в каждый проект:
- Coyote time — разрешить прыжок ещё 0,1 секунды после того, как персонаж сошёл с края платформы. Игрок нажал чуть позже, чем надо, но игра «прощает». Реализация: вместо
isGroundedхраните таймерcoyoteTimer, который сбрасывается в 0,1 на земле и убывает в воздухе; прыгать можно, пока он больше нуля. - Переменная высота прыжка — если игрок отпустил кнопку рано, прыжок короче. Реализация: когда кнопка не зажата и персонаж летит вверх (
rb.linearVelocity.y > 0), умножайте вертикальную скорость на0.5f.
Оба приёма — по 5–6 строк, но именно они отличают «какую-то демку» от игры, в которую хочется играть. Когда движение готово, следующий естественный шаг — переход на следующий уровень через финишную зону: там мы вешаем на персонажа тег Player и загружаем сцену из триггера.
Частые ошибки и как их починить
- Персонаж кувыркается. Не включён Freeze Rotation Z у
Rigidbody2D. - Проваливается сквозь пол. У пола нет коллайдера, либо у персонажа стоит Is Trigger. Если проваливается только на большой скорости — поставьте Collision Detection: Continuous.
- Прыгает бесконечно. Не назначен слой
Groundв поле Ground Layer или платформам не выставлен слойGround. Ещё вариант — кругGroundCheckвисит слишком высоко и никогда не касается пола: выделите персонажа и посмотрите на жёлтый гизмо. - Не прыгает вообще. Не перетащен объект в поле Ground Check (в консоли будет
NullReferenceException), или круг стоит внутри собственного коллайдера персонажа, а персонаж сам на слоеGround— уберите его с этого слоя. - Залипает на стенах в прыжке. Трение капсулы о стену. Создайте Physics Material 2D с Friction = 0 и назначьте его коллайдеру персонажа.
- Движение дёргается. Скорость задаётся в
Update(), а не вFixedUpdate(), либо камера следует за героем без сглаживания — вRigidbody2Dвключите Interpolate.
Вывод
Рабочий контроллер 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.


