Главная » Программирование игр под Android » ANDROIDGAME: СВЯЖЕМ ВСЕ ВМЕСТЕ

0

 

Мы почти закончили небольшой каркас нашей игры. Все, что осталось сделать, – связать все вместе, реализовав интерфейс Game, с помощью классов, написанных в предыдущем разделе. Список задач выглядит следующим образом: организовать управление окнами. В нашем случае это означает правильно обрабатывать жизненный цикл активности; использовать WakeLock и отслеживать его работу, чтобы экран не тускнел; инстанцировать и отдавать ссылки на Graphics, Audio, FilelO и Input тем элементам, которым они требуются; управлять экземплярами Screen и интегрировать их в жизненный цикл активности.

Наша главная цель – получить единый класс Androi dGame, на котором мы можем основываться. Все, что мы хотим делать впоследствии, – реализовать метод Game. getStartScreenC, который запустит нашу игру, например вот так:

Надеюсь, вы понимаете, почему было так важно организовать хороший компактный фреймворк перед тем, как заняться непосредственно программированием самой игры. Мы сможем заново использовать данный фреймворк во всех наших будущих играх, которые не требуют интенсивной работы с графикой. Рассмотрим листинг 5.15, который демонстрирует класс AndroidGame.

Листинг 5.15. AndroidGame.Java; свяжем все вместе package com.badlogi с.androi dgames.framework.i mpl:

Определение класса начинается с того, что Androi dGame дополняет класс Activity и реализует интерфейс Game. Далее определяем несколько членов, которые вам уже знакомы. Первый – Androi dFastRenderView, в котором мы будем рисовать и который будет управлять потоком основного цикла. Члены Graphics, Audio, Input и FilelO будут хранить экземпляры классов AndroidGraphics, AndroidAudio, Androidlnput и AndroidFi1e10. Как видите, ничего удивительного тут не происходит. Следующий член хранит текущий Screen. И наконец, еще один член отвечает за WakeLock, который нужен для того, чтобы экран не гаснул.

Уже знакомый метод onCreateO, вызываемый при запуске Activity, начинает свою работу с вызова метода базового класса onCreate, как это и требуется. Далее мы делаем активность Activity полноэкранной. В следующих нескольких строках устанавливаем искусственный фреймбуфер. В зависимости от ориентации активности нам необходим фреймбуфер размером либо 320 х 480 (в книжной ориентации), либо 480 х 320 (в альбомной ориентации). Чтобы определить ориентацию экрана, которую использует Activity, выбираем член ori entati on из класса Conf i gurati on, который мы получаем с помощью вызова getResources. getConf i gurati on . Основываясь на значениях данного члена, устанавливаем размер фреймбуфера и содаем Bitmap, который мы передадим экземплярам классов AndroidFastRenderView и AndroidGraphics чуть позже.

ПРИМЕЧАНИЕ

Экземпляр класса Bitmap имеет цветовой формат RGB565. Таким образом, нам не придется тратить память, а все наши рисунки будут обрабатываваться немного быстрее.

Мы также можем вычислить значения seal еХ и seal eY для классов Si ngl eTouchHandl ег nMultitouchhandler, которые необходимы для перевода координат событий касания в нашу систему фиксированных координат.

Далее инстанцируем AndroidFastRenderView, AndroidGraphics, Androi dAudi о, Androidlnput и AndroidFi lelO с необходимыми значениями аргументов конструктора. Потом вызываем метод getStartScreen, который реализуется нашей игрой, и задаем AndroidFastRenderView как вид с контентом Activity. Все эти вспомогательные классы, которые мы только что инстанцировали, конечно, будут выполнять еще кое-какую работу в фоновом режиме. Например, класс Androidlnput свяжет свой обработчик касаний с видом Androi dFastRenderVi ew.

Переходим к методу onResumeO из класса Acti vity. Как обычно, первое, что мы делаем, – вызываем метод родительского класса, как это и положено при программировании на Android. Далее получаем WakeLock и убеждаемся, что Screen получил информацию о том, что игра (а соответственно, и активность) была возобновлена. Затем просим AndroidFastRenderView возобновить поток визуализации, также запускающий основной цикл игры, в котором мы указываем текущему Screen обновить данные и отобразиться на каждой интерации.

