Уроки · 50
Линейная интерполяция
В уроке «Проценты» уже была доля от 0.0 до 1.0 — «сколько от целого». Применим её к отрезку: если доля равна 0, точка окажется точно в начале отрезка, если 1 — точно в конце, а любое число между ними — где-то посередине пути. Такое движение между двумя точками называется линейной интерполяцией, а сокращённо, как обычно говорят в играх и графике — lerp (от английского linear interpolation).
Работает это не только с точками на плоскости. Так можно интерполировать и одно-единственное число (например, яркость света от 0 до 255), и точку в 3D с тремя координатами, и вообще сколько угодно чисел сразу — просто каждое из них по отдельности считается той же формулой. Мы берём пример именно с 2D-точкой потому, что у нас 2D-игра — не потому, что lerp умеет работать только с точками на плоскости.
Где это пригождается
Заголовок раздела «Где это пригождается»Пригождается это не только для статичной точки на месте, а для
движения. Если на каждом кадре понемногу увеличивать t — например,
на 0.02 — объект долетит от start до end ровно за одно и то же
число кадров, каким бы длинным ни был путь. Это отличается от
привычного движения с постоянной скоростью, как в уроке
«Движение»: там чем дальше цель, тем дольше добираться,
а через lerp — всегда одинаково по времени, просто на длинных отрезках
объект летит быстрее. Так удобно, например, плавно подвинуть
UI-элемент или камеру к цели — неважно, далеко она или близко, доедут
они за одну и ту же долю секунды.
Для точки (x, y) доля t применяется к каждой координате отдельно,
причём совершенно одинаково: к первой координате прибавляется разница
между второй координатой и первой, умноженная на t. Для x и y —
буквально одна и та же формула, просто с разными числами:
let x = from.x + (to.x - from.x) * t; // x между from.x и to.x, доля tlet y = from.y + (to.y - from.y) * t; // то же самое по yПопробуй сам
Заголовок раздела «Попробуй сам»Двигай ползунок или впиши число — синяя точка едет по отрезку:
при t = 0 она в начале, при t = 1 — в
конце, а между ними — пропорционально доле пути:
use macroquad::prelude::*;
// Точка — как в уроке «Точка и прямоугольник».struct Point { x: f32, y: f32,}
// Линейная интерполяция (lerp): точка на пути от from к to, где t — доля// пройденного пути, от 0.0 (ещё в from) до 1.0 (уже в to).fn lerp(from: Point, to: Point, t: f32) -> Point { Point { x: from.x + (to.x - from.x) * t, y: from.y + (to.y - from.y) * t, }}
// t одна и та же на каждом кадре, поэтому это константа (как в уроке// «Константы»), а не переменная внутри loop.const T: f32 = 0.50;
#[macroquad::main("Линейная интерполяция")]async fn main() { loop { clear_background(BLACK);
// Начало и конец отрезка — по умолчанию на одной высоте. let start = Point { x: screen_width() * 0.2, y: screen_height() / 2.0 }; let end = Point { x: screen_width() * 0.8, y: screen_height() / 2.0 };
// Сам отрезок. draw_line(start.x, start.y, end.x, end.y, 4.0, YELLOW); draw_circle(start.x, start.y, 10.0, WHITE); draw_circle(end.x, end.y, 10.0, WHITE);
// Точка на отрезке, определяемая t. let point = lerp(start, end, T); draw_circle(point.x, point.y, 14.0, SKYBLUE);
next_frame().await; }}Что здесь происходит
Заголовок раздела «Что здесь происходит»struct Point { x, y }— снова та же точка, что и в уроке «Точка и прямоугольник».lerp(from, to, t)— берёт две точки и долюt, возвращает точку где-то между ними: приt = 0.0этоfrom, приt = 1.0—to.- Синяя точка — результат
lerp(start, end, t), пересчитывается каждый кадр под текущее значениеt. - Ползунок и числовое поле управляют одним и тем же
t— подвинь один, и другой обновится следом. tв коде —const T, а не переменная внутриloop: значение одно и то же на каждом кадре, пока ты сам не подвинешь ползунок, поэтому это константа, как в уроке «Константы».