Движение объекта через код и Time.deltaTime

Заставим объект двигаться кодом. Здесь же разберём важнейшую формулу геймдева — почему движение умножают на Time.deltaTime.

Суть: чтобы двигать объект в Update, меняй transform.position или вызывай transform.Translate. Скорость всегда умножай на Time.deltaTime — время прошлого кадра. Тогда объект движется одинаково при любом FPS.

Самое частое действие в играх — что-то двигать. В Unity объект двигают, меняя его transform.position. Но если просто прибавлять скорость каждый кадр, скорость будет зависеть от FPS: на 120 FPS объект улетит вдвое быстрее, чем на 60. Решение — Time.deltaTime.

Time.deltaTime — это время, прошедшее с прошлого кадра, в секундах. На 60 FPS это ~0.0166. Умножая скорость на него, ты переводишь «единиц в секунду» в «единиц за этот кадр». Итог: за секунду объект пройдёт одинаковый путь при любом FPS.

Как работает под капотом

Формула кадрового движения:

  путь_за_кадр = скорость * Time.deltaTime

  60 FPS:  dt=0.0166, скорость=5 -> за кадр 0.083, за сек ~5.0
  30 FPS:  dt=0.0333, скорость=5 -> за кадр 0.166, за сек ~5.0
                                       (тот же путь за секунду!)

Для движения по направлению используют вектор. Например ввод «вправо-вверх» даёт направление (1, 1). Но у диагонали длина больше 1, и объект двигался бы по диагонали быстрее! Поэтому направление нормализуют — приводят к длине 1, чтобы скорость была одинаковой во все стороны.

using UnityEngine;

public class SimpleMover : MonoBehaviour
{
    [SerializeField] private float speed = 5f;

    void Update()
    {
        // направление по стрелкам (упрощённо)
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        Vector2 dir = new Vector2(h, v).normalized;   // длина = 1
        transform.position += (Vector3)(dir * speed * Time.deltaTime);
    }
}

Эта математика языко-независима. Посчитаем движение по вектору с нормализацией на Python:

import math

def normalize(x, y):
    length = math.hypot(x, y)        # длина вектора
    if length == 0:
        return (0.0, 0.0)
    return (x / length, y / length)  # длина становится 1

speed = 5.0
dt = 1 / 60                          # один кадр при 60 FPS
x, y = 0.0, 0.0

# Игрок жмёт вправо-вверх: направление (1, 1)
dx, dy = normalize(1, 1)
print(f"Нормализованное направление: ({dx:.3f}, {dy:.3f})")

for frame in range(60):              # одна секунда
    x += dx * speed * dt
    y += dy * speed * dt

print(f"За секунду герой в точке ({x:.2f}, {y:.2f}), путь = {math.hypot(x, y):.2f}")

Та же логика на Python ▶ — обрати внимание: путь за секунду равен ~5 (наша скорость), хотя двигались по диагонали. Это заслуга нормализации. Без неё диагональ была бы быстрее в ~1.41 раза.

Частые ошибки

  • Забыть Time.deltaTime. Тогда скорость зависит от FPS: на мощном ПК герой носится, на слабом ползёт. Классический баг новичка.
  • Не нормализовать диагональ. Объект двигается по диагонали быстрее, чем по прямой — игроки это чувствуют.
  • Двигать физический объект через transform.position. Если у объекта есть Rigidbody2D, прямой сдвиг position конфликтует с физикой. Об этом — в разделе про физику.

Best practices

  • Всегда: скорость * Time.deltaTime в Update для любого плавного движения.
  • Нормализуй вектор направления перед умножением на скорость.
  • Скорость держи в [SerializeField]-поле, чтобы подбирать её мышкой.

Итоги: объект двигают, меняя transform.position в Update. Скорость умножают на Time.deltaTime, чтобы движение не зависело от FPS, и нормализуют направление, чтобы диагональ не была быстрее. Эта формула — фундамент любого движения в играх.

Кинематика против физики движения

Важно с самого начала различать два способа двигать объект. Первый — кинематический: ты напрямую задаёшь позицию через transform.position, как мы делали выше. Объект окажется ровно там, куда ты сказал, мгновенно и точно — но он игнорирует физику: пройдёт сквозь стену, не оттолкнётся, не упадёт. Второй — физический: ты задаёшь скорость или силу телу с Rigidbody, а движок сам двигает его с учётом столкновений и гравитации. Точного контроля меньше, зато всё взаимодействует по-настоящему. Правило простое: если у объекта есть Rigidbody2D, двигай его через физику, а не через transform.position — иначе два механизма начнут спорить за позицию и объект задёргается. К физическому движению мы перейдём в следующем разделе, а пока запомни эту развилку как одну из главных в геймдеве.

Проверьте себя
1. Зачем движение в Update умножают на Time.deltaTime?
AДля красоты
BЧтобы путь за секунду не зависел от FPS
CЧтобы сэкономить память
DЭто требование C#
2. Зачем нормализуют вектор направления движения?
AЧтобы объект светился
BЧтобы скорость по диагонали не была больше, чем по прямой
CЧтобы уменьшить FPS
DЧтобы объект не сталкивался