Vec2 и Vec3
Векторные типы SDK. Поля — обычные изменяемые числа (v.x = 3 работает), но каждый метод
чистый: a.add(b) возвращает НОВЫЙ вектор и никогда не трогает a. Единственные мутаторы —
set и copy. Оба типа делят один и тот же API; Vec3 добавляет операции только для 3D
(векторное произведение как вектор, преобразования кватернионом и матрицей).
Везде, где принимается вектор, подойдёт и «сырой» кортеж (Vec2Like / Vec3Like): обёртка,
кортеж [x, y] / [x, y, z], number[] или Float32Array. Векторы итерируемы — их можно
разворачивать и деструктурировать как кортежи.
Обзор
const toTarget = new Vec3(target.position).sub(pos)
const dir = toTarget.normalize() // чисто — toTarget не меняется
pos = pos.add(dir.scale(speed * dt)) // переприсваиваем; add() возвращает новый вектор
node.position = [0, 1, 0] // сырые кортежи работают везде, где ждут Vec3
const [x, y, z] = node.position // векторы деструктурируются как кортежи
if (toTarget.lengthSq() < 4) attack() // дешевле, чем length(), для проверки дистанцииСоздание
new Vec2(x?, y?) // компоненты; по умолчанию (0, 0)
new Vec2([3, 4]) // или копия любого Vec2Like
new Vec3(x?, y?, z?) // по умолчанию (0, 0, 0)
Vec3.from(v: Vec3Like): Vec3
Vec2.zero / .one / .up / .down / .left / .right
Vec3.zero / .one / .up / .down / .left / .right / .forward / .backСтатические константы — это геттеры: каждое обращение возвращает свежий экземпляр, поэтому менять
его безопасно. Vec3.forward — это −Z (камеры смотрят вдоль −Z), Vec3.back — это +Z.
Vec2.up — это +Y (2D-мир имеет ось Y вверх).
Мутация — только эти два метода меняют this
v.set(x, y): this // Vec3: set(x, y, z)
v.copy(other: Vec2Like): thisАрифметика (чистая — каждый метод возвращает новый вектор)
a.add(b) a.sub(b) a.mul(b) a.div(b) // покомпонентно; b может быть кортежем
a.scale(s) // умножить на скаляр
a.scaleAndAdd(b, s) // a + b·s за один вызов
a.negate()
a.withX(x) a.withY(y) // копия с заменой одной компоненты (Vec3: withZ)Произведения и меры
a.dot(b): number
a.length(): number a.lengthSq(): number
a.distanceTo(b): number a.distanceSqTo(b): number
a.angle(b): number // угол между векторами, РАДИАНЫ (0, если один из них нулевой)
a.normalize(): Vec // единичный вектор; нулевой вектор нормализуется в нулевойДля сравнений предпочитайте варианты с Sq — они пропускают извлечение корня.
Интерполяция и зажатие
a.lerp(b, t) // покомпонентное смешивание, t обычно 0..1
a.clamp(min, max) // покомпонентный зажим между двумя векторами
a.min(b) a.max(b) // покомпонентный минимум / максимумТолько Vec2
a.cross(b): number // скалярная z-компонента 3D-векторного произведения — знаковая площадь
a.perp(): Vec2 // (-y, x): перпендикуляр, повёрнутый на 90° против часовой
a.rotate(rad, origin?) // поворот против часовой на РАДИАНЫ вокруг origin (по умолчанию [0, 0])
a.heading(): number // atan2(y, x) — угол вектора в РАДИАНАХТолько Vec3
a.cross(b): Vec3
a.reflect(normal) // отражение относительно ЕДИНИЧНОЙ нормали: v − 2(v·n)n
a.project(onto) // проекция a на `onto` (нулевой `onto` → нулевой вектор)
a.rotateX(rad, origin?) // поворот точки вокруг оси через origin, РАДИАНЫ
a.rotateY(rad, origin?)
a.rotateZ(rad, origin?)
a.rotate(q) // поворот кватернионом Quat или сырым [x, y, z, w]
a.transform(m) // преобразование как ТОЧКИ матрицей Mat4 / сырым column-major массивом на 16
// — применяет перенос и перспективное делениеДля направления (без переноса) используйте Mat4.transformDirection, а не transform.
Совместимость
a.equals(b, eps? /* 1e-6 */): boolean // сравнение с относительным эпсилон
a.clone(): Vec
a.toArray(): [number, number] // Vec3: [number, number, number]
const [x, y] = a; [...a] // итерируемыйПодводные камни
// ✗ ждать, что методы мутируют — они этого не делают
dir.normalize() // результат отброшен; dir не изменился
dir = dir.normalize() // ✓ переприсвоить
// ✗ менять копию трансформа узла — геттеры возвращают свежие значения
node.position.x = 3 // тихий no-op (см. Соглашения)
node.x = 3 // ✓