Интерполяция позволяет создавать плавное движение объектов между дискретными состояниями. В контексте PixiJS это особенно важно при обновлении сцены с фиксированным шагом, когда частота рендеринга и логика игры могут работать с разной скоростью.
Наиболее часто используемый метод — линейная интерполяция. Она вычисляется по формуле:
[ (a, b, t) = a + (b - a) t]
Где:
a — начальное значение,b — конечное значение,t — коэффициент интерполяции (от 0 до 1).Пример применения для позиции спрайта в PixiJS:
const sprite = new PIXI.Sprite(texture);
sprite.x = 100;
sprite.y = 100;
let targetX = 400;
let targetY = 300;
let t = 0;
function update(delta) {
t += 0.01 * delta;
sprite.x = lerp(sprite.x, targetX, t);
sprite.y = lerp(sprite.y, targetY, t);
}
function lerp(a, b, t) {
return a + (b - a) * t;
}
Такой подход обеспечивает плавное приближение к конечной точке без резких скачков, даже если частота кадров колеблется.
При фиксированном шаге обновления (fixed timestep)
игровой логике необходимо обновляться с постоянной скоростью, например
60 раз в секунду, независимо от реальной частоты рендеринга.
Интерполяция между кадрами позволяет отрисовывать объекты между этими
фиксированными состояниями, сглаживая движение.
Классический пример реализации:
const timestep = 1 / 60;
let accumulator = 0;
let previousState = { x: 0 };
let currentState = { x: 0 };
let velocity = 100; // пикселей в секунду
function gameLoop(deltaTime) {
accumulator += deltaTime;
while (accumulator >= timestep) {
previousState.x = currentState.x;
currentState.x += velocity * timestep;
accumulator -= timestep;
}
const alpha = accumulator / timestep;
sprite.x = lerp(previousState.x, currentState.x, alpha);
}
Здесь:
accumulator аккумулирует реальное прошедшее время,alpha — коэффициент интерполяции между предыдущим и
текущим состоянием,lerp сглаживает переход между фиксированными
шагами.Интерполяция особенно эффективна для синхронизации:
Для спрайтов и контейнеров, имеющих координаты в виде векторов
(PIXI.Point или PIXI.ObservablePoint),
линейная интерполяция применяется по компонентам:
function lerpPoint(p1, p2, t) {
return new PIXI.Point(
lerp(p1.x, p2.x, t),
lerp(p1.y, p2.y, t)
);
}
sprite.position = lerpPoint(previousState.position, currentState.position, alpha);
Такой метод позволяет одновременно интерполировать позицию по
x и y без разрыва движения.
Для вращения (rotation) и масштаба (scale)
необходимо учитывать цикличность углов и неоднородность интервала.
Например, при вращении через π и -π нужно корректировать направление
интерполяции:
function lerpAngle(a, b, t) {
let diff = b - a;
while (diff > Math.PI) diff -= 2 * Math.PI;
while (diff < -Math.PI) diff += 2 * Math.PI;
return a + diff * t;
}
sprite.rotation = lerpAngle(previousState.rotation, currentState.rotation, alpha);
Для масштаба линейная интерполяция подходит напрямую:
sprite.scale.x = lerp(previousState.scale.x, currentState.scale.x, alpha);
sprite.scale.y = lerp(previousState.scale.y, currentState.scale.y, alpha);
lerp должно быть минимальным по количеству
операций на кадр, особенно для большого количества спрайтов.В сложных проектах используют комбинацию:
Такой подход позволяет поддерживать точность игровой логики и одновременно создавать визуально приятное движение объектов в PixiJS.