Разработка мобильного игрового приложения для операционной системы android
Обязательные и возможные составляющие структуры Android-приложения. Выбор инструментальных средств разработки, проектирование структур данных, алгоритмов и пользовательского интерфейса. Целостное представления о работе движка и связи его компонентов.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | русский |
Дата добавления | 24.06.2018 |
Размер файла | 1,3 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
[Введите текст]
СОДЕРЖАНИЕ
Введение
1. Постановка задачи
1.1 Основные понятия и определения
1.2 Общее описание разрабатываемого приложения
2. Анализ методов и средств решения поставленной задачи
2.1 Теоретические основы
2.2 Аналитический обзор аналогичных приложений
3. Анализ требований к игровому приложению
3.1 Требования к интерфейсу
4. Проектирование игрового приложения
4.1 Архитектура игрового приложения
4.2 Выбор инструментальных средств разработки
4.3 Проектирование структур данных и алгоритмов
4.4 Проектирование пользовательского интерфейса
5. Тестирование игрового приложения
5.1 Обоснование методики тестирования
5.2 Результаты тестирования
6. Разработка документации
Заключение
Список использованных источников
Приложения
ВВЕДЕНИЕ
Игровая индустрия зародилась в середине 1970-х годов и за несколько лет выросла из небольшого рынка в одну из крупнейших отраслей индустрии развлечений. На рынке работают крупные игроки, небольшие фирмы и стартапы, а также независимые разработчики и сообщества.
По мере развития компьютерных технологий и рынка игровой индустрии усложняется и разработка новых приложений, повышается стоимость разработки крупных продуктов, появляются новые жанры и направления, повышается уровень детализации игровых приложений и геймплейных возможностей.
В настоящее время рынок мобильных приложений очень быстро растет, и в 2017 сравнялся с рынком игр на пк, лидером игровой индустрии по платформам в настоящее время являются смартфоны(1).
Целью моей ВКР является создание небольшого игрового приложения на платформу Android, которое будет исправно работать и интерфейс которого будет точно откликаться на нужные действия пользователя, например, исполнение пользователем каких-либо действий в приложение.
В задачи исследования входит:
· Изучение любых материалов, необходимых для создания приложения.
· Изучение структуры движка, который будет использоваться при создании данного проекта, основные его возможности и функции.
· Получить целостное представления о работе движка и информации о том, как связаны его отдельные компоненты.
Объектом исследования является игровой движок Unity, а предметом исследования будет разработка игры в жанре Action - Platformer на данном движке, в процессе которой мы изучим те функции движка, которые помогут нам в создании своего приложения.
1. ПОСТАНОВКА ЗАДАЧИ
1.1 Основные понятия и определения
· Unity - межплатформенная среда разработки компьютерных игр.
· Геймплей (англ.gameplay) - игровой процесс, описывает то как игрок взаимодействует с игровым миром и механиками игры.
· Игровой движок - базовое программное обеспечение, которое может быть рассмотрено как основание для разработки множества компьютерных игр без существенных изменений.
· Графический движок - промежуточное программное обеспечение, основной задачей которого является визуализация компьютерной графики, может существовать как отдельный продукт, либо входить в состав игрового движка.
· Физический движок - программное обеспечение, позволяющее моделировать физические законы реального мира в виртуальном мире, чаще всего является не отдельным компонентом, а частью игрового движка.
· Сеттинг - (англ. setting -- с англ.?--?«помещение, установка, обстановка») время, место и обстоятельства, в которых развиваются события. Описывая сеттинг, пользователь определяет свойства реальности, моделируемой медиапродуктом.
· Фэнтези - (англ. fantasy -- «фантазия») жанр современного искусства, основанный на использование мифологических и сказочных мотивов в современном виде.
· Квест(англ. Quest) - в компьютерных ролевых играх - это задание, выполнив которое, персонаж игрока или его группа получает определенную награду(в т.ч. продвижение по сюжету).
· Сценарный язык(англ. scripting language) - высокоуровневый язык сценариев - кратких описаний действий, выполняемых системой.
· Сценарий -- это программа, имеющая дело с готовыми программными компонентам.
· Freemium(также известное как «условно-бесплатное ПО», англ. free -- бесплатный и англ. premium -- улучшенный, отличающийся более высоким качеством) - бизнес-модель заключающаяся в предложении воспользоваться приложением бесплатно, в то время как его расширенная версия, дополнительный функционал, сервисы, либо другие продукты связанные с основным предлагаются за дополнительную плату.
· Платформер(англ. Platformer) - жанр компьютерных игр, в которых основной чертой игрового процесса является прыгание по платформам, лазанье по лестницам, собирание предметов.
· Параллактическое отображение(англ. Parallax mapping) - программная техника в трёхмерной компьютерной графике, осуществляется смещением текстурных координат так, чтобы поверхность казалась объёмной.
1.2 Общее описание разрабатываемого приложения
Аналитическая компания Playliner проводила исследование по популярным жанрам и сеттингам мобильных игр во время которого были проанализировала 150 игровых приложений из списка лидеров Google play. Лидирующую позицию занял сеттинг фэнтези. Действие 31 игры из списка лидеров разворачивается именно в фэнтезийном мире. На втором месте оказались сказочный сеттинг и сеттинг казино, по 25 игр. Третью позицию занял “современный мир”. Для жанра RPG самым популярным сеттингом стал фэнтези(2). android движок алгоритм интерфейс
Следующий критерий популярности - длина игровой сессии. Согласно исследованиям GameAnalytics(3), в среднем человек проводит за мобильной игрой 6 минут. Для игр жанра Action средний показатель равняется 5 минутам.
Самым популярным жанром оказался с довольно большим отрывом Action 23.5% от всех приложений, на втором месте идет жанр Simulation 15.8%
Данное мобильное игровое приложение будет выполнено в жанре Platformer фэнтези сеттинга, написанная на движке Unity, ориентированное на пользователей, использующих мобильные устройства на базе OC Android, и предназначенное для развлечения пользователей, жанр был выбран из личных предпочтений. Визуальная составляющая будет выполнена в виде 2D графики с игровой камерой, фиксированной на персонаже. Также будет присутствовать развитие персонажа, разные типы врагов и другие геймплейные особенности.
Геймлей разрабатываемого проекта будет основан на прохождение основных сюжетных локаций с нарастающей сложностью боев и финальным боссом в конце каждой локации, нарастающая сложность будет реализована с помощью появления новых, более сложных врагов и повышения уровня старых по ходу игры. За каждого босса и победу в боях будут выдаваться определенный награды (опыт, улучшения для персонажа).
2. АНАЛИЗ МЕТОДОВ И СРЕДСТВ РЕШЕНИЯ ПОСТАВЛЕННОЙ ЗАДАЧИ
2.1 Теоретические основы
При разработке игрового приложения была определена необходимость выбора среды разработки, языка программирования, среда работы со звуком и средства создания анимации.
На процессе разработки можно сэкономить значительное количество денежных средств за счёт повторного использования одного игрового движка для создания множества различных игр, поэтому в качестве среды разработки было решено выбрать готовый игровой движок Unity, который на данный момент является одним из самых популярных игровых движков. В Unity присутствуют визуальная среда разработки, межплатформенная поддержка и модульная система компонентов, компонент для создания анимации.
В качестве инструмента для создания анимации был выбран встроенный в Unity компонент Animator. В качестве языка программирование был выбран C#.
Для работы со звуком и звуковыми эффектами были использованы стандартные возможности Unity, для более удобного контроля звуковых эффектов было принято решение написать скрипт AudioManager.
Более подробно движки будут рассмотрены в пункте 4.2
2.2 Аналитический обзор аналогичных приложений
В современной игровой индустрии существует множество мобильных игровых приложений в жанре Platformer, поэтому найти подходящие аналоги не составит труда.
· Magic Rampage
· Требования игры: Android 4.0 и выше
Жанр: Action Platformer
Метки: Экшен, Выбор редакции
Количество установок: 1.000.000+
Оценка: 4.6 на основе 206.337 отзывов
Разработчик: asanteegames
Управление: Сенсорный экран
Игра полностью состоит из уровней, каждый из которых мы должны пройти поочередно. Наш герой может победить любого врага, если его одеть правильно и направить на бой.
· Sword Of Xolan
· Требования игры: Android 2.3 и выше
Жанр: Action Platformer
Метки: Экшен
Количество установок: 1.000.000+
Оценка: 4.5 на основе 55.571 отзыва
Разработчик: Alper Sarэkaya
Управление: Сенсорный экран
Для управления героем используются сенсорные клавиши. Путешествуем по миру игры, уничтожаем противников, собираем монетки и стараемся освободить заключенных людей. На каждом уровне таких заключенных по три штуки, плюс можно найти тайное место с сундуком, в котором спрятано много монеток. Так же на уровнях попадаются ящики и вазы, разбив которые вы сможете найти очки жизней, бутылки с маной, монеты(4).
· Ghoulboy - Dark sword of Goblin-Action platform
· Требования игры: Android 4.1 и выше
Жанр: Action platformer
Метки: Экшен
Количество установок GooglePlay: 50.000+
Оценка: 4.3 на основе 1.165 отзывов
Разработчик: Serkan Bakar
Управление: Сенсорный экран
С точки зрения геймплея Ghoulboy -- Dark Sword of Goblin даже не пытается привнести что-то новое в жанр, но при этом искусно совмещает в себе правильно подобранные и всем нам знакомые элементы. Наш герой умеет прыгать, и использовать два вида оружия - дальнего и ближнего боя, каждого из которых в игре по три разновидности. Из интересных особенностей можно отметить использование копья в качестве платформы, чтобы забраться в недоступные для обычного прыжка места. Помимо сражений, вам также предстоит проходить зачастую очень непростые ловушки, решать головоломки и драться с боссами(5).
Все рассмотренные выше приложения распространяются по лицензии Freemium, что позволяет распространять приложение бесплатно, при этом получая доход с внутри-игровых продаж и рекламы.
3. АНАЛИЗ ТРЕБОВАНИЙ К ИГРОВОМУ ПРИЛОЖЕНИЮ
3.1 Требования к интерфейсу
На основе рассмотренных проектов можно сделать выводы на что стоит обратить внимание про создание игрового приложения.
Давайте распишем основные требования к интерфейсу, которые должны быть реализованы в нашей игре:
1) Вывод графического интерфейса игры на экран.
2) Фон генерируется в зависимости от положения персонажа.
3) Наличие разных игровых сцен.
4) В главном меню должно находиться кнопки начала игры, настроек и выхода из приложения.
5) В меню паузы должны находится кнопки продолжения игры, выхода в меню, рестарта уровня и выхода из приложения
6) На главном игровом экране должны присутствовать сенсорные кнопки взаимодействия с миром, управления персонажем и паузы.
7) На главной игровой сцене должны присутствовать панели состояний игрока, врагов и некоторых объектов.
4. ПРОЕКТИРОВАНИЕ ИГРОВОГО ПРИЛОЖЕНИЯ
4.1 Архитектура игрового приложения
Во всех Android-приложения могут быть простыми и сложными, но строение приложений всегда будет одинаковым. Есть обязательные элементы приложений, а есть опциональные, которые используются по мере необходимости. Android-приложение состоит из нескольких основных компонентов: манифест приложения, набор различных ресурсов и исходный код программы.
Таблица 4.1 - Обязательные и возможные составляющие структуры Android-приложения:
Название |
Описание |
Необходимость |
|
gen |
Файлы, сгенерированные самой Java. Здесь находится такой важный файл как R.java |
Да |
|
AndroidManifest.xml |
Файл манифеста AndroidManifest.xml предоставляет системе основную информацию о программе. Каждое приложение должно иметь свой файл манифеста |
Да |
|
src |
Каталог, в котором содержится исходный код приложения |
Да |
|
assets |
Произвольное собрание каталогов и файлов |
Нет |
|
res |
Каталог, содержащий ресурсы приложения. В данном каталоге могут находиться подпапки drawable, anim, layout, menu, values, xml и raw (см. ниже) |
Да |
Структура файлов и папок в моем игровом приложении показана на рисунке 4.1
Рисунок 4.1 - Пример класса Player.
Chars - содержит файлы относящиеся к персонажам, в т.ч. анимации.
Environment - содержит спрайты фонов и объектов.
Fonts - содержит используемые шрифты.
Plugins - содержит плагин для сенсорного контролера/джойстика.
Scenes - содержит все сцены приложения.
Scripts - содержит все c# скрипты приложения, суммарное количество скриптом используемых в проекте - 31, в таблице 4.2 представлен список скриптов с их кратким описанием.
Sounds - содержит все звуковые эффекты приложения.
Таблица 4.2 - Используемые скрипты.
Название |
Описание |
|
Camera2DFollow |
Содержит функции следования камеры за игроком и ограничение максимальных координат камеры. |
|
Character |
Абстрактный суперкласс, содержит основные функции персонажей и также их характеристики. |
|
GameOverUI |
Контролирует меню «конец игры». |
|
Parallaxing |
Содержит функцию параллактическое отображение |
|
PauseMenu |
Контролирует меню паузы. |
|
Player |
Singleton, содержит дополнительные функции и характеристики персонажа игрока. |
|
ThrowWeapon |
Содержит информацию о снарядах(скорость полета, удаление объекта при ударе, направление снаряда) |
|
Tiling |
Клонирует и позиционирует дополнительные фоны при контакте камеры с пустым краем. |
|
BarScript |
Отвечает за отображение статусов персонажа и врагов. |
|
Stat |
Отвечает за характеристики всех персонажей. |
|
AttackBehaviour |
Конечный автомат, отвечает за реакцию всех персонажей на определенные действия и связывает их с анимацией.Attack - атака и нанесение урона врагу.Damage - получение урона.Death - смерть персонажа.Jump - прыжок, нахождение в воздухе.Land - приземление.Slide - скольжение. |
|
DamageBehaviour |
||
DeathBehaviour |
||
JumpBehaviour |
||
LandBehaviour |
||
SlideBehaviour |
||
IgnoreCollision |
Содержит функция для игнорирования столкновений между двумя объектами. |
|
IUseable |
Интерфейс для всех используемых предметов. |
|
JumpOff |
Содержит функцию для спрыгивания с платформ. |
|
Название |
Описание |
|
Range1EnemyCollider |
Содержит функцию для удаления снарядов через определенное время после попадания по цели. |
|
Enemy |
Содержит дополнительные функции и характеристики персонажей врага. |
|
EnemySight |
Контролирует область видимости врага. |
|
IEnemyState |
Интерфейс, отвечает за поведение врагов. |
|
MeleeState |
Отвечает за поведение врагов в ближнем бою. |
|
PatrolState |
Поведение врагов во время патрулирования. |
|
IdleState |
Поведение врагов во время простоя. |
|
RangedState |
Поведение врагов в дальнем бою. |
|
AudioManager |
Отвечает за все звуки в игре. |
|
GameManager |
Отвечает за выпадение предметов. |
|
MenuManager |
Отвечает за экран главного меню и экран загрузки. |
|
SettingMenu |
Меню настроек. |
Далее рассмотрим примеры некоторых скриптов.
Класс Player реализуется с использованием паттерна singleton и абстрактного суперкласса Character часть кода показана на рисунке 4.2.
Рисунок 4.2 - Пример класса Player.
Класс Enemy использует абстрактный суперкласс Character и паттерн state machine. Пример кода показан на рисунке 4.3.
Рис 4.3. Пример класса Enemy.
Пример абстрактного суперкласса Character показан на рисунке 4.4.
Рисунок 4.4 - Пример класса Character.
Для реализации поведения врагов и используемых предметов используется паттерн state machine (конечный автомат) обобщенная UML диаграмма которого представлена на рисунке 4.5.
Рисунок 4.5 - UML State Machine для поведения врагов.
4.2 Выбор инструментальных средств разработки
При выборе технологий для проекта были рассмотрены три популярных движка и выбран наиболее подходящий из них.
При анализе движков были выбраны и более подробно проанализированы 3 движка: Unity, Unreal Engine, Cocos2D
1)Unity
Один из самых популярных игровых движков.
*Входной порог
Очень низкий. Имеется подробная документация и очень большое число руководств на любом языке. Огромное сообщество.
*Инструментарий
Большой набор инструментов, имеется магазин с дополнительным инструментарием, модулями и т.д..
*Удобство
Один из главных плюсов движка. Отличные инструменты ускоряют разработку в разы.
*Языки
C#. Наличие JavaScript
*Гибкость
Очень гибкие инструменты и архитектура движка в целом. Однако, движок имеет закрытый код поэтому во многих вещах остается недоступен для изменения. Есть свобода для оптимизаций, но тонкие оптимизации не получится сделать в следствие своей закрытости.
*Портируемость
Приложения легко портируется на большинство платформ
*Производительность
В целом движок очень хорошо оптимизирован, и на нем при должном усердии можно сделать производительную игру. Однако, C# довольно сильно ограничивает. Сложные диалоги, длинные списки без оптимизаций довольно сильно нагружают систему.
2)Unreal Engine
Профессиональный игровой движок, является бесплатным если суммарный доход за квартал не превышает 3000$.
*Входной порог
Довольно высок.
*Инструментарий
Огромный набор инструментов, от редактора частиц до анимирования персонажей
*Удобство
Для опытных пользователей является крайне удобным. Однако в руках новичков результат получается не очень качественным и долгим.
*Сложность разработки игровой логики
в Unreal игровая логика пишется либо на С++ с использованием Blueprints. По словам разработчиков, все работает так: С++ программисты пишут ядро и «блоки» для Blueprint'ов, а дизайнеры из этих блоков составляют игровые механики.
*Гибкость
Архитектура и С++ дают безграничные возможности в разработке, однако уклон идет в трехмерные проекты. Двумерные игры для создателей никогда не были в приоритете.
*Портируемость
Приложения на Unreal Engine можно портировать на все мобильные платформы, однако ходят слухи о многочисленных проблемах, таких как размер приложения.
*Производительность
Одни из лучших инженеров игровой индустрии разработали движок, имеются очень широкие возможности для оптимизации.
3)Cocos2D
Полностью бесплатный и с открытым исходным кодом движок. Есть all-in-one редактор, документация и куча примеров. Игровая логика пишется на С++, либо на JavaScript. Очень много проектов на нем выпущено, отличительная особенность этих проектов -- хорошая оптимизация. Однако при более детальном рассмотрении редактор оказывается не таким удобным как кажется на первый взгляд, находится куча недочетов в исходниках и приходится много чего доделывать
*Входной порог
Довольно высок, необходимо знание С++ и довольно хорошее понимание как делаются игры.
*Инструментарий
Довольно обширный набор инструментов.
*Удобство
Очень много скопировано из других технологий. Множество консольных утилит.
*Сложность разработки игровой логики
В основном используются С++, но также есть возможность использовать JavaScript. Однако использовать их вместе несколько затруднительно.
*Гибкость
Открытый исходный код и С++ позволяют сделать все что угодно
*Портируемость
Все мобильные платформы.
*Производительность
Из-за своей простоты и гибкости позволяет достичь очень хороших результатов.
В результате выберем Unity т.к он имеет огромную базу документации, является бесплатным и наиболее удобным движком для разработки мобильного игрового приложения из рассмотренных.
4.3 Проектирование структур данных и алгоритмов
Для генерации фонов используется скрипт Tiling - который дублирует фон при соприкосновении камеры с пустым краем, для придания эффекта объемности фонов при движении используется параллактическое отображение (скрипт Parallaxing). Блок-схема показана на рисунке 4.6
Рисунок 4.6 - Блок-схема скрипта Tiling.
Большая часть движений и действий персонажей реализуется в скриптах Character, Enemy и Player, все действия имею свою анимацию и при активации устанавливают триггеры для аниматора, который выполняет переходы между анимацией. Уровень Ground контроллера Player показан на рисунках 4.7-4.9. Уровень Air показан на рисунке 4.10. Также на рисунке 4.11 показана блок-схема скрипта передвижения персонажа, большая часть алгоритмов действий реализуется похожим образом.
Рисунок 4.7 - Уровень Ground - Player.
Рисунок 4.8 - ActionStates группа - Ground - Player.
Рисунок 4.9 - IdleStates группа - Ground - Player.
Рисунок 4.10 - Уровень Air - Player.
Рисунок 4.11 - Блок схема горизонтального передвижения персонажа.
4.4 Проектирование пользовательского интерфейса
Так как игровое приложение нацелено на мобильные устройства, то следует использовать сенсорные кнопки для всех действий.
Для движения, прыжков и скольжения используется экранный стик-контроллер, показанный на рисунке 4.12 с горизонтальной и вертикальной осями.
Рисунок 4.12 - стик-контроллер.
Для взаимодействия с миром и атак используются экранные клавиши X, A, B. X - дистанционная атака, B - атака в ближнем бою, A - спрыгивание с платформ. Данные клавиши представлены на рисунке 4.13.
Рисунок 4.13 - Клавиши взаимодействия.
Игра состоит из двух сцен:
1) Главное меню.
2) Уровень.
Сначала разберем меню, присутствующие на сцене уровня. Сенсорная кнопка “Pause” служит для вызова меню паузы, рисунок 4.14. На рисунке 4.15 показано меню паузы и на рисунке 4.16 меню «конец игры» когда у персонажа заканчиваются все жизни, все клавиши анимированные и имеют звуковой эффект при нажатии.
Рисунок 4.14 - Клавиши вызова меню паузы.
Рисунок 4.15 - Меню паузы.
Resume - снимает паузу и возобновляет игру.
Restart - полностью перезапускает текущую сцену.
To Menu - выполняет выход в главное меню.
Quit - выполняет выход из приложения.
Рисунок 4.16 - Меню «конец игры».
Retry - полностью перезапускает текущую сцену.
Quit to menu - выполняет выход в главное меню.
Главное меню представлено на рисунке 4.17 и содержит анимированного персонажа, кнопки начала игры и выхода из приложения.
Рисунок 4.17 - Главное меню.
Play - запускает асинхронную загрузку уровня и переходит на экран загрузки представленный на рисунке 4.18.
Settings - переход на экран меню настроек - рисунок 4.19.
Quit - выполняет выход из приложения.
Рисунок 4.18 - Экран загрузки.
Рисунок 4.19 - Экран настроек.
Так как необходимо отображать здоровье, ману, количество оставшихся жизней персонажа, а также количество убитых врагов сделаны отдельные UI элементы значение и заполнения которых меняется скриптами. Индикатор здоровья врагов выполнен отдельно и появляется над каждым врагом, если его здоровье не является полным. Элементы показаны на рисунках 4.20-4.22.
Рисунок 4.20 - Панели здоровья, маны, а также индикатор оставшихся жизней персонажа.
Рисунок 4.21 - Индикатор количества убитых врагов.
Рисунок 4.22 - Индикатор здоровья врага.
За управление меню и индикаторов интерфейса отвечают скрипты GameManager, PauseMenu, BarScript, Stat, MenuManager.
5. ТЕСТИРОВАНИЕ ИГРОВОГО ПРИЛОЖЕНИЯ
5.1 Обоснование методики тестирования
Так как игровое приложение содержит множество файлов и функций было принято решение применить методику тестирования, состоящую из следующих этапов:
1) Модульное тестирование.
Приложение включает в себя множество классов от работоспособность каждого из которых важна для правильного функционирования приложения в целом, исходя из этого было принято решение провести данный вид тестирования разбив приложение на небольшие модули проверив их функциональность.
2) Функциональное тестирование.
Тестирование всего приложения целиком в целях проверки работоспособности и реализуемости функциональных требований, необходимо так как большинство модулей приложения взаимосвязаны и зависимы друг от друга.
3) Тестирование производительности.
Тестирование, которое проводится с целью определения быстродействия приложение. Необходимость тестирования производительности обусловлена тем что нужно добиться приемлемого времени отклика приложения на разные устройства.
4) Тестирование инсталляции.
Проводится путем установки приложения на различные устройства и проверки корректности работы на них. Необходимость обусловлена тем, что приложение является мобильным и должно быть работоспособно на различных мобильных устройствах пользователей.
5.2 Результаты тестирования
1) Результаты модульного тестирование.
Тестирование выполнялось по завершению разработки каждого скрипта, в результате тестов все обнаруженные ошибки были исправлены. Работоспособность всех модулей корректна.
2) Результаты функционального тестирования.
Каждая функция тестировалась согласно функциональным требованиям, тестирование приведено в таблице 5.1.
Таблица 5.1 - функциональное тестирование.
Функция |
Результат |
|
Меню: кнопка Play |
Переносит на экран загрузки уровня. |
|
Меню: кнопка Settings |
Открывает меню настроек. |
|
Меню: кнопка Quit |
Выполняет выход из приложения. |
|
Меню настроек: ползунок Volume |
Меняет общую громкость звука в приложение. |
|
Меню настроек: кнопка Back |
Возвращает назад в главное меню. |
|
Игровая сцена: кнопки X, A, B |
При нажатии на кнопку проигрывается анимация, звук и эффект действия. |
|
Игровая сцена: стик-контроллер |
Передвижение, прыжок и скольжение персонажа выполняется верно, в зависимости от положение осей контроллера. |
|
Игровая сцена: кнопка Pause |
Вызывает меню паузы, приостанавливает игру. |
|
Меню паузы: кнопка Resume |
Возобновляет игру. |
|
Меню паузы: кнопка Restart |
Перезапускает уровень. |
|
Меню паузы: To Menu |
Возвращает в главное меню. |
|
Меню паузы: Quit |
Выполняет выход из приложения. |
|
Игровая сцена: элемент UI Count |
При убийстве врагов числовое значение увеличивается на верную величину. |
|
Функция |
Результат |
|
Игровая сцена: элемент UI HealthBar |
При получение урона уровень заполнения и числовое значение снижаются, при подборе сферы здоровья уровень заполнения и числовое значение корректно увеличиваются. |
|
Игровая сцена: элемент UI ManaBar |
При выполнение действий требующих ману уровень заполнение и числовое значение снижаются, при подборе сферы маны уровень заполнения и числовое значение корректно увеличиваются. |
|
Игровая сцена: элемент UI LivesLeft |
При опустошение шкалы здоровья снижается на 1. |
|
Игровая сцена: элемент UI EnemyHealthBar |
Появляется и при получение врагами урона, уровень заполнения и числовое значение корректно снижаются. |
|
Игровая сцена: взаимодействие с противниками. |
Корректно реагируют на попадание игрока в поле зрения, переходят в режим патрулирования при полной потере игрока из обзора. |
|
Игровая сцена: выпадение сфер. |
При смерти врага в случайном порядке выпадает либо сфера здоровья, либо сфера маны. При подборе сфер корректно пополняется нужная характеристика. |
|
Меню «конец игры»: кнопка RETRY |
Перезапускает уровень. |
|
Меню «конец игры»: кнопка QUIT TO MENU |
Возвращает на главное меню. |
3) Результаты тестирование производительности.
Тестирование проводилось на устройстве Meizu M3 Note с android прошивкой версии. Результаты производительности оценивались инструментом Unity Profiler на рисунке 5.1 представлены результаты.
Рисунок 5.2 - тестирование производительности.
Приложение тестировалось на настройках графики показанных на рисунке 5.3.
Рисунок 5.3 - настройки графики.
В ходе всего тестирования приложение работало корректно, наибольшую нагрузку вызывали процессы прорисовки текстур и вертикальная синхронизация. Результаты тестирования показывают, что приложение полностью работоспособно, стабильно и выполняет требование по времени отклика.
4) Результаты тестирования инсталляцией
Приложение было успешно установлено как на личный мобильный телефон, так и на мобильные устройства нескольких друзей. Никаких проблем с производительностью, отображением, установкой либо удалением приложения замечено не было.
6. РАЗРАБОТКА ДОКУМЕНТАЦИИ
Общие данные:
Программа представляет из себя игровое приложение в жанре платформер, фэнтези стилистике предназначенное для мобильных устройств на базе Android версии 4.1 и выше.
Программа, предназначенная для развлекательных целей. Целевая аудитория - люди, желающие занять себя прохождением легко осваиваемого игрового приложения.
Игра содержит короткие уровни, по завершению которых прогресс сохраняется, в следствие чего проходить данную игру можно постепенно, не тратя много времени на игровую сессию.
Описание игровых сцен:
Интерфейс игровой сцены был разработан и упрощён с учетом сенсорных экранов мобильных устройств.
В главном меню находятся кнопки начала игры, вызова меню настроек и выхода из игры. При начале игры на экране начинают отображаться основные элементы управления, панели характеристик и счетчик врагов, также присутствует кнопка паузы, для перезапуска уровня, возврата в главное меню, либо выхода из приложения.
Описание игровых правил:
Игровые правила, на прохождение уровня дается 3 попытки, при снижении количества попыток до 0 уровень игры полностью перезапускается, падения характеристики здоровья до 0 воскрешает персонажа спустя 3 секунды, но убавляет число попыток на 1. Шкала здоровья снижается от получения урона, шкала маны снижается при использование специальных атак. При смерти с врагов в случайном порядке падает либо сфера, пополняющая шкалу здоровья, либо шкалу маны. Необходимо пройти уровень за 3 попытки, при переходе на следующий уровень число попыток полностью восстанавливается.
Пояснения по техническим вопросам:
Громкость звука в игре можно регулировать как через специальную функцию в меню настроек, так и аппаратными клавишами устройства.
Приложение можно в любой момент выключить как через меню паузы, так и через операционную систему.
Установка возможно на любой из накопителей устройства и требует порядка 40 мегабайт свободного места.
Приложение полностью бесплатно, при запуске приложение присутствует небольшая реклама - требование использования бесплатного движка Unity.
ЗАКЛЮЧЕНИЕ
В процессе работы над ВКР была произведена аналитика жанров, сеттингов и аудитории мобильных приложение, в результате чего было решено сделать приложение в жанре action platformer 2D с фэнтези сеттингом. Проведен анализ аналогичных приложений, выявлены некоторые их особенности, которые в последствие помогли с формированием стилистики и игровых механик моего игрового приложения. Для реализации продукта был выбран готовый движок Unity3D. Цель работы - создание игрового мобильного приложения на базе ОС Android - была достигнута.
В ходе работы были получены практические и теоретические знание о процессе разработки небольших мобильных игровых приложений. Были изучены возможности и особенности движка Unity3D и язык программирования C#.
Разработанный продукт успешно прошел все этапы тестирования, все найденные в результате баги были исправлены.
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
1. Game Perimeter [Электронный ресурс]: Юрий Шпак / Мировой рынок компьютерных игр в 2017 году - тенденции и перспективы - Режим доступа:
2. https://gameperimeter.com/materials/analytics/the-world-market-of-computer-games-in-2017
3. habr [Электронный ресурс]: Вячеслав Уточкин / Жанры и сеттинги мобильных игр -- статистика на апрель 2017г. - Режим доступа: https://habr.com/company/miip/blog/327164/
4. GameAnalytics [Электронный ресурс]: - Режим доступа:
5. https://public-production.gameanalytics.com/assets/GameAnalytics%20Benchmarks%20Report.pdf
6. МегаОбзор [Электронный ресурс]: Влад Волошин - Режим доступа:
7. https://megaobzor.com/obzor-Magic-Rampage-platformer-dlya-dotoshnyh.html
8. https://megaobzor.com/obzor-Sword-Of-Xolan-opasnyy-pikselnyy-geroy.html
9. blast games [Электронный ресурс]: jERS / Ghoulboy -- Dark Sword of Goblin -- ретро-платформер в стиле Castlevania - Режим доступа:
10. http://blast-games.ru/obzori/ghoulboy-dark-sword-of-goblin-obzor/
11. Википедия [Электронный ресурс]:
12. Платформер - Режим доступа:
13. https://ru.wikipedia.org/wiki/Платформер
14. habr [Электронный ресурс]: Зенкович Андрей / Зачем в 2017 году писать свой движок для мобильных игр? - Режим доступа: https://habr.com/post/338214/
15. {InScopeStudios.com} [Электронный ресурс]: - Режим доступа:
16. https://inscopestudios.com/
17. BRACKEYS [Электронный ресурс]: - Режим доступа:
18. http://brackeys.com/
19. OpenGameArt.Org [Электронный ресурс]: - Режим доступа:
20. https://opengameart.org/
21. UNITY Asset Store [Электронный ресурс]: - Режим доступа:
22. https://assetstore.unity.com/
23. Itch.io [Электронный ресурс]: - Режим доступа:
24. https://itch.io/
25. Unity | Documentation [Электронный ресурс]: - Режим доступа:
26. https://docs.unity3d.com/Manual/index.html
ПРИЛОЖЕНИЕ 1
(обязательное)
Скрипты проекта
1) BarScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BarScript : MonoBehaviour
{
private float fillAmount;
[SerializeField]
private Image content;
[SerializeField]
private float lerpSpeed;
[SerializeField]
private Text valueText;
public float MaxValue { get; set; }
public float Value
{
set
{
string[] tmp = valueText.text.Split(':');
valueText.text = tmp[0] + ": " + value;
fillAmount = Map(value, 0, MaxValue, 0, 1);
}
}
void Update ()
{
HandleBar();
}
private void HandleBar()
{
if (fillAmount != content.fillAmount)
{
content.fillAmount = Mathf.Lerp(content.fillAmount,fillAmount,Time.deltaTime*lerpSpeed);
}
}
private float Map(float value, float inMin, float inMax, float outMin, float outMax)
{
return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}
}
2) Stat.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[Serializable]
public class Stat
{
[SerializeField]
private BarScript bar;
[SerializeField]
private float maxVal;
[SerializeField]
private float currentVal;
public float CurrentVal
{
get
{
return currentVal;
}
set
{
this.currentVal = Mathf.Clamp(value, 0, MaxVal);
bar.Value = currentVal;
}
}
public float MaxVal
{
get
{
return maxVal;
}
set
{
this.maxVal = value;
bar.MaxValue = maxVal;
}
}
public void Initialize()
{
this.MaxVal = maxVal;
this.CurrentVal = currentVal;
}
}
3) AttackBehaviour.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AttackBehaviour : StateMachineBehaviour {
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.GetComponent<Character>().Attack = true;
animator.SetFloat("Speed", 0);
if (animator.tag =="Player")
{
if (Player.Instance.OnGround)
{
Player.Instance.rb2d.velocity = Vector2.zero;
}
}
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.GetComponent<Character>().Attack = false;
animator.GetComponent<Character>().SwordCollider.enabled = false;
animator.ResetTrigger("attack");
animator.ResetTrigger("throw");
}
}
4) DamageBehaviour.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DamageBehaviour : StateMachineBehaviour {
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.GetComponent<Character>().TakingDamage = true;
animator.GetComponent<Rigidbody2D>().velocity = Vector2.zero;
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.GetComponent<Character>().TakingDamage = false;
}
}
5) DeathBehaviour.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DeathBehaviour : StateMachineBehaviour {
private float respawnTime = 3;
private float DeathTimer;
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
DeathTimer = 0;
}
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
DeathTimer += Time.deltaTime;
if (DeathTimer>=respawnTime)
{
DeathTimer = 0;
animator.GetComponent<Character>().Death();
}
}
}
6) JumpBehaviour.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JumpBehaviour : StateMachineBehaviour {
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Player.Instance.Jump = true;
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Player.Instance.Jump = false;
}
}
7) JumpBehaviour.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JumpBehaviour : StateMachineBehaviour {
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Player.Instance.Jump = true;
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Player.Instance.Jump = false;
}
}
8) LandBehaviour.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LandBehaviour : StateMachineBehaviour {
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
if (Player.Instance.OnGround)
{
animator.SetBool("land", false);
animator.ResetTrigger("jump");
}
}
}
9) SlideBehaviour.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SlideBehaviour : StateMachineBehaviour
{
private Vector2 slideOffset = new Vector2(-0.16f,-1.0f);
private Vector2 slideSize = new Vector2(1.9f, 1.52f);
private Vector2 size;
private Vector2 offset;
private BoxCollider2D PlayerCollider2D;
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Player.Instance.Slide = true;
if (PlayerCollider2D == null)
{
PlayerCollider2D = Player.Instance.GetComponent<BoxCollider2D>();
size = PlayerCollider2D.size;
offset = PlayerCollider2D.offset;
}
PlayerCollider2D.size = slideSize;
PlayerCollider2D.offset = slideOffset;
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
Player.Instance.Slide = false;
animator.ResetTrigger("slide");
PlayerCollider2D.size = size;
PlayerCollider2D.offset = offset;
}
}
10) IgnoreCollision.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IgnoreCollision : MonoBehaviour {
[SerializeField]
private Collider2D other;
void Awake ()
{
Physics2D.IgnoreCollision(GetComponent<Collider2D>(), other, true);
}
}
11) IUseable.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IUseable
{
void Use();
}
12) JumpOff.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JumpOff : MonoBehaviour, IUseable
{
[SerializeField]
private Collider2D platformCollider;
public void Use()
{
if (Player.Instance.OnPlatform)
{
UsePlatform(false);
}
else
{
UsePlatform(true);
Physics2D.IgnoreCollision(Player.Instance.GetComponent<Collider2D>(), platformCollider, true);
}
}
private void UsePlatform(bool onPlatform)
{
Player.Instance.OnPlatform = onPlatform;
}
private void OnTriggerExit2D(Collider2D other)
{
if(other.tag == "Player")
{
UsePlatform(false);
Physics2D.IgnoreCollision(Player.Instance.GetComponent<Collider2D>(), platformCollider, false);
}
}
}
13) Range1EnemyCollider.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Range1EnemyCollider : MonoBehaviour
{
[SerializeField]
private string targetTag;
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == targetTag)
{
Destroy(gameObject, .15f);
}
}
}
14) Enemy.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : Character
{
private IEnemyState currentState;
public GameObject Target { get; set; }
[SerializeField]
private float MeleeRange;
[SerializeField]
private float ThrowRange;
private int HPorMP;
private bool Dropped = false;
[SerializeField]
private Transform leftEdge;
[SerializeField]
private Transform rightEdge;
private Canvas healthCav;
public bool InMeleeRange
{
get
{
if (Target!=null)
{
return Vector2.Distance(transform.position, Target.transform.position) <= MeleeRange;
}
return false;
}
}
public bool InThrowRange
{
get
{
if (Target != null)
{
return Vector2.Distance(transform.position, Target.transform.position) <= ThrowRange;
}
return false;
}
}
public override bool IsDead
{
get
{
return HPStat.CurrentVal <= 0;
}
}
public override void Start ()
{
base.Start();
Player.Instance.Dead += new DeadEventHandler(RemoveTarget);
ChangeState(new IdleState());
healthCav = transform.GetComponentInChildren<Canvas>();
}
void Update ()
{
if (!IsDead)
{
if (!TakingDamage)
{
currentState.Execute();
}
LookAtTarget();
}
HPorMP = UnityEngine.Random.Range(0, 2);
}
public void RemoveTarget()
{
Target = null;
ChangeState(new PatrolState());
}
private void LookAtTarget()
{
if (Target != null)
{
float xDir = Target.transform.position.x - transform.position.x;
if (xDir < 0 && fRight || xDir > 0 && !fRight)
{
ChangeDirection();
}
}
}
private void RunSound(int i)
{
if (i == 0)
{
AudioManager.instance.PlaySound("enemyStep0");
}
if (i == 1)
{
AudioManager.instance.PlaySound("enemyStep1");
}
if (i == 2)
{
AudioManager.instance.PlaySound("enemyStep2");
}
}
private void AttackSound()
{
AudioManager.instance.PlaySound("EnemySword");
}
public void ChangeState(IEnemyState newState)
{
if (currentState != null)
{
currentState.Exit();
}
currentState = newState;
currentState.Enter(this);
}
public void Move()
{
if (!Attack)
{
if((GetDirection().x > 0 && transform.position.x < rightEdge.position.x) || (GetDirection().x < 0 && transform.position.x > leftEdge.position.x))
{
mAnimator.SetFloat("Speed", 1);
transform.Translate(GetDirection() * (mSpeed * Time.deltaTime));
}
else if (currentState is PatrolState || currentState is IdleState)
{
ChangeDirection();
}
else if (currentState is RangedState)
{
Target = null;
ChangeState(new IdleState());
}
}
}
public Vector2 GetDirection()
{
return fRight ? Vector2.right : Vector2.left;
}
public override void OnTriggerEnter2D(Collider2D other)
{
base.OnTriggerEnter2D(other);
currentState.OnTriggerEnter(other);
}
public override IEnumerator TakeDamage()
{
if(!healthCav.isActiveAndEnabled)
{
healthCav.enabled = true;
}
HPStat.CurrentVal -= 10;
if (!IsDead)
{
AudioManager.instance.PlaySound("enemyhit");
mAnimator.SetTrigger("damage");
}
else
{
if (HPorMP == 1 && Dropped == false)
{
GameObject hp =(GameObject)Instantiate(GameManager.Instance.HPPrefab, new Vector3(transform.position.x, transform.position.y + 2), Quaternion.identity);
Physics2D.IgnoreCollision(hp.GetComponent<Collider2D>(), GetComponent<Collider2D>());
Dropped = true;
}
else if (HPorMP == 0 && Dropped == false)
{
GameObject mp = (GameObject)Instantiate(GameManager.Instance.MPPrefab, new Vector3(transform.position.x, transform.position.y + 2), Quaternion.identity);
Physics2D.IgnoreCollision(mp.GetComponent<Collider2D>(), GetComponent<Collider2D>());
Dropped = true;
}
AudioManager.instance.PlaySound("enemydead");
mAnimator.SetTrigger("die");
yield return null;
}
}
public override void Death()
{
Destroy(gameObject, 3);
mAnimator.ResetTrigger("die");
healthCav.enabled = false;
}
public override void ChangeDirection()
{
Transform tmp = transform.Find("EnemyHPCanvas").transform;
Vector3 pos = tmp.position;
tmp.SetParent(null);
base.ChangeDirection();
tmp.SetParent(transform);
tmp.position = pos;
}
public void CountOnDeath()
{
GameManager.Instance.EnemyKilled++;
}
}
15) EnemySight.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemySight : MonoBehaviour
{
[SerializeField]
private Enemy enemy;
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
enemy.Target = other.gameObject;
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.tag == "Player")
{
enemy.Target = null;
}
}
}
16) IdleState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IdleState : IEnemyState
{
private Enemy enemy;
private float idleTimer;
private float idleDuration;
public void Enter(Enemy enemy)
{
idleDuration = UnityEngine.Random.Range(3, 6);
this.enemy = enemy;
}
public void Execute()
{
Idle();
if (enemy.Target != null)
{
enemy.ChangeState(new PatrolState());
}
}
public void Exit()
{
}
public void OnTriggerEnter(Collider2D other)
{
if (other.tag == "PlayerThrow" || other.tag == "SwordDMG")
{
enemy.Target = Player.Instance.gameObject;
}
}
private void Idle()
{
enemy.mAnimator.SetFloat("Speed", 0);
idleTimer += Time.deltaTime;
if (idleTimer >= idleDuration)
{
enemy.ChangeState(new PatrolState());
}
}
}
17) IEnemyState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IEnemyState
{
void Execute();
void Enter(Enemy enemy);
void Exit();
void OnTriggerEnter(Collider2D other);
}
18) MeleeState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeleeState : IEnemyState
{
private Enemy enemy;
public float attackTimer;
public float attackCD=2;
public bool canAttack = true;
public void Enter(Enemy enemy)
{
this.enemy = enemy;
}
public void Execute()
{
Attack();
if (enemy.InThrowRange && !enemy.InMeleeRange)
{
enemy.ChangeState(new RangedState());
}
else if (enemy.Target == null)
{
enemy.ChangeState(new IdleState());
}
}
public void Exit()
{
}
public void OnTriggerEnter(Collider2D other)
{
}
private void Attack()
{
attackTimer += Time.deltaTime;
if (attackTimer >= attackCD)
{
canAttack = true;
attackTimer = 0;
}
if (canAttack)
{
canAttack = false;
enemy.mAnimator.SetTrigger("attack");
}
}
}
19) PatrolState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PatrolState : IEnemyState
{
private Enemy enemy;
private float patrolTimer;
private float patrolDuration;
public void Enter(Enemy enemy)
{
patrolDuration = UnityEngine.Random.Range(6, 12);
this.enemy = enemy;
}
public void Execute()
{
Patrol();
enemy.Move();
if (enemy.Target != null && enemy.InThrowRange)
{
enemy.ChangeState(new RangedState());
}
if (enemy.Target != null && enemy.InMeleeRange)
{
enemy.ChangeState(new MeleeState());
}
}
public void Exit()
{
}
public void OnTriggerEnter(Collider2D other)
{
if (other.tag == "PlayerThrow" || other.tag == "SwordDMG")
{
enemy.Target = Player.Instance.gameObject;
}
}
private void Patrol()
{
patrolTimer += Time.deltaTime;
if (patrolTimer >= patrolDuration)
{
enemy.ChangeState(new IdleState());
}
}
}
20) RangedState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RangedState : IEnemyState
{
private Enemy enemy;
public float throwTimer;
public float throwCD = 10;
public bool canThrow = true;
public void Enter(Enemy enemy)
{
throwCD = UnityEngine.Random.Range(8, 12);
this.enemy = enemy;
}
public void Execute()
{
ThrowWeapon();
if (enemy.InMeleeRange)
{
enemy.ChangeState(new MeleeState());
}
else if (enemy.Target != null)
{
enemy.Move();
}
else
{
enemy.ChangeState(new IdleState());
}
}
public void Exit()
{
}
public void OnTriggerEnter(Collider2D other)
{
}
private void ThrowWeapon()
{
throwTimer += Time.deltaTime;
if(throwTimer >= throwCD)
{
canThrow = true;
throwTimer = 0;
}
if (canThrow)
{
canThrow = false;
enemy.mAnimator.SetTrigger("throw");
}
}
}
21) AudioManager.cs
using UnityEngine;
[System.Serializable]
public class Sound
{
public string name;
public AudioClip clip;
private AudioSource source;
[Range(0f,1f)]
public float volume = 0.7f;
[Range(0.5f,1.5f)]
public float pitch = 1f;
public bool loop = false;
public void SetSource(AudioSource _source)
{
source = _source;
source.clip = clip;
source.loop = loop;
}
public void Play()
{
source.volume = volume;
source.pitch = pitch;
source.Play();
}
public void Stop()
{
source.Stop();
}
}
public class AudioManager : MonoBehaviour
{
public static AudioManager instance;
[SerializeField]
Sound[] sounds;
void Awake()
{
if (instance != null)
{
if (instance != this)
{
Destroy(this.gameObject);
}
}
else
{
instance = this;
DontDestroyOnLoad(this);
}
}
void Start()
{
for (int i = 0; i<sounds.Length; i++)
{
GameObject _go = new GameObject("Sound_" + i + "_" + sounds[i].name);
_go.transform.SetParent(this.transform);
sounds[i].SetSource (_go.AddComponent<AudioSource>());
}
PlaySound("Music");
}
public void PlaySound(string _name)
{
for (int i = 0; i < sounds.Length; i++)
{
if (sounds[i].name == _name)
{
sounds[i].Play();
return;
}
}
Debug.LogWarning("AudioManager: Sound not found in list: "+ _name);
}
public void StopSound(string _name)
{
for (int i = 0; i < sounds.Length; i++)
{
if (sounds[i].name == _name)
{
sounds[i].Stop();
return;
}
}
Debug.LogWarning("AudioManager: Sound not found in list: " + _name);
}
}
22) GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour {
private static GameManager instance;
[SerializeField]
private GameObject hPPrefab;
[SerializeField]
private GameObject mPPrefab;
[SerializeField]
private Text EnemyKilledTXT;
private int enemyKilled;
[SerializeField]
private Text LivesLeftTXT;
private int livesLeft=3;
public int LivesLeft
{
get
{
return livesLeft;
}
set
{
LivesLeftTXT.text = value.ToString();
livesLeft = value;
}
}
public static GameManager Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<GameManager>();
}
return instance;
}
}
public GameObject HPPrefab
{
get
{
return hPPrefab;
}
}
public GameObject MPPrefab
{
get
{
return mPPrefab;
}
}
public int EnemyKilled
{
get
{
return enemyKilled;
}
set
{
EnemyKilledTXT.text = value.ToString();
this.enemyKilled = value;
}
}
}
23) MenuManager.cs
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
using UnityEngine.UI;
public class MenuManager : MonoBehaviour
{
public GameObject loadingScreen;
public Slider slider;
public GameObject settingMenu;
public void Start()
{
AudioManager.instance.PlaySound("Music");
}
public void StartGame()
{
AudioManager.instance.PlaySound("buttonPress");
StartCoroutine(LoadAsynchronously());
}
IEnumerator LoadAsynchronously()
{
AsyncOperation operation = SceneManager.LoadSceneAsync(SceneManager.GetActiveScene().buildIndex + 1);
loadingScreen.SetActive(true);
while (!operation.isDone)
{
float progress = Mathf.Clamp01(operation.progress / .9f);
slider.value = progress;
yield return null;
}
}
public void QuitGame()
{
AudioManager.instance.PlaySound("buttonPress");
Application.Quit();
}
public void toSettings()
{
settingMenu.SetActive(true);
}
public void BacktoMenu()
{
settingMenu.SetActive(false);
}
}
24) SettingMenu.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
public class SettingMenu : MonoBehaviour
{
public void SetVolumeMixer(float slidevolume)
{
AudioManager.instance.PlaySound("buttonPress");
AudioListener.volume = slidevolume;
}
}
25) Camera2DFollow.cs
using System;
using UnityEngine;
namespace UnityStandardAssets._2D
{
public class Camera2DFollow : MonoBehaviour
{
public Transform target;
public float damping = 1;
public float lookAheadFactor = 3;
public float lookAheadReturnSpeed = 0.5f;
public float lookAheadMoveThreshold = 0.1f;
private float m_OffsetZ;
private Vector3 m_LastTargetPosition;
private Vector3 m_CurrentVelocity;
private Vector3 m_LookAheadPos;
public bool border;
public float minX;
public float maxX;
public float minY;
public float maxY;
private void Start()
{
m_LastTargetPosition = target.position;
m_OffsetZ = (transform.position - target.position).z;
transform.parent = null;
}
private void Update()
{
float xMoveDelta = (target.position - m_LastTargetPosition).x;
bool updateLookAheadTarget = Mathf.Abs(xMoveDelta) > lookAheadMoveThreshold;
if (updateLookAheadTarget)
{
m_LookAheadPos = lookAheadFactor * Vector3.right * Mathf.Sign(xMoveDelta);
}
else
{
m_LookAheadPos = Vector3.MoveTowards(m_LookAheadPos, Vector3.zero, Time.deltaTime * lookAheadReturnSpeed);
}
Vector3 aheadTargetPos = target.position + m_LookAheadPos + Vector3.forward * m_OffsetZ;
Vector3 newPos = Vector3.SmoothDamp(transform.position, aheadTargetPos, ref m_CurrentVelocity, damping);
transform.position = newPos;
Подобные документы
Преимущества операционной системы Android. Проектирование интерфейса приложений. Визуальные редакторы и средства кроссплатформенной разработки. Оптимизация игрового процесса, выбор фреймворка и библиотек. Классификация и характеристика игр по жанрам.
дипломная работа [2,6 M], добавлен 10.07.2017Обзор мобильной ОС Android. Выбор инструментов и технологий. Проектирование прототипа графического интерфейса. Характеристика и описание пользовательского интерфейса. Проектирование и разработка базы данных. Определение списка необходимых разрешений.
курсовая работа [376,6 K], добавлен 13.09.2017Архитектура и история создания операционной системы Android. Язык программирования Java. Выбор средства для реализации Android приложения. Программная реализация Android приложения. Проведение тестирования разработанного программного обеспечения.
курсовая работа [167,8 K], добавлен 18.01.2017Структура Android-приложений. Особенности игрового движка. Алгоритмизация и программирование. Список игровых состояний. Настройка, отладка и тестирование программы. Разработка руководства пользователя. Тестирование инсталляции и отображения элементов.
дипломная работа [4,5 M], добавлен 19.01.2017Современное состояние рынка мобильных приложений. Основные подходы к разработке мобильных приложений. Обоснование выбора целевой группы потребителей приложения. Этапы проектирования и разработки мобильного приложения для операционной системы Android.
курсовая работа [987,1 K], добавлен 27.06.2019Создание, изучение и разработка приложение на Android. Среда разработки приложения DelphiXE5. Установка и настройка среды программирования. Этапы разработки приложения. Инструменты для упрощения конструирования графического интерфейса пользователя.
курсовая работа [1,6 M], добавлен 19.04.2017Структура и архитектура платформы Android. Основные достоинства и недостатки операционной системы Android. Среда разработки Eclipse, платформа Java. Подготовка среды разработки. Вкладка "Погода", "Курс валют", "Новости". Просмотр полной новости.
дипломная работа [1,0 M], добавлен 11.07.2014Архитектура операционной системы Android, набор библиотек для обеспечения базового функционала приложений и виртуальная машина Dalvik. Объектно-ориентированный язык программирования Java как инструмент разработки мобильных приложений для ОС Android.
дипломная работа [1,6 M], добавлен 08.07.2015Разработка клиент-серверного игрового приложения на примере игры в шашки для мобильных устройств на базе операционной системы Android. Обзор мобильных платформ. Экраны приложения и их взаимодействие. Графический интерфейс, руководство пользователя.
курсовая работа [2,6 M], добавлен 15.06.2013Общая схема работы приложения Android. Разработка обучающего приложения для операционной системы Android, назначение которого - развитие речи посредством произнесения скороговорок. Описание компонентов разработанного приложения, его тестирование.
дипломная работа [1,2 M], добавлен 04.02.2016