Векторная математика движения
За движением объектов стоит несложная векторная математика. Разберём её на живых примерах, которые можно запустить прямо здесь.
Вектор — это направление и длина. Нормализация даёт чистое направление (длина 1), а интерполяция — плавный переход между значениями.
Длина и нормализация
В Three.js векторы — это THREE.Vector3 с готовыми методами .length() и .normalize(). Но чтобы понять, что под капотом, посчитаем руками. Длина вектора — теорема Пифагора в 3D; нормализация — деление каждой компоненты на длину.
function length(v) {
return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}
function normalize(v) {
const len = length(v);
return { x: v.x / len, y: v.y / len, z: v.z / len };
}
const v = { x: 3, y: 4, z: 0 };
const n = normalize(v);
console.log('Длина исходного:', length(v));
console.log('Нормализованный:', {
x: +n.x.toFixed(2), y: +n.y.toFixed(2), z: n.z
});
console.log('Длина нормализованного:', +length(n).toFixed(4));
Вывод:
Длина исходного: 5
Нормализованный: { x: 0.6, y: 0.8, z: 0 }
Длина нормализованного: 1
Зачем это нужно: чтобы двигать объект с постоянной скоростью, берут нормализованный вектор направления и умножают на скорость. Иначе скорость зависела бы от случайной длины вектора.
Линейная интерполяция (lerp)
Чтобы объект плавно переехал из точки A в точку B, используют lerp — линейную интерполяцию. Формула проста: a + (b - a) * t, где t идёт от 0 (старт) до 1 (финиш).
function lerp(a, b, t) {
return a + (b - a) * t;
}
const from = 0, to = 10;
for (const t of [0, 0.25, 0.5, 0.75, 1]) {
console.log('t=' + t, '->', lerp(from, to, t));
}
Вывод:
t=0 -> 0 t=0.25 -> 2.5 t=0.5 -> 5 t=0.75 -> 7.5 t=1 -> 10
Тот же приём двигает камеру, плавно меняет цвет, подтягивает объект к цели. В Three.js это vectorA.lerp(vectorB, t).
Поворот точки
Чтобы заставить объект кружить по окружности, точку поворачивают вокруг оси. Поворот в плоскости XZ на угол a описывается синусом и косинусом.
function rotateXZ(x, z, angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return {
x: +(x * cos - z * sin).toFixed(3),
z: +(x * sin + z * cos).toFixed(3)
};
}
const start = { x: 1, z: 0 };
console.log('0 град:', rotateXZ(start.x, start.z, 0));
console.log('90 град:', rotateXZ(start.x, start.z, Math.PI / 2));
console.log('180 град:', rotateXZ(start.x, start.z, Math.PI));
Вывод:
0 град: { x: 1, z: 0 }
90 град: { x: 0, z: 1 }
180 град: { x: -1, z: 0 }
Точка (1, 0) при повороте на 90° уезжает в (0, 1), а на 180° — в (-1, 0). Подставляя в угол текущее время, вы получаете объект, который вечно кружит по орбите.
Итог
- Нормализация даёт единичный вектор направления — основа движения с постоянной скоростью.
- lerp(a,b,t) — плавный переход; t от 0 до 1.
- Поворот в плоскости — через sin/cos; так делают орбиты и круговое движение.