Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Векторна математика
Вступ
Ця стаття — це короткий вступ до лінійної алгебри та опис способів її використання в розробці ігор. Лінійна алгебра — це наука про вектори та їх використання. Використовувати їх можна як в 2D, так і в 3D й Godot робить це дуже часто. Для того, щоб бути добрим програмістом, розуміти векторну математику просто необхідно.
Примітка
Ця стаття — це не підручник з лінійної алгебри. Ми лише розглянемо, як їх можна використовувати в прив'язці до розробки ігор. Для ширшого розуміння математики, погляньте на https://www.khanacademy.org/math/linear-algebra
Система координат (2D)
У двовимірному просторі, координати описуються за допомогою горизонтальної (x) та вертикальної (y) осей. Певна точка в просторі описується як пара чисел. Наприклад: (4,3).
Примітка
Якщо ви раніше не працювали з комп'ютерною графікою, то вам може здатись дивним, що вісь y напрямлена вниз а не вгору, як зазвичай, в підручниках з математики. Проте, такий підхід поширений серед більшости графічних комп'ютерних програм.
Будь-яка точка на площині може бути визначена як пара чисел у такий спосіб. А ще, ми можемо уявляти точку (4,3) як зміщення від точки (0,0), або початкової точки. Намалюймо стрілку, яка тягнеться від початкової точки до нашої:
Це вектор. Вектор представляє багато корисної інформації. Крім того, що точка знаходиться в (4, 3), ми також можемо думати про це як про кут θ (тета) і довжину (або величину) m. У цьому випадку стрілка є вектором позиції - вона позначає положення в просторі відносно початку координат.
Дуже важливий момент, який слід враховувати щодо векторів, полягає в тому, що вони представляють лише відносний напрямок та величину. Немає поняття положення вектора. Наступні два вектори ідентичні:
Обидва вектори позначають точку яка лежить на 4 одиниці вправо та на 3 одиниці вниз від деякої початкової точки. Не важливо де ви намалюєте вектор, він завжди буде позначати відносний напрямок та зміщення.
Дії над векторами
Ви можете описувати вектор як пару координат x та y, або як пару з кута й модуля. Але, зазвичай, програмісти використовують координатну форму. Наприклад, в Godot, початок координат знаходиться в верхньому лівому куті екрану. А тому, щоб розмістити 2D вузол під назвою Node2D на 400 пікселів правіше й на 300 пікселів нижче, потрібно використати такий код:
$Node2D.position = Vector2(400, 300)
var node2D = GetNode<Node2D>("Node2D");
node2D.Position = new Vector2(400, 300);
Godot підтримує як Vector2, так і Vector3 для 2D і 3D використання відповідно. Ті самі математичні правила, які обговорюються в цій статті, застосовуються до обох типів, і скрізь, де ми посилаємося на методи Vector2 у посиланні на клас, ви також можете перевірити їхні аналоги Vector3.
Доступ до компонентів
Окремі компоненти вектора можна отримати напряму, по назві.
# Create a vector with coordinates (2, 5).
var a = Vector2(2, 5)
# Create a vector and assign x and y manually.
var b = Vector2()
b.x = 3
b.y = 1
// Create a vector with coordinates (2, 5).
var a = new Vector2(2, 5);
// Create a vector and assign x and y manually.
var b = new Vector2();
b.X = 3;
b.Y = 1;
Додавання векторів
Коли додаються або віднімаються два вектори, додаються і їхні відповідні компоненти:
var c = a + b # (2, 5) + (3, 1) = (5, 6)
var c = a + b; // (2, 5) + (3, 1) = (5, 6)
Суму векторів можна зобразити додавши один вектор, до кінця іншого:
Сума a + b дає такий же результат як і b + a.
Множення на скаляр
Примітка
Вектори представляють як напрямок, так і величину. Значення, яке представляє лише величину, називається скаляром. Скаляри використовують тип float у Godot.
Вектор можна помножити на скаляр:
var c = a * 2 # (2, 5) * 2 = (4, 10)
var d = b / 3 # (3, 6) / 3 = (1, 2)
var e = d * -2 # (1, 2) * -2 = (-2, -4)
var c = a * 2; // (2, 5) * 2 = (4, 10)
var d = b / 3; // (3, 6) / 3 = (1, 2)
var e = d * -2; // (1, 2) * -2 = (-2, -4)
Примітка
Множення вектора на додатний скаляр не змінює його напрямок, змінює лише його величину. Множення на від’ємний скаляр призводить до вектора в протилежному напрямку. Ось як ви масштабуєте вектор.
Практичне застосування
Погляньмо на два найпоширеніші способи використання суми та різниці векторів.
Пересування
Вектор може представляти будь-яку величину з величиною та напрямком. Типовими прикладами є: положення, швидкість, прискорення та сила. На цьому зображенні космічний корабель на кроці 1 має вектор положення (1, 3) та вектор швидкості (2, 1). Вектор швидкості показує, наскільки далеко рухається корабель на кожному кроці. Ми можемо знайти положення для кроку 2, додавши швидкість до поточного положення.
Порада
Швидкість вимірює зміну положення за одиницю часу. Нове положення визначається шляхом додавання швидкості, помноженої на час, що минув (тут приймається за одну одиницю, наприклад, 1 с) до попереднього положення.
У типовому сценарії двовимірної гри ви матимете швидкість у пікселях на секунду та помножите її на параметр дельта (час, що минув з попереднього кадру) з _process() або _physics_process() зворотні виклики.
Указування на ціль
В цій ситуації, ви маєте танка, який хоче направити свою гармату на робота. Різниця положень танка й робота дасть нам вектор що вказує від танка, до робота.
Порада
Щоб знайти вектор, що вказує від A до B, використовуйте B - A.
Одиничні вектори
Вектор з довжиною 1 називається одиничним вектором. Їх ще іноді називають напрямними векторами чи нормалями. Одиничні вектори корисні, коли необхідно знати про напрямок без довжини.
Нормалізація
Нормалізація вектора означає зменшення його довжини до 1 зі збереженням його напрямку. Це робиться шляхом ділення кожного з його компонентів на його величину. Оскільки це така поширена операція, Godot надає для цього спеціальний метод normalized():
a = a.normalized()
a = a.Normalized();
Попередження
Оскільки нормалізація передбачає ділення на довжину вектора, ви не можете нормалізувати вектор довжини 0. Спроба зробити це зазвичай призведе до помилки. Однак у GDScript спроба викликати метод normalized() для вектора довжини 0 залишає значення недоторканим і уникає помилки.
Відбиття
Вектори часто використовуються для того, щоб позначати нормалі. Вектор нормалі — це одиничний вектор, спрямований перпендикулярно до якоїсь поверхні. Він вказує на сторону, в яку повернута ця поверхня. Нормалі використовуються при обчисленні освітлення, зіткнень та інших операцій, зв'язаних із поверхнями.
Наприклад, уявімо, що ми маємо кулю, що рухається і ми хочемо, щоб вона відбилася від стіни чи іншого об'єкта:
Нормаль поверхні має значення (0, -1), оскільки це горизонтальна поверхня. Коли м’яч стикається, ми беремо його рух, що залишився (кількість, що залишилася після удару об поверхню), і відображаємо його за допомогою нормалі. У Godot є метод bounce() для вирішення цього. Ось приклад коду наведеної вище діаграми з використанням CharacterBody2D:
var collision: KinematicCollision2D = move_and_collide(velocity * delta)
if collision:
var reflect = collision.get_remainder().bounce(collision.get_normal())
velocity = velocity.bounce(collision.get_normal())
move_and_collide(reflect)
KinematicCollision2D collision = MoveAndCollide(_velocity * (float)delta);
if (collision != null)
{
var reflect = collision.GetRemainder().Bounce(collision.GetNormal());
_velocity = _velocity.Bounce(collision.GetNormal());
MoveAndCollide(reflect);
}
Скалярний добуток
Скалярний добуток — це одне з найважливіших понять у векторній математиці. Проте, його часто не розуміють. Скалярний добуток — це операція, яка бере два вектори, та повертає скаляр. На відміну від векторів, які мають модуль та напрямок, скаляр — це просто число, яке має лише модуль.
Є дві формули для обчислення скалярного добутку:
і
Математична нотація ||A|| представляє величину вектора A, а Ax означає x компонент вектора A.
Однак у більшості випадків найпростіше використовувати вбудований метод dot(). Зверніть увагу, що порядок двох векторів не має значення:
var c = a.dot(b)
var d = b.dot(a) # These are equivalent.
float c = a.Dot(b);
float d = b.Dot(a); // These are equivalent.
Скалярний добуток найбільш корисний, коли використовується з одиничними векторами, завдяки чому перша формула зводиться лише до cos(θ). Це означає, що ми можемо використовувати скалярний добуток, щоб сказати нам щось про кут між двома векторами:
Якщо множаться одиничні вектори, то результат буде в межах від -1 (180°) до 1 (0°).
Напрямок
Ми можемо використати це для того, щоб дізнатись, чи дивиться один об'єкт на іншого. На схемі нижче, гравець p намагається уникати зомбі A і B. Припустімо, поле зору зомбі має кут 180°, чи можуть вони побачити гравця?
Зелені стрілки fA і fB є единичними векторами, що представляють напрямок руху зомбі, а синє півколо представляє його поле зору. Для зомбі A ми знаходимо вектор напрямку AP, який вказує на гравця, за допомогою P - A і нормалізуємо його, однак у Godot є допоміжний метод для цього під назвою direction_to (). Якщо кут між цим вектором і спрямованим вектором менше 90°, то зомбі може бачити гравця.
У коді це виглядатиме ось так:
var AP = A.direction_to(P)
if AP.dot(fA) > 0:
print("A sees P!")
var AP = A.DirectionTo(P);
if (AP.Dot(fA) > 0)
{
GD.Print("A sees P!");
}
Векторний добуток
Як і скалярний добуток, векторний добуток — це операція над двома векторами. Але результат векторного добутку — це ще один вектор, який направлений перпендикулярно до двох інших. Його довжина залежить від кута між ними. Якщо вектори паралельні — то результатом векторного добутку буде нульовий вектор.
Ось так обчислюється векторний добуток:
var c = Vector3()
c.x = (a.y * b.z) - (a.z * b.y)
c.y = (a.z * b.x) - (a.x * b.z)
c.z = (a.x * b.y) - (a.y * b.x)
var c = new Vector3();
c.X = (a.Y * b.Z) - (a.Z * b.Y);
c.Y = (a.Z * b.X) - (a.X * b.Z);
c.Z = (a.X * b.Y) - (a.Y * b.X);
З Godot ви можете використовувати вбудований метод Vector3.cross():
var c = a.cross(b)
var c = a.Cross(b);
Перехресний добуток не є математично визначеним у 2D. Метод Vector2.cross() є широко використовуваним аналогом 3D перехресного добутку для 2D векторів.
Примітка
У векторному добутку порядок має значення. a.cross(b) дає не той самий результат, що b.cross(a). Отримані вектори вказують у протилежних напрямках.
Обчислення нормалей
Одним із поширених способів використання перехресних добутків є знаходження нормалі до площини або поверхні в тривимірному просторі. Якщо у нас є трикутник ABC, ми можемо використовувати векторне віднімання, щоб знайти два ребра AB і AC. Використовуючи перехресний добуток, AB × AC створює вектор, перпендикулярний до обох: нормалі до поверхні.
Ось функція для обчислення нормалі трикутника:
func get_triangle_normal(a, b, c):
# Find the surface normal given 3 vertices.
var side1 = b - a
var side2 = c - a
var normal = side1.cross(side2)
return normal
Vector3 GetTriangleNormal(Vector3 a, Vector3 b, Vector3 c)
{
// Find the surface normal given 3 vertices.
var side1 = b - a;
var side2 = c - a;
var normal = side1.Cross(side2);
return normal;
}
Указування на ціль
У розділі про скалярний добуток ми побачили, як він може бути використаний для знаходження кута між векторами. Цього не достатньо для повороту в 3D. Нам також потрібно знати навколо якої осі обертатись. Ми можемо знайти її знайшовши векторний добуток між вектором який дивиться уперед і тим, що направлений на ціль. Отриманий перпендикуляр і буде віссю обертання.
Додаткова інформація
Дізнатись більше про використання векторів у Godot можна в наступних статтях: