Математика в Defold. Часть 1: Действия с векторами

В этой статье Вы изучите линейную алгебру — раздел алгебры, изучающий математические объекты линейной природы: векторные (или линейные) пространства, линейные отображения, системы линейных уравнений. А конкретно Вы изучите вычисления с векторами.

Что такое вектор?

В играх вектора используются для хранения местоположений, направлений и скоростей. Двухмерный вектор выглядит вот так: (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()

2 лайка

Математика в Defold. Часть :two:: Эйлеры, радианы и кватернионы.

Вращение объектов в трёхмерном пространстве

Подробнее :backhand_index_pointing_right: тут

2 лайка