Разработка игрового приложения для мобильных платформ

  • Вид работы:
    Дипломная (ВКР)
  • Предмет:
    Информационное обеспечение, программирование
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    1,45 Мб
  • Опубликовано:
    2017-11-18
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Разработка игрового приложения для мобильных платформ

Оглавление

Введение

. Техническое предложение

. Анализ предметной области

.1 Обзор аналогов

.2 Выводы по предметной области

.3 Индивидуальность проекта

. Выбор среды разработки и сторонних библиотек

.1 Выбор среды разработки

.2 Выбор фрэймворка

. Реализация приложения

.1 Проектирование интерфейса

.2 Подготовка графических материалов

. Вектор развития

Заключение

Список использованных источников

Приложение 1

Введение

В настоящее время в индустрии видеоигр направление мобильных платформ получило активное развитие. Наиболее популярные игры по аудитории значительно опережают игры с ПК и игровых консолей, а по данным компании SuperData [1] на рынке мобильных игр доход за 2016 год составил $40.6 млрд. при общих для индустрии 91$ млрд.

Разработка игровых приложений для мобильных платформ представляет собой наименее сложный, затратный и рискованный способ для разработчика получить опыт в разработке игр. Мобильные игры по структуре значительно проще аналогов с других платформ, а цена размещения готового продукта в таких системах цифрового распространения, как Google Play [2], чаще всего ограничивается разовым взносом регистрации разработчика.

Мобильные игры так же предоставляют разработчику множество способов развития проекта, в том числе распространение уже получившей некоторую известность игры на персональные компьютеры. Такое расширение можно увидеть на примерах игр Braveland [3] или Warhammer 40,000: Deathwatch [4], которые в настоящее время размещены в системе цифрового распространения Steam [5].

По всем вышеприведённым доводам мною было принято решение начать разработку мобильного игрового приложения. Система Android была выбрана по причине её максимальной среди мобильных платформ простоте и открытости, но это не ограничивает проект и в процессе дальнейшего развития также возможно распространение на iOS и Windows Phone.

В вопросе жанра игры выбор остановился на тактических пошаговых стратегиях. Представители этого жанра редко выходят на первые места по популярности, но практически всегда быстро находят некоторую аудиторию. Игры этого жанра имеют длительный срок жизни, хорошо поддерживают развитие проекта на стадии эксплуатации и наиболее удачно из всех жанров переносятся на ПК. Кроме того, этот жанр не требователен к характеристикам систем и прост в оптимизации из-за медленного темпа игры, а интерес к проекту в большей степени вызван механиками игрового процесса, нежели визуальной составляющей.

Помимо жанра для игры необходимо определение образа внутреннего мира (сеттинга, от англ. setting) и модели монетизации.

В качестве сеттинга было выбрано фэнтези, что является одним из наиболее популярных вариантов. Решающим фактором выбора стали личные предпочтения разработчика.

Модель монетизации игры возможна в двух вариантах: платная и условно-бесплатная. Первый подразумевает под собой продажу игры за определённую цену, второй - бесплатное распространение игры с получением прибыли другими способами, такими как реклама или продажа внутриигрового контента. Стоит отметить, что условно-бесплатная модель значительно более широко распространена на мобильных платформах, так как позволяет быстро привлечь максимально широкую аудиторию к новому проекту. В свою очередь, платная модель требует от разработчика гарантий качества своего продукта, которые могут заключаться только в его известности по прошлым проектам. По этой причине мой выбор остановился на условно бесплатной модели.

В итоге, полное определение проекта - условно-бесплатная фэнтезийная тактическая пошаговая стратегия для мобильных платформ на базе ОС Android. Для опытного в играх человека такое определение даст значительное количество информации о проекте и может мотивировать на дальнейшее приобщение.

1. Техническое предложение

Игровой процесс приложения в выбранном жанре основан на правилах игры в шахматы: две стороны игрового конфликта держат в управлении отряды, которыми могут управлять, передвигая их по разделённому на клетки полю или атакуя противника. Ходы сторон чередуются между собой, время на обдумывания хода правилами не ограничивается.

В нашем приложении одной из сторон будет пользователь приложения, второй - искусственный интеллект.

В связи с этим, игровое приложение будет обладать следующими техническими параметрами: время отклика программы и задержка хода искусственного интеллекта.

Первый параметр помимо оптимизационных свойств приложения зависит также от технических параметров устройства, на котором запущенно приложение. На платформе Android каждая версия операционной системы устанавливает полный набор технических требований к устройству. В свою очередь, для каждого приложения заявляется минимальная версия операционной системы, для которой работа приложения гарантируется корректной и оптимизированной.

Задержка хода искусственного интеллекта означает время между окончанием хода игрока и завершением хода искусственного интеллекта. За этот промежуток времени система должна выбрать оптимальный с точки зрения сложности для игрока вариант хода. Этот параметр должен быть достаточно малым для поддержания непрерывного интереса игрока к игровому процессу, но также достаточным для выбора системой подходящего хода.

Ввиду вышеуказанной информации, мною решено принять следующие параметры:

время отклика приложения - 0,5 секунды;

минимальная версия операционной системы - Android 7.0;

задержка хода искусственного интеллекта - 1 секунда.

Стоит также отметить, что поддержка более ранних версий операционной системы может быть реализована при обновлениях приложения уже после выпуска первой версии приложения. Это является достаточно распространённой практикой среди разработчиков на мобильных платформах и позволяет значительно расширить потенциальную аудиторию игры, но параметры быстродействия приложения должны быть неизменными на все поддерживаемых версиях операционной системы.

2. Анализ предметной области

2.1 Обзор аналогов

В этой главе рассматриваются примеры игр, схожих с моим проектом по жанру, модели распространения или другим особенностям. При этом стоит учитывать, что в сфере видеоигровой индустрии конкуренция имеет свои особенности. Пользователи склонны не выбирать один проект из определённого списка путём сравнения характеристик, как это часто бывает с неигровыми программными продуктами. Чаще всего игроки пробуют множество существующих проектов, а от качества конкретного проекта зависит количество времени и денежных средств, которые игрок готов в этот проект вложить. В связи с этим, я считаю некорректным рассмотрение аналогов как прямых конкурентов, которых необходимо превзойти. На мой взгляд, большую пользу для проекта можно извлечь из рассмотрения успешных и провальных решений каждого аналога.

