Підручник з руху
У цьому посібнику для початківців ви дізнаєтеся, як зробити реалістичне рух за допомогою векторів та деякої простої векторної алгебри.
Підручник інтегровано з редактором Defold і легко доступний:
- Початок Defold.
- Виберіть Новий проект ліворуч.
- Виберіть вкладку From Tutorial .
- Виберіть "Підручник руху"
- Виберіть місце для проекту на своєму локальному диску та натисніть Створити новий проект .
Підручник з руху
Це підручник для початківців. Він пройде через кроки створення керованого космічним кораблем гравця, який рухається природним способом, коли ви клавіатури вводите. Після закінчення цього посібника ви будете знати відповіді на наступні питання:
- Що таке вектори?
- Як ви можете використовувати векторні для представлення позицій, швидкості та прискорення?
- Як ви використовуєте це для створення ембріону гри, яку можна експериментувати та розвивати далі?
Передбачається, що у вас є базові уявлення про поняття фізики, такі як швидкість і прискорення. Вам також знадобиться деяке базове розуміння програмування Lua.
Цей проект готується заздалегідь для вас, тому не потрібно запускати налаштування. Запустіть гру, щоб отримати огляд того, з чим потрібно працювати:
- Вона включає в себе графіку: анімований корабель і фон.
- Введення налаштовано для клавіш зі стрілками та клацаннями миші.
- Існує об'єкт "космічний корабель", до якого додається скрипт.
- Сценарій має код для реагування на вхід гравців. Спочатку він друкує повідомлення на консолі як реакцію на введення.
Запустивши гру, спробуйте натискати кнопки зі стрілками або клацнути у вікні гри, а потім перевірити консоль редактора для введення результатів. Зверніть увагу, що в консолі друкується інший текст залежно від того, яку кнопку ви натиснули:
Створення корабля
Перш ніж копати в будь-які подробиці, давайте спочатку проведемо простий експеримент і дамо рух космічного корабля.
Відкрийте "spaceship.script" і прокрутіть вниз до початку функції
on_input()
де ви знайдете цей код: функція on_input ( сам , action_id , дія )
якщо action_id == hash ( " up " ) тоді
друк ( " UP! " )
інше ...
Видаліть рядок з твердженням
print("UP!")
І відредагуйте код, щоб початок функції виглядав так: функція on_input ( сам , action_id , дія )
якщо action_id == hash ( " up " ) тоді
місцевий р = йти get_position ()
р. y = р. y + 1
йти set_position (p)
інше ...
Запустіть гру знову і натисніть клавішу зі стрілкою вгору та подивіться, як корабель рухається вгору. Код дуже простий, але давайте подивимось на нього по рядку, щоб отримати правильне уявлення про те, що відбувається:
якщо action_id == hash ( " up " ) тоді
Вхідні прив'язки, встановлені в проекті (у файлі "/input/game.input_binding") пов'язують кожну клавішу зі стрілками з діями з назвою "вгору", "вниз", "вліво" та "праворуч". Гра працює на 60 кадрів в секунду, і кожний кадр ви натискаєте, назва
on_input()
дії "up" надсилається на функцію on_input()
. Таким чином, утримання кнопки вниз зроблять код між оператором if
а перший ще виконуватиметься 60 разів кожну секунду. місцевий р = йти get_position ()
Функція
go.get_position()
отримує позицію об'єкта гри. Оскільки функція викликається без будь-яких аргументів, повертається положення поточного об'єкта гри. Цей код належить до об'єкту гри космічного корабля, тому повертається положення космічного корабля.
Позиція призначена для локальної змінної з назвою
p
тому її можна маніпулювати. Об'єкт позиції - це vector3
, який є вектором, який містить три значення. р. y = р. y + 1
Об'єкт
vector3
у p
описує точку в 3D-просторі, що складається з координати X, координати Y та координати Z.Оскільки натискання кнопки " Вгору" має рухати корабель у позитивному напрямку осі Y, y
компонент позиції збільшується на 1. йти set_position (p)
Нарешті, новий змінений розмір позиції записується назад до поточного об'єкту гри.
Перш ніж перейти, спробуйте змінити значення, додане в
py
від 1 до 5, і запустіть гру знову. Зверніть увагу, як корабель рухається набагато швидше.
Нарешті, додати рядок нижче
go.set_position(p)
щоб надрукувати значення p
: функція on_input ( сам , action_id , дія )
якщо action_id == hash ( " up " ) тоді
місцевий р = йти get_position ()
р. y = р. y + 1
йти set_position (p)
друк (р)
інше ...
Запустіть гру знову і подивіться, як двигун друкує значення вектора позиції кожного кадру. Зверніть увагу, що друге значення вектора змінюється, коли рухається космічний корабель:
... DEBUG:SCRIPT: vmath.vector3(640, 460, 0) DEBUG:SCRIPT: vmath.vector3(640, 465, 0) DEBUG:SCRIPT: vmath.vector3(640, 470, 0) DEBUG:SCRIPT: vmath.vector3(640, 475, 0) ...
Вектори
Вектор - математичний об'єкт, який має напрямок і величину (довжину). Вектор описує певну точку в векторному просторі. На практиці вектор складається з набору чисел, що дають координати до точки. У двомірному просторі (площині) для опису векторів необхідні два числа: одне значення для осі X, а одне для осі Y:
У тривимірному просторі потрібно три цифри: одна для осі Х, одна для осі Y, одна для осі Z:
Величина або довжина вектора v розраховується за допомогою теореми Піфагора:
Вектор з величиною 1 називається нормованим вектором .
Незважаючи на те, що Defold має набір інструментів для 2D-ігор, движок справді є 3D-двигуном. Всі ігрові об'єкти та компоненти розташовуються в 3D-просторі з позиціями, вираженими як об'єкти
vector3
. Коли ви переглядаєте свою гру в 2D, значення X і Y визначають положення об'єкта вздовж осі "ширина" та "висота", а позиція Z визначає положення вздовж осі "глибини". Позиція Z дозволяє вам контролювати видимість об'єктів, що перекриваються: спрайт з Z значення 1 з'явиться перед спрайтом у положенні Z позиції 0. За замовчуванням Defold використовує систему координат, що дозволяє Z значення між -1 і 1:
Бібліотека vmath бібліотеки Defold Lua містить функції для створення і керування об'єктами
vector3
: - створити новий вектор3 в положенні X і положення 350 350.
місцеве положення = vmath вектор3 ( 100 , 350 , 0 )
- встановити позицію ігрового об'єкта "player" у новий вектор.
йти set_position ( " player " , position)
Вектори в більш високих розмірах, ніж 3, також можливі. Defold використовує
vector4
об'єкти з чотирма компонентами для кодування кольорів. Перші три компоненти дають кількість червоного, зеленого та синього, а останній компонент дає кількість прозорості, також називається "альфа".
У повсякденному житті ви звикли робити арифметику зі скалярними значеннями, реальними числами, які описують окуляри рядка номеру. Ми використовуємо скаляри для позначення багатьох різних речей. Номер 12 може означати кількість метрів, кілограмів, фунтів, секунд, метрів у секунду, вольт або доларів. Те саме стосується векторів. Ви вже бачили, як вектори можуть бути використані для опису позиції об'єкта. Вони також дуже добре описують рух об'єкта через простір.
Для опису руху на екрані комп'ютера (2D-площині) потрібні дві величини: швидкість вздовж осі Х і швидкість уздовж осі Y. Ви можете дуже добре використовувати два окремі скалярні значення та додати значення швидкості до положень X і Y окремо:
position_x = position_x + speed_x * expired_seconds
position_y = position_y + speed_y * expired_seconds
Це приблизно те, що ви зробили, коли ви раніше зробили корабель у напрямку догори, і немає нічого поганого, обчислюючи рух, як це. Вектори, однак, дозволяють висловити рух зрозуміліше та лаконічніше. Оскільки вектор описує напрямок і величину, вони інтуїтивно придатні для руху: напрямок вектора дорівнює напрямку руху, а величина описує кількість руху:
позиція = позиція + швидкість * пройшло секунд
Поки значення
position
та speed
виражаються як вектори у тому самому просторі, ви можете додавати і віднімати їх, а також масштабувати їх, множивши їх скалярними значеннями. Ці операції є центральною частиною векторної алгебри .Векторна алгебра
Векторна алгебра визначає математичні операції на векторах. Починаючи з найпростішого, заперечення, додавання та віднімання.
Відхилення: відхилення вектора v , позначене значенням - v , заперечує кожну складову вектора. Це робить вектор, який вказує в протилежному напрямку оригінального вектора з однаковою величиною:
Додавання. Додавання вектора u до вектора v , позначеного u + v , додає кожен компонент u до v . Результатом є новий вектор:
Вектори часто витягуються з системи координат, що забезпечує ясність операціям:
Віднімання: Віднімаючи вектор v від вектора u , позначений u - v , дорівнює додаванню заперечення v до u . Так що у - v = u + (- v ):
Мультиплікація з скаляром: множення вектора v з дійсним числом r виробляє новий вектор з масштабною величиною: вектор проривається фактором r . Помножене на негативне значення φ, орієнтація 180 градусів:
Це були основні операції над векторами, які ви будете використовувати весь час. Крім того, існує два спеціальних операції, які стануть у нагоді, якщо ви, наприклад, хочете перевірити, чи є два вектора паралельними або під прямим кутом один одного:
Точка продукту: крапковий виріб двох векторов u та v , позначених u ∙ v , є скалярним значенням. Вона визначається як:
- ‖u‖ - величина вектора u .
- ‖v‖ - величина вектора v .
- θ - кут між векторами.
Якщо вектори ортогональні (кут між ними становить 90 градусів), то точка продукту дорівнює нулю.
Хрестовий продукт: поперечний продукт двох векторов u та v , позначених u × v , являє собою вектор, перпендикулярний як u, так і v :
Отриманий вектор є нульовим вектором, якщо:
- Будь-який один або обидва вхідних введення є нульовиками, ( u = 0 або v = 0)
- Два вхідних вектора паралельні (θ = 0 °)
- Два вхідних вектора є антипаралельними (θ = 180 °)
Створення руху з векторами
Використовуючи векторну алгебру, тепер можна просто переписати рух космічного корабля.
Відкрийте "spaceship.script" та змініть функції
init()
, update()
та on_input()
: функція init ( сам )
msg повідомлення ( " . " , " acquire_input_focus " )
самоврядування input = vmath. вектор3 () - [1]
кінець
- Створіть новий нульовий
vector3
для збереження напряму введення. Вона поміщається в поточний екземпляр сценарію (self
), тому його можна використовувати протягом усього життя об'єкту гри космічного корабля.
оновлення функції ( self , dt )
місцевий рух = себе . вхід * 3 - [1]
місцевий р = йти get_position () - [2]
йти set_position (p + рух) - [3]
самоврядування input = vmath. вектор3 () - [4]
кінець
- Обчислити вектор руху на основі вхідного вектора гравця.
- Отримати позицію поточного об'єкта гри (космічний корабель). Позиція -
vector3
. - Встановити позицію поточного ігрового об'єкта до
p
а також вектор руху. - Нульовий вхідний вектор. Функція
on_input()
називається кожним кадром передupdate()
і несе відповідальність за встановлення вхідного вектора.
функція on_input ( сам , action_id , дія )
якщо action_id == hash ( " up " ) тоді
самоврядування вхід y = 1 - [1]
elseif action_id == хеш ( " down " ) потім
самоврядування вхід у = - 1 - [1]
elseif action_id == hash ( " left " ) потім
самоврядування вхід x = - 1 - [1]
elseif action_id == хеш ( " правий " ) тоді
самоврядування вхід x = 1 - [1]
elseif action_id == хеш ( " клацання " ) і дія. потім натискати
друк ( " CLICK! " )
кінець
кінець
- Встановіть компонент x або y вхідного вектора залежно від входу гравця. Якщо програвач натискає та
left
одночасно, функція буде викликана двічі, і обидва компоненти встановлюються, що призведе до діагонального напрямку введення.
Є два проблеми з цим кодом:
По-перше, вхідний вектор має довжину 1, якщо ви рухаєтеся вертикально або горизонтально, але по діагоналі довжина 1,4142 (квадратний корінь з 2), так що діагональний рух швидше. Ви, мабуть, цього не хочете.
По-друге, одиниці швидкості виражаються в пікселях / кадрах, незалежно від довжини кадру. Його встановлено на 3 пікселя руху кожного кадру (або приблизно 4,2 діагоналі). Щоб зробити корабель швидше, змініть значення 3 на більш високе. Якщо ви хочете, щоб це йшло повільніше, зменшіть значення. Було б краще, якщо б ви могли виразити швидкість у пікселях / секунди.
Перша проблема легко виправити, просто нормалізуйте вхідний вектор, тому довжина вводу завжди 1:
оновлення функції ( self , dt )
якщо vmath length_sqr ( self input ) > 1 then - [1]
самоврядування input = vmath. нормалізувати ( сам вхід )
кінець
місцевий рух = себе . введення * 3
місцевий р = йти get_position ()
йти set_position (p + рух)
самоврядування input = vmath. вектор3 ()
кінець
- Якщо квадратна довжина вхідного вектора більша за 1, нормалізуйте вектор, щоб він мав величину 1. Порівняйте її з квадратною довжиною, оскільки вона швидше, ніж порівнюючи довжину.
Друга проблема вимагає використання значення етапу часу.
Часовий крок
У кожному кадрі движок Defold викликає функцію
update()
кожного сценарію. Гра Defold зазвичай проходить зі швидкістю 60 кадрів в секунду, тому кожен кадр займає 0,016666 секунди. Це час, який минув між кожним дзвінком до update()
. Вектор швидкості з величиною 3 буде відображати швидкість 3 * 60 = 180 пікселів за секунду (за допомогою звичайного сценарію візуалізації), доки дійсно буде 60 кадрів на секунду . Що трапиться, якщо там з якоїсь причини є збій у частотному діапазоні? З поточним рухом коду буде нерівномірним і непередбачуваним.
Працюючи з пікселями в секунду, ви можете правильно використовувати змінну частоту кадрів, ви також зможете вимірювати свою гру з допомогою секундоміра та причини щодо відстаней та часових поясів.
Defold - значення параметра аргументу часу для функції
update()
. Аргумент зазвичай називається dt
(для "delta time"), а його значення - кількість секунд, що минули з часу останнього кадру. Якщо ви наблизите швидкість від dt
ви отримаєте відповідні одиниці: оновлення функції ( self , dt )
якщо vmath length_sqr ( self input ) > 1 тоді
самоврядування input = vmath. нормалізувати ( сам вхід )
кінець
місцевий рух = себе . вхід * 150 * dt - [1]
місцевий р = йти get_position ()
йти set_position (p + рух)
самоврядування input = vmath. вектор3 ()
кінець
- Швидкість тепер становить 150 пікселів у секунду. Екран завширшки 1280 пікселів, тому воно повинно займати корабель 8,53 секунди, щоб пролетіти. Ви можете перевірити це за допомогою секундоміра.
Запустіть гру знову і спробуйте код руху. На цьому етапі це працює, але це жорстка і не дуже динамічна. Для того, щоб донести до космічного корабля відчуття ваги, хорошим способом є рух керування входом гравця шляхом зміни прискорення замість швидкості.
Прискорення
У наведеному вище коді швидкість була встановлена на постійне значення, що означає, що результуюче рух або переведення швидкості, що діє протягом етапу часу (
dt
), може бути розраховане шляхом множення швидкості на етап часу: рух = швидкість * dt , або помаранчева область на наступній діаграмі:
Прискорення визначає, як швидко щось змінює швидкість та напрямок. Прискорення діє на етап часу кадру (
dt
), а потім додається до швидкості. Швидкість спрацьовує над кадром, і отримане рух додають до положення. Оскільки швидкість з часом змінюється, рух слід розрахувати як площа під кривою. У математиці це називається інтеграцією з часом .
При невеликому достатньому кроці часу хорошу геометричну апроксимацію області можна розрахувати, припустивши, що прискорення, що діє між v0 та v1, є постійним, а це означає, що швидкість лінійно змінюється між двома точками. За цим припущенням v1 можна обчислити як v0 + acceleration * dt, а отримане рух стає таким:
Тепер ви можете написати остаточний код для
init()
та update()
(код для on_input()
зберігається як is): функція init ( сам )
msg повідомлення ( " . " , " acquire_input_focus " )
самоврядування швидкість = vmath. вектор3 () - [1]
самоврядування input = vmath. вектор3 ()
кінець
оновлення функції ( self , dt )
якщо vmath length_sqr ( self input ) > 1 тоді
самоврядування input = vmath. нормалізувати ( сам вхід )
кінець
місцеве прискорення = самоврядування . вхід * 200 - [2]
локальний dv = прискорення * dt - [3]
місцевий v0 = себе . швидкість - [4]
місцевий v1 = сам . швидкість + dv - [5]
місцевий рух = (v0 + v1) * dt * 0,5 - [6]
місцевий р = йти get_position ()
йти set_position (p + рух) - [7]
самоврядування швидкість = v1 - [8]
самоврядування input = vmath. вектор3 ()
кінець
- Створіть вектор для зберігання швидкості з часом.
- Прискорення встановлюється до 200 пікселів за секунду в секунду у напрямку входу гравця.
- Розрахуйте зміну швидкості цього кроку.
- v0 встановлено на швидкість з попереднього етапу часу.
- v1 є v0 плюс зміна швидкості цього етапу часу.
- Визначте, скільки корабель буде рухати цей час.
- Застосувати зміни в позиції.
- Зберігайте швидкість v1, щоб її можна було використовувати в наступному кроці.
Тепер прийшов час взяти ваш новий важкий космічний корабель на обертання.
Вітаю! Ви закінчили навчальний посібник. Але не зупиняйтеся тут. продовжити експериментувати з кодом.
Ось деякі ідеї, які ви можете спробувати:
- Покладіть шапку на швидкість.
- Зробіть відсік космічного корабля біля країв екрана.
- Дозволити клацанням миші диктувати напрямок введення.
Перегляньте сторінки документації для отримання додаткових прикладів, навчальних посібників, посібників та документів API.
Якщо ви зіткнулися з проблемами, допомога доступна на нашому форумі .
Щасливий захист!
Цей проект випущений за ліцензією Creative Commons CC0 1.0 Universal.
Ви можете використовувати ці активи в будь-якому проекті, особистому чи комерційному. Перш ніж використовувати їх, не потрібно запитувати дозвіл. Подання атрибуції не вимагається, але дуже цінується! Повний текст ліцензії
Немає коментарів:
Дописати коментар
Kоментарі неуkраїнсьkою видалятимуться