В этой статье Вы изучите линейную алгебру — раздел алгебры, изучающий математические объекты линейной природы: векторные (или линейные) пространства, линейные отображения, системы линейных уравнений. А конкретно Вы изучите вычисления с векторами.
Что такое вектор?
В играх вектора используются для хранения местоположений, направлений и скоростей. Двухмерный вектор выглядит вот так: (0, 0). Первое число — значение координаты вдоль оси абсциссы (оси X). Второе число — значение координаты вдоль оси ординаты (оси Y).
Однако Defold — это полностью трёхмерный движок и в нём нет двухмерных векторов. Вместо этого используются трёхмерные вектора и выглядят они вот так: (0, 0, 0). Первые два числа означают тоже самое, а третье число — значение координаты вдоль оси аппликата (оси Z)
Сложение и вычитание векторов
Сложение векторов
Чтобы сложить вектора, нам надо просто сложить каждую их составляющую друг с другом. Например:
(0, 1, 4) + (3, -2, 5) = (0+3, 1-2, 4+5) = (3, -1, 9)
На языке Lua это выглядит вот так:
print(vmath.vector3(0, 1, 4)+vmath.vector3(3, -2, 5))
print() - это функция, которая выводит сообщение в консоль.
vmath - это набор функций, который позволяет работать с векторной математикой. Приписываем к слову vmath точку и выбираем нужную функцию. vmath.vector3(x,y,z) — создает новый вектор с компонентами, установленными на указанные значения.
Вычитание векторов
Вычитание рассчитывается по тому-же принципу что и сложение — вычитаем соответствующие компоненты векторов. Вычитание векторов удобно для получения вектора, который показывает из одного местоположения на другое. С помощью вычитания векторов мы можем задать направление движения пули. Пример рассмотрим в подтеме “Нормализация”
Длина вектора
Длина вектора — это длина направленного отрезка, определяющего вектор. Другими словами, это расстояние между его началом и концом. Длину вектора можно получить по этой формуле: |a| = √(x² + y²)
Это по сути теорема Пифагора (нахождение длины гипотенузы по двум катетам). (Для трёхмерных векторов |a| = √(x² + y²+z²) ). В Defold есть встроенная функция vmath.length(v).
Параметры этой функции (значения, которые можно вводить): vector3, vector4, quat. О vector4 и quat поговорим в других статьях.
Длину вектора можно использовать, чтобы определить расстояние между двумя объектами. Пример на Defold:
local player_pos = go.get_position("/player")
local explosion_pos = go.get_position("/explosion")
local vector_distance = player_pos-explosion_pos
local distance = vmath.length(vector_distance)
print(distance)
-- В консоли выведется десятичное число.
-- Например, если vector_distance = vmath.vector3(1, 2, 0), то выведется 2.2360680103302
Однако, в официальной документации Defold советуют использовать для сравнения длины векторов квадрат длины — vmath.length_sqr(v), так как это немного эффективнее для вычисления (исключает вычисление квадратного корня)
Нормализация
Когда мы имеем дело с направлениями (в отличие от местоположений и скоростей), важно, чтобы вектор направления имел длину, равную единице. Это сильно упрощает нам жизнь.
Вектор с длиной равной единице называется «нормализованным». Как сделать вектор нормализованным? Надо делить каждый компонент вектора на его длину. Если, к примеру, мы хотим нормализовать вектор V с компонентами (3, 4), мы просто делим каждый компонент на его длину, то есть на 5, и получаем (3/5, 4/5). Теперь, с помощью теоремы Пифагора, мы убедимся в том, что его длина равна единице:
(3/5)² + (4/5)² = 9/25 + 16/25 = 25/25 = 1
В Defold есть функция vmath.normalize(v1). Она нормализует вектор, т.е. возвращает новый вектор с тем же направлением, что и входной вектор, но с длиной 1. Длина вектора должна быть больше 0, в противном случае произойдет деление на ноль.
Рассмотрим пример, где враг каждую секунду стреляет в направлении позиции игрока:
enemy.script
local function fire()
local player_pose = go.get_position("/player")
local enemy_pos = go.get_position()
local direction = vmath.normalize(player_pose-enemy_pos)
-- Вычисляем нужное направление
factory.create("/enemy#factory", enemy_pos, nil, {direction_bullet = direction})
-- Создаём пулю и указывем ей начальную позицию и направление движения
end
function init(self)
timer.delay(1, true, function ()
fire()
end)
-- Каждую секунду враг стреляет в игрока
end
bullet.script
go.property("speed", 300)
go.property("direction_bullet", vmath.vector3(0, 0, 0))
function init(self)
if vmath.length_sqr(self.direction_bullet) > 0 then
local angle = math.atan2(self.direction_bullet.y, self.direction_bullet.x)
go.set_rotation(vmath.quat_rotation_z(angle))
--Устанавливаем угол пули в заданном направлении
end
end
function update(self, dt)
if vmath.length_sqr(self.direction_bullet) > 0 then
local pos = go.get_position()
pos = pos + vmath.normalize(self.direction_bullet)*self.speed * dt
go.set_position(pos)
--Задаём движение пули в указанном направлении и скорость движения
end
end
Умножение вектора на скаляр
Когда мы говорим о векторах, мы называем отдельные числа скалярами. Например:
(3, 4, 0)*5 = (3*5, 4*5, 0*5) = (15, 20, 0)
(3, 4, 0) — вектор, а 5 — это скаляр. Здесь мы умножаем вектор на скаляр.
На языке Lua это выглядит вот так:
print(vmath.vector3(3, 4, 0)*5)
Скалярное произведение векторов
Чтобы рассчитать скалярное произведение двух векторов, мы должны умножить их компоненты, а затем сложить полученные результаты вместе
(x1, y1, z1) • (x2, y2, z2) = x1*x2 + y1*y2 + z1*z2
Например: (3, 2, 0) • (1, 4, 0) = 3*1 + 2*4 + 0*0 = 11.
Посмотрим на картинку ниже:
Здесь мы можем увидеть, что если если скалярное произведение положительно, то угол между векторами меньше 90 градусов. Если скалярное произведение равно нулю, векторы перпендикулярны (расположены под прямым углом друг к другу). Если скалярное произведение отрицательно, то угол между векторами больше 90 градусов.
В основном, с помощью скалярного произведения векторов можно рассчитать, сколько их указывает в одном направлении.
Пример в Defold:
local v1 = vmath.vector3(3, 2, 0)
local v2 = vmath.vector3(1, 4, 0)
print(vmath.dot(v1, v2)) -- скалярное произведение двух векторов
В движке для скалярного произведения векторов есть функция vmath.dot()
Векторное произведение
Векторное произведение — это операция над двумя векторами в трёхмерном пространстве, в результате которой получается новый вектор.
Векторное произведение A(x1,y1,z1) и B(x2,y2,z2) будет равно:
(y1⋅z2-z1⋅y2, z1⋅x2-x1⋅z2, x1⋅y2-y1⋅x2)
Пример с числами:
(0, 1, 0) x (1, 0, 2) = ([1*2 — 0*0], [0*1 — 0*2], [0*0 — 1*1]) =
=(2, 0, -1)
Пример на Defold:
local vec1 = vmath.vector3(1, 0, 0)
local vec2 = vmath.vector3(0, 1, 0)
print(vmath.cross(vec1, vec2)) --> vmath.vector3(0, 0, 1)
local vec3 = vmath.vector3(-1, 0, 0)
print(vmath.cross(vec1, vec3)) --> vmath.vector3(0, -0, 0)
В движке для векторного произведения векторов есть функция vmath.cross()


