Please use this identifier to cite or link to this item:
https://er.chdtu.edu.ua/handle/ChSTU/6189| Title: | Комп’ютерна 3-D гра “PLS DONATE X” на онлайн-платформі Roblox |
| Authors: | Підгорний, Микола Володимирович Колісник, Максим Володимирович |
| Keywords: | ПРЕДМЕТНА ОБЛАСТЬ;ГРА;ROBLOX;ROBLOX STUDIO. |
| Issue Date: | 12-Jun-2024 |
| Abstract: | Онлайн-ігрові платформи набули великої популярності в останні роки, забезпечуючи доступ до безлічі ігор різного жанру та стилю. Однією з найпопулярніших серед них є платформа Roblox, яка дозволяє користувачам створювати та розгортати власні ігри. У цій роботі досліджується процес розробки 3-D гри на платформі Roblox. Метою кваліфікаційної роботи є розробка та реалізація повноцінної 3-D гри на платформі Roblox з використанням доступних інструментів та технологій. Об’єкт дослідження: програмування гри на онлайн платформі Roblox Предмет дослідження: гра «PLS DONATE X» Методи дослідження. Для вирішення поставлених завдань були застосовані наступні методи дослідження: теоретичний (аналіз літератури з проблеми дослідження); порівняльний – для здійснення порівнянь існуючих аналогів для гри. |
| URI: | https://er.chdtu.edu.ua/handle/ChSTU/6189 |
| Appears in Collections: | 122 Комп’ютерні науки (Комп’ютерні науки та прикладне програмування) |
Files in This Item:
| File | Description | Size | Format | |
|---|---|---|---|---|
| Пояснювальна записка_Колісник Максим_КНС-2201_2023-2024.pdf Restricted Access | 4.29 MB | Adobe PDF | View/Open Request a copy |
Items in DSpace are protected by copyright, with all rights reserved, unless otherwise indicated.
Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ
Факультет інформаційних технологій і систем
Кафедра комп’ютерних наук та системного аналізу
Пояснювальна записка
до кваліфікаційної роботи
бакалавра
(освітньо-кваліфікаційний рівень)
на тему: «Комп’ютерна 3-D гра “PLS DONATE X” на онлайн-платформі Roblox»
Виконав: студент 4 курсу, групи КНС-2201
спеціальності 122 «Комп’ютерні науки»
(шифр і назва спеціальності)
освітня програма «Комп’ютерні науки та
прикладне програмування
(назва освітньої програми)
Колісник Максим Володимирович
Керівник __________ Підгорний М.В.
(прізвище та ініціали)
Рецензент __________ Мельник В.П.
(прізвище та ініціали)
Черкаси 2024 року
Бланк завдання на кваліфікаційну роботу бакалавра студенту
Черкаський державний технологічний університет
Факультет Інформаційних технологій і систем
Кафедра Комп’ютерних наук та системного аналізу
Освітньо-кваліфікаційний рівень Бакалавр
Спеціальність 122 – Комп’ютерні науки
Освітня програма Комп’ютерні науки та прикладне програмування
ЗАТВЕРДЖУЮ
Завідувач кафедри КНСА
_______________ Юрій ТРИУС
«____» _____________ 2024 р.
ЗАВДАННЯ
на кваліфікаційну роботу бакалавра студенту
Коліснику Максиму Володимировичу
(прізвище, ім’я, по батькові)
1. Тема роботи Комп’ютерна 3-D гра “PLS DONATE X” на онлайн-платформі Roblox
Керівник роботи Підгорний М.В., к.т.н., доцент
(прізвище, ім’я, по батькові, науковий ступінь, вчене звання)
затверджені наказом університету від «26» лютого 2024 р. №60/04.
2. Строк подання студентом роботи до 10 червня 2024 року
3. Вихідні дані до роботи:
Практичні навики роботи з інформаційними ресурсами.
Звіт з виробничої практики. Звіт з переддипломної практики.
4. Зміст пояснювальної записки (перелік питань, що їх належить розробити):
Вступ
4.1. Дослідження предметної області.
4.2. Проектування та розробка гри «PLS DONATE X»
4.3. Тестування гри
Висновки.
5. Перелік додатків (з точним зазначенням назв додатків):
5.1. Додаток А. Специфікація 482.ЧДТУ. 42285-01.
5.2. Додаток Б. Текст програми.
5.3. Додаток В. Інструкція користувача.
5 .4. Презентація у вигляді 15 слайдів.
6. Консультанти розділів роботи
Прізвище, ініціали та Підпис, дата
Розділ посада
завдання видав завдання прийняв
консультанта
7. Дата видачі завдання 15.01.2024
КАЛЕНДАРНИЙ ПЛАН
Строк виконання
№ з/п Назва етапів кваліфікаційної роботи бакалавра Примітка
етапів роботи
1 Видача завдання на кваліфікаційну роботу
до 15.01.2024 Виконано
бакалавра.
2 Аналіз літературних джерел, об’єкту та
до 12.02.2024 Виконано
предмету дослідження.
3 Написання теоретичного розділу
до 18.03.2024 Виконано
кваліфікаційної роботи бакалавра.
4 Написання аналітичного розділу кваліфікаційної
до 01.04.2024 Виконано
роботи бакалавра.
5 Написання практичних розділів й висновків до
до 01.05.2024 Виконано
кваліфікаційної роботи бакалавра.
6 Передзахист кваліфікаційної роботи бакалавра
до 03.06.2024 Виконано
на засіданні кафедри КНСА.
7 Подання роботи завідувачу кафедри КНСА. до 10.06.2024 Виконано
8 Захист кваліфікаційної роботи бакалавра. 12.06.2024 Виконано
Студент _____________________________ Максим КОЛІСНИК
(підпис)
Керівник роботи ____________________________ Микола ПІДГОРНИЙ
(підпис)
РЕФЕРАТ
Кваліфікаційна робота бакалавра містить: 41 с., 40 рис., 1 таблицю, 3
використаних джерел, 2 додатки.
Актуальність теми. Онлайн-ігрові платформи набули великої популярності в
останні роки, забезпечуючи доступ до безлічі ігор різного жанру та стилю. Однією з
найпопулярніших серед них є платформа Roblox, яка дозволяє користувачам
створювати та розгортати власні ігри. У цій роботі досліджується процес розробки
3-D гри на платформі Roblox.
Мета роботи і задачі дослідження. Метою кваліфікаційної роботи є розробка
та реалізація повноцінної 3-D гри на платформі Roblox з використанням доступних
інструментів та технологій.
Для досягнення поставленої мети були поставлені такі завдання:
дослідити предметну область;
виконати порівняльний аналіз існуючих ігор на онлайн платформі Roblox;
обрати програмно-технічні засоби для реалізації гри;
розробити гру «PLS DONATE X» та виконати її тестування.
Об’єкт дослідження: програмування гри на онлайн платформі Roblox
Предмет дослідження: гра «PLS DONATE X»
Методи дослідження. Для вирішення поставлених завдань були застосовані
наступні методи дослідження: теоретичний (аналіз літератури з проблеми
дослідження); порівняльний – для здійснення порівнянь існуючих аналогів для гри.
Перелік ключових слів: ПРЕДМЕТНА ОБЛАСТЬ, ГРА, ROBLOX, ROBLOX
STUDIO.
ABSTRACT
Bachelor's qualification work contains: 41 pages, 40 figures, 1 table, 3 references,
2 appendices.
Relevance of the topic. Online gaming platforms have become very popular in
recent years, providing access to many games of different genres and styles. One of the
most popular among them is the roblox platform, which allows users to create and deploy
their own games. This paper investigates the process of developing a 3-d game on the
roblox platform.
Purpose and objectives of the study. The purpose of the qualification work is to
develop and implement a full-fledged 3-d game on the roblox platform using available
tools and technologies.
To achieve this goal, the following tasks were set:
− research the subject area;
− perform a comparative analysis of existing mobile applications for ordering
food;
− to select software and hardware for the game implementation;
− develop the pls donate x game and test it;
− Object of research: roblox online platform.
Subject of research: the game "PLS DONATE X"
Research methods. To solve the tasks, the following research methods were used:
theoretical (analysis of the literature on the research problem); comparative - to compare
existing analogues for game.
List of keywords: SUBJECT AREA, GAME, ROBLOX, ROBLOX STUDIO.
7
ЗМІСТ
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ, СИМВОЛІВ, СКОРОЧЕНЬ І ТЕРМІНІВ ........ 8
ВСТУП .................................................................................................................................. 9
1 ДОСЛІДЖЕННЯ ПРЕДМЕТНОЇ ОБЛАСТІ ............................................................... 10
1.1 Опис предметної області…………………… ......................................................... 10
1.2 Огляд існуючих аналогів ......................................................................................... 12
1.3 Аналіз порівнянь існуючих рішень розглянутих реалізацій та постановка
завдання……………….. ................................................................................................ 15
Висновки до розділу 1 ................................................................................................... 16
2 ПРОЄКТУВАННЯ ГРИ ................................................................................................. 17
2.1 Опис функціонального програмного середовища для розробки ........................ 17
2.2 Опис процесу проєктування гри ............................................................................. 21
Висновки до розділу 2 ................................................................................................... 37
3 ТЕСТУВАННЯ ГРИ ....................................................................................................... 38
3.1 Особливості тестування ігор ................................................................................... 38
3.2 Тестування гри ......................................................................................................... 40
Висновки до розділу 3 ................................................................................................... 41
ВИСНОВКИ ....................................................................................................................... 42
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ ......................................................................... 43
Додаток А. Специфікація 482.ЧДТУ. 42285-01 ……………………………………… 44
Додаток Б. Текст програми............................................................................................... 46
Додаток В. Інструкція користувача ................................................................................. 81
8
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ, СИМВОЛІВ, СКОРОЧЕНЬ І ТЕРМІНІВ
MSC – MacNeal-Schwendler Corporation
ПЗ – Програмне забезпеченя
9
ВСТУП
Початком розвивання індустрії відеоігор є друга половина двадцятого століття.
Вона зародилась на території США та Японії у 1970-х роках [1].
Індустрія відеоігор – це сектор економіки, який пов’язаний із розробкою,
просуванням та реалізацією відеоігор. Підтвердженням актуальності і своєчасності
обраної для дослідження тематики є постійне зростання загального доходу ігрової
продукції у світі [1].
Поява та розвиток сучасних персональних комп’ютерів, консолей та мобільних
платформ призвели до еволюції продукції ігрових студій. Також з’явились нові способи
розповсюдження ігор, а саме через онлайн платформи такі, як: Steam, GOG, Epic Games
тощо. Через це ринок почав зростати швидше. У 2023 році ігрова індустрія згенерувала
$184 мільярди доходу, що на 0,6% більше, ніж у 2022-му. Ріст забезпечили проєкти для
консолей та ПК.
Лиш одна Roblox має дохід у 2023 році: $2,79 млрд. Згідно з останніми
фінансовими звітами Roblox, поточний дохід компанії становить 2,79 мільярда доларів
США. У 2022 році дохід компанії склав 2,22 мільярда доларів США, збільшившись у
порівнянні з доходом у 2021 році, який становив 1,91 мільярда доларів США. Також
Roblox посів 5 місце мобільного геймінг маркету в номінації «worldwide consumer
spending». Та 3 місце в номінації «worldwide downloads», адже його завантажили понад
200 мільйонів разів.
Roblox - це чистий успіх, і ці цифри свідчать про його величезну популярність. У
цій платформі існує безліч ігор різних жанрів, і саме це робить його таким унікальним.
Гравці можуть знаходити нові ігри з різноманітними геймплеями, персонажами, світами
та можливостями. Ці ігри привносять у світ Roblox свіжі ідеї та дозволяють гравцям
насолоджуватися новими пригодами безмежної віртуальної всесвіту.
10
1 ДОСЛІДЖЕННЯ ПРЕДМЕТНОЇ ОБЛАСТІ
1.1 Опис предметної області
Roblox — безплатна онлайн-платформа для створення ігор. Користувачі
можуть створювати власні ігри в Roblox Studio, грати в ігри, створені іншими
користувачами, а також створювати та вигадувати одяг для свого персонажа. Деякі
товари в каталозі можна купити за ігрову валюту — Robux [2].
Сага про Roblox (Рисунок 1.1) розпочалася наприкінці 80-х років, коли Девід
Башукі розпочав проєкт під назвою Knowledge Revolution. Це підприємство мало на
меті поєднати освіту з технологіями, спеціалізуючись на програмному забезпеченні
для фізики та механічного моделювання. Одним із важливих творінь Башукі стала
інтерактивна фізика, яка заклала основу для більш інтерактивних цифрових сфер. Ці
зусилля призвели до випадкової зустрічі з Еріком Касселем, інноватором-
однодумцем. Їхнє спільне бачення та взаємодоповнюючі навички створили міцне
партнерство, підготувавши основу для того, що згодом перетворилося на
новаторську платформу Roblox Corporation, яка принесла свіжий погляд на онлайн-
ігри та взаємодію.
У 1999 році відбувся перехід, коли MSC Software Corporation придбала
Knowledge Revolution за значні 20 мільйонів доларів, а Башукі обійняв посаду віце-
президента. 2004 рік став знаменним, оскільки він ознаменував відхід Башукі та
Касселя з MSC, що призвело до народження корпорації Roblox. Дует разом з
деякими колишніми співробітниками MSC почав розробку Roblox, спочатку
розглядаючи такі назви, як "GoBlocks" і "DynaBlocks". Офіційний запуск Roblox
відбувся 1 вересня 2006 року, після виходу з бета-версії.
2007 рік став роком еволюції Roblox з появою нових функцій, таких як
пошуковий рядок, приватний чат з безпечним чатом для молодших користувачів,
розробники квитків, клуби та можливості налаштування персонажів. Цього ж року
з'явилися "Roblox Points" - щоденні бонуси за вхід в систему, які згодом, у травні
2007 року, були замінені на "Robux". Інша валюта, "Tix", була введена, але
11
припинила своє існування в березні 2016 року. Roblox продовжував розширювати
можливості свого сервера і запустив преміум-членство, відоме як "Клуб
будівельників", яке у вересні 2019 року було перейменовано на "Roblox Premium",
що демонструє постійне прагнення Roblox до вдосконалення та розвитку.
У холодному фіналі 2011 року всесвіт Roblox став свідком світанку свого
першого Тижня Хака, що ознаменував зародження традиції, яка процвітатиме
щороку. Це свято зібрало найкращих розробників Roblox, запалило горнило
інновацій, де ідеї перетворилися на абсолютно нові проєкти ігрової системи, які
згодом були продемонстровані великим шишкам Roblox. З настанням першого
жовтневого дня 2013 року було запущено " Developer Exchange program"(Рисунок
1.2), що відкрила портал для віртуозів гри, які могли перетворити своїх Robux на
реальні скарби. Історія Roblox перейшла в більш інтригуючу главу 31 травня 2015
року, коли відбулося урочисте відкриття функції "Згладжування рельєфу". Це
нововведення покращило візуальну привабливість і достовірність гри,
перетворивши місцевість на більш автентичну, позбувшись її колишньої блокової
подоби. У тандемі з цим еволюціонував і фізичний рушій гри, що ще більше
покращило ігровий досвід.
Рисунок 1.1 – Логотип Roblox
12
Рисунок 1.2 – Приклад тарифу Robux’ів
1.2 Огляд існуючих аналогів
Для огляду існуючих рішень розробки гри доцільно виконати порівняльний
аналіз інших ігор в Roblox.
PLS DONATE BUT GOOD – гра є прямим конкурентои, недоліками якої є:
– некоректне відображення моделі гравця (Рисунок 1.3);
– під час першого візиту в гру гравцям видають предмети, які повинні
з’являтись після певних виконаних досягнень (Рисунок 1.3);
– відсутність редагування тексту на кіоску;
13
– накладання тексту на чат в грі (Рисунок 1.3).
Рисунок 1.3 –Демонстрація недоліків гри PLS DONATE BUT GOOD
PLS DONATE S – гра є прямим конкурентом, недоліками якої є:
– відсутність редагування тексту на кіоску;
– застраріла мапа гри;
– відустність міні-ігор для довшого утримання гравців в грі.
Рисунок 1.4 – Головна сторінка «PLS DONATE S»
14
1.3 Аналіз порівнянь існуючих рішень розглянутих реалізацій та
постановка завдання
Проаналізувавши існуючі рішення розглянутих реалізацій, в кваліфікаційній
роботі бакалавра є кілька пропозицій щодо усунення обмежень існуючих сервісів:
1. Необхідно, щоб моделі гравців коректно відображались в грі.
2. Видавати предмети лиш при виконанні певних досягнень.
3. Потрібно додати можливість редагування тексту (стиль, прозорість, тощо)
4. Варто мати актуальну мапу та своєчасно оновлювати гру.
5. Додати міні-ігри для довшого утримання гравців в грі.
У таблиці 1.1 представлено порівняльний аналіз розглянутих ігор, а саме:
«PLS DONATE BUT GOOD», «PLS DONATE S».
Таблиця 1.1 – Таблиця порівнянь існуючих рішень розглянутих реалізацій
Критерії Назва гри
порівняння «PLS DONATE
«PLS DONATE S»
сервісів BUT GOOD»
Дизайн
3 3
(0-5)
Швидкість
завантаження 1 5
(0-5)
Оптимізація
2 4
(0-5)
Як видно із табл. 1.1, що у якості критеріїв для порівняння було обрано:
дизайн; швидкість завантаження; оптимізація.
Підсумовуючи перегляд та оцінку приведених вище ігор, варто зазначити, що
кожній з них притаманні такі характеристики, як зрозумілий дизайн.
Проаналізувавши існуючі рішення розглянутих реалізацій, в кваліфікаційній
роботі бакалавра при розробці гри варто звернути увагу на такі моменти:
− гра повинна мати зручний, зрозумілий та приємний інтерфейс;
15
− гра повинна мати швидкість завантаження серед оцінок (0-5), а саме: 5;
− гра повинна мати оптимізацію серед оцінок (0-5), а саме: 5;
Постановка завдання
1. Аналіз роботи гри, функціональних вимог та використаних засобів
користувацьких інтерфейсів існуючих ігор.
2. Дослідження та вибір найактуальніших технологій розробки для створення
гри в Roblox.
3. Розробка гри, що задовольняє таким функціональним вимогам:
− система промо-кодів;
− функція офлайн донатів;
− система налаштування кіоску;
− придбання в магазині речей;
− система налаштувань гри;
− рейтинг гравців який відображає суму донатів, які вони отримали протягом
всього часу;
− глобальна таблиця всіх донатів в реальному часі;
− міні-гра футбол;
− функція донатів;
− можливість виганяти гравців з приватного серверу;
− система репортів;
− можливість переглядати історію донатів користувача;
− можливість переглядати найбільших донатерів користувача;
− можливість переглянути рейтинг сум донатів за весь час користувача, які він
надсилав іншим користувачам;
− адміністраторська панель;
− система досягнень;
− додати предмети з різним функціоналом для користувачів;
− рейтинг найбільших донатерів.
16
Висновки до розділу 1
В даному розділі досліджено предметну область, а саме: Roblox; огляд
існуючих аналогів.
Виконано порівняльний аналіз існуючих ігор: «PLS DONATE BUT GOOD»,
«PLS DONATE S».
Представлено таблицю порівнянь існуючих рішень розглянутих реалізацій та
визначено постановку завдання.
17
2 ПРОЄКТУВАННЯ ГРИ
2.1 Опис функціонального програмного середовища для розробки
Розглянемо детальніше програмне середовище та його особливості для
розробки гри «PLS DONATE X».
Для розробки гри використано ігровий рушій «Roblox Studio».
Roblox Studio – це інтегроване середовище для створення і редагування ігор на
платформі Roblox. Roblox – це веб-платформа для масових онлайн-ігор, яка
дозволяє користувачам створювати власні ігри та долучатися до ігор, розроблених
іншими користувачами. Roblox Studio надає інструменти, щоб допомогти
розробникам створювати різноманітні ігри, від простих до складних, з
використанням 3D-графіки, анімації та багатьох інших функцій.
Roblox Studio надає можливості такі як:
− створення світу (Рисунок 2.1): Roblox Studio дозволяє розробникам
створювати унікальні ігрові світи, які можуть бути складними або
простими залежно від креативності користувача. Деякі з основних
можливостей цієї функції включають:
1) ландшафт: Roblox Studio має інструменти для створення
різноманітних ландшафтів, включаючи гори, долини, річки тощо;
2) зміщення об'єктів: Розробники можуть додавати об'єкти, такі як
будівлі, рослини, тварини та інше, для створення реалістичних
ігрових світів;
3) конфігурація параметрів: Roblox Studio дозволяє налаштовувати
параметри світу, такі як текстури, освітлення, погодні умови, щоб
створити певну атмосферу в грі.
− Скриптування (Рисунок 2.2): скриптування в Roblox Studio дозволяє
розробникам програмувати поведінку об'єктів та персонажів у грі.
Основні можливості цієї функції включають:
1) мова програмування Lua: Roblox Studio використовує мову
програмування Lua для створення скриптів;
18
2) керування рухом: розробники можуть програмувати рух
персонажів, об'єктів та інших елементів у грі;
3) взаємодія об'єктів: скрипти дозволяють розробникам створювати
взаємодію між різними об'єктами у грі, таку як взаємодія зі
світлом, рухомі елементи тощо.
− персонажі та анімація: Roblox Studio надає інструменти для створення
персонажів та їх анімації. Основні можливості цієї функції включають:
1) створення персонажів: розробники можуть створювати унікальних
персонажів з різними виглядами та характеристиками;
2) анімація (Рисунок 2.3): Roblox Studio надає можливості для
створення анімацій персонажів, включаючи рухи, взаємодію з
об'єктами тощо.
− соціальні можливості: спільнота і взаємодія між гравцями грає важливу
роль у платформі Roblox. Деякі з соціальних можливостей включають:
1) чат та взаємодія (Рисунок 2.4): гравці можуть спілкуватися один з
одним за допомогою вбудованого чату, обмінюватися
повідомленнями, дружити та взаємодіяти;
2) публікація та обмін: розробники можуть публікувати свої ігри,
щоб інші гравці могли їх грати, а також обмінюватися власними
творіннями з іншими користувачами.
− тестування та публікація: Roblox Studio надає зручні інструменти для
тестування ігор та їх подальшої публікації на платформі Roblox.
Основною можливістю цієї функції є тестування у реальному часі
(Рисунок 2.5): розробники можуть тестувати свої ігри безпосередньо у
середовищі розробки, щоб виявити помилки або недоліки.
Також в Roblox Studio є плагіни (Рисунок 2.6) користувачі мають можливість
придбати різноманітні плагіни, які розширюють функціональність середовища
розробки та полегшують процес створення ігор.
19
Рисунок 2.1 – Приклад створеного світу
Рисунок 2.2 – Приклад скрипту
20
Рисунок 2.3 – Приклад анімації в роботі
Рисунок 2.4 – Відображення чату
21
Рисунок 2.5 – Приклад cross-platform testing
Рисунок 2.6 – Зображення панелі плагінів
2.2 Опис процесу проєктування гри
Створено елементи гри, а саме:
− ігрова мапа;
− система промо-кодів – Можливість введення промокодів знайдених підчас гри
задля отримання бонусів;
− функція офлайн донатів – Можливість відправки донатного подарунку людині
яка є офлайн;
− система налаштування кіоску – Меню налаштування кіоску;
− придбання в магазині речей;
− система налаштувань гри – Меню налаштування гри (гучність музики, звукові
ефекти, тощо);
− рейтинг гравців який відображає суму донатів, які вони отримали протягом
всього часу;
− глобальна таблиця всіх донатів в реальному часі;
22
− міні-гра футбол;
− функція донатів;
− можливість виганяти гравців з приватного серверу;
− система репортів – Можливість поскаржитись на гравця, та можливість
переглянути скарги поза грою адміністатору;
− можливість переглядати історію донатів користувача;
− можливість переглядати найбільших донатерів користувача;
− можливість переглянути рейтинг сум донатів за весь час користувача, які він
надсилав іншим користувачам;
− адміністраторська панель;
− система досягнень – система яка винагороджує гравця за досягнення певних
цілей в грі;
− предмети з різним функціоналом для користувачів;
− рейтинг найбільших донатерів.
Для того, щоб створити гру для початку необхідно створити проєкт
використовуючи довільний шаблон. Проєкт – це набір ресурсів, налаштувань та
інших ресурсів, які разом представляють гру.
Далі заповнюємо ігрову мапу об’єктами (Рисунок 2.7).
Рисунок 2.7 – Приклад ігрової мапи з об’єктами
23
Наповнивши ігрову мапу далі її необхідно зберегти. Для цього необхідно в
верхній панелі інструментів натиснути на «File» та обрати у випадаючому меню
функцію «Save to file».
Рисунок 2.8 – Ілюстрація випадаючого меню
Для того, щоб створити систему промокодів, адміністраторську панель,
функцію офлайн донатів, придбання речей в магазині, меню налаштування кіоску,
меню налаштування гри. Необхідно створити в інтерфейсі користувача навігаційну
панель.
Для цього необхідно в вікні «Explorer» необхідно навести курсор миші на
папку «StarterGui». Після цього з'явиться «Плюсик» натиснувши на нього в полі
пошуку ввести «ScreenGui» (Рисунок 2.9).
24
Рисунок 2.9 – Приклад пошуку «ScreenGui»
Додавши «ScreenGui» в «StarterGui» необхідно в нього додати елементи, а
саме:
– LocalScript – Локальний скрипт для написання скриптів;
– Frame – Окреме вікно в графічному інтерфейся користувача для
відображення навігаційної панелі;
Рисунок 2.10 Приклад доданих елементів
Після цього потрібно заповнити фрейм кнопками задля формування
навігаційної панелі.
Для того, щоб додати кнопки в фрейм потрібно навести курсор миші на фрейм
та натиснути на «Плюсик». Після цього в пошуковому полі ввести «ImageButton».
25
Рисунок 2.11 Приклад пошуку «ImageButton»
Після цього буде сформована навігаційна панель (Рисунок 2.12) в якій
розміщено більшість ігрових функцій.
Рисунок 2.12 Приклад створеної навігаційної панелі
Для того, щоб навігаційна панель працювала потрібно прописати скрипти в
створеному «LocalScript».
Після цього необхідно створити вікна для кожної кнопки адже ці кнопки в
навігаційній панелі і є тими елементами гри, як: меню налаштування гри,
промокоди, офлайн донати, магазин, меню налаштування кіоску, панель
адміністратора. Для того, щоб створити вікна для даних елементів потрібно навести
курсор миші на «ScreenGui» та натиснути на «Плюсик». Після цього в пошуковому
полі ввести «CanvasGroup».
26
Рисунок 2.13 Приклад пошуку «CanvasGroup»
Кожні дані елементи створені, а саме: «LocalScript», «ScreenGui»,
«CanvasGroup», «ImageButton» та інші можна налаштовувати у вікні «Properties».
Рисунок 2.14 – Приклад вікна «Properties» для «ScreenGui»
Завдяки вікну «Properties» для вікон елементів навігаційної панелі можна
вимкнути їх видимість для того, щоб дані вікна з’являлись на екрані після
натискання на потрібну кнопку в навігаційній панелі.
27
Рисунок 2.15 – Приклад вимкнутої видимості та увімкнутої видимості вікна
елементу промокодів
Для того, щоб створити кіоски, необхідно створити їх модель з «MeshPart» та
завантажити її в «Model». Також потрібно додати «PrimaryPart» для того, щоб
позиціонування в просторі стало коректне. Далі необхідно для коректного
позиціонування в просторі тексту на вивісці та малюнку предмета додати
«SignPart», «ItemsPart» (Рисунок 2.16).
Рисунок 2.16 – Приклад створеного кіоску
Також для кіосків потрібно прописати скрипти які завантажуватимуть кіоски
на серверах ігрової мапи. Для цього потрібно створити «Script» в
«ServerScriptService».
28
Рисунок 2.17 – Приклад створеного «Script» з назвою «BoothLoader» для
завантаження кіосків
Створені кіоски надають можливість реалізувати систему донатів. Дана
система поділена на два скрипти. Один з яких відповідає за основну частину
системи донатів, а інший демонструє великі донати на всіх серверах одночасно в
реальному часі (Рисунок 2.18).
Рисунок 2.18 – Приклад створених двох скриптів для системи донатів
Для донатів також мати валюту а саме несправжні «Robux», тому необхідно
створити скрипт який нараховуватиме певну кількість валюти підчас проведенння
часу в грі, та елемент інтерфейсу для відображення накопиченої суми несправжній
«Robux». Елемент інтерфейсу потрібно розмістити в «ScreenGui».
29
Рисунок 2.19 – Приклад створеного скрипта для накопичення гравцями валюти
Рисунок 2.20 – Приклад створеного елементу інтерфейсу для відображення
валюти
Також потрібно створити міні гру «Футбол». Для цього потрібно розмістити
об’єкти даної міні гри на мапі: футбольне поле, ворота, м’яч (Рисунок 2.21). Та
додати скрипт пов’язаний з футбольним полем завдяки якому м’яч не вилітатиме за
кордони футбольного поля, лічильник забитих у ворота м’ячів та переміщення м’яча
на центр футбольного поля після забиття у ворота (Рисунок 2.22).
30
Рисунок 2.21 – Приклад доданого на ігрову мапу футбольного поля та м’яча
Рисунок 2.22 – Приклад написаного скрипту для міні гри футбол
31
Для розробки стендів «Top Donated», та «Top Raised» необхідно створити та
завантажити їх моделі на ігрову мапу (Рисунок 2.23). Дані стенди відображають
рейтинг найбільших донатерів та рейтинг гравців який відображає суму донатів, які
вони отримали протягом всього часу.
Рисунок 2.23 – Приклад доданих стендів на ігрову мапу
Для роботи даних стендів потрібно написати скрипти (Рисунок 2.24), які
відображатимуть на стендах рейтинг топ гравців на стенді «TopDonated» та рейтинг
гравців який відображає суму донатів, які вони отримали протягом всього часу для
стенду «TopRaised».
Рисунок 2.24 – Приклад створених скриптів для стенду «TopDonated» та
«TopRaised»
Результатом створення даних стендів є рейтинги гравців які оновлюються
кожні 60 секунд (Рисунок 2.25).
32
Рисунок 2.25 – Результат роботи стендів «TopDonated» та «TopRaised»
Для створення стенду «LiveDonations» потрібно зробити аналогічні кроки як з
стендами «TopDonated» та «TopRaised», окрім скрипту. Адже, даний стенд
відображатиме донати гравців в реальному часі.
Рисунок 2.26 – Приклад створеного скрипту для стенду «LiveDonations»
Для системи репортів потрібно створити вікно для репорту в «ScreenGui», та
написати скрипт для даного вікна. Система репортів працюватиме так, що якщо
гравець вирішив поскаржитись на іншого гравця то даний репорт завдяки
«webhook» потрапляє в текстовий канал, на сервері гри в додатку «Discord». Завдяки
цьому адміністратору та модераторам не потрібно перебувати весь час в грі для
фіксації репортів.
33
Рисунок 2.27 – Приклад створеного вікна репортів
Рисунок 2.28 – Приклад створеного скрипту системи репортів
34
Рисунок 2.29 – Результат роботи системи репортів
Для того, щоб створити досягнення в грі необхідно зайти на офіційну сторінку
Roblox та перейти в навігаційному меню на сторінку «Create». Далі обрати створену
гру та перейти в «Badges». Після цього з’явиться вікно створення досягнень
(Рисунок 2.30).
Рисунок 2.30 – Приклад вікна створення досягнень
35
Настиснувши лівою кнопкою миші по кнопці «Create A Badge» з’явиться вікно
створення досягнення (Рисунок 2.31).
Рисунок 2.31 – Приклад вікна створення досягнення
Дане вікно має такі елементи, а саме:
− кнопка «Upload Image» для завантаження зображення досянення;
− текстове поле «Name» для введення назви досягнення;
− текстове поле «Description» для опису даного досягнення;
− кнопка «Cancel»;
− кнопка «Create Badge».
Заповнивши всі необхідні поля та завантаживши зображення потрібно
натиснути на кнопку «Create Badge».
36
Рисунок 2.32 – Приклад заповнених полів створення досягнення
Після того як досягнення створилось необхідно зачекати деякий час для
затвердження даного досягнення модерацією Roblox. Далі це досягнення потрібно
додати в гру задля того, щоб користувач міг його отримати виконавши певну умову.
Наприклад гравець задонатив вперше будь яку суму, для цього потрібно створити
скрипт в якому потрібно мати «Asset ID» (Рисунок 2.23). Дане ID береться з
досягнення натиснувши по ньому правою кнопкою миші та обравши пункт «Copy
Asset ID» в меню яке з’явилось (Рисунок 2.34).
Рисунок 2.33 – Приклад створеного скрипта з застосуванням «Asset ID»
досягнення
37
Рисунок 2.34 – Приклад меню з пунктом«Copy Asset ID» досягнення
Отримавши досягнення його можна переглянути в профілі на сторінці Roblox
(Рисунок 2.35).
Рисунок 2.35 – Приклад отриманого досягнення
Висновки до розділу 2
В даному розділі представлено опис функціонального програмного
середовища для розробки гри «PLS DONATE X», а також досліджено основні
принципи створення ігор в Roblox Studio.
За допомогою Roblox Studio була розроблена гра «PLS DONATE X»
38
3 ТЕСТУВАННЯ ГРИ
3.1 Особливості тестування ігор
Тестування гри це процес тестування програмного забезпечення для
тестування ігор для контролю якості. Основна мета тестування ігор — визначити та
виявити дефекти та помилки у відеогрі та покращити стабільність і продуктивність.
Тестування ігор — це компонент розробки ігор, який допомагає забезпечити
розгортання відеоігри не містить помилок.
Робота тестувальника ігор передбачає:
− класифікування вимог, виходячи з цільової мети та цільової аудиторії;
− визначте вимоги користувача та системи, та їх слід класифікувати на
функціональні, нефункціональні вимоги;
− визначити предмети, що підлягають перевірці, елементи, що не
підлягають тестуванню, цілі та заходи щодо функціональних і
нефункціональних вимог;
− перевіряти, чи функціональні вимоги повні, послідовні та зрозумілі;
− визначте настроювані вимоги, суперечливі вимоги;
− визначення взаємозалежних вимог є одним із завдань тестування гри;
− пріоритезація вимог на основі унікальності, комплексність, критичність;
− визначити тему гри, персонажів, анімації, штучного інтелекту,
кінематографії, перегляду камери, ігрового процесу.
Види тестування гри:
Функціональне тестування.
Тестувальники перевірки функціональності шукають загальні проблеми в грі
або її користувальницькому інтерфейсі та графіці, як-от проблеми з ігровою
механікою, проблеми зі стабільністю та цілісністю ігрових ресурсів. Тестування
інтерфейсу користувача забезпечує зручність гри.
Тестування продуктивності.
39
Перевіряється загальна продуктивність гри. Налаштування продуктивності
виконується для оптимізації швидкості гри.
Час відповіді на клієнті та серверах, час завершення транзакцій,
продуктивність при піковому навантаженні, довговічність, покриття мережі, витік
пам’яті, низький рівень пам’яті, низький заряд акумулятора, час, витрачений на
завантаження програм, одночасно (кілька користувачів) доступ до сервера
програми, швидкість, пропускна здатність, надійність, масштабованість тощо.
Споживання батареї та продуктивність графіки: Виміряйте споживання
батареї мобільної гри. Споживання батареї має бути оптимальним протягом
тривалого часу, і реакція гри має бути задовільною за різних великих навантажень
на різних пристроях.
Обмеження процесора та пам'яті: Лічильники продуктивності
використовуються для вимірювання споживання процесора та пам’яті програмою.
Мережеве підключення: Вимірює час відгуку мобільних ігор у різних типах
мереж (Wi-Fi, 2G, 3G, 4G). Це дає загальне уявлення про те, наскільки добре гра
працюватиме в ненадійних мережах. Він також перевіряє з’єднання між мобільними
пристроями, центрами обробки даних або хмарою. Відстежуються всі часи піку,
нестабільні з’єднання, дублювання даних, втрата пакетів, фрагментація даних.
Тестування локалізації.
Тестування локалізації стає надзвичайно важливим, коли гра орієнтована на
глобальні ринки. Назви ігор, вміст і тексти потрібно перекладати та тестувати на
пристроях кількома мовами. Ці типи тестів можна виконати швидко (за допомогою
хмарного доступу до пристрою та автоматизації тестування).
Тестування безпеки.
Це робиться для того, щоб перевірити, наскільки захищено програмне
забезпечення від зовнішніх загроз. Захист даних від зовнішніх загроз,
неконтрольованих обмежень доступу до системи, порушення даних, помилки
системи зв'язку та слабке шифрування коду [3].
40
3.2 Тестування гри
Для перевірки належного функціонування додатку з точки зору користувача
використано тестування програмного забезпечення, що виконується вручну без
використання автоматичних тестів або скриптів. Цей процес включає запуск гри,
введення різних даних та перевірку результатів, з метою забезпечення належного
функціонування програмного забезпечення та виявлення можливих помилок та
дефектів.
Тестування продуктивності розробленої гри показало найвищі результати, що
означає про його швидкісну та ефективну роботу. В процесі тестування було
проведено перевірку використання оперативної пам’яті, обробки великого обсягу
даних та інших вимог продуктивності. Отримані результати свідчать про те, що він
може забезпечити швидку та зручну роботу для користувачів.
Це позитивний результат, який підтверджує відповідність функціональних
вимог гри та її здатність працювати з оптимальною продуктивністю. Такі результати
тестування дозволяють зробити висновок про успішну розробку та належну роботу
розробленої гри (Рисунок 3.1).
Проте варто зазначити, що тестування продуктивності є одним з багатьох
аспектів тестування, і необхідно проводити й інші види тестів, такі як:
функціональне тестування, тестування безпеки та інші, щоб забезпечити повну
перевірку функціональності та якості гри.
Рисунок 3.1 – Результати тестування «PLS DONATE X»
41
Проведено кросплатформне тестування гри на різних пристроях, включаючи
комп'ютери, планшети та мобільні телефони. В результаті тестування було
виявлено, що на малих екранах телефонів виникають помилки розміщення
елементів інтерфейсу. Зокрема, кнопки та текстові блоки відображаються
некоректно або перекривають один одного, що суттєво ускладнює взаємодію
користувачів з грою (Рисунок 3.2). Це вказує на необхідність додаткової оптимізації
інтерфейсу для забезпечення повної сумісності з мобільними пристроями.
Рисунок 3.2 – Результат кросплатформного тестування
Висновки до розділу 3
В даному розділі описано технології тестування ігор.
Виконано тестування розробленої гри «PLS DONATE X» на онлайн-платформі
Roblox. Результати тестування дозволяють зробити висновок про успішну розробку
гри та її належну роботу.
42
ВИСНОВКИ
Мета кваліфікаційної роботи бакалавра досягнута. В ході її виконання
спроєктовано та розроблено гру «PLS DONATE X».
Для досягнення поставленої мети в роботі розв’язувалися наступні завдання:
досліджено предметну область;
виконано порівняльний аналіз існуючих ігор, а саме: «PLS DONATE BUT
GOOD», «PLS DONATE S»;
обрано програмно-технічні засоби для реалізації задачі дослідження;
розроблено гру «PLS DONATE X»та виконано її тестування.
Кросплатформна гра «PLS DONATE X» розроблена на мові Lua. Створення та
робота з базою даних відбувалась із застосуванням Roblox Studio. Контроль проєкту
та написання коду здійснювалось у середовищі Roblox Studio.
Обрані технології задовольняють всім функціональним і не функціональним
вимогам до такого типу програмного забезпечення.
Розроблений розроблена гра має зручний, зрозумілий та приємний інтерфейс.
Користується попитом в багатьох гравців.
43
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ
1. GamesIndustry.biz presents… The Year in Numbers 2020 URL:
https://www.gamesindustry.biz/articles/2020-12-21-gamesindustry-biz-presents-the-
year-in-numbers-2020 (дата звернення: 10.03.2024)
2. Roblox URL: https://uk.wikipedia.org/wiki/Roblox (дата звернення: 10.03.2024).
3. Тестування ігор: типи та способи тестування мобільних/настільних програм
URL: https://www.guru99.com/uk/game-testing-mobile-desktop-apps.html (дата
звернення: 10.03.2024)
44
ДОДАТОК А
Затверджую
Зав. кафедри КНСА,
______________ Юрій ТРИУС
«____»____________2023 р.
КОМП’ЮТЕРНА 3-D ГРА “PLS DONATE X” НА ОНЛАЙН-ПЛАТФОРМІ
ROBLOX
Специфікація
482.ЧДТУ. 42285-01
Листів 2
Розробник ____________________ Колісник М.В.
Керівник ____________________ Підгорний М.В.
Черкаси – 2024
45
482.ЧДТУ. 42285-01
Позначення Найменування Примітка
Документація
482.ЧДТУ. 42285-01 12 01 Текст програми
482.ЧДТУ. 42285-01 34 01 Інструкція користувача
46
ДОДАТОК Б
КОМП’ЮТЕРНА 3-D ГРА “PLS DONATE X” НА ОНЛАЙН-ПЛАТФОРМІ
ROBLOX
Текст програми
482.ЧДТУ. 42285-01 12 01
Листів 23
Розробник _____________ Колісник М.В.
Черкаси – 2024
47
Фрагмент програми
BoothLoader
-- SERVICES --
local ServerStorage = game:GetService('ServerStorage')
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local CollectionService = game:GetService('CollectionService')
-- MODULES --
local BoothService = require(game.ServerScriptService.Services.BoothService)
-- INSTANCES --
local Templates = ReplicatedStorage.Templates
local Booths = ServerStorage:WaitForChild('BoothModels')
-- MAIN SCRIPT --
local BoothsPositions = {
Booth1 = {Position = Vector3.new(241.8, 0.98, 279.223), Orientation = 120},
Booth2 = {Position = Vector3.new(235.268, 0.98, 347.292), Orientation = 60},
Booth3 = {Position = Vector3.new(245.8, 0.98, 323.223), Orientation = 90},
Booth4 = {Position = Vector3.new(115.69, 0.98, 245.387), Orientation = -150},
Booth5 = {Position = Vector3.new(126.948, 0.98, 238.886), Orientation = -150},
Booth6 = {Position = Vector3.new(206.275, 0.98, 238.918), Orientation = 150},
Booth7 = {Position = Vector3.new(241.768, 0.98, 336.034), Orientation = 60},
Booth8 = {Position = Vector3.new(104.37, 0.98, 358.496), Orientation = -60},
Booth9 = {Position = Vector3.new(217.534, 0.98, 245.418), Orientation = 150},
Booth10 = {Position = Vector3.new(228.8, 0.98, 256.707), Orientation = 120},
Booth11 = {Position = Vector3.new(115.636, 0.98, 369.785), Orientation = -30},
Booth12 = {Position = Vector3.new(126.894, 0.98, 376.285), Orientation = -30},
Booth13 = {Position = Vector3.new(245.8, 0.98, 291.223), Orientation = 90},
Booth14 = {Position = Vector3.new(91.37, 0.98, 335.98), Orientation = -60},
Booth15 = {Position = Vector3.new(217.48, 0.98, 369.816), Orientation = 30},
Booth16 = {Position = Vector3.new(91.401, 0.98, 279.169), Orientation = -120},
Booth17 = {Position = Vector3.new(138.207, 0.98, 232.387), Orientation = -150},
Booth18 = {Position = Vector3.new(235.3, 0.98, 267.965), Orientation = 120},
Booth19 = {Position = Vector3.new(195.017, 0.98, 232.418), Orientation = 150},
Booth20 = {Position = Vector3.new(126.517, 0.98, 519.711), Orientation = -75},
Booth21 = {Position = Vector3.new(97.901, 0.98, 267.911), Orientation = -120},
Booth22 = {Position = Vector3.new(194.963, 0.98, 382.816), Orientation = 30},
Booth23 = {Position = Vector3.new(97.87, 0.98, 347.238), Orientation = -60},
Booth24 = {Position = Vector3.new(87.293, 0.98, 291.161), Orientation = -90},
Booth25 = {Position = Vector3.new(126.533, 0.98, 498.202), Orientation = -105},
48
Booth26 = {Position = Vector3.new(206.804, 0.98, 498.263), Orientation = 105},
Booth27 = {Position = Vector3.new(104.401, 0.98, 256.652), Orientation = -120},
Booth28 = {Position = Vector3.new(206.221, 0.98, 376.316), Orientation = 30},
Booth29 = {Position = Vector3.new(206.788, 0.98, 519.771), Orientation = 75},
Booth30 = {Position = Vector3.new(196.064, 0.98, 479.628), Orientation = 135},
Booth31 = {Position = Vector3.new(87.293, 0.98, 323.161), Orientation = -90},
Booth32 = {Position = Vector3.new(228.768, 0.98, 358.55), Orientation = 60},
Booth33 = {Position = Vector3.new(181.026, 0.552, 123.042), Orientation = 94},
Booth34 = {Position = Vector3.new(137.257, 0.98, 538.346), Orientation = -45},
Booth35 = {Position = Vector3.new(152.947, 0.524, 143.891), Orientation = -107},
Booth36 = {Position = Vector3.new(137.301, 0.98, 479.583), Orientation = -135},
Booth37 = {Position = Vector3.new(138.152, 0.98, 382.785), Orientation = -30},
Booth38 = {Position = Vector3.new(196.02, 0.98, 538.39), Orientation = 45},
}
local BoothInteraction = {
Interaction1 = {Position = Vector3.new(240.959, 4.973, 279.766), Orientation = 120},
Interaction2 = {Position = Vector3.new(234.378, 4.973, 346.836), Orientation = 60},
Interaction3 = {Position = Vector3.new(244.801, 4.973, 323.273), Orientation = 90},
Interaction4 = {Position = Vector3.new(116.233, 4.973, 246.227), Orientation = 30},
Interaction5 = {Position = Vector3.new(127.491, 4.973, 239.727), Orientation = 30},
Interaction6 = {Position = Vector3.new(205.819, 4.973, 239.808), Orientation = -30},
Interaction7 = {Position = Vector3.new(240.878, 4.973, 335.578), Orientation = 165},
Interaction8 = {Position = Vector3.new(105.21, 4.973, 357.953), Orientation = 165},
Interaction9 = {Position = Vector3.new(217.077, 4.973, 246.308), Orientation = -165},
Interaction10 = {Position = Vector3.new(227.959, 4.973, 257.249), Orientation = -165},
Interaction11 = {Position = Vector3.new(116.092, 4.973, 368.895), Orientation = -165},
Interaction12 = {Position = Vector3.new(127.35, 4.973, 375.395), Orientation = -135},
Interaction13 = {Position = Vector3.new(244.801, 4.973, 291.273), Orientation = -90},
Interaction14 = {Position = Vector3.new(92.21, 4.973, 335.437), Orientation = -135},
Interaction15 = {Position = Vector3.new(216.937, 4.973, 368.976), Orientation = -45},
Interaction16 = {Position = Vector3.new(92.291, 4.973, 279.625), Orientation = -45},
Interaction17 = {Position = Vector3.new(138.749, 4.973, 233.227), Orientation = -45},
Interaction18 = {Position = Vector3.new(234.46, 4.973, 268.508), Orientation = -15},
Interaction19 = {Position = Vector3.new(194.561, 4.973, 233.308), Orientation = -15},
Interaction20 = {Position = Vector3.new(127.469, 4.973, 519.404), Orientation = -15},
Interaction21 = {Position = Vector3.new(98.791, 4.973, 268.367), Orientation = 15},
Interaction22 = {Position = Vector3.new(194.42, 4.973, 381.976), Orientation = 15},
Interaction23 = {Position = Vector3.new(98.71, 4.973, 346.695), Orientation = 45},
Interaction24 = {Position = Vector3.new(88.292, 4.973, 291.111), Orientation = 45},
Interaction25 = {Position = Vector3.new(127.511, 4.973, 498.413), Orientation = 45},
Interaction26 = {Position = Vector3.new(205.852, 4.973, 498.569), Orientation = 75},
Interaction27 = {Position = Vector3.new(105.291, 4.973, 257.109), Orientation = 75},
Interaction28 = {Position = Vector3.new(205.678, 4.973, 375.476), Orientation = 75},
Interaction29 = {Position = Vector3.new(205.81, 4.973, 519.561), Orientation = 75},
Interaction30 = {Position = Vector3.new(195.393, 4.973, 480.369), Orientation = 75},
Interaction31 = {Position = Vector3.new(88.292, 4.973, 323.111), Orientation = 75},
49
Interaction32 = {Position = Vector3.new(227.878, 4.973, 358.094), Orientation = -120},
Interaction33 = {Position = Vector3.new(180.033, 4.545, 123.161), Orientation = 75},
Interaction34 = {Position = Vector3.new(137.928, 4.973, 537.604), Orientation = 75},
Interaction35 = {Position = Vector3.new(153.917, 4.517, 144.135), Orientation = 75},
Interaction36 = {Position = Vector3.new(138.043, 4.973, 480.254), Orientation = 75},
Interaction37 = {Position = Vector3.new(138.609, 4.973, 381.895), Orientation = 75},
Interaction38 = {Position = Vector3.new(195.278, 4.973, 537.719), Orientation = 75},
}
local BoothModels = {}
for Name, Booth in pairs(BoothsPositions) do
local BoothClone = Booths.BasicBooth:Clone()
BoothClone:SetPrimaryPartCFrame(CFrame.new(Booth.Position))
BoothClone:SetPrimaryPartCFrame(BoothClone.PrimaryPart.CFrame * CFrame.Angles(0,
math.rad(Booth.Orientation), 0))
BoothClone.Name = 'BasicBooth'
BoothClone:SetAttribute('BoothID', Name:sub(6, #Name))
BoothClone.Parent = workspace:WaitForChild('BoothModels')
local BoothUI = Templates.BoothUI:Clone()
BoothUI.Parent = workspace:WaitForChild('MapUI'):WaitForChild('BoothUI')
BoothUI.Details.Adornee = BoothClone:WaitForChild('Base')
BoothUI.Items.Adornee = BoothClone:WaitForChild('ItemsPart')
BoothUI.Sign.Adornee = BoothClone:WaitForChild('SignPart')
BoothUI.Name = 'BoothUI'..Name:sub(6, #Name)
rawset(BoothModels, Name:sub(6, #Name), BoothClone)
--CreateBoothStore(Name)
end
local function findBoothByBoothID(boothID)
for _, boothModel in pairs(BoothModels) do
if boothModel:GetAttribute('BoothID') == boothID then
return boothModel
end
end
return nil
end
local BoothInteractions = {}
for Name, Interaction in pairs(BoothInteraction) do
local InteractionClone = script.BoothInteraction:Clone()
InteractionClone.Position = Interaction.Position
InteractionClone.Orientation = Vector3.new(0, Interaction.Orientation, 0)
InteractionClone.Name = 'BoothInteraction'
CollectionService:AddTag(InteractionClone, 'BoothInteraction')
InteractionClone:SetAttribute('BoothSlot', Name:sub(12, #Name))
50
InteractionClone:SetAttribute('BoothOwner', nil)
local BoothID = InteractionClone:GetAttribute('BoothSlot')
local Booth = findBoothByBoothID(BoothID)
InteractionClone.Parent = workspace:WaitForChild('BoothInteractions')
InteractionClone.BoothModel.Value = Booth
rawset(BoothInteractions, Name:sub(12, #Name), InteractionClone)
end
wait(0.15)
BoothService:UpdateTables(BoothInteractions, BoothModels)
wait(1)
script:Destroy()
Admin
Local remotes = require(game.replicatedstorage.remotes)
Local dataservice = require(game.serverscriptservice.services.dataservice)
Local logservice = require(game.serverscriptservice.services.logservice)
Local messagingservice = game:getservice("messagingservice")
Local function checkplayer(player)
if player:getrankingroup(16756900) < 4 then
logservice:logexploit(player, 'trying to use admin panel without permissions')
player:kick('xuataun\n exploiting')
return
end
End
Remotes.function("checkifbanned").onserverinvoke = function(player, userdata)
checkplayer(player)
local bandata = dataservice:isplayerbanned(userdata)
if bandata == nil then
bandata = {isbanned = false, reason = ""}
end
return bandata
End
Remotes.event("ban").onserverevent:connect(function(player, userid, reason)
checkplayer(player)
dataservice:banplayer(userid, reason)
local target = game.players:getplayerbyuserid(userid)
if target then
target:kick(string.format("you've been banned for reason %s", reason))
end
End)
Remotes.event("unban").onserverevent:connect(function(player, userid)
checkplayer(player)
51
dataservice:unbanplayer(userid)
End)
Remotes.event("kick").onserverevent:connect(function(player, userid)
checkplayer(player)
local player = game.players:getplayerbyuserid(userid)
player:kick("you've been kicked from this server")
End)
Remotes.function("checkuseringame").onserverinvoke = function(player, userid)
checkplayer(player)
local userdata = {ingame = false, placeid = 17114655272}
local success, errormessage, placeid, jobid = pcall(function()
return game["teleport service"]:getplayerplaceinstanceasync(userid)
end)
if success then
if placeid == game.placeid then
userdata.ingame = true
end
end
return userdata
End
Remotes.event("joinuserserver").onserverevent:connect(function(player, userid)
checkplayer(player)
local success, errormessage, placeid, jobid = pcall(function()
return game["teleport service"]:getplayerplaceinstanceasync(userid)
end)
if success then
game["teleport service"]:teleporttoplaceinstance(placeid, jobid, player)
end
End)
Chat commands
-- Services --
Local players = game:getservice('players')
Local serverstorage = game:getservice('serverstorage')
Local replicatedstorage = game:getservice('replicatedstorage')
Local messagingservice = game:getservice("messagingservice")
-- modules --
Local remotes = require(replicatedstorage.remotes)
Local playersservice = require(game.serverscriptservice.services.playersservice)
-- stuff --
52
Local validcmds = {'headless', 'purplevalk', 'blackvalk', 'pinkvalk', 'bluevalk', 'icevalk',
'greenvalk', 'redvalk', 'car'}
Local foundercommands =
require(game:getservice('serverscriptservice'):waitforchild('services'):waitforchild('founderc
mds'))
-- main script --
Remotes.function('getplayerrank').onserverinvoke = function(player)
local playerrank = player:getrankingroup(16756900)
return playerrank
End
Players.playeradded:connect(function(plr)
plr.chatted:connect(function(message)
if table.find(validcmds, string.lower(message)) then
if plr:getrankingroup(16756900) >= 254 then
foundercommands:applycommand(string.lower(message), plr)
end
end
end)
End)
Content creator
-- Services --
Local players = game:getservice('players')
Local userservice = game:getservice('userservice')
Local textservice = game:getservice('textservice')
Local serverstorage = game:getservice('serverstorage')
Local tweenservice = game:getservice("tweenservice")
Local physicsservice = game:getservice('physicsservice')
Local replicatedstorage = game:getservice('replicatedstorage')
Local collectionservice = game:getservice('collectionservice')
Local replicatedstorage = game:getservice('replicatedstorage')
53
Local serverscriptservice = game:getservice('serverscriptservice')
Local messagingservice = game:getservice('messagingservice')
-- instances --
Local templates = replicatedstorage.templates
-- modules --
Local remotes = require(replicatedstorage.remotes)
Local logservice = require(game.serverscriptservice.services.logservice)
Local numbershelper =
require(replicatedstorage:waitforchild('common'):waitforchild('numberhelpers'))
-- events --
Local firenuke = remotes.event('firenuke')
Local firesmite = remotes.event('firesmite')
Local fireoldsmite = remotes.event('fireoldsmite')
Local firehalloween = remotes.event('firehalloween')
Local firemap = remotes.event('firemaphalloween')
Local firebadnuke = remotes.event('firebadnuke')
Local firestarfall = remotes.event('firestarfall')
Local addplayerasset = remotes.event('addplayerasset')
Local fakedonateserver =
serverscriptservice:waitforchild('donations'):waitforchild('donatedhandler'):waitforchild('fak
edonated')
-- data --
Local contentcreator = {}
Local playerincooldown = {}
-- functions --
Function getuserinfo(userid)
local issuccess, result = pcall(function()
return userservice:getuserinfosbyuseridsasync({userid})
end)
if not issuccess then
return false
else
return table.unpack(result)
end
End
Function checkplayer(player)
54
if contentcreator[player.userid] then
return
end
if player:getrankingroup(16756900) < 5 then
logservice:logexploit(player, 'trying to use cc panel without permissions')
player:kick('xuataun\n exploiting')
return
else
contentcreator[player.userid] = true
end
End
-- main script
Remotes.event('fakedonate').onserverevent:connect(function(player, raiser, robuxamount)
checkplayer(player)
if player:getrankingroup(16756900) >= 5 then
if playerincooldown[player.userid] then
return
end
fakedonateserver:fire(player, raiser, robuxamount)
playerincooldown[player.userid] = true
wait(2)
playerincooldown[player.userid] = nil
end
55
End)
Remotes.event('spawneffect').onserverevent:connect(function(player, type, donator, raiser,
robuxamount)
checkplayer(player)
if player:getrankingroup(16756900) >= 5 then
if playerincooldown[player.userid] then
return
end
local issuccess, errormessage = pcall(function()
players:getuseridfromnameasync(donator)
end)
if issuccess then
donator = getuserinfo(players:getuseridfromnameasync(donator))
else
donator = {
id = 4006272005,
username = donator
}
end
local issuccess, errormessage = pcall(function()
players:getuseridfromnameasync(raiser)
end)
if issuccess then
raiser = getuserinfo(players:getuseridfromnameasync(raiser))
else
raiser = {
id = 4006272005,
username = raiser
}
end
if type == 'blimp' then
local blimp =
replicatedstorage:waitforchild('templates'):waitforchild('blimp'):clone()
for _, blimpmesh in pairs(blimp:getchildren()) do
56
if blimpmesh:isa('meshpart') then
if blimpmesh.cancollide then
physicsservice:setpartcollisiongroup(blimpmesh,
'blimp')
end
end
end
blimp.a.donationframe.donator.avatar.image =
string.format("rbxthumb://type=avatarheadshot&id=%s&w=420&h=420", donator.id)
blimp.a.donationframe.donator.username.text = donator.username
blimp.a.donationframe.boothowner.avatar.image =
string.format("rbxthumb://type=avatarheadshot&id=%s&w=420&h=420", raiser.id)
blimp.a.donationframe.boothowner.username.text = raiser.username
blimp.a.donationframe.robux.price.text = string.format("\238\128\130 %s",
numbershelper.formatcommas(robuxamount))
blimp.b.donationframe.donator.avatar.image =
string.format("rbxthumb://type=avatarheadshot&id=%s&w=420&h=420", donator.id)
blimp.b.donationframe.donator.username.text = donator.username
blimp.b.donationframe.boothowner.avatar.image =
string.format("rbxthumb://type=avatarheadshot&id=%s&w=420&h=420", raiser.id)
blimp.b.donationframe.boothowner.username.text = raiser.username
blimp.b.donationframe.robux.price.text = string.format("\238\128\130 %s",
numbershelper.formatcommas(robuxamount))
blimp.parent = game:getservice('workspace')
elseif type == 'nuke' then
firenuke:fireallclients(donator.username, raiser.username, robuxamount)
elseif type == 'speed' then
local issuccess, errormessage = pcall(function()
messagingservice:publishasync('speedevent', {
music = game.soundservice.music.name
})
end)
elseif type == 'smite' then
57
firesmite:fireallclients(donator.username, raiser.username, robuxamount,
donator.id)
elseif type == 'halloween' then
firehalloween:fireallclients()
elseif type == "ufo" then
local issuccess, errormessage = pcall(function()
messagingservice:publishasync('ufoevent', {
message = "ufo"
})
end)
elseif type == 'starfall' then
firestarfall:fireallclients(donator.username, raiser.username,
robuxamount, donator.id)
end
playerincooldown[player.userid] = true
wait(2)
playerincooldown[player.userid] = nil
end
End)
Remotes.event('globalmessage').onserverevent:connect(function(player, type, message)
checkplayer(player)
if player:getrankingroup(16756900) >= 253 then
if message ~= '' then
messagingservice:publishasync('global'.. Type, message)
replicatedstorage:findfirstchild(type.."text").value = message
else
58
messagingservice:publishasync('global'.. Type, '')
replicatedstorage:findfirstchild(type.."text").value = ''
end
elseif player:getrankingroup(16756900) == 6 then
if message ~= '' then
replicatedstorage:findfirstchild(type.."text").value = message
else
replicatedstorage:findfirstchild(type.."text").value = ''
end
end
End)
BoothEvents
-- Services --
Local serverstorage = game:getservice('serverstorage')
Local messagingservice = game:getservice('messagingservice')
Local replicatedstorage = game:getservice('replicatedstorage')
Local collectionservice = game:getservice('collectionservice')
Local marketplaceservice = game:getservice('marketplaceservice')
Local serverscriptservice = game:getservice('serverscriptservice')
Local badgeservice = game:getservice("badgeservice")
-- instances --
Local templates = replicatedstorage.templates
-- data --
Local uppercases = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}
-- modules --
Local remotes = require(replicatedstorage.remotes)
Local dataservice = require(game.serverscriptservice.services.dataservice)
59
Local itemsservice = require(game.serverscriptservice.services.itemsservice)
Local boothservice = require(game.serverscriptservice.services.boothservice)
Local filterservice = require(game.serverscriptservice.services.filterservice)
Local temporarydataservice = require(game.serverscriptservice.services.temporarydataservice)
Local numbershelper =
require(replicatedstorage:waitforchild('common'):waitforchild('numberhelpers'))
Local booths = require(serverstorage.booths)
-- events --
Local removeplayerasset = remotes.event('removeplayerasset')
Local changeboothinfo = remotes.event('changeboothinfo')
Local changetextinfo = remotes.event('changetextinfo')
Local changesomeinfo = remotes.event('changesomeinfo')
Local seteditboothtext = remotes.event('seteditboothtext')
-- functions --
Function getdonatesign(player)
local donatesign = player.character:findfirstchild('donatesign')
if not donatesign then
donatesign = player.backpack:waitforchild('donatesign')
end
return donatesign
End
Function getcolor3(colorstring)
local colorstringsplitted = string.split(colorstring, ",")
local v1 = tonumber(colorstringsplitted[1])
local v2 = tonumber(colorstringsplitted[2])
local v3 = tonumber(colorstringsplitted[3])
return color3.new(v1, v2, v3)
End
Function getboothname(boothid)
local boothname = ''
for i = 1, #boothid do
60
local letter = string.sub(boothid, i, i)
if not table.find(uppercases, letter) or i == 1 then
boothname = boothname .. Letter
else
boothname = boothname .. ' ' .. Letter
end
end
return boothname
End
-- main script --
Remotes.event('preloaditems').onserverevent:connect(function(player)
local playerbooth = boothservice:getplayerbooth(player.userid)
if not playerbooth then
itemsservice:getplayeritems(player)
end
End)
Remotes.event('setboothtext').onserverevent:connect(function(player, text, boothtype)
task.wait(0.015)
local filteredtext = filterservice:getfilteredtext(tostring(text), player.userid)
if boothtype == 'sign' then
local playersign = player.character:findfirstchild('donatesign')
local playerbigsign = player.character:findfirstchild('bigdonatesign')
--local startergear =
player:waitforchild('startergear'):waitforchild('donatesign')
if playersign then
61
playersign.textsign.surfacegui.textlabel.text = filteredtext
end
if playerbigsign then
playerbigsign.textsign.surfacegui.textlabel.text = filteredtext
end
--startergear.textsign.surfacegui.textlabel.text = filteredtext
--temporarydataservice:saveplayersingtext(player.userid, filteredtext)
else
local boothslot = boothservice:getplayerbooth(player.userid)
if boothslot then
local boothui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild(string.format('boothui%
s', boothslot))
boothui.sign.textlabel.text = filteredtext
changetextinfo:fireclient(player, filteredtext, nil, nil, nil, nil, nil,
nil)
player.playerdata.boothtext.value = filteredtext
--temporarydataservice:saveplayerbooth(player.userid, filteredtext)
end
end
dataservice:setboothtext(player.userid, boothtype, filteredtext)
End)
Remotes.function('claimbooth').onserverinvoke = function(player, boothslot)
task.wait(0.015)
local playerbooth = boothservice:getplayerbooth(player.userid)
if playerbooth then
return
end
62
spawn(function()
if not badgeservice:userhasbadgeasync(player.userid, 2073982504693055) then
badgeservice:awardbadge(player.userid, 2073982504693055)
end
end)
local boothui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Boothslot)
local donatesing = getdonatesign(player)
local playerboothtextcolor = player.playerdata.textcolor.value
local playerstroketextcolor = player.playerdata.textstrokecolor.value
local currentbooth = player.playerdata.currentbooth.value
local playerboothtext = player.playerdata.boothtext.value
local textopacity = player.playerdata.textopacity.value
local textfont = player.playerdata.textfont.value
local boothtextcolor = getcolor3(playerboothtextcolor)
local boothtextstrokecolor = getcolor3(playerstroketextcolor)
boothui.items.frame:setattribute('assettype', player.playerdata.buttonlayout.value)
boothui.items.frame:setattribute('assetfont', player.playerdata.buttonfont.value)
boothui.items.frame:setattribute('assettextcolor',
player.playerdata.buttontextcolor.value)
boothui.items.frame:setattribute('assetbuttoncolor',
player.playerdata.buttonbackcolor.value)
boothui.items.frame:setattribute('assetstrokecolor',
player.playerdata.buttonstrokecolor.value)
boothui.items.frame:setattribute('assethovercolor',
player.playerdata.buttonhovercolor.value)
if player:getattribute("donatednumber") >= 1e6 then
boothui.items.frame:setattribute('assethovercolorenabled', true)
donatesing.signgui.frame:setattribute("assethovercolorenabled", true)
end
task.spawn(function()
63
boothservice:claimbooth(player, boothslot, playerboothtext, currentbooth,
tostring(numbershelper.format(player:waitforchild('leaderstats'):waitforchild('raised').value)
), boothtextcolor, boothtextstrokecolor, textfont, textopacity)
end)
spawn(function()
task.spawn(function()
if not booths.booths[currentbooth] then
changeboothinfo:fireclient(player, getboothname(currentbooth), nil,
nil)
else
changeboothinfo:fireclient(player, getboothname(currentbooth),
booths.booths[currentbooth].icon.image, booths.booths[currentbooth].icon.imagerectoffset)
end
changetextinfo:fireclient(player, playerboothtext, boothtextcolor,
boothtextstrokecolor, textfont, textopacity, player.playerdata.buttonlayout.value,
player.playerdata.buttonfont.value, getcolor3(player.playerdata.buttontextcolor.value))
changesomeinfo:fireclient(player,
getcolor3(player.playerdata.buttonbackcolor.value),
getcolor3(player.playerdata.buttonstrokecolor.value),
getcolor3(player.playerdata.buttonhovercolor.value))
end)
seteditboothtext:fireclient(player, playerboothtext)
end)
local assetcount = itemsservice:loaditemsbutton(player.userid, boothui.items.frame)
donatesing.signgui.frame:setattribute('assettype',
player.playerdata.buttonlayout.value)
donatesing.signgui.frame:setattribute('assetfont', player.playerdata.buttonfont.value)
donatesing.signgui.frame:setattribute('assettextcolor',
player.playerdata.buttontextcolor.value)
donatesing.signgui.frame:setattribute('assetbuttoncolor',
player.playerdata.buttonbackcolor.value)
donatesing.signgui.frame:setattribute('assetstrokecolor',
player.playerdata.buttonstrokecolor.value)
64
donatesing.signgui.frame:setattribute('assethovercolor',
player.playerdata.buttonhovercolor.value)
if donatesing ~= nil then
if not donatesing.signgui.frame:findfirstchildofclass("textbutton") then
itemsservice:loaditemsbutton(player.userid, donatesing.signgui.frame)
end
end
return {
assetcount = assetcount,
isinventoryprivate = false
}
End
Remotes.event('editboothmodel').onserverevent:connect(function(player, boothid)
task.wait(0.015)
task.spawn(function()
local boothslot = boothservice:getplayerbooth(player.userid)
boothservice:changebooth(tostring(boothid), boothslot)
temporarydataservice:saveplayerbooth(player.userid, boothid)
player.playerdata.currentbooth.value = tostring(boothid)
end)
dataservice:setcurrentbooth(player.userid, tostring(boothid))
if not booths.booths[boothid] then
changeboothinfo:fireclient(player, getboothname(boothid), nil, nil)
else
changeboothinfo:fireclient(player, getboothname(boothid),
booths.booths[boothid].icon.image, booths.booths[boothid].icon.imagerectoffset)
end
65
End)
Remotes.function('refreshitems').onserverinvoke = function(player)
task.wait(0.015)
local boothslot = boothservice:getplayerbooth(player.userid)
local boothui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild(string.format('boothui%
s', boothslot))
for _, itembutton in pairs(boothui.items.frame:getchildren()) do
if itembutton:isa('textbutton') then
itembutton:destroy()
end
end
boothui.items.frame.canvassize = udim2.fromoffset(0, 0)
itemsservice:loaditemsbutton(player.userid, boothui.items.frame)
End
Remotes.event('unclaimbooth').onserverevent:connect(function(player)
local playerbooth = temporarydataservice:getplayerbooth(player.userid)
local signtext = temporarydataservice:getplayersingtext(player.userid)
local boothtext = temporarydataservice:getplayerboothtext(player.userid)
local boothslot = boothservice:getplayerbooth(player.userid)
if boothslot then
local boothui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild(string.format('boothui%
s', boothslot))
boothui.items.frame:setattribute("assettype", "normal")
boothui.items.frame:setattribute("assetfont", "gothamblack")
boothui.items.frame:setattribute("assettextcolor", "0, 0, 0")
boothui.items.frame:setattribute("assetbuttoncolor", "0, 1, 0.0666667")
boothui.items.frame:setattribute("assethovercolor", "0, 1, 0.0666667")
boothui.items.frame:setattribute("assetstrokecolor", "0, 0, 0")
boothservice:resetbooth(boothslot, player.userid)
66
end
if playerbooth then
dataservice:setcurrentbooth(player.userid,
temporarydataservice:getplayerbooth(player.userid))
end
if signtext then
dataservice:setboothtext(player.userid, 'sign',
temporarydataservice:getplayersingtext(player.userid))
end
if boothtext then
dataservice:setboothtext(player.userid, 'booth',
temporarydataservice:getplayerboothtext(player.userid))
end
End)
Remotes.event("setboothfont").onserverevent:connect(function(player, font)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
ui.sign.textlabel.font = font;
player.playerdata.textfont.value = font.name
dataservice:setboothfont(player, font)
End);
Remotes.event("changeboothtextcolor").onserverevent:connect(function(player, colour)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
ui.sign.textlabel.textcolor3 = colour;
dataservice:setplayertextcolor(player.userid, ui.sign.textlabel.textcolor3)
player.playerdata.textcolor.value = tostring(ui.sign.textlabel.textcolor3)
End);
67
Remotes.event("changeboothstroketransparency").onserverevent:connect(function(player,
transparency)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
local invertedtransparency = 1 - transparency
-- set textstroketransparency to inverted transparency value
ui.sign.textlabel.textstroketransparency = invertedtransparency
dataservice:setplayerstrokeopacity(player.userid, transparency)
player.playerdata.textopacity.value = transparency
End);
Remotes.event("changeboothstrokecolor").onserverevent:connect(function(player, colour)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
ui.sign.textlabel.textstrokecolor3 = colour;
dataservice:setplayerstrokecolor(player.userid, ui.sign.textlabel.textstrokecolor3)
player.playerdata.textstrokecolor.value = tostring(ui.sign.textlabel.textstrokecolor3)
End);
Remotes.event("changeboothrichtext").onserverevent:connect(function(player, value)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
ui.sign.textlabel.richtext = value
End);
Remotes.event("setboothbuttonlayout").onserverevent:connect(function(player, layout)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
local donatesing = getdonatesign(player)
if ui.items.frame:getattribute("assettype") == layout then
return
68
end
if layout == "image" then
ui.items.frame:setattribute("assettype", "image")
donatesing.signgui.frame:setattribute("assettype", "image")
for _, items in pairs(ui.items.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.texttransparency = 1
items.uicorner.cornerradius = udim.new(0.2, 0)
items.uilistlayout.filldirection = enum.filldirection.vertical
items.backgroundtransparency = 0
items.uipadding.paddingleft = udim.new(0, 4)
items.uipadding.paddingright = udim.new(0, 4)
items.uipadding.paddingtop = udim.new(0, 5)
items.uipadding.paddingbottom = udim.new(0, 5)
items.uistroke.thickness = 3.5
if items:findfirstchild("imagelabel") then
items.imagelabel:destroy()
end
local image = instance.new("imagelabel", items)
local price = instance.new("textlabel", items)
local uicorner = instance.new("uicorner", image)
uicorner.cornerradius = udim.new(1, 0)
image.image = items:getattribute("assetimage")
image.size = udim2.new(0.7, 0, 0.7, 0)
image.scaletype = enum.scaletype.fit
image.backgroundtransparency = 1
price.name = "price"
price.backgroundtransparency = 1
price.text = items.text
price.anchorpoint = vector2.new(0.5, 0.5)
price.textcolor3 = items.textcolor3
price.size = udim2.new(0.8, 0, 0.25, 0)
price.textscaled = true
items:waitforchild("price", 5).font = items.font
69
end
end
for _, items in pairs(donatesing.signgui.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.texttransparency = 1
items.uicorner.cornerradius = udim.new(0.2, 0)
items.uilistlayout.filldirection = enum.filldirection.vertical
items.backgroundtransparency = 0
items.uipadding.paddingleft = udim.new(0, 4)
items.uipadding.paddingright = udim.new(0, 4)
items.uipadding.paddingtop = udim.new(0, 5)
items.uipadding.paddingbottom = udim.new(0, 5)
items.uistroke.thickness = 3.5
if items:findfirstchild("imagelabel") then
items.imagelabel:destroy()
end
local image = instance.new("imagelabel", items)
local price = instance.new("textlabel", items)
local uicorner = instance.new("uicorner", image)
uicorner.cornerradius = udim.new(1, 0)
image.image = items:getattribute("assetimage")
image.size = udim2.new(0.7, 0, 0.7, 0)
image.scaletype = enum.scaletype.fit
image.backgroundtransparency = 1
price.name = "price"
price.backgroundtransparency = 1
price.text = items.text
price.anchorpoint = vector2.new(0.5, 0.5)
price.textcolor3 = items.textcolor3
price.size = udim2.new(0.8, 0, 0.25, 0)
price.textscaled = true
items:waitforchild("price", 5).font = items.font
end
70
end
elseif layout == "imagefill" then
ui.items.frame:setattribute("assettype", "imagefill")
donatesing.signgui.frame:setattribute("assettype", "imagefill")
for _, items in pairs(ui.items.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.texttransparency = 1
items.uicorner.cornerradius = udim.new(0.2, 0)
items.backgroundtransparency = 0.9
items.uistroke.thickness = 0
items.uilistlayout.filldirection = enum.filldirection.vertical
items.uipadding.paddingleft = udim.new(0, 0)
items.uipadding.paddingright = udim.new(0, 0)
items.uipadding.paddingtop = udim.new(0, 0)
items.uipadding.paddingbottom = udim.new(0, 0)
if items:findfirstchild("imagelabel") then
items.imagelabel.uicorner.cornerradius = udim.new(0.2, 0)
items.imagelabel.size = udim2.new(1, 0, 1, 0)
items:waitforchild("price", 5).parent = items.imagelabel
items.imagelabel.price.anchorpoint = vector2.new(0.5, 0.5)
items.imagelabel.price.position = udim2.new(0.5, 0, 0.9, 0)
items.imagelabel.price.textcolor3 = items.textcolor3
items.imagelabel.price.font = items.font
items.imagelabel.price.textstroketransparency = 0.5
items.imagelabel.price.text = items.text
else
local image = instance.new("imagelabel", items)
local price = instance.new("textlabel", image)
local uicorner = instance.new("uicorner", image)
uicorner.cornerradius = udim.new(0.2, 0)
image.image = items:getattribute("assetimage")
image.size = udim2.new(1, 0, 1, 0)
image.scaletype = enum.scaletype.crop
image.backgroundtransparency = 1
71
price.name = "price"
price.backgroundtransparency = 1
price.text = items.text
price.anchorpoint = vector2.new(0.5, 0.5)
price.position = udim2.new(0.5, 0, 0.9, 0)
price.textstroketransparency = 0.5
price.textcolor3 = items.textcolor3
price.size = udim2.new(0.8, 0, 0.25, 0)
price.textscaled = true
price.font = items.font
end
end
end
for _, items in pairs(donatesing.signgui.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.texttransparency = 1
items.uicorner.cornerradius = udim.new(0.2, 0)
items.backgroundtransparency = 0.9
items.uistroke.thickness = 0
items.uilistlayout.filldirection = enum.filldirection.vertical
items.uipadding.paddingleft = udim.new(0, 0)
items.uipadding.paddingright = udim.new(0, 0)
items.uipadding.paddingtop = udim.new(0, 0)
items.uipadding.paddingbottom = udim.new(0, 0)
if items:findfirstchild("imagelabel") then
items.imagelabel.uicorner.cornerradius = udim.new(0.2, 0)
items.imagelabel.size = udim2.new(1, 0, 1, 0)
items:waitforchild("price", 5).parent = items.imagelabel
items.imagelabel.price.anchorpoint = vector2.new(0.5, 0.5)
items.imagelabel.price.position = udim2.new(0.5, 0, 0.9, 0)
items.imagelabel.price.textcolor3 = items.textcolor3
items.imagelabel.price.font = items.font
items.imagelabel.price.textstroketransparency = 0.5
items.imagelabel.price.text = items.text
else
72
local image = instance.new("imagelabel", items)
local price = instance.new("textlabel", image)
local uicorner = instance.new("uicorner", image)
uicorner.cornerradius = udim.new(0.2, 0)
image.image = items:getattribute("assetimage")
image.size = udim2.new(1, 0, 1, 0)
image.scaletype = enum.scaletype.crop
image.backgroundtransparency = 1
price.name = "price"
price.backgroundtransparency = 1
price.text = items.text
price.anchorpoint = vector2.new(0.5, 0.5)
price.position = udim2.new(0.5, 0, 0.9, 0)
price.textstroketransparency = 0.5
price.textcolor3 = items.textcolor3
price.size = udim2.new(0.8, 0, 0.25, 0)
price.textscaled = true
price.font = items.font
end
end
end
elseif layout == "normal" then
ui.items.frame:setattribute("assettype", "normal")
donatesing.signgui.frame:setattribute("assettype", "normal")
for _, items in pairs(ui.items.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.texttransparency = 0
items.uicorner.cornerradius = udim.new(0, 8)
items.uilistlayout.filldirection = enum.filldirection.horizontal
items.backgroundtransparency = 0
items.uipadding.paddingleft = udim.new(0, 4)
items.uipadding.paddingright = udim.new(0, 4)
items.uipadding.paddingtop = udim.new(0, 5)
items.uipadding.paddingbottom = udim.new(0, 5)
items.uistroke.thickness = 3.5
73
if items:findfirstchild("imagelabel") then
items:waitforchild("imagelabel", 5):destroy()
end
if items:findfirstchild("price") then
items:waitforchild("price", 5):destroy()
end
end
end
for _, items in pairs(donatesing.signgui.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.texttransparency = 0
items.uicorner.cornerradius = udim.new(0, 8)
items.uilistlayout.filldirection = enum.filldirection.horizontal
items.backgroundtransparency = 0
items.uipadding.paddingleft = udim.new(0, 4)
items.uipadding.paddingright = udim.new(0, 4)
items.uipadding.paddingtop = udim.new(0, 5)
items.uipadding.paddingbottom = udim.new(0, 5)
items.uistroke.thickness = 3.5
if items:findfirstchild("imagelabel") then
items:waitforchild("imagelabel", 5):destroy()
end
if items:findfirstchild("price") then
items:waitforchild("price", 5):destroy()
end
end
74
end
end
player.playerdata.buttonlayout.value = layout
dataservice:setplayerbuttonlayout(player.userid, layout)
End)
Remotes.event('setboothbuttonfont').onserverevent:connect(function(player, font)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
local donatesing = getdonatesign(player)
ui.items.frame:setattribute('assetfont', font.name)
donatesing.signgui.frame:setattribute('assetfont', font.name)
for _, items in pairs(ui.items.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.font = font
if items:findfirstchild('price') then
items:waitforchild("price", 5).font = font
else
if items:findfirstchild('imagelabel') then
items.imagelabel:waitforchild("price", 5).font = font
end
end
end
end
for _, items in pairs(donatesing.signgui.frame:getchildren()) do
75
if (items:isa("textbutton")) and items.name ~= "additem" then
items.font = font
if items:findfirstchild('price') then
items:waitforchild("price", 5).font = font
else
if items:findfirstchild('imagelabel') then
items.imagelabel:waitforchild("price", 5).font = font
end
end
end
end
player.playerdata.buttonfont.value = font.name
dataservice:setplayerbuttonfont(player.userid, font)
End)
Remotes.event('setboothbuttontextcolor').onserverevent:connect(function(player, color)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
local donatesing = getdonatesign(player)
ui.items.frame:setattribute('assettextcolor', color)
donatesing.signgui.frame:setattribute('assettextcolor', color)
for _, items in pairs(ui.items.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.textcolor3 = getcolor3(color)
if items:findfirstchild('price') then
76
items:waitforchild("price", 5).textcolor3 = getcolor3(color)
else
if items:findfirstchild('imagelabel') then
items.imagelabel:waitforchild("price", 5).textcolor3 =
getcolor3(color)
end
end
end
end
for _, items in pairs(donatesing.signgui.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.textcolor3 = getcolor3(color)
if items:findfirstchild('price') then
items:waitforchild("price", 5).textcolor3 = getcolor3(color)
else
if items:findfirstchild('imagelabel') then
items.imagelabel:waitforchild("price", 5).textcolor3 =
getcolor3(color)
end
end
end
end
player.playerdata.buttontextcolor.value = color
dataservice:setplayerbuttontextcolor(player.userid, color)
End)
77
Remotes.event('setboothbuttonbackcolor').onserverevent:connect(function(player, color)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
local donatesing = getdonatesign(player)
ui.items.frame:setattribute('assetbuttoncolor', color)
donatesing.signgui.frame:setattribute('assetbuttoncolor', color)
for _, items in pairs(ui.items.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.backgroundcolor3 = getcolor3(color)
items.assetcolor.value = getcolor3(color)
end
end
for _, items in pairs(donatesing.signgui.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.backgroundcolor3 = getcolor3(color)
items.assetcolor.value = getcolor3(color)
end
end
player.playerdata.buttonbackcolor.value = color
dataservice:setplayerbuttonbackcolor(player.userid, color)
End)
Remotes.event('setboothbuttonstrokecolor').onserverevent:connect(function(player, color)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
local donatesing = getdonatesign(player)
ui.items.frame:setattribute('assetstrokecolor', color)
78
donatesing.signgui.frame:setattribute('assetstrokecolor', color)
for _, items in pairs(ui.items.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.uistroke.color = getcolor3(color)
end
end
for _, items in pairs(donatesing.signgui.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.uistroke.color = getcolor3(color)
end
end
player.playerdata.buttonstrokecolor.value = color
dataservice:setplayerbuttonstrokecolor(player.userid, color)
End)
Remotes.event('setboothbuttonhovercolor').onserverevent:connect(function(player, color)
local slot = boothservice:getplayerbooth(player.userid)
local ui =
workspace:waitforchild('mapui'):waitforchild('boothui'):findfirstchild('boothui' .. Slot);
local donatesing = getdonatesign(player)
ui.items.frame:setattribute('assethovercolor', color)
donatesing.signgui.frame:setattribute('assethovercolor', color)
for _, items in pairs(ui.items.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.assethover.value = getcolor3(color)
end
end
79
for _, items in pairs(donatesing.signgui.frame:getchildren()) do
if (items:isa("textbutton")) and items.name ~= "additem" then
items.assethover.value = getcolor3(color)
end
end
player.playerdata.buttonhovercolor.value = color
dataservice:setplayerbuttonhovercolor(player.userid, color)
End)
Game.players.playeradded:connect(function(player)
local playerdata = instance.new("folder", player)
playerdata.name = "playerdata"
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "currentbooth"
newvalue.value = dataservice:getplayerbooth(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "boothtext"
newvalue.value = dataservice:getboothtext(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "textfont"
newvalue.value = dataservice:getboothfont(player.userid)
local newvalue = instance.new("numbervalue", playerdata)
newvalue.name = "textopacity"
newvalue.value = dataservice:getplayerstrokeopacity(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "textcolor"
newvalue.value = dataservice:getplayertextcolor(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "textstrokecolor"
newvalue.value = dataservice:getplayerstrokecolor(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
80
newvalue.name = "buttonlayout"
newvalue.value = dataservice:getplayerbuttonlayout(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "buttonfont"
newvalue.value = dataservice:getplayerbuttonfont(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "buttontextcolor"
newvalue.value = dataservice:getplayerbuttontextcolor(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "buttonbackcolor"
newvalue.value = dataservice:getplayerbuttonbackcolor(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "buttonstrokecolor"
newvalue.value = dataservice:getplayerbuttonstrokecolor(player.userid)
local newvalue = instance.new("stringvalue", playerdata)
newvalue.name = "buttonhovercolor"
newvalue.value = dataservice:getplayerbuttonhovercolor(player.userid)
End)
81
ДОДАТОК В
КОМП’ЮТЕРНА 3-D ГРА “PLS DONATE X” НА ОНЛАЙН-ПЛАТФОРМІ
ROBLOX
ІНСТРУКЦІЯ КОРИСТУВАЧА
482. ЧДТУ. 42285-01 34 01
Листів 6
Розробник _____________ Колісник М.В.
Черкаси – 2024
82
Для початку роботи з грою «PLS DONATE X», потрібно створити акаунт на
офіційному сайті Roblox (Рисунок В.1).
Рисунок В.1 – Приклад створеного акаунта
Далі потрібно знайти гру «PLS DONATE X» в полі пошуку (Рисунок В.2).
Рисунок В.2 – Ілюстрація поля пошуку
Знайшовши гру «PLS DONATE X» далі натисніть на кнопку «Play» (Рисунок В.3).
Рисунок В.3 – Приклад знайденої гри «PLS DONATE X»
83
Зайшовши в гру в гравцю необхідно зайняти один з незайманих іншими
гравцями кіосків. Для цього потрібно затиснути клавішу «E» англійської розкладки
клавіатури (Рисунок В.4).
Рисунок В.4 – Приклад незайманого кіоску
Після цього даний кіоск буде присвоєно гравцю (Рисунок В.5), тому гравець
має змогу налаштовувати його так як забажає (стиль кіоску, текст, стиль тексту,
колір тексту, тощо) (Рисунок В.6).
84
Рисунок В.5 – Приклад займаного гравцем кіоску
Рисунок В.6 – Приклад вікна редагування кіоску займаного грацем
85
Для отримання пасивно ігрової валюти гравцю потрібно перебувати в грі
(Рисунок В.7), дану валюту він може жертувати іншим гравцям у вигляді донатів.
Рисунок В.7 – Приклад відображення кількості ігрової валюти
Для того, щоб пожертвувати іншому гравцю кошти у вигляді донату потрібно
підійти до його кіоску та обрати суму донату (Рисунок В.8). Для цього потрібно
навести курсор миші на бажану суму. Настиснувши на неї лівою кнопокою миші
з'явиться вікно з підтвердження донату (Рисунок В.9).
86
Рисунок В.8 – Приклад вибору кількості ігрової валюти для пожертви
87
Рисунок В.9 – Приклад вікна підтвердження пожертви