Метод onPauseC снова вызывает метод родительского класса. Далее он высвобождает WakeLock и проверяет, завершен ли поток визуализации. Если мы не завершили поток визуализации до того, как вызывали текущий onPause Screen, могут возникнуть проблемы конкурентного доступа, так как потоку пользовательского интерфейса и потоку основного цикла одновременно нужен Screen. Когда мы удостоверились, что поток основного цикла завершен, мы приказываем текущему Screen остановиться. Если Activity должна быть удалена, мы также сообщаем Screen об этом событии, чтобы он мог выполнить необходимую очистку.

Пожалуй, методы getGraphi cs  и getAudi o понятны без объяснения. Мы просто возвращаем вызывающей стороне соответствующие экземпляры классов. Вызывающая сторона всегда будет одной из реализаций Screen нашей игры.

Метод setScreen , который мы наследуем от интерфейса Game, на первый взгляд кажется достаточно простым. Поскольку пи Л в качестве значения Screen нам не подходит, выполняем классическую nul1 -проверку. Далее указываем текущему Screen остановиться и уничтожить себя, Чтобы мы могли освободить место для нового Screen. Приказываем новому Screen возобновиться и обновиться с дельтой времени, равной нулю. Далее присваиваем полю Screen значение нового Screen.

Давайте задумаемся, кто и когда должен вызывать метод setScreen. Когда мы создавали Мистера Нома, мы определили все переходы между различными экземплярами класса Screen. Обычно мы вызываем метод AndroidGame.setScreenO в методе updateO одного из экземпляров этого класса Screen.

Допустим, у нас есть Screen главного меню, где мы можем проверить, нажата ли кнопка Play (Воспроизвести) в методе updateO. В этом случае нам понадобится переход к следующему Screen, и мы можем его выполнить с помощью вызова метода Androi dGame. setScreen О из метода MainMenu.updateO с новым экземпляром класса следующего экрана Screen. После вызова AndroidGame.setScreenC) экран MainMenu получит управление, после чего он должен сразу же вернуть управление, поскольку не является активным Screen. В данном случае вызывающая сторона – это AndroidFastRenderView в потоке основного цикла. Если рассмотреть ту часть основного цикла, которая отвечает за обновление и визуализацию активного Screen, то видно, что метод updateC) будет вызываться для класса MainMenu, а метод present будет вызываться для нового текущего Screen. Этого лучше избежать, поскольку мы определили интерфейс Screen таким образом, что методы resumeO и updateO должны быть вызваны как минимум однажды перед тем, как Screen должен будет отобразить себя. Поэтому мы вызываем два этих метода в методе Androi dGame. setScreen для нового Screen. Мощный класс AndroidGame выполнит всю сопутствующую работу.

Последний метод называется getCurrentScreen. Он просто возвращает текущий активный Screen.

Таким образом, мы создали каркас простой игры для Android. Все, что нам осталось сделать, – реализовать Screen нашей игры. Мы можем также заново использовать данный фреймворк для любых будущих игр, если они не требуют значительной мощности при обработке графики (иначе не обойтись без OpenGLES). Следует также заменить графическую часть фреймворка. Все остальные классы для аудио, ввода и файлового ввода-вывода могут быть использованы заново.

Подводя итог

В этой главе мы с нуля создали полноценный фреймворк для 20-игры на Android, который сможем использовать в будущем для других игр (если в них нет какой-то запредельной графики). Достаточно подробно был рассмотрен удобный и расширяемый дизайн. Чтобы наш мистер Ном стал ЗБ-игрой, необходимо просто взять этот код и заменить в нем части кода, отвечающие за визуализацию, с помощью OpenGL ES.

Теперь, когда фреймворк готов, сконцентрируемся на нашей основной задаче – написании игры.

Источник: Mario Zechner / Марио Цехнер, «Программирование игр под Android», пер. Егор Сидорович, Евгений Зазноба, Издательство «Питер»

По теме:

  • Комментарии