Braveland [3] - игра 2014 года. Один из наиболее близких аналогов, так как практически полностью совпадает по жанру и сеттингу, однако распространялась по платной модели. Помимо Android так же вышла на iOS, а позже версия для ПК была опубликована в Steam [5]. Получила известность среди любителей жанра и высокие оценки пользователей - средняя оценка в Google Play [2] составляет 4,4 балла (каждый пользователь выставляет оценку по пятибалльной шкале), а в настоящее время опубликовано два продолжения. Примечательна простотой игровых механик и дизайном - практически все элементы игрового процесса можно найти в более ранних представителях жанра, что не помешало хорошим продажам игры. Пример игрового процесса представлен на рисунке 2.1.

Battle for Wesnoth [6] - распространяемая по лицензии GNU General Public License [7] игра 2003 года выпуска от независимых разработчиков. К настоящему времени получила широкую известность, множество обновлений и пользовательских модификаций. Из достоинств игры самыми значительными являются глубокая проработка сюжета, новизна игровых механик, качественное музыкальное сопровождение и постоянные обновления. Является примером многолетней поддержки игрового проекта с очень простым двухмерным графическим исполнением. Пример игрового процесса представлен на рисунке 2.2.

Рисунок 2.1 Braveland

Рисунок 2.2 Battle for Wesnoth

King’s Bounty: Legions [8] - первая игра для мобильных платформ из известной серии King’s Bounty. Вышла в 2011 году на Android, iOS и Windows Phone, в 2012 году на iPad и в 2013 году опубликована в Steam [5]. Получила крайне широкую известность благодаря множеству уже существовавших на момент выхода игры фанатов серии, однако подверглась критики в связи с упрощением игрового процесса оригинальной серии и чрезмерным упрощением игры при вложении в неё денежных средств. Последнее является основным пунктом критики условно-бесплатной модели распространения - большинство пользователей считает, что вложение в игру денежных средств не должно затрагивать внутриигровой баланс. Несмотря на известность, получила средние оценки пользователей и осталась единственной игрой в серии на мобильных платформах. Пример игрового процесса представлен на рисунке 2.3.

Warhammer 40,000: Deathwatch [4] - игра по известной научно-фантастической вселенной Warhammer 40,000. Вышла в 2015 голу для iOS и ПК. Несмотря на популярность, полученную в первую очередь благодаря фанатам вселенной, игра получила большое количество негативных отзывов - 37% рецензий в Steam [5] негативны. Основными проблемами игры являлась слабая оптимизация, а также множество ошибок, связанных с 3D графикой, большое количество платного контента и отсутствие обновление практически от момента выхода игры. Пример игрового процесса представлен на рисунке 2.4.

Рисунок 2.3 Warhammer 40,000: Deathwatch

Рисунок 2.4 Warhammer 40,000: Deathwatch

.2 Выводы по предметной области

По вышеприведённым примерам можно заключить следующее:

пользователи не стремятся к инновациям в жанре, простая двухмерная графика и привычные игровые механики не оказывают негативного эффекта на успешность игры, наибольшее значение для игрока имеют стилистика, музыкальное сопровождение и разнообразие игрового процесса;

высокую важность имеет развитие проекта после выпуска, регулярные обновления необходимы для поддержания интереса уж имеющейся аудитории;

необходимо максимально аккуратное применение условно-бесплатной модели монетизации, продажа внутриигрового контента не должна затрагивать игровой баланс.

.3 Индивидуальность проекта

В настоящее время появляется множество примеров игр, которые несут в себе классический для жанра игровой процесс за исключением одной произвольной детали, которая выделяет игру среди остальных как нечто уникальное, а также помогает представить игру тем, кто ещё о ней не слышал и на более долгий период удержать внимание уже имеющейся аудитории.

Как пример можно назвать игру 2016 года Hieroglyphika. Эта игра показала обычный для своего жанра игровой процесс, но максимально ярко преподнесла ключевую особенность - в игре полностью отсутствует текст, а информация подаётся в виде иероглифов, о значение который игрок может догадываться самостоятельно. Пример внешнего вида этой игры можно увидеть на рисунке 2.5.

Рисунок 2.5 Hieroglyphika

Наличие подобной особенности не является для игры чем-то обязательным, равно как и не гарантирует значительный успех.

Для своего проекта я выбрал особенность, которая не только повлияет на вид игрового процесса, но и задаст вектор развития проекту. Идея заключается в том, чтобы поставить основой многим игровым механикам подобные решения из шахмат. Для далёкого от жанра человека это может показаться неочевидным, но современные пошаговые стратегии очень далеко ушли от древнейшего представителя жанр.

Помимо очевидного разделения поля на клетки, наиболее яркой демонстрацией реализации такой идеи будет система передвижения персонажей. Передвижение фигур в шахматах основано на абстрактных, но чётких и стабильных паттернах. Это не слишком реалистично - передвижение коня буквой «г» слабо соотносится с реально конницей, - но определённо положительно сказывается на разнообразии партий. Так же и в игре - если лучники будут ходить только по диагонали, а рыцаря по окружности радиусом в две клетки, то игра уже потребует от игрока отказа от части прошлого игрового опыта и применения аналитических способностей.

. Выбор среды разработки и сторонних библиотек

.1 Выбор среды разработки

Мною были рассмотрены среды разработки Eclipse [9], IntelliJ IDEA [10] и Android Studio [11]. Все они предоставляют полные возможности по разработке приложений на языке Java последней версии. Android Studio имеет встроенные средства по настройке комплекта средств разработки Android SDK и эмулятор устройств с операционной системой Android. В Eclipse и IntelliJ IDEA настройка Android SDK и эмуляция устройств на ОС Android осуществляется при помощи официальных плагинов. К моменту начала работы с проектом у меня уже был значительный опыт работы в Eclipse и Android Studioи меньший в IntelliJ IDEA.

Из этих трёх сред разработки Android Studio имеет следующие преимущества в контексте разработки для ОС Android:

большее относительно других IDE количество обучающих материалов и документации по разработке для мобильных платформ;

