Please use this identifier to cite or link to this item:
https://er.chdtu.edu.ua/handle/ChSTU/6035| Title: | Комп’ютерна гра жанру 2D з процедурною генерацією рівнів |
| Authors: | Дяченко, Петро Васильович Теличко, Віталій Сергійович |
| Keywords: | РОЗРОБКА,;КОМП’ЮТЕРНА ГРА З ПРОЦЕДУРНОЮ ГЕНЕРАЦІЄЮ РІВНІВ,;2D,;ПРЕДМЕТНА ОБЛАСТЬ,;ЗАДАЧІ,;ПРОГРАМНІ ЗАСОБИ. |
| Issue Date: | 12-Jun-2025 |
| Abstract: | В умовах постійної діджиталізації, сфера розваг також не була залишена в стороні. Тема моєї кваліфікаційної роботи виглядає так: «Розробка комп’ютерної гри жанру 2d з процедурною генерацією рівнів». В сфері цифрових розваг зараз дуже велика конкуренція, тому продукт повинен бути унікальним, щоб залишити позитивні емоції після себе і втримувати користувача якомога довше. . Для створення гри використовується мова програмування C#, ігровий рушій Unity та графічний редактор Aseprite, для створення спрайтів. Метою кваліфікаційної роботи бакалавра є проектування та створення комп’ютерної 2d-гри.Об’єкт дослідження: комп’ютерні ігри жанру 2d з процедурною генерацією рівнів. Предмет дослідження: комп’ютерна гра жанру 2d з процедурною генерацією рівнів. В роботі застосовувалися вивчення практичних розробок, аналіз існуючих проектів. |
| URI: | https://er.chdtu.edu.ua/handle/ChSTU/6035 |
| Appears in Collections: | 122 Комп’ютерні науки (Комп’ютерні науки та прикладне програмування) |
Files in This Item:
| File | Description | Size | Format | |
|---|---|---|---|---|
| Пояснювальна записка_Теличко Віталій_КНС-2101_2024-2025.pdf Restricted Access | 2.18 MB | Adobe PDF | View/Open Request a copy |
Items in DSpace are protected by copyright, with all rights reserved, unless otherwise indicated.
Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ
Факультет інформаційних технологій і систем
Кафедра комп’ютерних наук та системного аналізу
Пояснювальна записка
до кваліфікаційної роботи
бакалавра
(освітній рівень)
на тему: «Комп’ютерна гра жанру 2D з процедурною генерацією рівнів»
Виконала: студентка 4 курсу, групи КНС-2101
спеціальності 122 «Комп’ютерні науки»
(шифр і назва спеціальності)
освітня програма «Комп’ютерні науки та
(назва освітньої програми)
прикладне програмування»
Теличко Віталій Сергійович
Керівник Дяченко П.В.
(прізвище та ініціали)
Рецензент ___________
(прізвище та ініціали)
Черкаси 2025 року
Черкаський державний технологічний університет
Факультет Інформаційних технологій і систем
Кафедра Комп’ютерних наук та системного аналізу
Освітній рівень Бакалавр
Спеціальність 122 – Комп’ютерні науки
Освітня програма Комп’ютерні науки та прикладне програмування
ЗАТВЕРДЖУЮ
Завідувач кафедри КНСА
_______________Юрій
ТРИУС
«____» _____________ 2025 р.
ЗАВДАННЯ
на кваліфікаційну роботу бакалавра студенту
Теличку Віталію Сергійовичу
(прізвище, ім‘я, по батькові)
1. Тема роботи «Комп’ютерна гра жанру 2D з процедурною генерацією рівнів»
Керівник роботи Дяченко П.В., к.т.н., доцент
(прізвище, ім’я, по батькові, науковий ступінь, вчене звання)
затверджені наказом університету від «25» лютого 2025 р. №53/03-03.
2. Строк подання студентом роботи до 11 червня 2025 року
3. Вихідні дані до роботи:
Практичні навички роботи з інформаційними ресурсами. Робота з базами даних.
Звіт з виробничої практики. Звіт з наукової практики.
4. Зміст пояснювальної записки (перелік питань, що їх належить розробити):
Вступ
4.1. Обґрунтування необхідності розробки гри з процедурною генерацією рівнів
4.2. Огляд аналогів та існуючих рішень.
4.3. Постановка задачі створення комп’ютерної гри.
4.4. Обгрунтування програмних засобів.
4.5. Розробка і тестування гри.
Висновки.
5. Перелік додатків (з точним зазначенням назв додатків):
5.1. Додаток А. Специфікація 482.ЧДТУ. 52350-01.
5.2. Додаток Б. Текст програми.
5.3. Додаток В. Інструкція користувача.
5.4. Презентація у вигляді 29 слайдів.
6. Консультанти розділів роботи
Прізвище, ініціали та Підпис, дата
Розділ посада
завдання видав завдання прийняв
консультанта
7. Дата видачі
завдання 17.12.2024
КАЛЕНДАРНИЙ ПЛАН
№ Строк виконання
Назва етапів кваліфікаційної роботи бакалавра Примітка
з/п етапів роботи
1 Видача завдання на кваліфікаційну роботу
17.12.2024 Виконано
бакалавра.
2 Аналіз літературних джерел, об’єкту та
до 28.02.2025 Виконано
предмету дослідження.
3 Написання теоретичного розділу
до 15.03.2025 Виконано
кваліфікаційної роботи бакалавра.
4 Написання аналітичного розділу кваліфікаційної
до 01.04.2025 Виконано
роботи бакалавра.
5 Написання практичних розділів й висновків до
до 01.05.2025 Виконано
кваліфікаційної роботи бакалавра.
6 Перед захист кваліфікаційної роботи бакалавра
01.06.2025 Виконано
на засіданні кафедри КНСА.
7 Подання роботи завідувачу кафедри КНСА. до 28.05.2025 Виконано
8 Захист кваліфікаційної роботи бакалавра. 12.06.2025 Виконано
Студент _____________________________ Віталій ТЕЛИЧКО
(підпис)
Керівник роботи _____________________________ Петро ДЯЧЕНКО
(підпис)
РЕФЕРАТ
Кваліфікаційна робота бакалавра містить: 60 с., 16 рис., 7 використаних
джерел, 3 додатків.
Актуальність теми.
В умовах постійної діджиталізації, сфера розваг також не була залишена в
стороні. Тема моєї кваліфікаційної роботи виглядає так: «Розробка комп’ютерної
гри жанру 2d з процедурною генерацією рівнів». В сфері цифрових розваг зараз
дуже велика конкуренція, тому продукт повинен бути унікальним, щоб залишити
позитивні емоції після себе і втримувати користувача якомога довше. .
Для створення гри використовується мова програмування C#, ігровий рушій
Unity та графічний редактор Aseprite, для створення спрайтів.
Було створену комп’ютерну гру, яка надасть гравцю цікавий досвід, який буде
унікальним на кожне проходження.
Мета роботи і задачі дослідження. Метою кваліфікаційної роботи бакалавра
є проектування та створення комп’ютерної 2d-гри. Для досягнення поставленої мети
в дослідженні необхідно вирішити такі задачі:
− виконати огляд методів та засобів для розробки комп’ютерної гри;
− провести порівняльний аналіз аналогів та рішень в існуючих проектах.
− обрати програмно-технічні засоби для реалізації задачі;
− спроектувати і створити комп’ютерну гру;
− провести адаптацію гри під різни системи та екрани;
− провести тестування розробленої гри.
Об’єкт дослідження: комп’ютерні ігри жанру 2d з процедурною генерацією
рівнів.
Предмет дослідження: комп’ютерна гра жанру 2d з процедурною генерацією
рівнів.
Методи дослідження. В роботі застосовувалися вивчення практичних
розробок, аналіз існуючих проектів.
Перелік ключових слів: РОЗРОБКА, КОМП’ЮТЕРНА ГРА З
ПРОЦЕДУРНОЮ ГЕНЕРАЦІЄЮ РІВНІВ, 2D, ПРЕДМЕТНА ОБЛАСТЬ, ЗАДАЧІ,
ПРОГРАМНІ ЗАСОБИ.
ABSTRACT
The qualification work of the bachelor contains: 60 pages, 16 figures, 7 used
sources, 3 appendices.
Actuality of theme.
In the context of ongoing digitalization, the entertainment industry has not been left
behind. The topic of my qualification work is: "Development of a 2D Computer Game
with Procedural Level Generation." The digital entertainment sector is currently highly
competitive, so a product must be unique to leave a lasting positive impression and retain
users for as long as possible.
To create the game, the following tools were used: the C# programming language,
the Unity game engine, and the Aseprite graphics editor for sprite creation.
A computer game was developed to provide players with an engaging experience
that is unique with each playthrough.
Purpose and tasks of research: The purpose of this bachelor's qualification work is
the design and development of a 2D computer game. To achieve this goal, the following
research objectives must be addressed:
− review methods and tools for computer game development;
− to conduct a comparative analysis of analogs and solutions in existing projects;
− choose software and technical tools for task implementation;
− design and develop the computer game;
− adapt the game for different systems and screens;
− to test the developed system.
Object of research: 2D computer games with procedural level generation.
Subject of research: A 2D computer game with procedural level generation.
Research methods. The study of practical developments, analysis of existing
projects.
List of key words: DEVELOPMENT, COMPUTER GAME WITH
PROCEDURAL LEVEL GENERATION, 2D, SUBJECT AREA, TASKS, SOFTWARE
TOOLS.
ЗМІСТ
ВСТУП 10
1. ОБГРУНТУВАННЯ НЕОБХІДНОСТІ РОЗРОБКИ ГРИ З ПРОЦЕДУРНОЮ
ГЕНЕРАЦІЄЮ РІВНІВ 12
1.1. Актуальність процедурної генерації рівнів у сучасних іграх 12
1.2. Аналіз переваг та обмежень процедурної генерації рівнів 13
1.3. Дослідження цільової аудиторії та її очікувань 14
1.4. Система вимог до гри з процедурною генерацією рівнів 16
Висновки до розділу 1 17
2. ОГЛЯД АНАЛОГІВ ТА ІСНУЮЧИХ РІШЕНЬ 18
2.1. Класифікація ігор з процедурною генерацією рівнів 18
2.2. Порівняльний аналіз існуючих ігор з процедурною генерацією рівнів 19
2.3. Висновки щодо вибору підходу для власної розробки 21
Висновки до розділу 2 22
3. ПОСТАНОВКА ЗАДАЧІ СТВОРЕННЯ КОМП'ЮТЕРНОЇ ГРИ 23
3.1. Постановка задачі 23
3.2. Вимоги до гри 23
3.2. Основні модулі гри 24
Висновки до розділу 3 25
4. ОБҐРУНТУВАННЯ ПРОГРАМНИХ ЗАСОБІВ 26
4.1. Обґрунтування вибору програмних засобів 26
4.2. Апаратні засоби 27
Висновки до розділу 4 28
5. РОЗРОБКА І ТЕСТУВАННЯ КОМП'ЮТЕРНОЇ ГРИ 29
5.1. Проєктування сюжету і дизайну спрайтів 29
5.2. Розробка генератора рівнів 30
5.3 Перевірка працездатності і тестування 34
Висновки до розділу 5 35
ВИСНОВКИ 36
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ 37
ДОДАТОК А Специфікація 482.ЧДТУ. 52350-01 38
ДОДАТОК Б Текст програми 40
ДОДАТОК В Інструкція користувача 56
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ, СИМВОЛІВ, СКОРОЧЕНЬ І ТЕРМІНІВ
2D – 2-dimension, двохвимірна графіка.
Геймдев – розробка ігор.
Спрайт – 2D-зображення предмета, персонажа чи оточення.
Префаб – Заздалегіть налаштований об'єкт, який можна повторно
використовувати без необхідності повторного налаштування.
Скрипт – Код, який запускається при певній умові.
HUD – Head-up-display, інтерфейс, який дає гравцю інформацію про події в грі.
10
ВСТУП
В умовах стрімкого розвитку цифрових технологій комп’ютерні ігри стають
важливим напрямком не лише у сфері розваг, а й в освіті, маркетингу та соціальних
комунікаціях. Особливий інтерес представляють ігри з процедурною генерацією
рівнів – вони створюють неповторний геймплей завдяки динамічному формуванню
контенту. Ця технологія не лише збагачує повторне проходження гри, але й значно
розширює творчі можливості розробників, дозволяючи створювати складні ігрові
світи з мінімальними витратами ресурсів.
Важливість дослідження полягає у вивченні та впровадженні останніх методів
процедурно-генеративного підходу для створення 2D-гри, яка відкриває нові
можливості в галузі геймдизайну. Використання алгоритмів автоматичного
створення рівнів дозволяє оптимізувати процес розробки та забезпечує широкий
спектр ігрового процесу.
Мета цього проекту полягає у створенні та втіленні 2D-гри з процедурною
генерацією рівнів, що поширює класичні елементи жанру з новаторським підходом
до формування ігрового контенту. Для досягнення цих цілей визначено наступні
завдання:
вивчення теоретичних засад процедурно-генеративного підходу в галузі
геймдеву.
дослідити вже існуючі аналоги та знайти оптимальні рішення для створення
свого власного продукту.
обґрунтувати вибір конкретних технологій та інструментів, які будуть
застосовані під час втілення проекту.
створити структуру гри та додати можливості процедурно створеного
контенту.
провести випробування та удосконалення продукту з метою забезпечення його
надійності в роботі.
11
Об’єктом дослідження є процес створення комп’ютерних ігор з динамічною
генерацією контенту, а предметом – методи та алгоритми процедурної генерації
рівнів у 2D-іграх.
Робота полягає в використанні різних дослідницьких методів: аналіз наукової
літератури; порівняльний огляд існуючих ігрових рішень; експериментальна
розробка програмного забезпечення та тестування його функціональності.
Матеріали дослідження можуть бути корисними для розробників ігор із
зацікавленістю в процедурній генерації і для студентів, що вивчають сучасні
технології щодо геймдизайну та програмування.
Структура роботи відображає послідовність вирішення поставлених задач і
включає теоретичний аналіз, практичну реалізацію та підсумкові висновки щодо
ефективності обраних підходів.
12
1 ОБГРУНТУВАННЯ НЕОБХІДНОСТІ РОЗРОБКИ ГРИ З ПРОЦЕДУРНОЮ
ГЕНЕРАЦІЄЮ РІВНІВ
1.1 Актуальність процедурної генерації рівнів у сучасних іграх
Сучасна ігрова індустрія демонструє постійне зростання інтересу до
процедурної генерації контенту як ефективного інструменту розробки. Ця
технологія, що передбачає автоматичне створення ігрових рівнів за допомогою
алгоритмів, набуває все більшого поширення через свої унікальні переваги у
порівнянні з традиційними методами ручного дизайну. Важливим аспектом
актуальності даного підходу є його здатність значно підвищувати реіграбельність
ігрового продукту, що є ключовим фактором утримання аудиторії в умовах високої
конкуренції на ринку відеоігор.
Технологія процедурної генерації знаходить застосування у різних жанрах,
зокрема у платформерах, roguelike-іграх та відкритих світах. Яскравими прикладами
успішної реалізації цього підходу є такі відомі проекти як "Spelunky", "The Binding
of Isaac" та "Dead Cells", які демонструють високу ефективність алгоритмічного
створення контенту. Особливу увагу заслуговує гра "Noita", де процедурна
генерація застосовується не лише для створення рівнів, але й для моделювання
фізичних процесів, що відкриває нові горизонти для ігрового дизайну.
З економічної точки зору процедурна генерація пропонує значні переваги для
розробників, особливо для невеликих студій та незалежних команд.
Автоматизований процес створення контенту дозволяє зменшити витрати часу та
ресурсів на розробку, при цьому забезпечуючи високий рівень варіативності
ігрового досвіду. Це стає особливо актуальним у контексті сучасних вимог гравців
до постійного оновлення контенту та збільшення тривалості ігрового процесу.
Важливим аспектом актуальності процедурної генерації є її потенціал у сфері
освітніх та навчальних ігор. Алгоритмічне створення завдань та навчальних
сценаріїв дозволяє адаптувати складність під рівень користувача, що робить
навчальний процес більш ефективним. Крім того, принципи процедурної генерації
13
знаходять застосування в таких галузях як генеративне мистецтво та віртуальна
археологія, демонструючи широкий спектр можливостей цієї технології за межами
ігрової індустрії.
Розвиток обчислювальних технологій та вдосконалення алгоритмів
машинного навчання відкривають нові перспективи для процедурної генерації.
Сучасні методи дозволяють створювати не лише геометрію рівнів, але й розробляти
цілісні ігрові всесвіти з унікальною історією та логікою розвитку подій. Це свідчить
про те, що потенціал процедурної генерації як інструменту створення ігрового
контенту ще далеко не вичерпаний, а її роль у майбутньому ігрового дизайну буде
лише зростати.
1.2 Аналіз переваг та обмежень процедурної генерації рівнів
Процедурна генерація рівнів як метод створення ігрового контенту має ряд
суттєвих переваг, які обумовлюють її широке застосування в сучасній ігровій
індустрії. Перш за це варто відзначити здатність даної технології забезпечувати
практично необмежену варіативність ігрового досвіду. Кожен новий сеанс гри
пропонує унікальну конфігурацію рівня, що суттєво підвищує реіграбельність
продукту та сприяє тривалому залученню користувачів. Ця характеристика
особливо цінна для жанрів, що базуються на повторному проходженні, таких як
roguelike або survival-ігри.
Важливим економічним аспектом процедурної генерації є її потенціал для
оптимізації процесу розробки. Автоматизоване створення контенту дозволяє значно
зменшити витрати часу та трудових ресурсів, необхідних для ручного проектування
рівнів. Це особливо актуально для невеликих розробницьких студій або
індивідуальних авторів, які мають обмежені можливості для створення великих
обсягів контенту традиційними методами. Крім того, процедурна генерація
відкриває можливості для динамічної адаптації складності гри під індивідуальні
здібності гравця, що сприяє створенню більш персоналізованого ігрового досвіду.
14
Однак процедурна генерація має і ряд суттєвих обмежень, які необхідно
враховувати при її застосуванні. Однією з основних проблем є складність
забезпечення збалансованості та цілісності ігрового досвіду. Автоматично
згенеровані рівні можуть містити логічні невідповідності або дисбаланс у
розміщенні ворогів, ресурсів та перешкод, що може негативно вплинути на ігрову
ергономіку. Ця проблема особливо актуальна для ігор зі складними механіками
взаємодії між ігровими об'єктами.
Ще одним істотним обмеженням є складність інтеграції процедурної генерації
з нарративними елементами гри. На відміну від ручно створених рівнів, де
розробник має повний контроль над розміщенням сюжетних елементів,
автоматизовані системи часто не здатні забезпечити послідовний і логічно зв'язний
розвиток історії. Це обмеження робить процедурну генерацію менш придатною для
ігор, де нарратив є ключовим елементом геймплею.
Технічна складність реалізації процедурної генерації також становить значний
виклик для розробників. Створення ефективних алгоритмів генерації вимагає
глибоких знань у галузі математичного моделювання та програмування, а також
ретельного тестування для забезпечення стабільної роботи системи. Крім того,
процедурно згенерований контент часто вимагає значно більших обчислювальних
ресурсів під час виконання гри порівняно з попередньо підготовленим контентом.
Незважаючи на ці обмеження, потенціал процедурної генерації залишається
високим, особливо в поєднанні з іншими сучасними технологіями, такими як
машинне навчання. Подолання існуючих обмежень є перспективним напрямком
досліджень у галузі ігрового дизайну та інтерактивних технологій. Розвиток більш
складних алгоритмів генерації, здатних враховувати контекст ігрового процесу та
особливості геймплею, відкриває нові можливості для створення якісного
процедурного контенту.
1.3 Дослідження цільової аудиторії та її очікувань
15
Аналіз цільової аудиторії грає вирішальну роль у процесі проектування гри з
процедурною генерацією рівнів, оскільки саме від її уподобань та поведінкових
характеристик залежить успішність реалізації ігрового продукту. Сучасні
дослідження ігрової індустрії дозволяють виділити кілька ключових сегментів
аудиторії, що проявляють найбільший інтерес до ігор з процедурною генерацією
контенту.
Основну частину цільової аудиторії складають гравці віком від 18 до 35 років,
які мають досвід взаємодії з різними жанрами відеоігор. Ця категорія користувачів
відрізняється високою технологічною компетентністю та схильністю до
експериментальних форм ігрового процесу. Важливим аспектом є те, що
представники цієї вікової групи часто віддають перевагу іграм з високим рівнем
реіграбельності, що робить їх ідеальною аудиторією для продуктів з процедурною
генерацією рівнів.
Особливу увагу при проектуванні слід приділити очікуванням гравців щодо
якості та характеру процедурної генерації. Сучасні дослідження ігрової психології
свідчать, що більшість користувачів очікують від процедурної генерації не просто
випадковості, а систематизованої варіативності, де кожен новий рівень зберігає
певний рівень передбачуваності та балансу. Це створює значний виклик для
розробників, які повинні знайти оптимальний баланс між елементами
непередбачуваності та ігровою справедливістю.
Проведений аналіз ігрових платформ показує, що аудиторія процедурних ігор
розподіляється між різними типами пристроїв. Значна частина гравців віддає
перевагу ПК-платформі, що обумовлено технічними можливостями для реалізації
складних алгоритмів генерації. Однак останні роки демонструють зростання
популярності мобільних пристроїв серед любителів процедурних ігор, що вимагає
від розробників додаткової уваги до оптимізації продуктивності та інтерфейсних
рішень.
Соціальний аспект взаємодії з гравцями також заслуговує особливої уваги.
Велика частина цільової аудиторії очікує можливості ділитися своїми ігровими
досягненнями та унікальними моментами, згенерованими системою. Це створює
16
додаткові вимоги до системи збереження та відтворення згенерованих рівнів, а
також до інтеграції соціальних функцій у ігровий процес.
Важливим фактором є рівень прийняття процедурної генерації серед різних
груп гравців. Дослідження показують, що досвідчені гравці, як правило, більш
позитивно сприймають процедурну генерацію, тоді як початківці можуть відчувати
певний дискомфорт через нестабільність ігрового досвіду. Це обумовлює
необхідність ретельного проектування системи навчання та адаптації складності для
різних категорій користувачів.
Тенденції останніх років свідчать про зростання попиту на гібридні системи,
де процедурна генерація поєднується з елементами ручного дизайну. Такий підхід
дозволяє задовольнити потреби як прихильників класичних ігрових механік, так і
шанувальників інноваційних рішень. Розуміння цих тенденцій є критично важливим
для створення успішного ігрового продукту, здатного задовольнити очікування
сучасної аудиторії.
1.4 Система вимог до гри з процедурною генерацією рівнів
Розробка комп'ютерної гри з процедурною генерацією рівнів вимагає чіткого
формулювання системи вимог, які забезпечать як технічну реалізацію алгоритмів
генерації, так і якість кінцевого ігрового досвіду. Вимоги до системи поділяються на
функціональні, що визначають основні можливості програми, та нефункціональні,
які стосуються якостей системи.
Серед ключових функціональних вимог необхідно виділити механізм
процедурної генерації рівнів, який повинен забезпечувати створення унікальних, але
графічно та логічно узгоджених ігрових просторів. Алгоритми генерації мають
враховувати базові принципи ігрового дизайну, такі як прогресуюча складність,
логічне розміщення ворогів та предметів, і збалансованість ігрового процесу.
Особливу увагу слід приділити системі зв'язків між окремими зонами рівня, що
забезпечує цілісність ігрового досвіду.
17
Важливим аспектом є вимога до системи збереження та відтворення
згенерованих рівнів. Гра повинна забезпечувати можливість збереження стану
процедурно створених рівнів з подальшим їх точним відтворенням при повторному
завантаженні гри. Це вимагає реалізації ефективних механізмів серіалізації даних та
відновлення стану гри на основі обмеженого набору параметрів.
Система динамічної адаптації складності становить окрему групу
функціональних вимог. Алгоритми генерації мають враховувати рівень
майстерності гравця, адаптуючи кількість та тип перешкод, характеристик ворогів
та доступність ресурсів. Ця система повинна працювати непомітно для гравця,
забезпечуючи плавний прогрес складності.
Нефункціональні вимоги включають критерії продуктивності системи.
Процедурна генерація повинна відбуватися за прийнятний час, не створюючи
помітних пауз у ігровому процесі. Особлива увага приділяється оптимізації
алгоритмів для мобільних платформ з обмеженими обчислювальними ресурсами.
Гра повинна забезпечувати стабільну частоту кадрів на всіх підтримуваних
платформах.
Вимоги до користувацького інтерфейсу передбачають створення інтуїтивно
зрозумілої системи взаємодії, яка дозволяє гравцеві легко орієнтуватись у
згенерованому просторі. Інтерфейс має наочно відображати ключові параметри
процедурної генерації, такі як рівень складності або особливості поточного рівня.
Крос-платформеність є важливою нефункціональною вимогою, особливо для
сучасних умов ринку. Система процедурної генерації повинна стабільно працювати
на різних апаратних платформах з однаковою якістю результатів. Це вимагає
ретельного тестування алгоритмів на різних конфігураціях обладнання.
Вимоги до безпеки та надійності включають захист від потенційних помилок
генерації, які можуть призвести до створення непрохідних або дисбалансованих
рівнів. Система повинна містити механізми самоперевірки та автоматичної корекції
результатів генерації при виявленні критичних помилок.
Висновки до розділу 1
18
У даному розділі було досліджено актуальність процедурної генерації рівнів у
сучасній ігровій індустрії, проаналізовано її переваги та обмеження, визначено
особливості цільової аудиторії та сформульовано систему вимог до гри з
процедурним контентом.
19
2 ОПИС АНАЛОГІВ ТА ІСНУЮЧИХ РІШЕНЬ
2.1 Класифікація ігор з процедурною генерацією рівнів
Сучасна ігрова індустрія демонструє різноманітні підходи до реалізації
процедурної генерації рівнів, які можна систематизувати за кількома ключовими
критеріями. Аналіз існуючих рішень дозволяє виокремити три основні категорії, що
відрізняються за принципами генерації та характером створюваного контенту.
Першу категорію становлять ігри з модульною системою генерації, де рівні
формуються шляхом комбінації попередньо підготовлених блоків. Цей підхід,
реалізований у таких проектах як "Spelunky" та "The Binding of Isaac", забезпечує
високий рівень контролю над якістю згенерованого контенту при збереженні
достатньої варіативності. Характерною рисою даного методу є можливість тонкого
налаштування правил з'єднання модулів, що дозволяє створювати логічно цілісні
ігрові простори.
Другу категорію представляють ігри з органічною генерацією, де рівні
створюються на основі математичних алгоритмів без використання готових модулів.
Такі проекти як "Noita" та "No Man's Sky" базуються на фрактальних алгоритмах і
методах шумових функцій, що дозволяє генерувати плавні, природні ландшафти.
Цей підхід відрізняється високою ступенем унікальності результатів, але вимагає
ретельного налаштування параметрів генерації для забезпечення ігрового балансу.
Окрему групу складають ігри з гібридною системою генерації, що поєднують
елементи модульного та алгоритмічного підходів. Яскравим прикладом є "Dead
Cells", де основні структурні елементи рівнів створюються процедурно, але
зберігають чітку логіку розміщення ключових об'єктів. Такий підхід дозволяє
уникнути основних недоліків "чистої" процедурної генерації, зберігаючи при цьому
її основні переваги.
Важливим аспектом класифікації є ступінь впливу гравця на процес генерації.
У деяких іграх параметри генерації визначаються виключно на початку гри, тоді як
20
в інших передбачена можливість динамічної корекції рівнів під час ігрового
процесу.
21
2.2 Порівняльний аналіз існуючих ігор з процедурною генерацією
Глибокий аналіз провідних ігрових проектів дозволяє виявити ключові
закономірності у реалізації процедурної генерації. Дослідження зосереджено на
трьох знакових іграх останнього десятиріччя, які представляють різні підходи до
створення процедурного контенту.
У грі "Spelunky", зображеній на рисунку 2.1, реалізовано модульний підхід до
генерації рівнів, де кожен ігровий простір складається з набору попередньо
створених сегментів. Ця система забезпечує високий рівень контролю над ігровим
балансом, оскільки розробники можуть точно налаштувати правила поєднання
модулів. Характерною особливістю є використання шаблонів розміщення ключових
об'єктів, що забезпечує логічну послідовність ігрового процесу при збереженні
варіативності. [2]
Рисунок 2.1 – Приклад модульної генерації в "Spelunky"
"Dead Cells", зображена на рисунках 2.2-2.3, демонструє гібридний підхід, де
процедурна генерація поєднується з ретельно продуманим ручним дизайном.
Особливістю є система "біомів" - тематичних зон, кожна з яких має унікальні
22
правила генерації. Це дозволяє створювати різноманітні ігрові простори, зберігаючи
при цьому чітку ігрову логіку. Алгоритми генерації враховують прогрес гравця,
динамічно адаптуючи складність рівнів. [3]
Рисунок 2.2 – Генерація биомів у "Dead Cells"
Рисунок 2.3 – Геймплей "Dead Cells"
У "Noita", зображеній на рисунку 2.4, реалізовано унікальну систему
органічної генерації на основі фізичних симуляцій. Кожен піксель ігрового світу має
фізичні властивості, що призводить до появи складних емерджентних систем.
Особливістю є використання алгоритмів шумових текстур для створення природних
23
форм ландшафту, що поєднується з детермінованими правилами розміщення
ключових об'єктів. [4]
Рисунок 2.4 – Фізична генерація середовища в "Noita"
Проведений аналіз виявляє загальну тенденцію до поєднання процедурних
методів з елементами ручного дизайну. Успішні ігри демонструють важливість
ретельної валідації згенерованого контенту та необхідність створення систем
контролю якості. Особливу увагу приділено балансу між випадковістю та
передбачуваністю, що є ключовим фактором задоволення гравців.
2.3 Висновки щодо вибору підходу для власної розробки
Проведений аналіз існуючих рішень у сфері процедурної генерації рівнів,
зокрема на прикладах таких ігор, як "Spelunky", "Dead Cells" та "Noita", дозволяє
зробити обґрунтовані висновки щодо вибору оптимального підходу для розробки
власної 2D-гри з процедурною генерацією рівнів у рамках кваліфікаційної роботи
бакалавра.
Кожен із розглянутих аналогів демонструє різні філософії та технічні
реалізації процедурної генерації. "Spelunky" пропонує контрольовану варіативність
24
за допомогою модульної системи, де заздалегідь створені блоки комбінуються за
певними правилами. Це забезпечує високий рівень контролю над ігровим балансом
та структурою рівнів. "Noita", навпаки, використовує складну органічну генерацію
на основі фізичних симуляцій, де кожен піксель має властивості, що призводить до
високої непередбачуваності та емерджентного геймплею. "Dead Cells" займає
проміжну позицію, реалізуючи гібридний підхід, що поєднує процедурну генерацію
структури рівнів у межах тематичних "біомів" з елементами ручного дизайну та
адаптивною складністю.
Враховуючи обмеження та цілі проекту, який передбачає створення
завершеного продукту з продемонстрованою функціональністю процедурної
генерації, складність реалізації підходу "Noita" може виявитися надмірною. Така
система вимагає значних ресурсів для розробки фізичного рушія та алгоритмів
генерації, що виходять за рамки типової кваліфікаційної роботи.
З іншого боку, суто модульний підхід, як у "Spelunky", є більш реалістичним
для втілення. Він дозволяє досягти значної реіграбельності та забезпечує хороший
контроль над кінцевим результатом, що важливо для забезпечення якісного ігрового
досвіду та можливості тестування конкретних ігрових механік.
Висновки до розділу 2
Було проведено порівняльний аналіз різних ігор і їх методів процедурної
генерації. Було обрано найкращий для даної роботи метод.
25
3 ПОСТАНОВКА ЗАДАЧІ СТВОРЕННЯ КОМП’ЮТЕРНОЇ ГРИ
3.1 Постановка задачі
Завданням даної кваліфікаційної роботи є розробка комп’ютерної 2D-гри з
процедурною генерацією рівнів, у якій гравець у ролі піксельного лицаря потрапляє
в підземелля і повинен знайти ключ, щоб відчинити двері та вибратися на свободу.
Особливістю цієї гри є використання процедурної генерації для побудови ігрових
рівнів, що забезпечує унікальність проходжень і підвищену реіграбельність.
Гра має включати наступні ключові елементи:
1. Процедурно згенеровані рівні підземелля;
2. Персонажа-гравця з можливістю руху та взаємодії з об'єктами;
3. Ключ, який потрібно знайти;
4. Вихід із рівня, який можна відкрити тільки з ключем;
5. Базову систему ворогів або перешкод;
6. Збереження і відтворення ігрового стану.
Проект реалізується з використанням сучасного стеку технологій, придатного
для створення 2D-ігор, таких як Unity (з мовою C#) . Для створення піксельної
графіки використовуються відповідні інструменти (наприклад, Aseprite, Piskel).
Основна мета — не лише реалізувати гру, а й продемонструвати роботу
алгоритмів процедурної генерації рівнів у реальному ігровому контексті.
3.2 Вимоги до гри
Для ефективної реалізації гри сформульовані функціональні та
нефункціональні вимоги.
Функціональні вимоги:
1. Генерація рівня: система повинна автоматично створювати підземелля з
кімнатами, стінами, проходами, які кожного разу матимуть нову
конфігурацію.
26
2. Початковий спавн гравця: персонаж гравця має з’являтися у випадковій
стартовій точці, яка завжди є прохідною.
3. Ключ і вихід: генератор повинен гарантувати наявність одного ключа і одного
виходу, причому між ними має бути щонайменше один прохід.
4. Взаємодія з об’єктами: гравець повинен мати змогу підібрати ключ і
відчинити двері.
5. Колізії: стіни мають блокувати рух, двері мають бути заблоковані до
отримання ключа.
6. Противники: базові вороги, які переслідують гравця або блокують шлях.
Нефункціональні вимоги:
1. Продуктивність: генерація рівня не повинна перевищувати 1 секунди часу,
щоб не створювати відчуття затримки при запуску нової гри або рівня.
2. Графіка: візуальний стиль – піксельна графіка з роздільною здатністю в межах
16x16 або 32x32 пікселя на спрайт.
3. Керування: підтримка клавіатури (рух W/A/S/D, взаємодія – клавіша E).
4. Кросплатформеність: гра повинна коректно працювати як мінімум на
Windows-платформі, з можливістю майбутньої адаптації під Linux або
мобільні ОС.
5. Збереження гри: можливість зберегти стан рівня для продовження гри пізніше.
3.3 Основні модулі гри
Гра передбачає реалізацію таких ключових модулів:
1. Генератор рівня: відповідає за створення карти рівня (на основі, наприклад,
алгоритму «поширення кімнат» або «перлінового шуму»).
2. Менеджер об'єктів: відповідає за розміщення ключа, виходу, ворогів та інших
об'єктів на згенерованій мапі.
3. Ігрова логіка: перевірка умов завершення рівня, відстеження підбору ключа,
активація виходу.
27
4. Інтерфейс користувача: виведення інформації про наявність ключа, стан
гравця, кількість спроб тощо.
5. Контролер гравця: обробка вводу від користувача, контроль переміщення,
взаємодії з об'єктами.
Висновки до розділу 3
Було сформовано вимоги до гри і виділені основні модулі.
28
4 ОБҐРУНТУВАННЯ ПРОГРАМНИХ ЗАСОБІВ
4.1 Обґрунтування вибору програмних засобів
Для того щоб почати розробку потрібно встановити необхідне програмне
забезпечення, зображене на рисунку 4.1:
Рисунок 4.1 – Використані технології
C# (вимовляється Сі-шарп) — об'єктно-орієнтована мова програмування з
безпечною системою типізації для платформи .NET. Розроблена Андерсом
Гейлсбергом, Скотом Вілтамутом та Пітером Гольде під егідою Microsoft Research
(належить Microsoft).
Синтаксис C# близький до С++ і Java. Мова має строгу статичну типізацію,
підтримує поліморфізм, перевантаження операторів, вказівники на функції-члени
класів, атрибути, події, властивості, винятки, коментарі у форматі XML.
Перейнявши багато від своїх попередників — мов С++, Object Pascal, Модула і
Smalltalk — С#, спираючись на практику їхнього використання, виключає деякі
моделі, що зарекомендували себе як проблематичні при розробці програмних
систем, наприклад, мова С#, на відміну від C++, не передбачає множинне
успадкування класів.
Станом на вересень 2023 року поточна стабільна версія мови C# 11.0, яка була
випущена в 2022 році як частина платформи .NET 7.0. [5]
Для роботи з даним програмним кодом було використано наступне
середовище розробки: Unity і допоміжне ПЗ для написання і редагування коду:
Visual Studio Code.
Unity — багатоплатформовий інструмент для розроблення відеоігор і
застосунків, і рушій, на якому вони працюють. Створені за допомогою Unity
програми працюють на настільних комп'ютерних системах, мобільних пристроях та
29
гральних консолях у дво- та тривимірній графіці, та на пристроях віртуальної чи
доповненої реальності. Застосунки, створені за допомогою Unity, підтримують
DirectX та OpenGL.
Ігрова логіка пишеться за допомогою мови C#, раніше також була можливість
використовувати Boo та JavaScript, але розробники відмовились від їх підтримки. [6]
Visual Studio Code (VS Code) — це текстовий редактор, розроблений Microsoft
для Windows, Linux та macOS. Він позиціонується як «легкий» редактор коду для
кросплатформної розробки веб- та хмарних застосунків. Включає в себе відладчик,
інструменти для роботи з Git, підсвічування синтаксису, IntelliSense та засоби для
рефакторингу. Має широкі можливості для кастомізації: користувацькі теми,
сполучення клавіш та файли конфігурації. Поширюється безкоштовно та
розробляється як програмне забезпечення з відкритим вихідним кодом. [7]
Aseprite — умовно-безкоштовний редактор зображень із відкритим вихідним
кодом, призначений насамперед для створення малюнків та анімації у стилі піксель-
арт. Програма працює на платформах Windows, macOS і Linux. Вона містить
різноманітні інструменти для редагування зображень і створення анімації, такі як
шари, кадри, підтримка тайлових карт, інтерфейс командного рядка тощо.
Розробником є Igara Studio SA, а проєкт очолюють Девід, Гаспар та Мартін Капелло.
4.2 Апаратні засоби
Для того щоб мати можливість створити веб ресурс, потрібно технічно
оснастити робоче місце. Залежно від виду роботи потрібно підібрати технічні
засоби. Для роботи з електронною інформацією – комп’ютер, технічні
характеристики якого: процесор : AMD Phenome II x4 B50 3.20 GHz, екран 21 дюйм
60Гц, формат зображення: 16:10, розширення екрану 1680x1050, оперативна
пам’ять DDR3 8Gb, материнська плата Asus M5A78L LE, SSD 512гб, відеокарта
AMD Radeon R9 380. Операційна системі Windows 10 Pro. Також використовувався
WI-FI роутер Netis WF2411E.
30
Також в роботі використовувалась комп’ютерна миша MSI Clutch GM08.
Висновок до розділу 4
Було проаналізовано і обрано програмні засоби для розробки гри, а також
задокументовано апаратні засоби.
31
5 РОЗРОБКА І ТЕСТУВАННЯ КОМП’ЮТЕРНОЇ ГРИ
5.1 Проектування сюжету і дизайну спрайтів
В рамках кваліфікаційної роботи бакалавра стояло завдання розробити
комп’ютерну гру в 2d жанрі з процедурною генерацією рівнів. Щоб більше
зацікавити гравця, було обрано додатковий жанр фентезі в піксельному стилі, який
дозволить робити привабливі спрайти, без особливих труднощів.
Сюжетом гри було вирішено зробити історію про лицаря, який заблукав в
підземеллі і не може знайти вихід.
Створені спрайти головного героя, скрині і деяких елементів оточення
зображені нижче на рисунках 5.1 – 5.4.
Рисунок 5.1 – Процес створення спрайту в Aseprite
32
Рисунок 5.2 – Спрайт головного героя
Рисунок 5.3 – Спрайт скрині
Рисунок 5.4 – Спрайт підлоги
5.2 Проектування сюжету і дизайну спрайтів
Так як темою кваліфікаційної роботи є розробка гри з процедурною
генерацією рівнів, необхідність в ручному створенні дизайну рівнів відпадає. Тому
було вирішено створити генератор рівнів, який буде генерувати кожен новий рівень
і тимчасово назначити його на клавішу.
Скрипт генерації підземелля Dungeon.cs:
Скрипт Dungeon.cs відіграє центральну роль у процедурній генерації ігрового.
33
Він дозволяє створювати динамічні та унікальні мапи з кожним запуском або
перезавантаженням, що є ключовим аспектом для забезпечення реіграбельності.
1. Призначення та Залежності:
Скрипт Dungeon є компонентом MonoBehaviour, що вимагає наявності
компонента Matrix на тому ж ігровому об'єкті за допомогою атрибуту
[RequireComponent(typeof(Matrix))].
2. Конфігурація та Змінні:
Скрипт надає широкі можливості для налаштування генерації підземелля
через інспектор Unity:
Параметри мапи:
– width та height: Визначають розміри генерованої мапи підземелля (від 0
до 100).
– GenerationMode: Перерахування (enum), яке дозволяє обрати режим
генерації – Horizontal (горизонтальний) або Vertical (вертикальний). Це
впливає на порядок, у якому скануються клітинки для генерації.
Параметри кімнат:
– numRooms: Діапазон значень (IntRange) для визначення випадкової
кількості кімнат, які будуть згенеровані (наприклад, від 13 до 15).
– roomWidth та roomHeight: Діапазони значень для випадкового
визначення ширини та висоти кожної кімнати (наприклад, від 3 до 7).
Предмети та персонажі:
– maxChests: Максимальна кількість скринь, які можуть бути розміщені на
мапі.
– chestPrefab: Посилання на префаб ігрового об'єкта скрині.
– hero: Посилання на префаб ігрового об'єкта героя.
Крім того, скрипт завантажує різноманітні префаби елементів підземелля
(підлога, стіни різних типів, колони, фонтани, вихід) з папки Resources під час фази
Awake. Це дозволяє динамічно використовувати ці ігрові об'єкти під час генерації.
3. Життєвий цикл та Тригери Генерації:
– Метод Awake(): Ініціалізує завантаження всіх необхідних префабів
34
елементів оточення з папки Resources.
– Метод Start(): Викликає метод Create() для початкової генерації
підземелля при старті гри.
– Метод Update(): Дозволяє перезапустити генерацію підземелля
натисканням клавіші Space.
4. Процес Генерації (Create() метод):
Метод Create() інкапсулює весь потік генерації підземелля, послідовно
викликаючи низку допоміжних методів:
Clear(): Очищає попередньо згенероване підземелля та ініціалізує матриці
matrix та board. matrix використовується для зберігання елементів оточення (підлога,
стіни), а board – для розміщення інтерактивних об'єктів, таких як скрині та герой.
Rooms(): Генерує випадкову кількість кімнат (виходячи з numRooms)
випадкового розміру (виходячи з roomWidth та roomHeight). Кожна кімната
заповнюється елементами підлоги (floor_1). Також, в одній з кімнат випадковим
чином розміщується елемент exit. Метод Avaiable() перевіряє, чи є достатньо
вільного місця для розміщення нової кімнати, включаючи відступи для стін та
інших елементів.
Connect(): З'єднує згенеровані кімнати між собою. Це досягається шляхом
збереження центральних точок кімнат (pointsList) та прокладання "коридорів" з
елементів floor_1 між ними, використовуючи випадкову послідовність з'єднання
точок.
Smooth(): Згладжує згенеровані ділянки, заповнюючи порожні прогалини між
елементами підлоги, якщо вони оточені підлогою з чотирьох сторін або по
горизонталі/вертикалі. Це допомагає створити більш природні форми приміщень та
коридорів.
Walls(): Розміщує базові стіни (wall_mid) навколо ділянок підлоги.
Structure(): Деталізує стіни, замінюючи базові wall_mid на wall_left, wall_right
та додаючи кутові елементи стін (wall_corner_top_left, wall_corner_top_right,
wall_corner_bottom_left, wall_corner_bottom_right) та бічні стіни (wall_side_front_left,
wall_side_front_right, wall_side_mid_left, wall_side_mid_right, wall_side_top_left,
35
wall_side_top_right). Це створює візуально привабливіші та реалістичніші стінові
структури.
Columns(): Додає декоративні колони (wall_column_top, wall_column_mid,
wall_column_base) в певних місцях стін, якщо це дозволяє оточення.
Fountains(): Випадковим чином розміщує сині або червоні фонтани
(fountain_blue, fountain_red) біля стін, якщо під ними є підлога.
InsertExit(): Вставляє єдиний елемент exit у випадковому місці на мапі,
забезпечуючи, щоб навколо нього була достатньо вільного місця з підлогою.
Chests(): Розміщує скрині (chestPrefab) на підлозі, дотримуючись обмеження
maxChests, та забезпечуючи, щоб скриня була розташована біля стіни та мала
вільний простір для розміщення.
SpawnHero(): Розміщує ігровий об'єкт героя (hero) у випадковому місці на
підлозі, переконуючись, що навколо нього є вільний простір і немає інших
інтерактивних об'єктів. Камера гри налаштовується на відстеження героя.
Generate(): Фіналізує процес, інстанціюючи всі ігрові об'єкти (плитки
підземелля та скрині) на сцені Unity відповідно до даних, збережених у матрицях
matrix.map та board.map. Всі згенеровані об'єкти поміщаються під дочірній ігровий
об'єкт "Dungeon" для кращої організації сцени.
5. Допоміжні методи (Tools):
InBounds(int x, int y): Перевіряє, чи знаходяться задані координати (x, y) в
межах розмірів матриці.
Find(int x, int y, GameObject tile): Перевіряє, чи знаходиться певний
GameObject (плитка) за заданими координатами, та чи знаходяться ці координати в
межах.
Avaiable(int X, int Y, int width, int height): Перевіряє, чи є достатньо вільного
місця для розміщення прямокутної області (кімнати) з урахуванням необхідних
відступів навколо неї.
SavePoints(), SetPoints(), RemovePoints(): Допоміжні методи для управління
масивом pointsList, що зберігає точки, які використовуються для з'єднання кімнат.
LauncError(string message): Виводить повідомлення про помилку в консоль
36
Unity та зупиняє відтворення гри в редакторі, що корисно для налагодження.
Текст скрипта можна знайти в Додатку Б.
5.3 Проектування сюжету і дизайну спрайтів
При запуску гри, гравець опиняється в випадковій точці підземелля. В одній із
скринь він повинен знайти ключ і потім за допомогою нього відчинити вихід, це
зображено на рисунку 5.5.
Рисунок 5.5 – Геймплей гри Light in darkness
Після розробки гри було проведене її тестування, після якого було виявлено і
знешкоджено помилки
При тестуванні критичних багів не було виявлено. Тобто продукт працює
добре.
Також були протестовані системні вимоги гри, зображені на рисунку 5.6.
37
Рисунок 5.6 – Навантаження грою на систему
Гра протестована під різні формати екранів (16:10, 16:9, 4:3 і т.д.), адаптація
на відмінному рівні, це зображено на рисунку 5.7.
Рисунок 5.7 – Гра в форматі екрану 4:3
Висновок до розділу 5
38
В данному розділі було проведено розробку комп'ютерної гри, проведено
створення сюжету і спрайтів, написання скриптів. Також була проведена перевірка
на працездатність і тестування гри.
39
ВИСНОВКИ
Мета кваліфікаційної роботи бакалавра досягнута. В ході її було розроблено
комп’ютерну гру жанру 2d процедурною генерацією рівнів, написано пояснювальну
записку до кваліфікаційної роботи, розроблено презентацію.
Згідно поставленої мети було вирішено наступні завдання:
– досліджено предметну область, та визначено основні процеси роботи гри;
– визначено основні вимоги до гри;
– проведено аналіз актуальності проекту на сьогоднішній день;
– проведено аналіз існуючих аналогів та рішень;
– проектування комп’ютерної гри з процедурною генерацією;
– оптимізація і адаптація гри;
– перевірка на наявність помилок і багів.
В перспективі розроблену гру можна розвивати, випускаючи оновлення і
викласти в ігрові магазини.
40
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ
1. Методичні рекомендації до підготовки кваліфікаційної роботи бакалавра
здобувачів освітнього ступеня «бакалавр» зі спеціальності 122 – комп’ютерні
науки усіх форм навчання / [упоряд. Триус Ю.В., Оксамитна Л.П.]; М-во освіти і
науки України, Черкас. держ. технол. ун-т. Черкаси: ЧДТУ, 2020.(дата звернення:
25.02.25).
2. Spelunky 2. URL: https://store.steampowered.com/app/418530/Spelunky_2/ (дата
звернення: 28.02.25)
3. Dead Cells. URL: https://store.steampowered.com/app/588650/Dead_Cells/ (дата
звернення: 28.02.25)
4. Noita. URL:
https://store.steampowered.com/app/881100/Noita/ (дата звернення: 28.02.25).
5. C#. URL: https://dotnet.microsoft.com/languages/csharp (дата звернення: 24.04.25).
6. Платформа Unity для розробки в реальному часі. URL: https://unity.com/ (дата
звернення: 25.04.25).
7. Your code editor. Visual studio code. URL: https://code.visualstudio.com (дата
звернення: 26.04.25)
ДОДАТОК А
Затверджую
Зав. кафедри КНСА,
______________ Юрій ТРИУС
«___»____________2025 р.
КОМП’ЮТЕРНА ГРА ЖАНРУ 2D З ПРОЦЕДУРНОЮ ГЕНЕРАЦІЄЮ РІВНІВ
Специфікація
482.ЧДТУ. 52351-01 12 01
Листів 2
Розробник ____________________ Віталій ТЕЛИЧКО
Керівник ____________________ Петро ДЯЧЕНКО
Черкаси – 2025
482.ЧДТУ. 52351-01
Позначення Найменування Примітка
Документація
482.ЧДТУ. 52351-01 12 01 Текст програми
482.ЧДТУ. 52351-01 34 01 Інструкція користувача
ДОДАТОК Б
КОМП’ЮТЕРНА ГРА ЖАНРУ 2D З ПРОЦЕДУРНОЮ ГЕНЕРАЦІЄЮ РІВНІВ
Текст програми
482.ЧДТУ. 52351-01 12 01
Листів 15
Розробник _____________ Віталій ТЕЛИЧКО
Черкаси – 2025
Приклад коду скрипта Dungeon.cs
using UnityEngine;
namespace PDG
{
[RequireComponent(typeof(Matrix))]
public class Dungeon : MonoBehaviour
{
#region Variables
public enum GenerationMode { Horizontal, Vertical }
[Header("Map")]
[SerializeField] [Range(0, 100)] private int width = 45;
[SerializeField] [Range(0, 100)] private int height = 45;
[SerializeField] private GenerationMode mode =
GenerationMode.Horizontal;
[Header("Rooms")]
[SerializeField] private IntRange numRooms = new
IntRange(13, 15);
[SerializeField] private IntRange roomWidth = new
IntRange(3, 7);
[SerializeField] private IntRange roomHeight = new
IntRange(3, 7);
[Header("Chests")]
[SerializeField] private int maxChests = 10;
[SerializeField] private GameObject chestPrefab;
[Header("Characters")]
[SerializeField] private GameObject hero;
[HideInInspector] public GameObject floor_1;
[HideInInspector] public GameObject wall_mid;
private GameObject wall_left;
private GameObject wall_right;
private GameObject wall_side_front_left;
private GameObject wall_side_front_right;
private GameObject wall_top_mid;
private GameObject wall_corner_top_left;
private GameObject wall_corner_top_right;
private GameObject wall_corner_bottom_left;
private GameObject wall_corner_bottom_right;
private GameObject wall_side_mid_left;
private GameObject wall_side_mid_right;
private GameObject wall_side_top_left;
private GameObject wall_side_top_right;
private GameObject wall_column_top;
private GameObject wall_column_mid;
private GameObject wall_column_base;
private GameObject fountain_blue;
private GameObject fountain_red;
private GameObject exit;
private int rooms = 0;
private int chestsCount = 0;
private Matrix matrix;
private Matrix board;
private Vector2[] pointsList;
private Transform itemParent;
#endregion Variables
#region Methods
private void Awake()
{
floor_1 = Resources.Load("Floor/floor_1") as
GameObject;
wall_mid = Resources.Load("Wall/Base/wall_mid") as
GameObject;
wall_left = Resources.Load("Wall/Base/wall_left") as
GameObject;
wall_right = Resources.Load("Wall/Base/wall_right") as
GameObject;
wall_side_front_left =
Resources.Load("Wall/Base/wall_side_front_left") as GameObject;
wall_side_front_right =
Resources.Load("Wall/Base/wall_side_front_right") as GameObject;
wall_top_mid = Resources.Load("Wall/Base/wall_top_mid")
as GameObject;
wall_corner_top_left =
Resources.Load("Wall/Base/wall_corner_top_left") as GameObject;
wall_corner_top_right =
Resources.Load("Wall/Base/wall_corner_top_right") as GameObject;
wall_corner_bottom_left =
Resources.Load("Wall/Base/wall_corner_bottom_left") as GameObject;
wall_corner_bottom_right =
Resources.Load("Wall/Base/wall_corner_bottom_right") as GameObject;
wall_side_mid_left =
Resources.Load("Wall/Base/wall_side_mid_left") as GameObject;
wall_side_mid_right =
Resources.Load("Wall/Base/wall_side_mid_right") as GameObject;
wall_side_top_left =
Resources.Load("Wall/Base/wall_side_top_left") as GameObject;
wall_side_top_right =
Resources.Load("Wall/Base/wall_side_top_right") as GameObject;
wall_column_top =
Resources.Load("Wall/Decoration/Column/wall_column_top") as
GameObject;
wall_column_mid =
Resources.Load("Wall/Decoration/Column/wall_column_mid") as
GameObject;
wall_column_base =
Resources.Load("Wall/Decoration/Column/wall_column_base") as
GameObject;
fountain_blue =
Resources.Load("Wall/Decoration/Fountains/fountain_blue") as
GameObject;
fountain_red =
Resources.Load("Wall/Decoration/Fountains/fountain_red") as
GameObject;
exit = Resources.Load("Floor/exit") as GameObject;
}
private void Start()
{
Create();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Create();
}
if (Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#endif
}
}
#region Initialization
public void Create()
{
Clear();
Rooms();
Connect();
Smooth();
Walls();
Structure();
Columns();
Fountains();
InsertExit();
Chests();
SpawnHero();
Generate();
}
public void Clear()
{
matrix = GetComponent<Matrix>();
matrix.rows = width;
matrix.columns = height;
matrix.map = new GameObject[matrix.rows + 2,
matrix.columns + 2];
board =
GameObject.Find("Board").GetComponent<Matrix>();
board.rows = matrix.rows;
board.columns = matrix.columns;
board.map = new GameObject[board.rows + 2,
board.columns + 2];
rooms = Random.Range(numRooms.m_Min, numRooms.m_Max);
chestsCount = 0;
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
matrix.map[x, y] = null;
board.map[x, y] = null;
}
}
SpriteRenderer[] tiles =
FindObjectsOfType<SpriteRenderer>();
for (int i = 0; i < tiles.Length; i++)
{
Destroy(tiles[i].gameObject);
}
GameObject lastDungeon = GameObject.Find("Dungeon");
if (lastDungeon != null) Destroy(lastDungeon);
GameObject map = new GameObject();
map.name = "Dungeon";
itemParent = map.transform;
}
private void Generate()
{
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
if (matrix.map[x, y] != null)
{
GameObject go =
Instantiate(matrix.map[x, y], new Vector3(x, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (board.map[x, y] == chestPrefab)
{
board.map[x, y] = floor_1;
GameObject go = Instantiate(chestPrefab,
new Vector3(x, y + 0.25F, 0), Quaternion.identity);
go.transform.SetParent(itemParent);
}
}
}
}
#endregion Initialization
#region Generation
private void Rooms()
{
int attemps = 0;
for (int i = 0; i < rooms; i++)
{
int X, Y, horizontal, vertical;
do
{
X = Random.Range(0, matrix.rows);
Y = Random.Range(0, matrix.columns);
horizontal = Random.Range(roomWidth.m_Min,
roomWidth.m_Max);
vertical = Random.Range(roomHeight.m_Min,
roomHeight.m_Min);
attemps++;
if (attemps > 1000)
{
LauncError("Insufficient space!");
break;
}
} while (!Avaiable(X, Y, horizontal, vertical));
for (int x = X; x < X + horizontal; x++) for (int
y = Y; y < Y + vertical; y++) matrix.map[x, y] = floor_1;
switch (Random.Range(0, 4))
{
case 0: matrix.map[Random.Range(X, X +
horizontal - 1), Y] = exit; break;
case 1: matrix.map[Random.Range(X, X +
horizontal - 1), Y + vertical - 1] = exit; break;
case 2: matrix.map[X, Random.Range(Y, Y +
vertical - 1)] = exit; break;
case 3: matrix.map[X + horizontal - 1,
Random.Range(Y, Y + vertical - 1)] = exit; break;
}
}
SavePoints();
}
private void Connect()
{
int x, y;
Vector2 current, target;
Vector2 direction = Vector2.zero;
for (int web = 0; web < 2; web++)
{
for (int i = 0; i < rooms - 1; i++)
{
current = pointsList[i];
target = pointsList[i + 1];
do
{
if (target.x > current.x) direction =
Vector2.right;
else if (target.x < current.x) direction
= Vector2.left;
else if (target.y > current.y) direction
= Vector2.up;
else if (target.y < current.y) direction
= Vector2.down;
else direction = Vector2.zero;
x = (int)current.x + (int)direction.x;
y = (int)current.y + (int)direction.y;
if (Find(x, y, null)) matrix.map[x, y] =
floor_1;
current = new Vector2(x, y);
} while (direction != Vector2.zero);
}
Vector2[] tmpList = pointsList;
for (int i = 0; i < tmpList.Length; i++)
{
pointsList[Random.Range(0,
pointsList.Length)] = tmpList[i];
}
}
}
private void Smooth()
{
for (int i = 0; i < 3; i++)
{
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
if (matrix.map[x, y] == null)
{
if (Find(x + 1, y, floor_1) &&
Find(x - 1, y, floor_1)
&& Find(x, y + 1, floor_1) &&
Find(x, y - 1, floor_1))
matrix.map[x, y] = floor_1;
if (Find(x + 1, y, floor_1) &&
Find(x - 1, y, floor_1))
matrix.map[x, y] = floor_1;
if (Find(x, y + 1, floor_1) &&
Find(x, y - 1, floor_1))
matrix.map[x, y] = floor_1;
if (Find(x, y + 1, null) && Find(x,
y + 2, floor_1) && Find(x, y - 1, floor_1))
{
matrix.map[x, y + 1] =
floor_1;
matrix.map[x, y] = floor_1;
}
}
}
}
}
}
private void Walls()
{
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
if (matrix.map[x, y] == floor_1)
{
if (Find(x, y + 1, null)) matrix.map[x,
y + 1] = wall_mid;
if (Find(x, y - 1, null)) matrix.map[x,
y - 1] = wall_mid;
}
}
}
}
private void Structure()
{
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
if (matrix.map[x, y] == wall_mid)
{
if (Find(x - 1, y, floor_1)
&& Find(x - 1, y - 1, floor_1)
&& Find(x, y - 1, floor_1))
matrix.map[x, y] = wall_left;
if (Find(x + 1, y, floor_1)
&& Find(x + 1, y - 1, floor_1)
&& Find(x, y - 1, floor_1))
matrix.map[x, y] = wall_right;
if (Find(x - 1, y, null)
&& Find(x - 1, y - 1, null)
&& Find(x, y - 1, null))
{
GameObject go =
Instantiate(wall_side_front_left, new Vector3(x - 1, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (Find(x + 1, y, null)
&& Find(x + 1, y - 1, null)
&& Find(x, y - 1, null))
{
GameObject go =
Instantiate(wall_side_front_right, new Vector3(x + 1, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
}
}
}
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
if (matrix.map[x, y] == wall_mid)
{
if (Find(x - 1, y, null) && !Find(x, y -
1, null))
{
GameObject go =
Instantiate(wall_side_mid_left, new Vector3(x - 1, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (Find(x + 1, y, null) && !Find(x, y -
1, null))
{
GameObject go =
Instantiate(wall_side_mid_right, new Vector3(x + 1, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (Find(x, y - 1, null) && (Find(x - 1,
y - 1, floor_1) || Find(x - 1, y - 1, wall_mid)))
{
GameObject go =
Instantiate(wall_side_mid_right, new Vector3(x, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (Find(x, y - 1, null) && (Find(x + 1,
y - 1, floor_1) || Find(x + 1, y - 1, wall_mid)))
{
GameObject go =
Instantiate(wall_side_mid_left, new Vector3(x, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (!Find(x - 1, y, floor_1) && !Find(x
+ 1, y, floor_1))
{
GameObject go =
Instantiate(wall_top_mid, new Vector3(x, y + 1, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
else if (Find(x - 1, y, floor_1))
{
GameObject go =
Instantiate(wall_corner_top_left, new Vector3(x, y + 1, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
else if (Find(x + 1, y, floor_1))
{
GameObject go =
Instantiate(wall_corner_top_right, new Vector3(x, y + 1, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
}
else if (matrix.map[x, y] == wall_left)
{
if (Find(x + 1, y, null))
{
GameObject go_ =
Instantiate(wall_side_mid_right, new Vector3(x + 1, y, 0),
Quaternion.identity);
go_.transform.SetParent(itemParent);
}
GameObject go =
Instantiate(wall_corner_bottom_left, new Vector3(x, y + 1, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
else if (matrix.map[x, y] == wall_right)
{
if (Find(x - 1, y, null))
{
GameObject go_ =
Instantiate(wall_side_mid_left, new Vector3(x - 1, y, 0),
Quaternion.identity);
go_.transform.SetParent(itemParent);
}
GameObject go =
Instantiate(wall_corner_bottom_right, new Vector3(x, y + 1, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
else if (matrix.map[x, y] == floor_1)
{
if (Find(x - 1, y, null))
{
GameObject go =
Instantiate(wall_side_mid_left, new Vector3(x - 1, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (Find(x + 1, y, null))
{
GameObject go =
Instantiate(wall_side_mid_right, new Vector3(x + 1, y, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (Find(x - 1, y + 2, null) && (Find(x,
y + 1, wall_mid) || Find(x, y + 1, wall_left) || Find(x, y + 1,
wall_right)))
{
GameObject go =
Instantiate(wall_side_top_left, new Vector3(x - 1, y + 2, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
if (Find(x + 1, y + 2, null) && (Find(x,
y + 1, wall_mid) || Find(x, y + 1, wall_left) || Find(x, y + 1,
wall_right)))
{
GameObject go =
Instantiate(wall_side_top_right, new Vector3(x + 1, y + 2, 0),
Quaternion.identity);
go.transform.SetParent(itemParent);
}
}
}
}
}
private void Columns()
{
for (int x = matrix.rows - 1; x > 0; x--)
{
for (int y = 0; y < matrix.columns; y++)
{
if (matrix.map[x, y] == wall_mid)
{
if (!Find(x - 1, y, wall_column_mid) &&
!Find(x + 1, y, wall_column_mid) && Find(x, y - 1, floor_1))
{
matrix.map[x, y + 1] =
wall_column_top;
matrix.map[x, y] = wall_column_mid;
matrix.map[x, y - 1] =
wall_column_base;
}
}
}
}
}
#endregion Generation
#region Extras
private void Fountains()
{
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
if (matrix.map[x, y] == wall_mid)
{
if (Random.Range(0, 60) == 0 && Find(x,
y - 1, floor_1))
{
matrix.map[x, y] = fountain_blue;
}
else if (Random.Range(0, 150) == 0 &&
Find(x, y - 1, floor_1))
{
matrix.map[x, y] = fountain_red;
}
}
}
}
}
private void InsertExit()
{
int x = 0; int y = 0;
do
{
x = Random.Range(0, matrix.rows);
y = Random.Range(0, matrix.columns);
} while (matrix.map[x, y] != floor_1
|| !Find(x + 1, y, floor_1) || !Find(x - 1, y, floor_1)
|| !Find(x, y + 1, floor_1) || !Find(x, y - 1, floor_1)
|| !Find(x + 1, y + 1, floor_1) || !Find(x - 1, y - 1,
floor_1)
|| !Find(x + 1, y - 1, floor_1) || !Find(x - 1, y + 1,
floor_1));
matrix.map[x, y] = exit;
}
private void Chests()
{
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
if (matrix.map[x, y] == floor_1 && (Find(x, y
+ 1, wall_mid) || Find(x, y + 1, wall_left) || Find(x, y + 1,
wall_right)) && Random.Range(0, 3) == 0
&& matrix.map[x, y - 2] == floor_1 &&
matrix.map[x + 1, y - 2] == floor_1 && matrix.map[x - 1, y - 2] ==
floor_1)
{
if (chestsCount < maxChests)
{
chestsCount++;
board.map[x, y] = chestPrefab;
}
}
}
}
}
private void SpawnHero()
{
if (hero == null) return;
int x = 0; int y = 0;
do
{
x = Random.Range(0, matrix.rows);
y = Random.Range(0, matrix.columns);
} while (matrix.map[x, y] != floor_1
|| !Find(x + 1, y, floor_1) || !Find(x - 1, y, floor_1)
|| !Find(x, y + 1, floor_1) || !Find(x, y - 1, floor_1)
|| !Find(x + 1, y + 1, floor_1) || !Find(x - 1, y - 1,
floor_1)
|| !Find(x + 1, y - 1, floor_1) || !Find(x - 1, y + 1,
floor_1)
|| board.map[x, y] != null);
board.map[x, y - 1] = hero;
GameObject h = Instantiate(hero, new Vector3(x, y -
0.25F, 0), Quaternion.identity);
h.name = "Hero";
FindObjectOfType<Camera>().target = h.transform;
}
#endregion Extras
#region Tools
private bool InBounds(int x, int y)
{
return !(x > matrix.rows - 1 || x < 0 || y >
matrix.columns - 1 || y < 0);
}
public bool Find(int x, int y, GameObject tile)
{
return InBounds(x, y) && matrix.map[x, y] == tile;
}
private bool Avaiable(int X, int Y, int width, int height)
{
for (int x = X; x < X + width; x++)
{
for (int y = Y; y < Y + height; y++)
{
if (!Find(x, y, null)) return false;
#region Around
if (!Find(X - 1, y, null)) return false;
if (!Find(x, Y - 1, null)) return false;
if (!Find(X + width, y, null)) return false;
if (!Find(x, Y + height, null)) return false;
if (!Find(X + width, Y + height, null))
return false;
if (!Find(X - 1, Y - 1, null)) return false;
if (!Find(X + width, Y - 1, null)) return
false;
if (!Find(X - 1, Y + height, null)) return
false;
if (!Find(X - 2, y, null)) return false;
if (!Find(x, Y - 2, null)) return false;
if (!Find(X + width + 1, y, null)) return
false;
if (!Find(x, Y + height + 1, null)) return
false;
if (!Find(X + width + 1, Y + height + 1,
null)) return false;
if (!Find(X - 2, Y - 2, null)) return false;
if (!Find(X + width + 1, Y - 2, null)) return
false;
if (!Find(X - 2, Y + height + 1, null))
return false;
if (!Find(X - 3, y, null)) return false;
if (!Find(x, Y - 3, null)) return false;
if (!Find(X + width + 2, y, null)) return
false;
if (!Find(x, Y + height + 2, null)) return
false;
if (!Find(X + width + 2, Y + height + 2,
null)) return false;
if (!Find(X - 3, Y - 3, null)) return false;
if (!Find(X + width + 2, Y - 3, null)) return
false;
if (!Find(X - 3, Y + height + 2, null))
return false;
#endregion
}
}
return true;
}
private void SavePoints()
{
pointsList = new Vector2[rooms];
RemovePoints();
switch (mode)
{
case GenerationMode.Horizontal:
for (int y = 0; y < matrix.columns; y++)
{
for (int x = 0; x < matrix.rows; x++)
{
SetPoints(x, y);
}
}
break;
case GenerationMode.Vertical:
for (int x = 0; x < matrix.rows; x++)
{
for (int y = 0; y < matrix.columns; y++)
{
SetPoints(x, y);
}
}
break;
}
}
private void SetPoints(int x, int y)
{
if (matrix.map[x, y] == exit)
{
for (int i = 0; i < pointsList.Length; i++)
{
if (pointsList[i] == Vector2.zero)
{
pointsList[i] = new Vector2(x, y);
break;
}
}
matrix.map[x, y] = floor_1;
}
}
private void RemovePoints()
{
for (int i = 0; i < pointsList.Length; i++)
{
pointsList[i] = Vector2.zero;
}
}
private void LauncError(string message)
{
Debug.LogError(message);
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#endif
}
#endregion Tools
#endregion Methods
}
}
ДОДАТОК В
КОМП’ЮТЕРНА ГРА ЖАНРУ 2D З ПРОЦЕДУРНОЮ ГЕНЕРАЦІЄЮ РІВНІВ
ІНСТРУКЦІЯ КОРИСТУВАЧА
482. ЧДТУ. 52350-01 34 01
Листів 4
Розробник _____________ Віталій ТЕЛИЧКО
Черкаси – 2025
Запустіть файл Light in Darkness.exe.
Ви опиняєтеся в ігровому середовищі, на рівні (рис. В.1). Управління
здійснюється на клавіші W A S D.
Рисунок В.1 – Ігровий рівень
Використовуючи ігрове управління, почніть обшукувати скрині, доки не
знайдете ключ (рисунок В.2).
Рисунок В.2 – Обшукана скриня
Після того, як був знайдений ключ, йдіть шукати вихід (або йдіть до нього,
якщо він був знайдений раніше)(рисунок В.3).
Рисунок В.3 – Відкритий вихід
При обшуку скринь, будьте уважними, деякі з них можуть виявитися
замаскованими ворогами (рисунок В.4).
Рисунок В.4 – Скриня-ворог