Главная » Программирование игр под Android » Непрерывная визуализация в потоке пользовательского интерфейса – РАЗРАБОТКА ИГР ДЛЯ ОС ANDROID

0

Все, что мы сделали до этого момента, – обеспечили изменение текста в TextView при необходимости. Перерисовка осуществляется самим TextView. Создадим наш собственный View, единственной задачей которого будет предоставление нам возможности рисовать на экране. Нам необходимо, чтобы этот View обновлялся как можно чаще. Нам также нужен простой способ выполнять рисование каким-нибудь волшебным способом.

Хотя на слух это может казаться довольно сложной задачей, на самом деле Android обеспечивает легкость в реализации таких задач. Все, что нам надо, – создать класс, наследуемый от View, и переопределить его метод View.onDraw. Этот метод вызывается системой Android каждый раз, когда View требуется перерисовка. Выглядит он примерно так:

Пока не так уж все сложно, не правда ли? Мы передаем методу onDraw экземпляр класса Canvas. В ближайших разделах он станет нашей рабочей лошадкой, позволяя нам рисовать фигуры и изображения в другом изображении или Vi ew (или на поверхности экрана, о чем мы поговорим чуть позже).

Использовать объект RenderView можно так же, как TextView, – мы просто устанавливаем его в качестве контейнера содержимого нашей активности и обрабатываем все процессы, которые нам нужны. Однако пока этот класс не слишком полезен для нас по двум причинам: во-первых, на самом деле он ничего не рисует; во-вторых, даже когда сможет, то будет делать это только тогда, когда активность получит запрос на перерисовку (то есть при ее старте или возобновлении либо после исчезновения перекрывающего ее диалогового окна). Как нам заставить его перерисовываться самостоятельно?

Вот так:

Вызов метода View.invalidateO в конце onDrawO сообщит Android о необходимости перерисовки RenderView при первой возможности. Все это по-прежнему происходит в пользовательском потоке, который обычно слегка нетороплив. Тем не менее у нас уже есть непрерывная перерисовка в методе onDraw, хотя и не слишком быстрая. Мы исправим это позже; пока для наших задач этого достаточно.

Итак, теперь пора вернуться к загадочному классу Canvas. Это довольно мощный инструмент, обертывающий низкоуровневую библиотеку Skia, предназначенную для выполнения 20-рендеринга центральным процессором. Класс Canvas предлагает нам множество методов рисования для фигур, изображений и даже текста.

Где мы будем использовать эти методы? Это зависит от поставленных целей. Canvas может перерисовываться в экземпляр Bitmap – другого класса, предлагаемого 2D API Android (мы рассмотри его позже). В данном случае прорисовка будет происходить для части экрана, занимаемой View. Конечно, это довольно значительное упрощение: на самом деле прорисовка производится не на самом экране, а в некое изображение, которое система позже будет использовать в комбинации с изображениями от всех других View активности, чтобы составить из них конечную итоговую картинку. Получившееся изображение передается графическому процессору, отображающему его на экране еще более загадочными путями.

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

Метод onDraw будет вызываться так часто, как это позволяет система. Для нас это очень похоже на тело нашего (теоретического) главного игрового цикла. Если мы будем реализовывать игру с помощью этого метода, в него можно поместить всю игровую логику. Однако мы не будем делать этого по разным причинам (одной из которых является производительность).

Давайте сделаем что-нибудь интересное. Каждый раз, когда выходит новое API для рисования, я пишу маленький тест, проверяющий частоту перерисовки экрана. Единственное содержание вызываемого метода перерисовки – заполнение экрана случайным цветом. Мне нужно только найти метод для данного API, который позволит это сделать, не задумываясь о деталях реализации. Напишем подобный тест для нашей реализации RenderView.

Метод класса Canvas, заполняющий цель прорисовки определенным цветом, называется Canvas.drawRGEK):

Аргументы г, g и b хранят по одному компоненту цвета, которым мы хотим заполнить экран. Каждый из них принимает значение от 0 до 255, поэтому цвета определяются в формате RGB888. Если вы не помните подробностей, касающихся цветов.

Листинг 4.12 показывает код нашего калейдоскопа.

ВНИМАНИЕ

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

Листинг 4.12. Активность RenderViewTest package com.bad.ogiс.androidgames;

Код для нашего первого графического примера довольно сложен. Мы определяем класс RenderView как внутренний для активности RenderViewTest. Этот класс наследуется от Vi ew и имеет обязательный конструктор, также переопределенный метод onDrawO. Кроме того, он включает в себя внутренний член типа Random (мы будем использовать его для генерации случайных цветов).

Метод onDraw предельно прост. В нем мы сначала командуем Canvas заполнить весь контейнер представления случайным цветом. Для каждого компонента цвета мы с помощью метода Random, next Into определяем случайное число от 0 до 255, после чего сообщаем системе, что ожидаем перерисовки экрана (то есть вызова метода onDraw) как можно быстрее.

Метод onCreate активности включает полноэкранный режим и устанавливает экземпляр RenderView в качестве контейнера представления. Для сохранения краткости примера в данном случае я решил игнорировать защиту от блокировки.

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

ПРИМЕЧАНИЕ

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

Получение разрешения экрана и координатной системы

Вы помните, что фреймбуфер хранит цвета пикселов, показываемых на экране. Количество доступных нам пикселов определяется разрешением экрана (шириной и высотой в пикселах).

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

Класс Canvas содержит два метода, помогающие нам в этом:

Они возвращают ширину и высоту элемента, для которого Canvas осуществляет прорисовку. Обратите внимание – в зависимости от текущей ориентации активности ширина может быть больше или меньше высоты. Например, в моем Nexus One установлено разрешение 480 х 800 пикселов в портретном режиме, поэтому метод Canvas. getWi dth О вернет 480, a Canvas .getHeight – 800. В ландшафтном режиме эти значения меняются местами.

Вторая часть необходимой информации – координатная система, используемая для рендеринга. Прежде всего, значения имеют только целочисленные координаты пикселов (существует так называемая концепция субпикселов, но мы пока игнорируем ее). Мы уже также знаем, что начало координатной системы всегда находится в точке (0; 0) в левом верхнем углу дисплея – неважно, в портретном или ландшафтном режиме. Ось х всегда направлена слева направо, ось у – всегда вниз. На рис. 4.12 показан гипотетический экран с разрешением 48 х 32 пиксела в ландшафтном режиме.

Рис. 4.12. Координатная система экрана 48 х 32 пиксела

Обратите внимание – начало координатной системы на рисунке совпадает с левым верхним пикселом экрана. Правый нижний пиксел экрана имеет координаты не (48; 32), как мы могли бы ожидать, а (47; 31). В целом (width – 1; height – 1) всегда является позицией правого нижнего пиксела экрана.

Рисунок 4.12 демонстрирует координатную систему гипотетического дисплея в ландшафтном режиме. Теперь вы можете представить, как дисплей будет выглядеть в портретном режиме.

Все методы прорисовки Canvas действуют внутри этой координатной системы. Обычно мы можем оперировать с гораздо большим количеством пикселов, чем 48 х 32 (например, 800 х 480). Теперь займемся рисованием пикселов, линий, кругов и прямоугольников.

ПРИМЕЧАНИЕ

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

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

По теме:

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