простота настройки Android SDK и эмуляторов.

Из недостатков Android Studio можно отметить только высокое потребление системных ресурсов средой разработки.

По вышеуказанным причинам для разработки мною была выбрана среда разработки Android Studio.

.2 Выбор фреймворка

На современном этапе развития информационных технологий практически ни один серьёзный проект не разрабатывается на одном языке программирования в его базовой комплектации без привлечения сторонних ресурсов, такие как графические и математические библиотеки, визуальные редакторы или средства кроссплатформенной разработки.

В случае игрового приложения первоочерёдную важность представляет собой выбор фреймворка, так называемого «движка», который отвечает в основном за жизненные циклы внутренних процессов и логику графической части приложения. Часто необдуманных выбор фреймворка в контексте конкретной игры может привести к серьёзным проблемам в оптимизации приложения, а значит и к множеству негативных отзывов от пользователей и низкой популярности игры в системах цифровой распространения. Подходящий же фреймворк напротив обеспечивает разработчику максимальные скорость и качество реализации.

Мною был выбран фреймворк libGDX. Этот движок разрабатывается с 2014 года, в графической части основан на OpenGL и ориентирован на язык Java, поддерживая при этом C и C++. Ключевой особенностью языка является модульная система обеспечения кроссплатформенной разработки: основной код пишется в одном общем модуле, а специальные используются для сборки проекта на разных платформах. Таким образом с введением минимального количества кода только в специальных модулях можно обеспечить запуск и корректную работу приложения на следующих системах: Windows, Android, Mac OS, iOS, Linux, и браузеры с поддержкой WebGL.

Несмотря на то, что проект изначально рассчитан на платформу Android, возможность обеспечения кроссплатформенности без значительных временных затрат является крайне важной, так как открывает широкие возможности для развития игры после релиза и публикации первой версии. Многие разработчики популярнейших проектов на определённом этапе были вынуждены переписывать большую часть проекта на другом фреймворке или даже языке программирования для распространения игры на лишь одну новую платформу. Наиболее часто такими платформами совершаются между Android и iOS.

Так же среди преимуществ libGDX можно отметить следующие качества:

открытая лицензия Apache 2.0;

высокая производительность графики при относительно низкой сложности её разработки. Это особенно заметно в 2D, что идеально подходит для разрабатываемого проекта;

наличие большого объёма документации и обучающих материалов, в том числе русскоязычных.

Стоит также отметить, что libGDX редко используется в действительно крупных игровых проектах. Объясняется это в первую очередь его новизной, а так неполной реализацией некоторых сложных 3D-алгоритмов. Фреймворк больше ориентирован на независимые разработки независимых студий и разработчиков, создание первых проектов и введение в сферу разработки видеоигр.

4. Реализация приложения

.1 Проектирование интерфейса

В минимально-полной комплектации игры с разобранной выше жанровой принадлежностью будет два основных состояния экрана: главное меню и игровой процесс. Первое представляет собой стандартное меню с кнопками перехода в другие состояния приложения и художественным задним планом. Пример этого экрана можно увидеть на рисунке 4.1. Экран же игрового процесса состоит из двух основных элементов - игровое поле и область информации о выбранном в текущий момент персонаже. Игровое поле внешне схоже с усложнёнными шахматами: прямоугольное поле из равных четырёхугольных сегментов, каждый из которых обозначает определённый тип местности - равнины, горы, лес, снег или вода. На клетках располагаются персонажи двух команд (игрока и искусственного интеллекта) визуально различимые по цвету флага рядом с ними. При выборе кликом определённого юнита на вторую область экрана выводится полная информация о его наименовании, его параметрах жизни, защиты и атаках. Этот экран показан на рисунке 4.2.

Рисунок 4.1 Главное меню

Рисунок 4.2 экран игрового процесса

4.2 Подготовка графических материалов

Создание графических материалов - наиболее сложная и затратная по времени часть создания игрового приложение, особенно если разговор идёт о одиночном разработчике или малой команде без специализированного художника. Оптимальным решением в такой ситуации может быть использование художественных материалов, распространяемых по свободной лицензии Creative Commons License [12]. Эта лицензия позволяет использовать и даже адаптировать защищённые ей материалы, в том числе и в коммерческих продуктах. Единственным условием является упоминание автора исходных материалов.

Было бы заблуждением считать, что игры с такими материалами не могут получить значительной аудитории. Достаточно часто такие игры можно находить в крупнейших системах цифрового распространения с преобладанием положительных отзывов и не рекордной, но значительной и стабильной аудиторией.

Хорошим примером игры от независимого разработчика, использующей свободные художественные материалы, будет Rogue’s Tale [13]. Игра вышла в 2014 году в системе Steam [5] и к настоящему времени получила множество положительных отзывов от пользователей. Игра выполнена в простой двухмерной стилистике и принадлежит к не имеющему корректного перевода на русский язык жанру rouge-like, который в последние годы переживает первый со времён псевдографических игр подъём популярности. Пример графического исполнения Rouge’s Tale можно увидеть на рисунке 4.3.

Рисунок 4.3 Rogue’s Tale

Мною были изучены различные ресурсы, предназначенные для начинающих разработчиков, и отобраны графические и музыкальные материалы, наиболее подходящие разрабатываемой игре по стилистике. Этих материалов достаточно для начальных этапов развития проекта. Многие из графических элементов были вручную адаптированы для большего соответствия авторскому замыслу у упрощения последующей обработки. Примеры свободных материалов представлены на рисунках 4.4 и 4.5. Кроме того, были дополнительно разработаны некоторые собственные графические материалы, как например карта игрового мира, являющаяся подводкой к будущему сюжету игры. Эту карту можно увидеть на заднем плане на рисунке 4.1.

Рисунок 4.4 Текстуры для игровых персонажей

Рисунок 4.5 Текстуры для клеток игрового поля

.3 Написание кода приложения

