Ігровий об’єкт
Ігровий об’єкт це єдиний об’єкт ігрового рушія, який може мати свою логіку в ігровій сцені. Ігровим об’єктом на сцені являється усе (від об’єкта гравця до невидимого об’єкта-тригера) окрім плиток рельєфу.
Усі ігрові об’єкти можуть впливати один на одного напряму в межах ігрового рівня. Розпізнають об’єкти один одного завдяки системі ідентифікаторів. Імена ідентифікаторів мусить задати розробник. Справа в тому, що задля економії ресурсів, прошивка для ESP компілюється без збереження type_info у класах. Тому ідентифікатор типу потрібно додавати самостійно.
Варто пояснити, чому об’єкти напряму звертаються один до одного. З точки зору класичного програмування ігор, такий підхід неправильний. Для прикладу, здавалося б, об’єкт з радіусом огляду 5 одиниць, не повинен бачити об’єкти далі цього радіусу. Що логічно. З іншого боку, займаючись програмуванням мікроконтролера потрібно завжди пам'ятати про його обмежені ресурси. Якщо об’єкт буде кожен кадр звертатися до менеджера ігрового світу, щоб отримати певну інформацію, то це призведе до декількох додаткових викликів методів та, скоріше за все, декількох додаткових копіювань даних. Якщо ж таких об’єктів на рівень декілька, то кількість додаткових операцій потрібно помножити на кількість об’єктів. Тобто, лише задля підтримки чистоти архітектури буде втрачено дорогоцінні мілісекунди ядра мікроконтролера. Саме через це ігрові об’єкти мають прямий доступ до ігрового світу без додаткових прошарків у вигляді ігрових менеджерів. Розробник гри мусить самостійно написати логіку поведінки об’єкта таким чином, щоб той не реагував на інші об’єкти, що знаходяться поза його радіусом дії. Зрештою, гравці не будуть дякувати розробнику за якісну архітектуру коду. Для них головними показниками в грі є геймплей та кількість згенерованих нею кадрів за секунду.
У випадку мультиплеєрної гри, валідацією команд від інших гравців повинен займатися сервер, та перевіряти, чи має гравець право на ту чи іншу дію.
З можливостями ігрового об’єкта ви можете ознайомитися через документацію в коді. Зверніть особливу увагу на обов'язкові параметри, які передаються в базовий клас, під час створення ігрового об’єкта. Дивіться класи HeroObj, PortalObj та BunnyObj для більш детального вивчення прикладів коду ігрових об'єктів.
Створення класу ігрового об’єкта
Класи всіх ігрових об’єктів повинні бути обов'язково успадковані від IGameObject або від його класів-спадкоємців та мусять реалізувати всі його виключно віртуальні методи. В методі init необхідно реалізувати первинну ініціалізацію об’єкта. Цей метод потрібно самостійно викликати, після створення об’єкта. Методі update обов’язковий до реалізації, проте наповнювати кодом його не обов’язково. Цей метод автоматично викликається ігровим рушієм для кожного об’єкта на ігровому рівні кожен кадр. Найкраще в цьому методі реалізовувати поведінку автономних об’єктів, наприклад, пошук інших об’єктів, атаку, переслідування, тощо. Методи serialize, deserialize, getDataSize також обов’язкові до реалізації. Вони необхідні для збереження стану ігрового рівня до файлу, для перенесення об’єктів між ігровими рівнями та для синхронізації об’єктів між ігровим сервером та клієнтами. serialize - записує важливі для об’єкта поля в потік. deserialize - зчитує дані з потоку до полів об’єкта в тому самому порядку, як вони були записані. getDataSize - повинен повертати загальний розмір усіх полів, які об’єкт буде серіалізувати. Віртуальний метод onDraw перевизначати не обов’язково. Проте, якщо потрібно розширити можливості малювання об’єкту, тоді onDraw можна перевизначити та додати в нього додаткове малювання. Це може бути корисним, якщо потрібно відмалювати, наприклад, індикатор міцності над об’єктом. З перевизначеного методу обов’язково потрібно викликати базовий IGameObject::onDraw(); який коректно відрисує спрайт об’єкта чи його анімацію.
Усі ігрові об’єкти та їх ресурси рекомендується розміщувати в окремій папці. В даному випадку це буде папка simple_rpg\obj.
На першому кроці розробки ігрових об’єктів необхідно визначитися, які об’єкти повинні бути в грі, та створити перечислення ClassID, що міститиме ідентифікатори їх типу.
Наступним кроком потрібно створити папки, в яких будуть розміщуватися файли з кодом об’єктів та їх ресурсами, наприклад, спрайтами. Це не обов’язкова дія, ви можете створювати таку ієрархію файлів та каталогів, яка підходить саме вам. Для головного персонажа я створив папку hero. В цій папці створив каталог sprites, який міститиме зображення спрайтів анімації об’єкта.
Перміщення ігрового об’єкта
Переміщення об’єкта на сцені відбувається шляхом зміни значення його глобальних координат. Глобальні координати є відкритими полями кожного об’єкта. Це зроблено навмисно, щоб будь-який механізм ігрового рушія, міг напряму звертатися до цих полів. До полів координат також можуть звертатися інші ігрові об’єкти та читати/змінювати їх значення без необхідності приводити об’єкт до відповідного типу. Уся відповідальність за доступ інших об’єктів до цих полів, покладається на розробника гри. Дивіться код HeroObj, щоб більш детально ознайомитися з механізмом переміщення.
Взаємодія об’єктів з рельєфом сцени
Менеджер рельєфу або, як його можна назвати, менеджер ігрової поверхні, дозволяє побудувати автоматичну систему контролю обмеження пересування об’єктів. Це досягається за рахунок того, що плитки ігрової поверхні містять інформацію про їх тип. Наприклад, земля, вода, болото тощо. Якщо передати менеджеру рельєфу інформацію про поточні координати об’єкта, координати куди його потрібно перемістити, та інформацію про характеристики тіла об’єкта, то менеджер визначить, чи може об’єкт бути переміщено за вказаним напрямом. Якщо підняти прапор, який буде вказувати на відсутність твердого тіла у об’єкта, він матиме можливість вільно переміщатися по всій поверхні ігрової сцени.
Попередження
Якщо крок переміщення об'єкта більший за розмір однієї плитки, менеджер рельєфу не враховує тип проміжних плиток, що знаходяться між поточною та тією, куди потрібно перемістити об’єкт.
Тверде тіло об’єкта
Хоча фізика зіткнень в рушії не реалізована, кожен ігровий об’єкт має по замовчуванню прямокутне тверде тіло, яке відповідає розміру спрайту. Розмір твердого тіла об’єкта налаштовується через його зміщення від країв.
Попередження
Розробник повинен самостійно контролювати, щоб загальне зміщення твердого тіла по ширині чи висоті не перевищило розмір спрайту об’єкта.
Тверде тіло об’єкта використовується в наступних випадках:
- Для контролю переміщення об'єкта по поверхні сцени. Тіла з відсутнім твердим тілом не можуть пройти тільки за сцену, але в межах рівня прохідність плитки не впливає на них.
- Для коректного накладення спрайтів об’єктів на плитки ігрового рівня. Наприклад, голова коня повинна, зазвичай, перекривати плитку з водою в той час як ноги не повинні ставати на воду. Або у випадку коли треба задати вертикальність об’єкту, наприклад, персонажу, щоб він міг перекривати своїм спрайтом плитку будинку чи впритул наближатися до інших об’єктів з твердим тілом. Це досягається за рахунок зміщення твердого тіла.
- Для створення динамічних перешкод на сцені. Наприклад, додавання об’єкта каменю з твердим тілом, який блокує прохід на деяку частину ігрового рівня, проте може бути видалений після виконання певних умов.
- Для фільтрації вибірки ігрових об’єктів в точці/колі/прямокутнику на ігровому рівні.
Попередження
Якщо зміщення повинне мінятися разом з анімацією, наприклад , під час зміни напрямку руху коня, то цей процес розробник мусить контролювати та міняти зміщення твердого тіла самостійно.
Кожен ігровий об’єкт має вбудовані методи, які дозволяють перевірити, чи пересікається його спрайт або тверде тіло з будь-яким спрайтами/тілами інших об'єктів.
Анміція ігрового об’єкта
Анімування об’єктів в ігровому рушії Meowui відбувається на основі ефекту GIF. Тобто наступний стан об’єкта відображається зміною зображення цього об’єкта. Анімація відтворюється автоматично, проте її зміну потрібно виконувати вручну. Тобто, в рушії відсутнє зв’язування анімації з певними подіями.
Щоб додати анімацію, потрібно в класі об’єкта визначити вектори, які міститимуть вказівники на зображення-кадри анімації та налаштувати швидкість її відтворення. Також необхідно налаштувати колір, який буде розпізнаватися ігровим рушієм, як повністю "прозорий", якщо потрібно. Це вимушений прийом, тому що змішування кольорів з альфа-каналом є дуже дорогою операцією для мікроконтролера. Усі окремі кадри анімації, так само, як і усі статичні зображення об’єкта, повинні мати однаковий розмір, що дорівнює розмірам його спрайту.
Зміна поточної анімації або її вимкнення може відбуватися в різних місцях коду об’єкта на розсуд розробника. Наприклад, в об’єкті персонажа перемикання відбувається в методі, що відповідає за обробку команд руху. В автономних об’єктах це може відбуватися в методі update. Саме перемикання є дуже простим. Вимикання і вмикання анімації відбувається за допомогою відповідних прапорів, зміна набору зображень анімації відбувається через заміну вказівника на вектор з цим набором. Єдина складність полягає у синхронізації станів. Адже розробник мусить самостійно визначити чи матиме об’єкт анімацію в стані спокою, а чи вона буде замінена статичним зображенням, чи буде анімація руху відтворюватися, якщо об’єкт не може пройти через рельєф і тд. Задокументований приклад коду взаємодії з анімацією можна знайти в методі HeroObj::moveUp.
Зображення для анімації чи статичного спрайту імпортуються так само в гру, як і зображення для плиток ігрового рівня.
Попередження
Ігровий об’єкт не видаляє дані зображень за вказівником. Це потрібно для можливості перевикористання одного і того ж зображення різними об’єктами. Якщо зображення завантажується динамічно з карти пам’яті, то і звільнене воно повинно бути тим, хто його завантажив.