Як ігри грають самі в себе. Налаштування автоматизації тестування в Unreal Engine з прикладами
Привіт Мене звати Микита Лужанський, я Unreal Engine розробник у Pingle Studio. Сьогодні хочу поговорити з вами про надзвичайно важливу тему, яка стосується кожного, хто працює над створенням ігор: автоматизацію процесів тестування в Unreal Engine. Ми розглянемо, як це може допомогти нам створювати якісніші та стабільні продукти.
Мабуть, кожен з нас стикався із ситуацією, коли довгоочікувана гра виходить на ринок... і розчаровує. Баги, вильоти, проблеми з продуктивністю — на жаль, це часте явище у сучасній ігровій індустрії, де проєкти стають дедалі складнішими та амбітнішими. Чим комплексніша гра, тим вища ймовірність зіткнутися з неочікуваними проблемами і що гравці відчують себе бета-тестерами. Гарантувати бездоганний досвід для гравця з першого запуску, особливо на безлічі конфігурацій ПК чи консолей, стає майже неможливим завданням.
У цій безперервній боротьбі за якість ключову роль відіграють наші незамінні колеги — QA-інженери. Вони — наші перші союзники й без перебільшення, найкращі друзі на всіх етапах розробки. QA не лише виявляють технічні помилки для нас, розробників. Вони тісно співпрацюють з технічними художниками, допомагаючи знаходити візуальні артефакти та вузькі місця у продуктивності. А для геймдизайнерів вони часто є першими гравцями, які можуть надати цінний зворотний зв’язок щодо ігрового процесу, адже саме QA проводять найбільше часу безпосередньо в грі під час її створення.
Виклики Ручного Тестування та Регресія
Розгляньмо типовий процес розробки та тестування нової функціональності на знайомому прикладі — механіці відкривання дверей. Розробник отримує завдання, реалізує базовий функціонал, скажімо, для однієї тестової локації. Навіть якщо розробнику здається, що все працює, він передає цю функціональність на перевірку QA, щоб зловити проблеми, які міг не помітити.
QA-спеціалісти ретельно тестують механіку і можуть виявити неочевидні сценарії. Наприклад, знаходять, що двері можуть відштовхнути гравця при певних умовах, або анімація відтворюється некоректно.
Розробник отримує детальний звіт, виправляє помилку. Цей цикл «розробка — тестування — зворотний зв’язок — виправлення» може повторюватися декілька разів, поки функціональність не буде вважатися стабільною та готовою до інтеграції в основний проєкт та на різні рівні.
І в цей час PM десь поруч питає: «А який статус цієї задачі? Вона вже в Done чи ще гойдаємось?»
Здавалося б, усе добре. Механіку фіналізували. Але проходить час, й інший розробник бере в роботу пов’язане завдання, наприклад, додає можливість вибивати ці двері або змінює фізику взаємодії. Під час тестування цих нових змін QA-команда, звісно, фокусується на нововведеннях. І тут може статися класична ситуація: якась рідкісна комбінація дій гравця або специфічне положення біля дверей, що раніше працювали ідеально, тепер призводить до помилки — раніше згаданого відштовхування гравця, яке начебто вже виправили.
Цю помилку, що вилізла у протестованому функціоналі через зміни в іншому місці, можна пропустити під час регресії. У такому випадку є ризик отримати «цікаві» відгуки від гравців. Щоб не було «сюрпризів», QA-інженери змушені регулярно проводити регресійне тестування — тобто, заново, з материнською любов’ю та терпінням, перевіряти все, що вже було колись протестовано, після внесення будь-яких змін у проєкт. Це надзвичайно трудомісткий, монотонний, і, будьмо чесними, досить депресивний процес. Він забирає дорогоцінний час спеціалістів, який міг би бути витрачений на тестування нових фіч або глибше дослідження ігрового процесу.
І саме тут ми підходимо до ключового питання: чи можемо ми зробити цей процес більш ефективним? Чи можемо ми зменшити тягар ручного тестування?
Автоматизація — світло в кінці тунелю регресії
Отож, ми з вами дійшли висновку, що ручне регресійне тестування — це такий собі персональний день бабака для наших QA-колег. Це довго, може бути нудно, коштує чимало ресурсів і нервових клітин. Та й будьмо відвертими, людський фактор ніхто не скасовував — щось можна й пропустити, особливо після п’ятої години одноманітних перевірок. Як же нам вирватися з цього захопливого, але малопродуктивного циклу?
Тут, як промінь світла у темному царстві багів, на сцену виходить автоматизація тестування!
Що це таке, якщо говорити простою мовою? Це коли ми делегуємо найнуднішу, найбільш повторювану частину роботи комп’ютерові. Замість того, щоб QA-інженер всоте затискав комбінацію клавіш, біг до тих самих дверей і перевіряв, чи не відкине його цього разу, ми пишемо спеціальний скрипт — автотест. Цей скрипт самостійно запускає гру (або її частину), виконує потрібні дії й перевіряє, чи відповідає результат очікуванням.
Навіщо нам це потрібно? Переваг тут чимало, і вони б’ють прямо по больових точках, які ми обговорювали:
- Швидкість. Автотести можуть виконати сотні й тисячі перевірок за той час, поки людина встигне випити кави й подумати, чи не час оновити статус задачі в Jira. Вони працюють невтомно, 24/7, якщо потрібно.
- Надійність та повторюваність. Комп’ютер не буває неуважним, у нього не буває поганого настрою, він не забуде перевірити той самий рідкісний граничний випадок, який вручну відтворювати просто ліньки. Якщо тест написаний правильно, він завжди виконає перевірку однаково. Прощавай, людський фактор. Принаймні, на цьому етапі.
- Економія ресурсів у довгостроковій перспективі. Так, написання та підтримка автотестів вимагає часу розробників або спеціалізованих інженерів з автоматизації. Але уявіть, скільки годин QA звільняється від монотонної регресії! Цей час можна інвестувати в тестування нових фіч, дослідницьке тестування, коли QA просто «грається» з грою, шукаючи неочевидні проблеми, перевірку геймдизайну, юзабіліті — тобто, в ті задачі, де потрібен саме людський інтелект та інтуїція.
- Раннє виявлення регресій: Це, мабуть, одна з найбільших переваг. Автотести можна інтегрувати в процеси Continuous Integration / Continuous Delivery (CI/CD). Уявіть: розробник зробив зміни, відправив їх у систему контролю версій, і автоматично запускається набір тестів. Якщо щось зламалося — команда дізнається про це майже миттєво, а не через кілька днів чи тижнів, коли баг вже обріс новими фічами і знайти його причину стає набагато складніше.
Хороша новина полягає в тому, що нам не потрібно винаходити велосипед! Unreal Engine надає потужні вбудовані інструменти для автоматизації тестування. Ключовими тут є:
- Automation System. Це фреймворк, який дозволяє створювати різні типи тестів — від юніт-тестів для C++ коду та функціональних тестів для Blueprint’ів до складних тестів, які перевіряють графіку (скриншот-тести) чи продуктивність.
- Gauntlet Automation Framework. Більш високорівнева система, призначена для запуску складних тестових сценаріїв, які можуть охоплювати запуск гри, завантаження рівнів, виконання певних дій та перевірку результатів на різних платформах.
А тепер — хвилинка реалізму. Послухавши про всі ці райдужні перспективи та потужні інструменти, дехто з вас, особливо ті, хто вже попрацював на кількох проєктах з Unreal Engine, може скептично посміхатися. І знаєте що? Я вас прекрасно розумію. Мій власний досвід, та й, підозрюю, досвід багатьох у цій залі, показує, що на практиці ситуація з автотестами часто, м’яко кажучи, не вражає.
У більшості випадків, якщо автотести взагалі існують, то вони покривають лише найбільш базові сценарії. Класика жанру:
- Boot test: Чи запускається гра взагалі, не падаючи на старті? (Вже добре!)
- Connection test: Чи може клієнт під’єднатися до сервера? (Супер!)
Це важливі перевірки, безперечно. Але вони не дають відповіді на питання: а чи працює сама гра? Чи працюють її ключові механіки?
Коли ж мова заходить про складніші функціональні тести, які б імітували дії гравця — перевіряли роботу інвентарю, системи квестів, взаємодію зі світом, ті ж нещасні двері в різних ситуаціях — тут картина стає значно сумнішою. Таких тестів або немає зовсім, або їх катастрофічно мало.
А навіть якщо колись команда героїчно інвестувала час і написала набір таких тестів, дуже часто трапляється наступне: проходить кілька місяців, гра активно розвивається, код змінюється... а тести ніхто не оновлює. Вони починають падати, бо функціонал змінився. Спочатку їх ігнорують («червоні тести? та то таке, потім подивимось»), а потім просто коментують або видаляють, бо вони «заважають» і «тільки створюють шум». В результаті цінні зусилля пропадають марно. Знайомо?
Чому так відбувається? Причин безліч:
- Високий поріг входу: Написання стабільних функціональних тестів в UE може бути складним.
- Вартість підтримки: Тести потрібно підтримувати так само як і основний код.
- Пріоритети: Під тиском дедлайнів нові фічі майже завжди виграють у битві за ресурси з тестами.
- Нерозуміння цінності: Не завжди вдається донести до менеджменту, чому варто витрачати час розробників на «якісь там тести», які не бачить гравець.
АЛЕ! Те, що на багатьох проєктах автоматизація перебуває в зародковому або занедбаному стані, не означає, що вона не потрібна або не працює. Навпаки! Це лише підкреслює існування проблеми й показує величезний потенціал для покращення. Ті виклики, які ми обговорювали — постійні регресії, перевантаження QA, ризики перед релізом — вони реальні. І саме грамотно побудована система автоматизованого тестування є одним з ключових інструментів для боротьби з ними. Розуміння того, як це працює і як це можна впровадити (навіть поступово), стає все більш важливим.
Звісно, важливо розуміти: автоматизація — це не срібна куля. Вона не може повністю замінити ручне тестування і досвід QA-інженера. Автотести чудово справляються з перевіркою чітко визначених сценаріїв та пошуком регресій, але вони не можуть оцінити задоволення від гри, знайти неочікувану візуальну помилку там, де тест не дивиться, або запропонувати покращення для ігроладу. Автоматизація — це потужний інструмент, який доповнює ручне тестування, звільняючи людей для більш креативних та складних завдань.
А тепер зануримося трохи глибше і подивимося, як саме можна почати будувати ці тести в Unreal Engine, навіть якщо ви раніше цього не робили.
Занурення в Автоматизацію
1. Почнемо з основ: тести на C++
Юніт-тести — це фундамент піраміди тестування. Вони призначені для перевірки найменших, ізольованих частин вашого коду — окремих функцій або методів класів у C++. Це швидкий і надійний спосіб переконатися, що базові будівельні блоки вашої логіки працюють коректно, незалежно від решти гри.
Уявімо, у вас є якийсь ігровий компонент, що відповідає за здоров’я персонажа, і ви хочете перевірити функцію отримання пошкодження. Як це може виглядати в коді:
Як бачите, структура проста: підготували об’єкт, виконали дію, перевірили результат за допомогою макросів TestEqual, TestTrue тощо. Якщо хоч одна перевірка не пройде, тест вважатиметься проваленим.
Такий тест не вимагає запуску рендера, він тестує окремий ігровий компонент та його внутрішню логіку.
Якщо ж нам потрібно перевірити взаємодію, наприклад інвентарю, то ми маємо створювати ігровий світ. Подивімось на приклад такого тесту:
Такі тести тривають довше, бо вимагають запуску, ініціалізації систем гри та підготовки до виконання самого тесту.
2. Де знайти та запустити тести? Session Frontend
Гаразд, тест написали. Як його запустити? В редакторі Unreal Engine є спеціальна панель — Session Frontend.
Ви можете вибрати окремі тести, цілі групи або всі тести одразу. Натискаєте кнопку «Start Tests» і дивитесь, як вони виконуються. Зелений індикатор — тест пройшов, червоний — щось пішло не так. Дуже зручно для швидких перевірок під час розробки, щоб переконатися, що ви нічого не зламали своїми останніми змінами.
Як бачите, на моєму скриншоті жодної зеленої стрічки — це індикатор для мене що я реально зламав якусь систему.
3. Автоматизація для просунутих: запуск з командного рядка (.bat)
Запускати тести вручну — це добре, але справжня сила автоматизації розкривається, коли тести виконуються самі, наприклад, у вашій системі Continuous Integration (CI/CD) або просто як частина нічної перевірки стабільності білда. Для цього нам потрібен командний рядок.
Ви можете запустити тести на вже зібраній грі (або в редакторі) за допомогою спеціальних параметрів. Ось приклад простого .bat скрипта для Windows, який запускає певну групу тестів у зібраному білді:
Пояснення параметрів:
- -ExecCmds="Automation RunTests Inventory" — запускає тести з категорії інвентарю;
- -TestExit="Automation Test Queue Empty" — закінчує роботу коли система пройшлась
по усім тестам; - -ReportOutputPath="%REPORT_DIR%« — копіює результати тестів у цю теку.
Цей скрипт можна легко інтегрувати в Jenkins, TeamCity або будь-яку іншу систему CI/CD.
4. Тестування ігроладу: функціональні тести та блюпринти
А як перевірити, чи працює взаємодія між об’єктами, логіка, зав’язана на блюпринтах, чи реагує гра на дії гравця? Для цього існують функціональні тести.
Візьмемо ваш приклад: гравець заходить у тригерну зону, і там спрацьовує лампочка. Як це протестувати:
У першому рядку написана логіка виконання тригерної зони. Під час колізії ми перевіряємо, що у зону зайшов гравець, надсилаємо повідомлення що колізія відбулася, а потім вмикаємо і вимикаємо лампочку.
Логіка тесту знаходиться знизу, тут ми шукаємо тригер на мапі, підписуємося на повідомлення від нього, а коли воно надходить, то завершуємо тест через 5 секунд.
Якщо ми хочемо дивитись на тест з іншої камери, ніж в нас встановлено в проєкті, то потрібно перемкнути ViewTarget.
Gauntlet — диригент ваших тестів
Ми розглянули тести. які можна запускати через Automation System в редакторі або командному рядку. Це чудово покриває багато потреб. Але що, якщо ваш тест вимагає більшого? Наприклад, вам потрібно:
- Протестувати взаємодію клієнта та сервера?
- Запустити тест на реальній консолі чи мобільному пристрої?
- Виконати складну послідовність дій: запустити гру, залогінитися, перейти в певне меню, завантажити мапу і лише потім виконати перевірку?
- Провести довготривалий тест стабільності, який триває години?
Для таких завдань стандартної Automation System може бути недостатньо. І тут на сцену виходить Gauntlet Automation Framework.
Що таке Gauntlet
Якщо Automation System — це інструменти для тестування всередині гри, то Gauntlet — це фреймворк для оркестрації тестів ззовні. Уявіть собі диригента, який керує цілим оркестром ігрових процесів. Gauntlet:
- Запускає один або кілька екземплярів вашої гри (клієнти, виділені сервери).
- Може розгортати та запускати їх на різних машинах або пристроях.
- Надсилає команди запущеним екземплярам гри.
- Аналізує їхні лог-файли в реальному часі.
- Визначає успіх або провал тесту на основі отриманих даних або таймаутів.
Навіщо він потрібен? Ключові сценарії:
- Тестування мультиплеєра. Незамінний для перевірки клієнт-серверної логіки, реплікації, синхронізації. Ви можете запустити сервер і кілька клієнтів, змусити їх виконувати певні дії та перевірити результат.
- Тестування на цільових пристроях. Дозволяє автоматизувати запуск і тестування на консолях (PlayStation, Xbox) та мобільних платформах (iOS, Android), керуючи процесом з ПК.
- Стрес-тести та тести стабільності. Запуск гри на тривалий час з певним навантаженням для виявлення витоків пам’яті, падінь або проблем з продуктивністю.
- Інтеграція з наявними тестами. Gauntlet може запускати тести, створені в Automation System (як функціональні, так і юніт), але в більш контрольованому середовищі, наприклад, після певних дій або на конкретному пристрої.
Як це працює: дворівнева система (C# Оркестратор + C++ Контролер)
На відміну від тестів Automation System (C++ та Blueprints), основна логіка керування тестами Gauntlet пишеться на C#. Однак, важливо розуміти, що Gauntlet часто функціонує як дворівнева система:
- Зовнішній C# Скрипт (Тест-контролер оркестрації):
- Це C# клас, який ми запускаємо через RunUAT.bat RunGauntlet.
- Діє як оркестратор: запускає один або кілька процесів гри, керує їх життєвим циклом і аналізує загальний результат (часто по логах або кодах повернення).
- Саме цей скрипт визначає, які тести і як запустити.
- Внутрішньоігровий C++ Контролер (UGauntletTestController):
- Це дуже корисний компонент, який ви можете створити на C++, успадкувавши його від базового класу UGauntletTestController.
- Його головна задача — знати про стан світу, слухати повідомлення, запускати тести тощо.
- Цей C++ контролер може:
- Безпосередньо взаємодіяти зі світом гри (UWorld).
- Знаходити та керувати іншими акторами (наприклад, персонажем тесту).
- Запускати події, анімації, спавнити ефекти.
- Викликати функції на інших блупринтах чи C++ об’єктах.
- Запускати або керувати виконанням тестів Automation System.
- Відправляти та зберігати детальні повідомлення про прогрес або результат тесту, які потім може проаналізувати зовнішній C# скрипт.
Приклад: замір перформансу через Gauntlet
Розглянемо ситуацію, коли в нас велика гра і є багато мап з різними деталізованими об’єктами. Ми хочемо виміряти перформанс на всіх мапах. Для цього на кожну мапу ми помістимо камери, які будуть записувати профайли у файли .csv.
Напишемо простий Gauntlet тест-контролер, який автоматично запустить один екземпляр гри без сервера та додасть до нього внутрішньоігровий C++ контролер.
Своєю чергою C++ контролер буде чекати на відкриття правильної мапи, починати та закінчувати запис профайлу та відео.
Як запустити Gauntlet тест
Gauntlet тести запускаються через RunUAT.bat (Unreal Automation Tool):
Ключові параметри:
- RunGauntlet: команда для запуску Gauntlet.
- -scriptdir=%SCRIPT_DIR% — вибір директорії, де знаходяться Automation скрипти
- -RunTest=%TEST% — запускає Gauntlet Automation скрипт
- -BuildConfig=Development (або DebugGame і т.д.): конфігурація білда для тестування.
- -Platform=Win64 (або PS5, Android і т.д.): Цільова платформа.
Gauntlet vs Automation System: коли що обрати
- Automation System (Unit/Functional Tests):
- Тести всередині одного процесу (редактор/гра).
- Перевірка ізольованої логіки C++/Blueprints.
- Швидкі перевірки під час розробки.
- Простіші сценарії.
- Gauntlet:
- Тести, що вимагають зовнішньої оркестрації.
- Запуск та керування кількома процесами (клієнт-сервер).
- Тестування на реальних пристроях/консолях.
- Складні підготовчі кроки перед тестом (меню, логін).
- Довготривалі тести стабільності.
Інструменти для CI/CD: Jenkins, TeamCity та спеціалізований Horde
Існує багато інструментів для реалізації CI/CD. Серед найпопулярніших універсальних рішень:
- Jenkins. Це безплатне open-source рішення з величезною спільнотою та тисячами плагінів. Дуже гнучке, але може вимагати більше часу на початкове налаштування.
- TeamCity. Комерційний продукт від JetBrains, хоча є безплатна версія з обмеженнями. Вважається простішим у налаштуванні «з коробки», має зручний інтерфейс.
Важливо зазначити, що навіть з Jenkins або TeamCity можна налаштувати процес запуску автотестів для проєктів на Unreal Engine. Наприклад, викликаючи ті ж .bat скрипти або команди Gauntlet, які ми розглядали раніше.
Однак, для масштабної розробки на Unreal Engine, Epic Games створили власне, глибоко інтегроване рішення — Horde. Це не просто CI/CD сервер, а ціла платформа, оптимізована під специфіку UE.
Ключові можливості та переваги Horde:
- Розподілені збірки та тести. Horde ефективно керує фермою білд-агентів (фізичних машин, віртуалок, пристроїв), розподіляючи між ними завдання збірки та тестування для максимального розпаралелення та прискорення процесу.
- Керування агентами. Централізоване керування станом тестових машин/пристроїв.
- Інтеграція з BuildGraph. BuildGraph — це система скриптування в UE на основі XML для опису складних, багатоетапних процесів збірки: скомпілювати код, приготувати контент для PS5, потім для Xbox, запакувати. Horde використовує ці скрипти BuildGraph для виконання реальних завдань на агентах. BuildGraph визначає, що робити, а Horde — як, де і коли.
- Інтеграція з Gauntlet: Horde може легко запускати Gauntlet-тести як один із кроків у пайплайні. Ви просто вказуєте, який Gauntlet-тест запустити, на яких агентах і з якими параметрами.
- Планування задач. Гнучке налаштування тригерів (наприклад, запуск при кожному коміті в певну гілку Perforce), залежностей між завданнями.
- Візуалізація результатів. Надає зручний веб-дашборд для відстеження статусу білдів, результатів тестів (успіх/провал), перегляду логів.
- Керування артефактами. Автоматично збирає та зберігає результати роботи пайплайну —- артефакти. Це можуть бути:
- Готові білди гри.
- Детальні логи збірки та тестів.
- Результати тестів, включно з даними про продуктивність, зібраними Gauntlet.
- Відео, скриншоти, зняті під час тестування
Налаштування пайплайну та запуск Gauntlet через Horde
У Horde ви візуально або через конфігураційні файли визначаєте кроки вашого пайплайну. Типовий пайплайн для тестування може виглядати так:
- Тригер: новий коміт у головну гілку Perforce.
- Крок 1: зібрати гру (використовуючи BuildGraph скрипт).
- Крок 2: розгорнути (деплойнути) зібраний білд на доступні тестові агенти, наприклад, кілька ПК з Windows, Mac, консоль.
- Крок 3: запустити набір Gauntlet-тестів на цих агентах, вказавши потрібний тест, платформу, конфігурацію.
- Крок 4: зібрати артефакти — логи, результати тестів.
Horde бере на себе всю логістику: знаходить вільних агентів, копіює файли, запускає команди, стежить за виконанням, збирає результати показує все це на дашборді.
Збираємо все докупи: «Ігри грають самі в себе»
Отож, ми розглянули різні інструменти та концепції: Unreal Automation, Horde та Gauntlet. Як це все поєднується в єдиний процес, який дійсно полегшує життя команді?
Уявіть собі такий типовий автоматизований нічний цикл:
- Вечір: розробники завершують роботу і комітять свої зміни в систему контролю версій, наприклад, Perforce).
- Ніч: автоматизація в дії:
- Horde помічає нові зміни та автоматично запускає пайплайн.
- Виконується збірка (Build) проєкту за допомогою BuildGraph скриптів на доступних білд-агентах.
- Якщо білд успішний, Horde автоматично розгортає його на тестові машини або пристрої — консолі, ПК різних конфігурацій.
- Далі Horde запускає на цих пристроях Gauntlet-тести:
- Проганяються функціональні тести (перевірка основних механік).
- Виконуються перфоманс-тести у ключових локаціях для вимірювання FPS, пам’яті тощо.
- Можливо, запускаються стрес-тести чи перевірки стабільності.
- Horde збирає всі результати: логи, статус тестів (pass/fail), дані про продуктивність, можливі креш-дампи.
- Ранок: результати та фідбек:
- Команда приходить на роботу і бачить ранковий звіт на дашборді Horde.
- Одразу видно: чи успішно зібрався нічний білд, чи не з’явилися нові регресійні баги, чи не впала продуктивність після вчорашніх змін.
Це і є сценарій, де ігри, фактично, тестують самі себе.
Реальні переваги такого підходу:
- Швидкий Зворотний Зв’язок. Проблеми виявляються майже миттєво, а не через тижні.
- Стабільніші білди. Регулярні автоматичні перевірки допомагають підтримувати проєкт у робочому стані.
- Менше «сюрпризів». Зменшується ризик знайти критичні проблеми перед самим релізом.
- Фокус команди. QA можуть зосередитися на складних, дослідницьких завданнях, а розробники — на створенні нових фіч, а не на постійному гасінні пожеж.
Якщо ви ще не використовуєте автоматизацію у своєму UE-проєкті, або використовуєте її мінімально, я закликаю вас почати експериментувати. Не обов’язково одразу впроваджувати весь стек з Horde. Почніть з малого:
- Напишіть кілька простих функціональних тестів для ключових механік.
- Спробуйте запустити їх через Gauntlet з командного рядка.
- Інтегруйте запуск цих тестів у вашу поточну CI/CD систему, якщо вона є.
Навіть невеликі кроки в автоматизації можуть принести значну користь у довгостроковій перспективі.
2 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів