Урок 4 из 8 · 25 мин · обновлено

Движение и управление: ввод, физика и камера

Как прочитать нажатия клавиш через Input System, двигать объект физикой через Rigidbody, чем Update отличается от FixedUpdate и как заставить камеру следовать за игроком.

Rigidbody: подключаем физику

Можно двигать шар, меняя transform.position, но тогда он будет проходить сквозь стены и не будет катиться. Пусть лучше движением займётся физика.

Выберите Player и добавьте компонент Rigidbody (Add Component → Physics → Rigidbody). Нажмите Play: если приподнять шар над полом, он упадёт и остановится. Сферический коллайдер у шара уже есть, у пола тоже — поэтому шар не проваливается.

Главные настройки Rigidbody:

  • Mass — масса: тяжёлый шар труднее разогнать;
  • Linear Damping — сопротивление: чем больше, тем быстрее шар останавливается;
  • Use Gravity — подчиняется ли объект гравитации.

Ввод: Input System

В проектах Unity 6 нажатия читает Input System. В шаблоне уже настроены действия: Move — движение (WASD, стрелки, левый стик геймпада), Jump — прыжок (пробел, кнопка геймпада) и другие. Удобно: один и тот же код работает и с клавиатурой, и с геймпадом.

Действие Move выдаёт Vector2 — пару чисел (x, y): x — вправо-влево, y — вперёд-назад. Нажата D — (1, 0), нажата W — (0, 1), ничего не нажато — (0, 0).

В старых уроках из интернета встречается Input.GetAxis("Horizontal"). Это прежняя система ввода: в новых проектах она выключена, и такой код выдаст ошибку.

Скрипт управления

C#
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField] private float speed = 10f;

    private Rigidbody rb;
    private InputAction moveAction;
    private Vector2 moveInput;

    void Start()
    {
        rb = GetComponent<Rigidbody>();
        moveAction = InputSystem.actions.FindAction("Move");
    }

    void Update()
    {
        moveInput = moveAction.ReadValue<Vector2>();
    }

    void FixedUpdate()
    {
        Vector3 force = new Vector3(moveInput.x, 0, moveInput.y);
        rb.AddForce(force * speed);
    }
}

Разберём:

  • GetComponent<Rigidbody>() находит компонент Rigidbody на этом же объекте. Ищем один раз в Start и запоминаем в поле rb;
  • InputSystem.actions.FindAction("Move") находит действие по имени;
  • в Update читаем ввод — каждый кадр;
  • в FixedUpdate толкаем шар. Этот метод вызывается с постоянным шагом физики, по умолчанию 50 раз в секунду, независимо от числа кадров;
  • Vector3 — три числа (x, y, z). Двумерный ввод превращаем в трёхмерную силу: y ввода становится осью z, «вперёд», а вверх не толкаем;
  • AddForce толкает шар, а дальше физика сама разгоняет и тормозит его.

Повесьте скрипт на Player, нажмите Play и покатайте шар клавишами.

Камера за игроком

Шар укатывается за край экрана. Пусть камера следует за ним на постоянном расстоянии:

C#
using UnityEngine;

public class CameraFollow : MonoBehaviour
{
    [SerializeField] private Transform target;

    private Vector3 offset;

    void Start()
    {
        offset = transform.position - target.position;
    }

    void LateUpdate()
    {
        transform.position = target.position + offset;
    }
}

Повесьте скрипт на Main Camera и перетащите Player из Hierarchy в поле Target в Inspector. В Start камера запоминает, где она стоит относительно игрока. В LateUpdate, который вызывается после всех Update, она переезжает на то же расстояние от нового положения игрока.

Попробуйте сами. Добавьте прыжок: по действию Jump шар подпрыгивает. Прыгать можно только с земли, а не в воздухе.

Решение

Добавьте в PlayerController поля и код в Start и Update:

C#
[SerializeField] private float jumpForce = 5f;

private InputAction jumpAction;

void Start()
{
    rb = GetComponent<Rigidbody>();
    moveAction = InputSystem.actions.FindAction("Move");
    jumpAction = InputSystem.actions.FindAction("Jump");
}

void Update()
{
    moveInput = moveAction.ReadValue<Vector2>();

    bool onGround = Mathf.Abs(rb.linearVelocity.y) < 0.01f;
    if (jumpAction.WasPressedThisFrame() && onGround)
    {
        rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
    }
}

WasPressedThisFrame() срабатывает один раз в момент нажатия. Без этого шар прыгал бы каждый кадр, пока клавиша зажата. ForceMode.Impulse даёт мгновенный толчок, а не постепенный разгон.

Проверка «на земле» простая: если шар почти не движется по вертикали, он не в полёте. В настоящих играх землю проверяют точнее — лучом вниз, но для начала хватит.

Мини-тест: проверьте себя

Закрепите урок: несколько вопросов, разбор ответов — в конце.

  1. Что произойдёт с шаром, если добавить ему компонент Rigidbody и нажать Play?

  2. В каком методе правильно прикладывать силы к Rigidbody?

  3. Что вернёт moveAction.ReadValue<Vector2>(), если игрок держит клавишу D?

  4. Почему камеру двигают в LateUpdate, а не в Update?