-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Seven for the dwarf-lords in their halls of stone
Saboteur - приложение под Android, которое симулирует реальную настольную игру Saboteur(Гномы вредители, см. https://ru.wikipedia.org/wiki/Гномы-вредители) Мы реализовали возможность играть на одном устройстве (без сетевого взаимодействия) от двух до семи человек. Логика игры соответствует нерасширенной версии настольной игры, то есть все правила оригинальные. Также реализованы возможность сохранять игру в базу данных, которая находится прямо на устройстве и загружать игру оттуда же. Также реализован гугловский мультиплеер. Он полностью написан, но на стадии дебаггинга.
Подробнее о том, что мы сделали:
Эта версия игры, уже почти полностью реализованная и отдебаженная, в плане декомпозиции программы состоит из двух больших блоков: логика игры и интерфейс.
Для реализации логики было придумано 3 класса - Field, Player, Card. Но так как карт в этой игре много, у Card есть много наследников. Например, есть карта Tunnel, которая хранит, можно ли по этому туннелю пройти вверх, вниз, вправо, влево. Класс Field хранит поле, на которое можно ставить туннели, и основную логику - какой игрок ходит, является ли текущий игрок саботёром, какие у него карты в руке и т.д.
Отвечает за связь всего происходящего во что-то единое. Хранит внутри себя экземпляр класса Multiplayer, Field и GameData.
Есть два больших класса - GameActivity и MainActivity.
MainActivity: Здесь происходит подготовка к игре, в частности именно здесь находится взаимодействие с базой данных и классом DataBaseHelper. Из базы данных игрок может получить информацию об игре, а именно, сериализованный контроллер, который хранится там в виде строки. Или же игрок может сам создать новый контроллер, для этого надо нажать кнопку newGame. При ее нажатии вызывается начало игры, и инициализация нового контроллера.
В этом активити еще есть опции: выбрать количество игроков (settings), сохранить текущий контроллер в базу данных. Коротко о том, что такое текущий контроллер. MainActivity хранит контроллер в качестве поля, и когда GameActivity завершается(то есть игра прекращается), то результат игры, то есть контроллер, передается обратно в MainActivity, что обеспечивает возможность продолжать игру, даже не сохраняя ничего в базу данных. Но если выйти из приложения, понятно, что все поля убьются, и кнопка continue не будет работать, придется загружать информацию из БД. Как происходит сохранение игры: Можно сохранить игру в БД по ключу - названию, которое надо написать самому или выбрать default(это про кнопку save). Соответственно загрузка игры происходит аналогично: надо выбрать на экране соответствующий ключ. Реализована также возможность поиска по базе данных(кнопка с лупой).
GameActivity: Несмотря на то, что этот класс обеспечивает взаимодействие с логикой игры, и только с ней, он достаточно объемны, и про него особо говорить нет смысла. Функциональность(коротко):
- Посмотреть на поле (кнопка FIELD)
- Посмотреть свои карты (кнопка CARDS)
- Сыграть карту или узнать о том, что это запрещено правилами (тыкнуть на карту)
- Переключить ход другому игроку (кнопка SWITCH)
- Узнать своего персонажа (тыкнуть на рубашку карты в правом верхнем углу)
- Посмотреть инструменты всех игроков (кнопка TOOLS)
- Узнать лог игры (кнопка LOG) Лог игры сохраняется в контроллере, то есть при загрузке контроллера из БД или просто из MainActivity старый лог сохраняется(то есть моно узнать, кто что делал в последний раз, когда играли).
Немного в заключение про Activity: Мы не очень хорошо представляем как правильно писать или декомпозировать UI, поэтому классы такие громоздкие, и нам приходилось переписывать и реструктурировать программу очень много раз. Тем не менее хоть какая-то декомпозиция есть. В частности есть внутренние класс Style, который хранит константы и методы форматирования. Есть внутренний класс Layouts, который занимается переходами от одного экрана к другому(то есть, например, раньше здесь была кнопка A, теперь тут кнопка B). В GameActivity даже есть наследники TableLayout и Button со своими расширенными методами, чтобы не наваливать по 30 методов в 1 класс.
MultiPlayer осуществляется с помощью Turn-based Multiplayer от Google. Соответственно пришлось разобраться, как включить соответствующую функцию для приложения, оказалось, что надо платить гуглу 25$. Со всем этим возникло наибольшее количество проблем, так как есть много странных нюансов, но очень помог пример класса опять же от гугла, который с октября сильно поменялся.
Реализуется через 2 класса:
Тут хранится вся информация об игре - текущий матч (TurnBasedMatch), id игрока и т.д. В нём реализованы методы, которые хочется получать из Controller, в том числе и во время работы GameActivity.
В этом Activity происходит авторизация в Google Play, создание игр и присоединение к ним. Но и то, и другое происходит с помощью стандартных гугловских activity, поэтому прежде всего класс занимается обработкой результатов их работы. MultiPlayerActivity сделан простым добавлением кнопок в стандартный Activity, поэтому выглядит не очень красиво.
Есть ещё набор классов, которые хранят состояние игры:
GameData { ArrayList turns, Shuffle shuffle}
Первое поле - это все ходы, а второе - это перестановка карт в колоде, на поле и перестановка ролей игроков. Это нужно, чтобы у первый игрок сгенерировал всё это рандомно, а уже каждый следующий получил всё готовое. Экземпляр GameData хранится в контроллере, соответственно в конце хода каждого игрока она рассылается всем остальным, перед этим в неё добавляется один ход.
Чтобы разработчику UI не понадобилось менять всё, пришлось менять Card и всех наследников. Они автоматически высылают информацию о том, что они разыгрались в конце хода.