В связи с особенностями архитектуры фреймворка libGDX полное следование парадигме MVC (Model-View-Control, «Модель-Отображение-Управление») не представляется возможным, так как представление и управление взаимосвязаны, а их функции объявляются в общих интерфейсах и абстрактных классах. Таким образом, программа разделяется на две основные части: контрольно-отображающий модуль, основанный на стандартных для libGDX классах и функциях, и модуль внутренней логики игрового процесса, написанный на стандартной комплектации языка Java. Также присутствуют модули сборки для платформ Windows и Android, но они генерируются фреймворком и требуют минимальных изменений со стороны разработчика. Отдельно можно выделить и класс Game1, который организует работу двух модулей и содержит наиболее важные игровые константы и статические функции.

Классы внутренней логики отображают такие элементы как карта, поле (клетка игрового поля), персонаж, тип атаки, сценарий (объединение карты и всех персонажей на ней) и так далее. Объект класса карты создаётся на основе двух входных файлов, которые отвечают за генерацию игрового поля и размещение юнитов. Стоит отметить, что в таких целях часто используют стандартные форматы передачи данных, такие как JSON или XML. Однако, по моему мнению, при относительно небольших объёмах информации эти форматы являются избыточными, так как увеличивают объём кода и время загрузки приложения, не обеспечивая ощутимых преимуществ взамен. Поэтому был использован собственный простейший текстовый формат. На рисунке 4.6 в окне Android Studio показан участок конструктора класса Map.java, который отвечает за разбор одного из двух текстовых файлов и генерации на его основе игрового поля, полностью готового к дальнейшей обработке и отображению.

Рисунок 4.6 Пример кода внутриигровой логики

Работа модуля отображения управляется объектом класс GameStateManager.java, который управляет стеком объектов класса State.java и его наследников. Последние реализуют состояния приложения, описанные в пункте 4.1. Наследники класса State.java - MenuState.java и ScenarioState.java, они представляют состояния игрового меню и игрового процесса соответственно. Простая и эффективная реализация класса GameStateManager.java можно увидеть в окне IDE на рисунке 4.7.

Рисунок 4.7 Пример кода модуля отображения

Стоит также отдельно упомянуть искусственный интеллект. Выбор хода для юнитов команды противника выбирается следующим весовым методом: для каждого из персонажей команды рассматривается каждый возможный ход и рассчитывается его рейтинг. Рейтинг хода рассчитывается по следующим критериям: усиление контроля на карте, рассчитываемое от уменьшения расстояния до центральных клеток поля, создание опасности игроку, что означает приближение к персонажам игрока, которым можно нанести значительный урон, и создание опасности для самого юнита, что рассчитывается из приближения к персонажам игрока, способным нанести серьёзные повреждения на следующем ходу. Для каждого персонажа команды запоминается его лучший ход и после они оцениваются между собой, что обеспечивает выбор оптимального хода.

5. Вектор развития

игра приложение интерфейс графический

У любого игрового проекта есть множество способов расширить предлагаемые пользователям возможности и вместе с этим выводить проект на новые уровни популярности и прибыльности. В настоящее время для таких целей разработчики часто приходят к решению вводить в игру микротранзакции - небольшие платежи за дополнительный внутриигровое наполнение. Недостатки этого метода часто приводят к широкому недовольству игроков и это явление уже становится серьёзным кризисом видеоигровой индустрии.

Но существуют и другие направления для развития. Наиболее удачными на мой взгляд являются следующие:

создание сюжетного наполнения. Это наиболее приоритетное наполнение, так как игра, содержащая абстрактное поле боя, и игра, рассказывающая на этом поле боя полноценную историю - две совершенно разные вещи и по-разному воспринимаются игроками. В настоящее время сценарий к игре и дополняющие его графические материалы находятся в активной стадии разработки;

случайная генерация карт и сражений. В игровой журналистике широко распространён термин «реиграбельность», обозначающей способность игры поддерживать интерес при повторном прохождении. Внесение в игру значительного влияния элемента случайности, не лишая при этом игрока возможности осознанно влиять на ход событий - простейший и эффективнейший способ увеличить реиграбельность проекта, что неоднократно доказано известнейшими представителями пошаговых стратегий и других жанров;

переход от свободных графических материалов к собственным. Является необходимым этапом для проекта, выходящего на мировой уровень, но требует привлечения квалифицированных специалистов-дизайнеров, что делает этот пункт практически невозможным на ближайших стадиях развития игры;

создание редактора внутриигрового содержимого и открытие пользователям возможности создавать модификации к игре. Такое решение может оказаться полностью невостребованным в одной игре и великолепной возможностью для игроков в другой. Чаще всего это больше зависит не от разработчика, а специфики сообщества пользователей игры. В качестве примеров можно вспомнить такие всемирно известные игры, как Minecraft, ставший огромным виртуальным конструктором для игроков в том числе и в плане создания модификаций к самой игре, или серию The Elder Scrolls, количество модификаций к любой поздней части которой исчисляется десятками тысяч;

модернизация искусственного интеллекта. Более подробный расчёт ходов будет означать более высокую сложность игры, а это в свою очередь привлекает многих игроков. Впрочем, искусственный интеллект является не единственным способом достижения нужной сложности игры, многие стратегии придерживаются скриптовых моделей поведения противников игрока;

введение многопользовательского режима. Спорное решение, которое совмещает большое расширение потенциальной аудитории с многими минусами, такими как необходимость изменять игру для обеспечения баланса в сетевом режиме в ущерб уникальности и разнообразию игровых механик и необходимость обеспечения игры сетевым кодом и серверной частью;

Заключение

Игра, получившаяся в процессе реализации данного проекта ещё не готова быть опубликованным продуктом. Техническая часть нуждается в доработках, но уже практически достаточна для обеспечения серьёзного проекта. Большее внимание в ближайшем будущем стоить уделить игровому наполнению - от новой игры пользователи ожидают не меньше нескольких часов игрового процесса, наполненного выделяющимися стилистикой и игровыми механиками.

Развитие проекта по указанным в пятой главе направлениям обеспечит игре стабильное приближению в таких системах, как Google Play [2] и Steam [5], что уже само по себе гарантирует первоначальную аудиторию. Система отзыва в этих средах так же позволит сделать выводы о состоянии проекта и скорректировать направления разработки.

Также при реализации проекта был получен новый опыт работы с языком программирования Java, со средой разработки Android Studio и фреймворком libGDX, были усвоены базовые навыки по работе с двухмерной графикой и искусственным интеллектом.

Список использованных источников

1.   SuperData Research [Электронный ресурс]: офиц. сайт. Режим доступа: https://www.superdataresearch.com/.

2.      Google Play [Электронный ресурс]: офиц. сайт. Режим доступа: https://play.google.com/store.

.        Приложение Braveland в Google Play [Электронный ресурс]. Режим доступа: https://play.google.com/store/apps/details?id=com.tortugateam.braveland.v2&hl=ru.

4.      Warhammer 40,000: Deathwatch: Tyranid Invasion [Электронный ресурс]: офиц. сайт. Режим доступа: http://rodeogames.co.uk/deathwatch.

5.      Steam [Электронный ресурс]: офиц. сайт. Режим доступа: store.steampowered.com.

.        Приложение Battle for Wesnoth в Google Play [Электронный ресурс]. Режим доступа: https://play.google.com/store/apps/details?id=it.alessandropira.wesnoth112&hl=ru.

.        Стандартная общественная лицензия GNU (GPL) [Электронный ресурс]: офиц. сайт. Режим доступа: https://www.gnu.org/licenses/gpl-3.0.ru.html.

.        Приложение King’s Bounty: Legions в Google Play [Электронный ресурс]. Режим доступа: https://play.google.com/store/apps/details?id=com.kranx.kbl.

9.      Eclipse [Электронный ресурс]: офиц. сайт. Режим доступа: https://eclipse.org/.

10.    IntelliJ IDEA [Электронный ресурс]: офиц. сайт. Режим доступа: https://www.jetbrains.com/idea/.

11.    Android Studio [Электронный ресурс]: офиц. сайт. Режим доступа: https://developer.android.com/studio/index.html.

12.    Creative Commons License [Электронный ресурс]: офиц. сайт. Режим доступа: https://creativecommons.org/licenses/by/3.0.

.        Приложение Rogue’s Tale в Steam [Электронный ресурс]. Режим доступа: http://store.steampowered.com/app/265990/Rogues_Tale/?l=russian.

Приложение 1

Коды классов приложения

Game1.java

package com.vlerof.game1;com.badlogic.gdx.ApplicationAdapter;com.badlogic.gdx.Gdx;com.badlogic.gdx.files.FileHandle;com.badlogic.gdx.graphics.Color;com.badlogic.gdx.graphics.GL20;com.badlogic.gdx.graphics.Pixmap;com.badlogic.gdx.graphics.Texture;com.badlogic.gdx.graphics.g2d.SpriteBatch;com.vlerof.game1.Scenario.Field;com.vlerof.game1.Scenario.Level;com.vlerof.game1.Scenario.Map;com.vlerof.game1.Scenario.Scenario;com.vlerof.game1.Scenario.Unit;com.vlerof.game1.States.GameStateManager;com.vlerof.game1.States.MenuState;class Game1 extends ApplicationAdapter {static final int WIDTH = 1080;static final int HEIGHT = 1920;static final int INTERFACE_HEIGHT = 160;static final int FIELD_HEIGHT = HEIGHT - INTERFACE_HEIGHT;static final int ATTACK_BORDER = WIDTH * 3 / 5;static final int SCALE = 32;static final String TITLE = "Game";static final Color COLOR_SIENNA = new Color(0.627f, 0.322f, 0.176f, 1f);static final Color COLOR_SADDLEBROWN = new Color(0.545f, 0.271f, 0.075f, 1f);static final Color COLOR_BLACK = new Color(0.0f, 0.0f, 0.0f, 1f);static Pixmap characterMap1;static Pixmap characterMap2;static Pixmap fieldMap;static Texture flagPlayer;static Texture flagEnemy;static Texture buttonStart;static Texture buttonExit;GameStateManager gsm;SpriteBatch batch;

@Overridevoid create () {= new SpriteBatch();= new GameStateManager();= new Pixmap(new FileHandle("Tiles/dg_classm32.png"));= new Pixmap(new FileHandle("Tiles/dg_uniques32.png"));= new Pixmap(new FileHandle("Tiles/dg_grounds32.png"));= new Texture(new FileHandle("flag1.png"));= new Texture(new FileHandle("flag2.png"));= new Texture(new FileHandle("start.png"));= new Texture(new FileHandle("exit.png"));.initLevels();.initTiles();.gl.glClearColor(0, 0, 0, 1);.push(new MenuState(gsm));

}

@Overridevoid render () {.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);.update(Gdx.graphics.getDeltaTime());.render(batch);

}

@Overridevoid dispose () {.dispose();.dispose();.dispose();.dispose();.dispose();.dispose();.dispose();.dispose();

}static Scenario getDefaultScenario() {sc = new Scenario();.setMap(new Map("Maps/map1.txt"));.setSpawns("Maps/respawn1.txt");unit;

//TODO: moves, attacks, hp, defs= new Unit("John", 15, new Tile(characterMap1, 3, 0));.addEnemyUnit(unit);= new Unit("Karl", 15, new Tile(characterMap1, 3, 1));.addEnemyUnit(unit);= new Unit("Guss", 15, new Tile(characterMap1, 3, 2));.addEnemyUnit(unit);= new Unit("Bob", 15, new Tile(characterMap1, 3, 3));.addEnemyUnit(unit);= new Unit("Ivan", 15, new Tile(characterMap1, 3, 4));.addEnemyUnit(unit);= new Unit("Jack", 15, new Tile(characterMap1, 3, 5));.addEnemyUnit(unit);= new Unit("Tzeentch", 15, new Tile(characterMap1, 4, 6));.addPlayerUnit(unit);= new Unit("Nurgle", 15, new Tile(characterMap2, 9, 5));.addPlayerUnit(unit);= new Unit("Malal", 15, new Tile(characterMap2, 5, 2));.addPlayerUnit(unit);= new Unit("Slaanesh", 15, new Tile(characterMap2, 4, 0));.addPlayerUnit(unit);= new Unit("Khorn", 15, new Tile(characterMap2, 6, 0));.addPlayerUnit(unit);sc;

}static String formatString(String s) {int length = s.length();char[] chars = s.toCharArray();(int i = 0; i < length; i++) {(chars[i] == '\r' || chars[i] == '\n') {[i] = ' ';

}

}str = new String(chars);str.replaceAll(" ", " ");

}

}

Tile.java

package com.vlerof.game1;com.badlogic.gdx.graphics.Pixmap;com.badlogic.gdx.graphics.Texture;class Tile {Pixmap pixmap;int xOffset;int yOffset;Tile(Pixmap _source, int _xOffset, int _yOffset) {= _source;= _xOffset;= _yOffset;

}Texture getTexture() {pxmp = new Pixmap(Game1.SCALE, Game1.SCALE, Pixmap.Format.RGBA8888);.drawPixmap(pixmap, 0, 0, xOffset * Game1.SCALE, yOffset * Game1.SCALE, Game1.SCALE, Game1.SCALE);new Texture(pxmp);

}Pixmap getPixmap() {pxmp = new Pixmap(Game1.SCALE, Game1.SCALE, Pixmap.Format.RGBA8888);.drawPixmap(pixmap, 0, 0, xOffset * Game1.SCALE, yOffset * Game1.SCALE, Game1.SCALE, Game1.SCALE);pxmp;

}

}

Map.java

package com.vlerof.game1.Scenario;com.badlogic.gdx.Gdx;com.badlogic.gdx.files.FileHandle;com.badlogic.gdx.graphics.Pixmap;com.badlogic.gdx.graphics.Texture;com.vlerof.game1.Game1;java.awt.Point;java.io.BufferedReader;java.io.File;java.io.FileInputStream;java.io.IOException;java.io.InputStreamReader;class Map {final int WIDTH;final int HEIGHT;Field map[][];Map(String _filename) {file = Gdx.files.internal(_filename);str = Game1.formatString(file.readString());spacePos = str.indexOf(" ");= Integer.parseInt(str.substring(0, spacePos));= str.substring(spacePos + 1);= str.indexOf(" ");= Integer.parseInt(str.substring(0, spacePos));= str.substring(spacePos + 1);= new Field[WIDTH][HEIGHT];fieldCode;(int y = 0; y < HEIGHT; y++) {(int x = 0; x < WIDTH - 1; x++) {= str.indexOf(" ");= str.substring(0, spacePos);= str.substring(spacePos + 1);[x][y] = parseField(fieldCode);

}(y == HEIGHT - 1) {= str.substring(0);

} else {= str.indexOf(" ");= str.substring(0, spacePos);= str.substring(spacePos + 1);

}[WIDTH - 1][y] = parseField(fieldCode);

}

}Field getField(int _x, int _y) {map[_x][_y];

}Field getField(Point _point) {map[_point.x][_point.y];

}Field parseField(String _code) throws StringIndexOutOfBoundsException {type;typeStr = _code.charAt(0);(typeStr) {'p': {= FieldType.PLAIN;;

}'f': {= FieldType.FOREST;;

}'m': {= FieldType.MOUNTAIN;;

}'w': {= FieldType.WATER;;

}'b': {= FieldType.BLOCKED;;

}'s': {= FieldType.SNOW;;

}: {= FieldType.BLOCKED;

}

}number = Integer.parseInt(_code.substring(1));new Field(type, number);

}Pixmap getPixmap() {pxmp = new Pixmap(Game1.SCALE * WIDTH, Game1.SCALE * HEIGHT, Pixmap.Format.RGBA8888);(int y = 0; y < HEIGHT; y++) {(int x = 0; x < WIDTH; x++) {.drawPixmap(map[x][y].getTile().getPixmap(), x * Game1.SCALE, y * Game1.SCALE, 0, 0, Game1.SCALE, Game1.SCALE);

}

}.setColor(0.3f, 0.3f, 0.3f, 0.5f);(int x = 1; x < WIDTH; x++) {.drawRectangle(x * Game1.SCALE, 0, 2, HEIGHT * Game1.SCALE);

}(int y = 1; y < HEIGHT; y++) {.drawRectangle(0, y * Game1.SCALE, WIDTH * Game1.SCALE, 2);

}pxmp;

}

}

Scenario.java

package com.vlerof.game1.Scenario;com.badlogic.gdx.Gdx;com.badlogic.gdx.files.FileHandle;com.badlogic.gdx.graphics.Pixmap;com.badlogic.gdx.graphics.g2d.SpriteBatch;com.vlerof.game1.Game1;java.awt.Point;java.util.ArrayList;class Scenario {Map map;ArrayList<Point> playerSpawn;ArrayList<Point> enemySpawn;ArrayList<Unit> playerTeam;ArrayList<Unit> enemyTeam;Scenario() {= new ArrayList<Point>();= new ArrayList<Point>();= new ArrayList<Unit>();= new ArrayList<Unit>();

}Scenario(String _fileMap, String _fileSpawn, String _filePlayer, String _fileEnemy) {

//TODO: scenario init

}void setMap(String _filename) {= new Map(_filename);

}void setMap(Map _map) {= _map;

}void setSpawns(String _filename) {= new ArrayList<Point>();= new ArrayList<Point>();(_filename);

}void addPlayerSpawn(Point _point) {.add(_point);

}void addEnemySpawn(Point _point) {.add(_point);

}void setPlayerTeam(String _filename) {= new ArrayList<Unit>();(_filename);

}void setEnemyTeam(String _filename) {= new ArrayList<Unit>();(_filename);

}void addPlayerUnit(Unit _unit) {.add(_unit);

_unit.setFlag(Game1.flagPlayer);

}void addEnemyUnit(Unit _unit) {.add(_unit);

_unit.setFlag(Game1.flagEnemy);

}void parseSpawns(String _filename) {file = Gdx.files.internal(_filename);str = Game1.formatString(file.readString());spacePos;(int y = 0; y < map.HEIGHT; y++) {(int x = 0; x < map.WIDTH - 1; x++) {= str.indexOf(" ");(Integer.parseInt(str.substring(0, spacePos)), x, y);= str.substring(spacePos + 1);

}(y == map.HEIGHT - 1) {(Integer.parseInt(str.substring(0)), map.WIDTH - 1, y);

} else {= str.indexOf(" ");(Integer.parseInt(str.substring(0, spacePos)), map.WIDTH - 1, y);= str.substring(spacePos + 1);

}

}

}void parsePlayerTeam(String _filename) {

//TODO: parse

}void parseEnemyTeam(String _filename) {

//TODO: parse

}void parseSpawnCode(int _code, int _x, int _y) {(_code) {1: {(new Point(_x, _y));;

}

}

}void placeUnits() {p;(Unit unit: playerTeam) {(!playerSpawn.isEmpty()) {= playerSpawn.remove(0);.getField(p).setUnit(unit);.setLocate(p);

}

}(Unit unit: enemyTeam) {(!enemySpawn.isEmpty()) {= enemySpawn.remove(0);.getField(p).setUnit(unit);.setLocate(p);

}

}

}void drawUnits(Pixmap _pxmp) {(Unit unit: playerTeam) {

_pxmp.drawPixmap(unit.getPixmap(), unit.getLocate().x * Game1.SCALE, unit.getLocate().y * Game1.SCALE, 0, 0, Game1.SCALE, Game1.SCALE);

}(Unit unit: enemyTeam) {

_pxmp.drawPixmap(unit.getPixmap(), unit.getLocate().x * Game1.SCALE, unit.getLocate().y * Game1.SCALE, 0, 0, Game1.SCALE, Game1.SCALE);

}

}void drawUnits(SpriteBatch _sb) {(Unit unit: playerTeam) {.setSpritePosition(Gdx.graphics.getWidth() * unit.getLocate().x / map.WIDTH,

((Gdx.graphics.getHeight() - Game1.INTERFACE_HEIGHT) * unit.getLocate().y / map.HEIGHT) + Game1.INTERFACE_HEIGHT);.getSprite().draw(_sb);.getFlag().draw(_sb);

}(Unit unit: enemyTeam) {.setSpritePosition(Gdx.graphics.getWidth() * unit.getLocate().x / map.WIDTH,

((Gdx.graphics.getHeight() - Game1.INTERFACE_HEIGHT) * unit.getLocate().y / map.HEIGHT) + Game1.INTERFACE_HEIGHT);.getSprite().draw(_sb);.getFlag().draw(_sb);

}

}

}

Unit.java

package com.vlerof.game1.Scenario;com.badlogic.gdx.graphics.Pixmap;com.badlogic.gdx.graphics.Texture;com.badlogic.gdx.graphics.g2d.Sprite;com.vlerof.game1.Tile;java.awt.Point;java.util.ArrayList;class Unit {Texture texture;Sprite sprite;Sprite flag;Pixmap pixmap;String name;Point locate;Level level;ArrayList<Point> moves;ArrayList<FieldType> moveTypes;ArrayList<Attack> attacks;boolean alive;int healthCurrent;int healthMax;int healthPerLvl;int defenseMelee;int defenseRange;int defenseFire;Unit(String _name, int _maxLevel, Tile _tile) {= _name;= new Level(0, _maxLevel);= new ArrayList<Attack>();= _tile.getTexture();= new Sprite(texture);= _tile.getPixmap();

}Unit setLocate(Point _locate) {= _locate;this;

}Point getLocate() {locate;

}void setSpritePosition(float _x, float _y) {.setPosition(_x, _y);.setPosition(_x + 32, _y);

}void addExp(int _amount) {lvlUps = level.addExp(_amount);(lvlUps > 0) {+= lvlUps * healthPerLvl;= healthMax;

}

}int getLevel() {level.getLevel();

}void setHealth(int _hpMax, int _hpPerLvl){= healthMax = _hpMax;= _hpPerLvl;= true;

}void setDefenses(int _defMelee, int _defRange, int _defFire) {= _defMelee;= _defRange;= _defFire;

}void addMove(Point _point) {.add(_point);

}void addMoveType(FieldType _type) {.add(_type);

}void addAttack(Attack _attack) {.add(_attack);

}ArrayList<Point> getMoves(){moves;

}ArrayList<FieldType> getMoveTypes(){moveTypes;

}ArrayList<Attack> getAttacks(){attacks;

}Texture getTexture() {texture;

}Sprite getSprite() {sprite;

}Pixmap getPixmap() {pixmap;

}void setFlag(Texture _texture) {= new Sprite(_texture);

}Sprite getFlag() {flag;

}boolean isAlive() {alive;

}

}

Level.java

package com.vlerof.game1.Scenario;java.util.ArrayList;class Level {int level;final int max;int experience;static final ArrayList<Integer> sumToUp = new ArrayList<Integer>();Level(int _experience, int _max) {(_experience > 0 && _experience < 1000000) {= _experience;

} else {= 0;

}(_max > 0 && _max < 100) {= _max;

} else {= 10;

}

}int getLevel() {level;

}int addExp(int _amount) {levelUps = 0;+= _amount;sumToNext = sumToUp.get(level);(experience > sumToNext && level < max) {-= sumToNext;++;++;= sumToUp.get(level);

}levelUps;

}static void initLevels() {.add(15);(int i = 1; i < 100; i++) {.add((i + 3) * 5 + sumToUp.get(i - 1));

}

}

}

FieldType.java

package com.vlerof.game1.Scenario;enum FieldType {,,,,,

}

Field.java

package com.vlerof.game1.Scenario;com.vlerof.game1.Game1;com.vlerof.game1.Tile;java.util.ArrayList;java.util.HashMap;class Field {static HashMap<FieldType, ArrayList<Tile>> tilesMap = new HashMap<FieldType, ArrayList<Tile>>();FieldType fieldType;Unit unit;int tileNumber;Field(FieldType _type, int _tileNumber) {= _type;= null;(_tileNumber < tilesMap.get(_type).size()) {= _tileNumber;

} else {= 0;

}

}Tile getTile() {tilesMap.get(fieldType).get(tileNumber);

}FieldType getType() {fieldType;

}Unit getUnit(){unit;

}void setUnit(Unit _unit) {= _unit;

}int getTilesCount() {tilesMap.get(fieldType).size();

}static int getTypeTilesCount(FieldType _type) {tilesMap.get(_type).size();

}static void initTiles() {.put(FieldType.PLAIN, new ArrayList<Tile>());.put(FieldType.FOREST, new ArrayList<Tile>());.put(FieldType.MOUNTAIN, new ArrayList<Tile>());.put(FieldType.WATER, new ArrayList<Tile>());.put(FieldType.BLOCKED, new ArrayList<Tile>());.put(FieldType.SNOW, new ArrayList<Tile>());

}(int i = 0; i < 6; i++) {.add(new Tile(Game1.fieldMap, i, 3));

}(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 3, 4));

}

