Цей урок призначений, щоб пояснити основи програмування Dota 2 призначених для користувача ігор (модов, аддонів, карт).
Скриптинг (сценарії)
Для початку вам знадобиться вже створена кастомними гра, де можна ходити. Саме час почати вчитися програмувати на Lua і взагалі розібратися з усім цим.
Зайдіть в dota 2 beta \ game \ dota_addons \ названіе_вашего_мода \ scripts. Дві основні папки cо скриптами - це NPC і vscripts. Перша може мати наступні файли з розширенням .txt (якщо їх немає, можете створити):
- npc_abilities _custom.txt - містить всі змінені і створені здатності в призначеній для користувача грі.
- npc_heroes _custom.txt - герої зі своїми здібностями і статистикою.
- npc_items _custom.txt - здатності предметів, які носять в інвентарі.
- npc_units _custom.txt - всі дані для не-героїв одиниць, будівлі або істот.
- npc_abilities _override.txt - змінені Dota 2 спроможності / деталі зі зміненими значеннями.
- herolist .txt - список героїв, доступних для вибору.
Ці файли використовують систему KeyValues (KV, ключ-значення) і є ядром в системі DataDriven. Це таблиці, що містять різноманітні дані і вони допомагають движку Source 2 зрозуміти, що є що. У цих файлах дуже простий синтаксис. Все що там є - це таблиці (починаються і закінчуються фігурними скобочки), в яких містяться ключ і його параметр (значення).
KV задають базові параметри здібностей, предметів, юнітів, а ось з Lua. буде трохи складніше.
Кожен .txt файл містить свої особливі KVs, і коли починається гра, движок Source 2 буде завантажувати їх, наприклад, щоб зрозуміти, який юніт з якими параметрами повинен заспавніться. Зміни в цих файлах не вступлять в силу до тих пір, поки гра не почнеться знову (тільки після перезапуску), так що будьте дуже обережні з синтаксисом, в разі зайвої або відсутньої дужки: " <>. все KeyValues. які йдуть після цієї помилки не будуть враховані. KV чутливі до регістру, тому також зверніть увагу, щоб ви написали все правильно, і движок не видасть помилку.
Це буде просто вступний приклад системи DataDriven. щоб зрозуміти, що відбувається, де і як розширити його.
Створіть новий документ в Sublime Text Editor і переконайтеся, що ви використовуєте Dota KV синтаксис (натисніть Ctrl + Shift + P і впишіть Dota KV, щоб вибрати його швидко).
Ми будемо робити дуже просту здатність, яка завдає один шкоди для однієї мети. Почніть з написання імені здатності між "" і без пробілів. Потім напишіть BaseClass. і натисніть Enter, щоб вставити авто завершення (ось навіщо потрібні були сніпети вище). Переміщайтеся за допомогою клавіші Tab.
"BaseClass" має важливе значення для кожного DataDriven визначення, змушує гру інтерпретувати, що це здатність DataDriven. Предмети, юніти і герої мають свої власні базові класи (BaseClass).
AbilityTextureName може бути вашої іконкою здатності або будь-яким внутрішнім назвою здатності доти, наприклад lina_laguna_blade.
Інша важлива KV - AbilityBehavior. запишіть AbilityB і викорис автозаповнення
Потім нам потрібно подія (event) здібності - це тригер, коли певна подія відбувається з власником здатності. Найголовніший з них OnSpellStart. додайте один з автозаповненням, і ви побачите новий "рівень" створеним в <>. це називається блок. В [ДІЇ], напишіть «Damage» дію, деякі ключі і% AbilityDamage. Знак відсотка% представляє значення, яке буде прийнято десь ще, в цьому випадку, в ключі-значенні AbilityDamage. Додати цей останній ключ, і перша основна заклинання повинна виглядати так:
Тепер, цю здатність потрібно додати в npc_abilities_custom.txt файл для героя або юніта, щоб мати можливість використовувати її.
Після додавання test_ability. яку ви тільки що створили, пора додати здатність і герою. В папці вашого мода є папка heroes, яка має різні файли з описом здібностей героя. Відкрийте будь-якого героя і змініть стандартну здатність на test_ability.
Всякий раз, коли вам необхідно створити "маникен" (в ігроестрое це dummy unit), використовуйте в консолі -createhero (unit_name) enemy.
В чаті, unit_name є одним з доступних для піку імен героїв або просто будь-яке ім'я блоку, який доступний. Він також приймає скорочені імена, наприклад "ancient", а не "ancient_apparition". Швидка команда -createhero kobold enemy створює за замовчуванням противника нейтрала кобольди. Повне ім'я юніта "npc_dota_neutral_kobold", але коротка команда буде працювати. Ви також можете відключити перезарядки здібностей шляхом написання -wtf (і -unwtf включатиме її).
Велику документацію і поглиблені приклади системи DataDriven можна знайти на сторінках Dota 2 Workshop Tools Wiki.
Lua скриптинг (сценарії)
Ось 4 головних розробок на Lua в Dota:
- Ігрова Логіка
- DataDriven RunScript
- Hammer введення / виведення
- Інтерфейс Подій
Ігрова Логіка - Структура
У кожній призначеній для користувача грі, файл з ім'ям addon_game_mode.lua повинен обов'язково бути присутнім. По крайней мере, до тих пір, поки логіка гри знаходиться саме в цьому файлі (і справді, Valve зробили так само в своєму прикладі), рекомендується, щоб ви зарезервували цей файл тільки для цих 3 функцій:
- Require (необхідні), тут ми ставимо всі необхідні файли, які будуть використовуватися в ігровій логіці, розташовуються як бібліотеки, тобто всі функції всередині цих файлів можна використовувати в будь-якій точці.
- Precache (попередньо кешированниє), коли гра починається і гравці забирають своїх героїв, движок буде намагатися завантажити пов'язані моделі / частки / звуки цих героїв. Якщо ми динамічно використовуємо ресурси в Lua. перед предзагрузкі - то скоріше за все буде будь-які помилки.
- Activate. створює основу для користувача гри і викликає функцію ініціалізації.
Функція Precache була згорнута в Sublime Text Editor (там де три крапки).
Після виконання в addon_game_mode Precache і Activate. перша функція, яка буде виконана в файлі lua є GameMode: InitGameMode ().
І тут починається гра з ініціалізацією всіх видів правил і функцій, які зареєстровані у всіх GameRules і GameMode файлах. Для цього, багато змінні визначені на початок файлу, щоб нормально організувати параметри, такі як налаштування золота, вбивства, кастомниє рівні, і т.д.
Це синтаксис функції, застосованої на GameRules, з одним параметром BOOL:
Так само, як KV, Lua чутливий до регістру. Розташування функції в основному файлі Lua як правило, не має значення. Всі рядки скрипта всередині виклику функції буде виконуватися один за іншим, потенційно в тому ж кадрі; один кадр в Dota - 1/30 секунди.
Зверніть увагу на використання: двокрапки перед функцією. У Lua. від цього залежить доступ функції API гри. Ми говоримо, що GameRules є HScript або handle (дескриптор - число, за допомогою якого можна іденфіціровать об'єкт). Дескриптори в основному величезні таблиці, з усією відповідною інформацією. На сторінці Scripting API ви побачите багато різних типів функцій, які можна використовувати різні дескриптори.
Глобальним функцій не потрібні префікси: в дескрипторах. Герої, юніти, здібності і предмети мають свої різні класи дескрипторів і намагаючись викликати функцію несумісного класу викличе помилку VScript - рожевий текст в консолі і червоним текстом на ігровому екрані.
Ви можете отримати доступ до ігрової консолі, натиснувши клавішу '.
Консоль буде повідомляти, коли помилка Lua сценаріїв відбувається, або коли гра завантажується (помилка синтаксису компіляції) або під час виконання. У цій помилки, я написав GameRules.SetHeroRespawnEnabled с. замість:
Ви можете простежити помилку цій лінії і спробувати її вирішити, прописати script_reload в консолі, щоб перезавантажити скрипт і перевірити, чи була вона виправлена.
Синтаксична помилка DataDriven. як правило, виглядає наступним чином:
Події в призначеній для користувача грі Source 2
Другим сегментом функції InitGameMode являтся (ються?) Слухачі:
Структура цього ListenToGameEvent читається як:
Всякий раз, коли подія dota_player_gained_level спрацьовує, виконати сценарії всередині функції OnPlayerLevelUp.
OnPlayerLevelUp і GameMode просто імена функцій і основного класу, як правило, вам не потрібно турбуватися про них, всі слухачі. Dynamic_Wrap є функцією для того, щоб script_reload команда також перезавантажувати слухачів. script_reload перезапускає Lua скрипти під час виконання, на відміну від DataDriven файлів, які вимагають, щоб гра була повністю перезапущено заново.
3-й і останній головний елемент в InitGameMode. який сам визначає змінні для відстеження інформації. Вони використовують приставку self .. яка є локальною посиланням на GameMode. крізь всі функції всередині основного файлу Lua. Додавання інформації до об'єкта entity. називається "індексація" і в основному додає ще один запис до великої таблиці цього об'єкта. Це дуже корисно, тому що ця інформація зберігається в дескрипторі об'єкта і помітні всюди (можна використовувати всюди), і нічого не зміниться, поки ми не перепризначити або знищимо його.
Досить теорії, давайте подивимося, як все це приходить. Ми додамо кілька простих рядків сценарію в OnNPCSpawned функції, яка є слухачем до npc_spawned і викликає щораз юніта або героя в карту.
Перший рядок буде друкувати рядок в "" в VConsole. Функція print рідна в Lua, і приймає кілька параметрів, розділених комами, і конкатенацію (взаємний зв'язок) рядків з "..". як це:
DeepPrintTable є глобальною Valve-ігор функцією, яка буде відображати інформацію з таблицею події. Для ключів в даному випадку, це буде .entindex і .splitscreenplayer. Індекс об'єкта є дуже важливим номером для посилання на об'єкт. Ігноруйте splitscreenplayer. це просто залишки старого движка Source і ніколи не використовувалося в Dota 2.
Наступний рядок визначає локальну змінну. У Lua обсяг локальних змінних в блоці, в якому вони були оголошені, обмежується. Це хороший стиль програмування, використовувати локальні змінні, тільки тоді, коли потрібно. Локальні змінні допоможуть вам уникнути захаращення глобального навколишнього середовища з непотрібними іменами. Крім того, доступ до локальних змінних швидше, ніж до глобальних.
Це в основному читання інформації, яка надається в події, і зберігається в локальній змінній всередині цього виклику функції. У цьому прикладі всі Слухачі і їх функції вже були оброблені, але для довідки, ви завжди можете перевірити In_Engine_Events вікі-сторінку. щоб точно знати, які параметри кожної події переносяться.
Локальна змінна npc HScript, типу дескриптора. Всі зміни, зроблені в змінної NPC буде відображати на заспауненном юніте.
Наступний рядок спочатку перевіряє, якщо npc герой true (це виключає ілюзії), а також перевіряє, якщо індекс .bFirstSpawned не призначено. Якщо обидві умови виконуються, змінюється логічне значення = true і викликається функція OnHeroInGame.
Щоб закінчити цей базовий урок Dota Lua. давайте змінимо OnNPCSpawned функцію, так що якщо блок з ім'ям npc_dota_neutral_kobold породжується, зачекайте 1 секунду, а потім вбийте себе:
Тут ми скористаємося бібліотекою Таймери для простої 1.0-секундної затримки, є багато різних функцій таймера їх пояснення в timers.lua. BOOL на ForceKill це для того, щоб відтворити анімацію смерті.
Таблиці найбільш важлива структура, яку ми повинні використовувати. Як згадувалося раніше, всі дані на об'єкти можна розглядати як таблицю (хоча це технічно покажчик C ++ об'єкта), тим самим, ви зможете отримати і встановити значення по різних функцій API гри.
Є деякі функції в API, які повертають таблицю дескрипторів об'єктів.
Припустимо, ви хочете знайти всі одиниці породжених (заспауненних) поблизу кобольдов і вбити їх. Функція FindUnitsInRadius може бути використана для цієї мети, і включає в себе багато параметрів з різними типами, які варто пояснити:
Параметри повинні бути в такому порядку. Ця функція глобальна, тому не потрібен handle: (дескриптор), але ми повинні тримати в таблиці в змінні, такі як:
З'ясувати teamNumber - яка команда об'єкта знаходиться в радіусі можна зробити за допомогою GetTeamNumber () за допомогою інформації від дескриптора NPC. Що стосується інших параметрів фільтра, замість дійсних чисел ми використовуємо купу констант. які представляють різні числові значення. Повний список констант на цій сторінці вікі.
Вектор представляється у вигляді вектора (х, у, z) координат. Функція, щоб отримати позицію конкретного блоку під назвою GetAbsOrigin і приймає дескриптор (handle) NPC.
Що стосується параметрів кешу, просто поставте нуль і false, від них не багато користі в цілому.
Завершена функція викликає героя в радіусі 500 від народженого (заспауненого) кобольди буде виглядати так:
Використання додаткові рядки розриву, щоб зробити його більш читабельним. Тепер ми хочемо ітерацію (переброр) об'єктів у цій таблиці, це здійснюється наступним чином:
Існує ще одна річ, на яку варто звернути увагу: проблема "почекайте один кадр". Тому що все юніти фактично народилися в (0,0,0) координатах, а потім переїхали в потрібне положення, в багатьох випадках ви повинні будете створити другий таймер з затримкою 0.03 сек (1 кадр) для деяких сценаріїв, щоб все працювало.
Тепер, OnNPCSpawned виглядає так: