Вступление
Думаю, большинство из вас в детстве игрались в фишки. Помните, такие маленькие кружочки с рисунками? Они были на разные тематики. Лично я игрался в них не очень много, но еще застал их. И тут поступила идея сделать такую игру на телефон — на Android. Знания были, опыт тоже, и я приступил к реализации. Я расскажу немного о игровом процессе, и, конечно же, коснусь технических моментов — куда же без них на хабр-то?:)
Суть игры
Смысл игры довольно прост, и не вызывает особых затруднений. Вам дается немного денег и три фишки. Ваша задача — расширять вашу коллекцию фишек. Это можно сделать или покупкой их в игровом магазине, или игрой с соперниками — это основной способ. Глобальная цель игры — собрать все-все фишки. Их порядка пятисот, все они разбиты на определенные альбомы по тематикам. Эти альбомы можно просмотреть, отдельные фишки можно купитьпродать. Бывает такое, что вы выигрываете такую фишку, которая у вас уже есть — такие дубликаты можно продать, вместо них купить новые. Ну вот так вы и расширяете свою коллекцию фишек.
Особенности игры
Порядок выбивания определяется мини-игрой «камень-ножницы-бумага». Наверное, это самая напряжная часть, которую я писал в этой игре.
В игре есть несколько игровых режимов. Можно играть против компьютера — эдакий синглплеер. Против вас играет 3 противника, играют сбалансированно — разгромить в лоб их не получится. Можно играть на одном телефоне с друзьями — ходят по очереди, получается, как «горячий стул» в Героях Меча и Магии. И самый интересный режим — сетевой. Есть сервер со списком игр, вы можете играть там с игроками со всего света. Сейчас реализована сетевая игра только для двоих игроков, в перспективе планируется расширить это количество до 4-х.
В игре действительно много фишек — больше десятка альбомов, в каждом из которых несколько десятков фишек. Все это скроллится, все фишки можно посмотреть в большом разрешении. Фанаты будут довольны.
Также есть такая штука, как биты. Бита — это такой железный или пластмасовый кружок, которым выбиваются фишки. У нас биты были не в особом почете (лично я ни одну не видел), а вот за границей они использовались. У каждой биты свои особенности, они влияют на процесс выбивания фишек. Есть биты из суперспособностями — позволяет при некоторых условиях выбить все кепсы в стопке.
Биты выбиваются рукой. Она появляется на экране, хватает стопку кепсов или биту, и бьет ней об стол — и это таки довольно приятно выглядит. Впрочем, руку можно и отключить в настройках.
Конечно же есть справка, где описаны игровые нюансы. Игра на двух языках — русский и английский.
Есть красивости — крутящиеся биты, разлетающиеся звездочки при покупке альбомовфоновбит, шевелящиеся надписи и тому подобное.
Есть система наград — например, за все собранные биты, за все открытые альбомы и т. д.
Вообще, в игре есть еще много чего интересного — просмотр больших картинок кепсов (с подгрузкой с интернета), разные фоны (с ними я тоже помучился, ниже напишу почему).
Технические детали
Игра писалась на Java, игровой движок — libGDX (если кто не знает — он позволяет писать для десктопа, андроида без проблем вообще, с бубном — и для iOS и html5). Если точнее, я построил свой небольшой фреймворк вокруг libGDX, и писал уже на нем. На этом же фреймворке я писал еще несколько игр. Потом в нем обнаружились некоторые недочеты, и я начал его переделывать. Сейчас этот мой новый движок уже почти завершен, чуть позже я опубликую о нем статью — там есть некоторые вкусняшки :) Среда разработки стандартная — Eclipse.
В самом начале для создания проекта использовал прогу под названием libGDX UI Project Setup. Эта штука умеет создавать связанные проекты для эклипса, бросать в эти проекты нужные библиотеки, делать классы-запускалки для разных платформ. Умеет скачивать из интернета последнюю версию libGDX, поддерживает также сторонние библиотеки. Это шикарнейшая штука, жаль, что сторонних библиотек почти и нет-то(. В результате работы программы получаем workspace, заходим туда Eclipse, импортируем проекты — и вот, у вас рабочая заготовка, которую вы можете запустить на десктопе, на андроиде, в браузере (с некоторыми ограничениями). Если есть макбук — можно и на iPhone попробовать. У меня айфона не было, тестил на компе и телефоне.
Широко использовал такую утилиту, как GDX TexturePacker. Это пакер текстур — у вас есть много маленьких картинок, вы натравляете пакер на папку, и получаете одну большую картинку (атлас текстур) из сторонами, кратными степени двойки (требования OpenGL для текстур) и текстового файлика, где описана, где в этом большом файлике расположена какая картинка. Потом в игре вы загружаете это файлик и текстуру, получаете удобный доступ к картинкам. Кто использует libGDX и не слышал про эту штуку — настоятельно рекомендую, вы сэкономите себе кучу времени. Он позволяет сохранять набор атласов в проект, потом добавлятьудалять оттуда картинки — в общем, автор неплохо постарался и сделал действительно достойный инструмент.
Разработка проходила следующим образом: вся игра была разбита на отдельные экраны. Один экран — один Screen (класс libGDX). По такому же принципу и были сделаны атласы текстур — для одного экрана — один атлас. В принципе, это позволяло бы держать в памяти только нужный атлас, а остальные выгружать — но памяти вроде и так хватало, поэтому я этим не заморачивался. Главная проблема андроида — мультиэкранность. В libGDX есть класс Table — он позволяет довольно неплохо бороться с этим, размещая компоненты не жестко, а в ячейках таблицы. Каждой ячейке можно задать свои параметры — выравнивание, размеры и т. д. В итоге получилось довольно неплохо — на разных экранах раскладка немного разная, но за границы ничего не выезжает, все прилично.
В игре был один глобальный класс-синглтон типа Game (это тоже класс libGDX). Он хранил ссылки на все ресурсы, позволял переключать экраны, отлавливать моменты выходапаузывозобновления с паузы и т. д. Также там был код, специфичный для взаимодействия с Android. Это касается рекламы adMob — показзапрет показа, и tapjoy. Для общения с android был написан интерфейс. Этот интерфейс был реализован в android-проекте, юзал специфичные для него вещи. В игру передавался этот интерфейс из android-проекта. Потом игра дергала его, и android исполнял нужные команды.
Такой же синглтон был создан для настроек — я назвал его хитрым именем GameSettings. Туда я сохранял состояние игрока, и, собственно, настройки — звукмузыка и т. д. Сам игрок перед сохранением сериализовался в json-строку, а при загрузке — десериализовался с нее. Получилось очень удобно — добавление нового свойства к игроку не требовало лезть в его сохранениезагрузку.
В каждой фишки есть свои свойства — картинка, цена, альбом и т. д. Эти свойства также хранятся в отдельных json-файлах. Получилось около 500 json-файлов. В этом есть и свой плюс — они подгружаются автоматом, для новой фишки мне достаточно создать новый файлик, и бросить его в эту папку. Понятно, что вручную редактировать эти файлы было бы напряжно — был написан простенький редактор фишек — можно было выбрать картинку, указать альбомстоимость фишки.
Вообще, все, что можно было, я вынес в json-файлы — те же биты, фоны. Я не люблю лезть в код без надобности — я ленивый :) Таким образом, добавлениеудаление бит, фонов, фишек — это операции с файлами. Автоматизация, однако.
Для сборки апк и версии для компьютера я написал ant-скрипт. Он собирает апк, подписывает его, собирает версию для компьютера, и бросает это все в dropbox папку. Проходит немножко времени — и мои друзья запускают и тестируют игрушку. Ant-скрипт для сборки — неоценимая штука, если вы часто делаете релизы (а поскольку я работал удаленно, мне это было очень и очень необходимо).
Основое время игра запускалась на компьютере, а потом уже запускалась на телефоне. Это неоценимое преимущество libGDX — можно очень быстро отлаживать игру, не ожидая, пока она зальется на эмуляторустройство. Я не знаю, сколько бы времени заняла разработка, заливай я каждый раз игру на телефон.
Не обошлось и без монетизации. Показывается рекламный баннер admob внизу экрана. Также разобрался и интегрировал tapjoy — если кто не знает, это система, где пользователь за выполнение заданий (например, установка приложений из Google Playe) получает виртуальные монетки. А дальше вы даете ему возможность тратить эти виртуальные монетки. Я, например, просто конвертирую их в внутриигровые монетки.
Проблемы, с которыми столкнулся
Не обошлось и без проблем.
Первая проблема — это сетевая игра.
Сначала я хотел сделать следующим образом. Выделяем одного игрока, делаем его сервером. Остальные игроки присылаютотсылают ему команды. Он управляет всеми игроками и одновременно собой. В таком варианте программа-сервер только управляла созданием игры и пересылала команды между игроками, оповещала про созданиеудаление игры. Все было бы хорошо, но на практике оказалось, что такая реализация очень напряжная (ну мне она показалась именно такой). Реализовать одновременно и клиент, и сервер в одной программе толком так и не вышло, пришлось потом дописывать сервер. На будущее я для себя уяснил — на сервер много задач не бывает :) Если есть возможность реализовать что-то на сервере — делайте это на сервере. Это не говоря уже про безопасность.
Вторая проблема — подгрузка больших картинок фишек из интернета для их просмотра. Они хранились в jpg. Изначально каждая картинка была размера 400*400 — или что-то около того. В свойствах движка я выставил использование OpenGL 2.0 — это позволило загружать картинки, где стороны не были степенью двойки. Оказалось, что на некоторых устройствах картинки не грузятся, а показывается только белый экран. После танцев с бубном я пережал все картинки в 512*512 — проблема пропала.
Была проблема, что игра лагала и падала. Осмотр показал, что создается и удаляется очень много (порядка сотен) обьектов фишек. Сначала сборщик мусора это терпит, а потом игра начинает лагать и падать. Выход — использование пулов. Вызов new Chip() (да, я назвал так фишку:) ) встречается лишь в одном месте — в менеджере фишек, он же пул фишек. После использования фишки возвращаются в пул обратно. Так же сделано с битами, фонами — мы пихаем это все в пул, потом возвращаем.
Еще была такая проблема, как правильное отображение фонов. Если просто растягивать его на весь экран, то на некоторых разрешениях получается очень некрасиво — например, круглые элементы стают совсем не круглыми. Решение — вырезаем из фона нужные элементы, остальную картинку располагаем на весь экран. Элементы располагаем на своих местах с указанием относительных координат. После этого проблема исчезла.
Широко юзался паттерн «листенер». Он связывал все и всех. Например, когда игрок выбил все фишки, ему на экране показывалась награда. Эту же награду нужно было показывать в экране статистики после игры. Решение — делаем класс-писатель, которого дергаем при достижении награды. Отдельные экраны — это подписчики. Таким образом, добавление еще чего-то (например, денежки за награды) — это создание нового подписчика, и подпись его на нужные события.
В принципе, описанные выше проблемы я успешно преодолел. Были и такие, которые я решить не смог — длинная(ну слишком длинная) загрузка игры на некоторых телефонах. Фрагментация андроида — да, она такая. Но в общем, основные грабли были обойдены.
Скриншоты
Что же за игра без скриншотов? Вот несколько:
"
"
"
"
"
Заключение
В процессе создания игры я много чему научился, хоть это и не моя первая игра. Я вник в детали libGDX — и я скажу, что я не разочаровался. Неплохо помучался с оптимизацией игры, из созданием правильной архитектуры. Побочный результат — я понял, какие места самые рутинные, сейчас дописываю свой движок, где большинство этих проблем я решил. Приобрел бесценный опыт работы с людьми — общение с менеджером, художниками и другими людьми. В общем, чувствую, за время написания этой игры левел повысился. Конечно, работа над ней еще не завершена — будут баги, которые нужно будет править, будут обновления (например, хочется сетевую игру для четырех человек, больше кепсов и т. д.). На данный момент настал период уже не для программиста — распространение и реклама. Игра получилась довольно интересной, думаю, не затеряется и даст о себе знать :)
P. S. Если кому-то интересны технические детали более подробно, на уровне кусков кода — пишите в комментарииличку, поделюсь, я не жадный :)
Автор: 1nt3g3r