///////////////////////////////////////////////////////

//Forests= tilesMap.get(FieldType.FOREST);(int i = 0; i < 9; i++) {.add(new Tile(Game1.fieldMap, i, 6));

}(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 6, 7));

}

///////////////////////////////////////////////////////

//Mountains= tilesMap.get(FieldType.MOUNTAIN);(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i, 9));

}(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 6, 9));

}(int i = 0; i < 9; i++) {.add(new Tile(Game1.fieldMap, i, 13));

}

///////////////////////////////////////////////////////

//Water= tilesMap.get(FieldType.WATER);(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 6, 1));

}(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i, 2));

}

///////////////////////////////////////////////////////

//Blocked= tilesMap.get(FieldType.BLOCKED);(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i, 3));

}(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 6, 4));

}(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 6, 8));

}

///////////////////////////////////////////////////////

//Snow= tilesMap.get(FieldType.SNOW);(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 3, 9));

}(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 6, 10));

}(int i = 0; i < 3; i++) {.add(new Tile(Game1.fieldMap, i + 6, 11));

}

///////////////////////////////////////////////////////

}

}

GameStateManager.java

package com.vlerof.game1.States;com.badlogic.gdx.graphics.g2d.SpriteBatch;java.util.Stack;class GameStateManager {Stack<State> states;GameStateManager() {= new Stack<State>();

}void push(State _state) {.push(_state);

}void pop() {.pop().dispose();

}void set(State _state) {.pop().dispose();.push(_state);

}void update(float _dt) {.peek().update(_dt);

}void render(SpriteBatch _sb) {.peek().render(_sb);

}

}

State.java

package com.vlerof.game1.States;com.badlogic.gdx.graphics.OrthographicCamera;com.badlogic.gdx.graphics.g2d.SpriteBatch;com.badlogic.gdx.math.Vector3;abstract class State {OrthographicCamera camera;Vector3 mouse;GameStateManager gsm;State(GameStateManager _gsm) {= _gsm;= new OrthographicCamera();= new Vector3();

}abstract void handleInput();abstract void update(float dt);abstract void render(SpriteBatch sb);abstract void dispose();

}

MenuState.java

package com.vlerof.game1.States;com.badlogic.gdx.Gdx;com.badlogic.gdx.graphics.Texture;com.badlogic.gdx.graphics.g2d.SpriteBatch;com.vlerof.game1.Game1;class MenuState extends State {Texture background;MenuState(GameStateManager _gsm) {(_gsm);= new Texture("Map.jpg");

}

@Overridevoid handleInput() {(Gdx.input.justTouched()) {x = Gdx.input.getX();y = Gdx.input.getY();(x > Gdx.graphics.getWidth() / 2 - Game1.buttonStart.getWidth() / 2 &&< Gdx.graphics.getWidth() / 2 + Game1.buttonStart.getWidth() / 2 &&< Gdx.graphics.getHeight() / 3 &&> Gdx.graphics.getHeight() / 3 - Game1.buttonStart.getHeight()) {.set(new ScenarioState(gsm, Game1.getDefaultScenario()));

} else if (x > Gdx.graphics.getWidth() / 2 - Game1.buttonExit.getWidth() / 2 &&< Gdx.graphics.getWidth() / 2 + Game1.buttonExit.getWidth() / 2 &&< Gdx.graphics.getHeight() * 2 / 3 &&> Gdx.graphics.getHeight() * 2 / 3 - Game1.buttonExit.getHeight()) {.app.exit();

}

}

}

@Overridevoid update(float dt) {();

}

@Overridevoid render(SpriteBatch sb) {.begin();.draw(background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());.draw(Game1.buttonStart, Gdx.graphics.getWidth() / 2 - Game1.buttonStart.getWidth() / 2,.graphics.getHeight() * 2 / 3);.draw(Game1.buttonExit, Gdx.graphics.getWidth() / 2 - Game1.buttonExit.getWidth() / 2,.graphics.getHeight() / 3);

//TODO: Buttons.end();

}

@Overridevoid dispose() {.dispose();

}

}

ScenarioState.java

package com.vlerof.game1.States;com.badlogic.gdx.Gdx;com.badlogic.gdx.graphics.GL20;com.badlogic.gdx.graphics.Pixmap;com.badlogic.gdx.graphics.Texture;com.badlogic.gdx.graphics.g2d.SpriteBatch;com.vlerof.game1.Game1;com.vlerof.game1.Scenario.Scenario;class ScenarioState extends State {Scenario scenario;Pixmap mapPixmap;Texture mapTexture;Texture interfaceTexture;ScenarioState(GameStateManager gsm, Scenario _scenario) {(gsm);= _scenario;.placeUnits();= _scenario.map.getPixmap();= new Texture(mapPixmap);pxmp = new Pixmap(Game1.WIDTH, Game1.INTERFACE_HEIGHT, Pixmap.Format.RGBA8888);.setColor(Game1.COLOR_SIENNA);.fillRectangle(0, 0, Game1.WIDTH, Game1.INTERFACE_HEIGHT);.setColor(Game1.COLOR_SADDLEBROWN);.fillRectangle(Game1.ATTACK_BORDER, 0, 2, Game1.INTERFACE_HEIGHT);.setColor(Game1.COLOR_SADDLEBROWN);.fillRectangle(0, 0, Game1.WIDTH, 5);= new Texture(pxmp);

}

@Overridevoid handleInput() {

}

@Overridevoid update(float dt) {

}

@Overridevoid render(SpriteBatch sb) {.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);.begin();.draw(mapTexture, 0, Game1.INTERFACE_HEIGHT, Gdx.graphics.getWidth(), Gdx.graphics.getHeight() - Game1.INTERFACE_HEIGHT);.draw(interfaceTexture, 0, 0, Game1.WIDTH, Game1.INTERFACE_HEIGHT);.drawUnits(sb);.end();

}

@Overridevoid dispose() {.dispose();.dispose();

}

}

Похожие работы на - Разработка игрового приложения для мобильных платформ

 

Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!