Текст книги "Создание игр для мобильных телефонов"
Автор книги: Майкл Моррисон
сообщить о нарушении
Текущая страница: 7 (всего у книги 35 страниц) [доступный отрывок для чтения: 13 страниц]
Хотя текст занимает не центральное место в играх, полезно познакомиться с его выводом. По крайней мере, в любых играх выводится количество набранных очков, и графический код должен отображать эту информацию. В мидлете текст выводится с применением текущего выбранного шрифта. По умолчанию используется шрифт среднего размера, но может возникнуть необходимость в шрифте другого размера, полужирного или курсива. Для этого вы должны создать шрифт и выбрать его перед тем, как применять. Метод setFont() используется для выбора шрифта, он объявлен так:
void setFont(Font font)
Объект Font моделирует текстовый шрифт и определяет вид, стиль и его размер. Объект Font поддерживает четыре разных стиля, которые определяются следующими константными членами класса: STYLE_PLAIN, STYLE_BOLD, STYLE_ITALIC и STYLE_UNDERLINED. Эти стили действительно являются константами, а последние три можно использовать в совокупности. Стиль STYLE_PLAIN отменяет все примененные к шрифту стили. Чтобы создать объект класса Font, вызовите статический метод getFont() и передайте в него вид, стиль и размер текста, каждый из этих параметров представляет собой целое число:
static Font getFont(int face, int style, int size)
Поскольку шрифты ограниченны в мидлетах, вы должны использовать целочисленные константы для определения каждого параметра. Например, вид шрифта должен быть определен одним из следующих значений: FACE_SYSTEM, FACE_MONOSPACE или FACE_PROPORTINAL. Аналогично, стиль шрифта должен быть определен одной из констант, о которых я упоминал ранее: STYLE_PLAIN или комбинацией STYLE_BOLD, STYLE_ITALIC и STYLE_UNDERLINED. Наконец, размер шрифта задается одной из предопределенных констант: SIZE_SMALL, SIZE_MEDIUM или SIZE_LARGE. Ниже приведен пример создания крупного полужирного подчеркнутого шрифта:
Font myFont = Font.getFont(Font.MONOSPACE, Font.LARGE,
Font.BOLD | Font.UNDERLINED);
Обратите внимание, что в MIDP-графике вы не можете создавать собственные шрифты, что вполне обоснованно ввиду ограниченных возможностей дисплея мобильного устройства. Также помните, что тот или иной шрифт доступен на мобильном телефоне, это хороший довод тестировать приложение на реальном устройстве.
Совет Разработчику
Если возникнет необходимость вернуться к настройкам шрифта, принятым по умолчанию, воспользуйтесь методом getDefaultFont() класса Font.
После того как вы определили шрифт методом getFont(), вы должны применить его к выводимому тексту, для чего используйте метод setFont():
g.setFont(myFont);
Теперь вы готовы к тому, чтобы вывести текст с нужными настройками. Метод drawString(), определенный в классе Graphics, – это как раз то, что нужно. Этот метод объявлен так:
void drawstring(String str, int x, int y, int anchor)
Метод drawString() в качестве первого параметра принимает объект класса String, который содержит выводимый текст. Следующие два параметра x и y определяют точку вывода текста. Особое значение этой точке придает параметр anchor. Чтобы упростить вывод текста и изображений, MIDP API предусматривает анкеры, которые помогают сэкономить массу сил при выводе текста и изображений и избавляют от излишних вычислений. Анкер (или точка привязки) ассоциирован с горизонтальной и вертикальной константами, каждая из которых определяет соответственно горизонтальное и вертикальное положения текста по отношению к анкеру. Горизонтальные константы, используемые для описания анкера, – это LEFT, RIGHT и HCENTER. Одна из этих констант в сочетании с вертикальной константой полностью описывает положение выводимого объекта. Вертикальные константы – это TOP, BASELINE и BOTTOM.
В качестве примера использования анкеров рассмотрим, как можно вывести текст и разместить его по центру у верхней границы экрана. Для этого нужно вызвать метод drawString() со следующими параметрами:
g.drawStirng(«Look up here!», getWidth() / 2, 0, Graphics.HCENTER | Graphics.TOP);
В этом коде текст выводится на точке экрана, расположенной в его верхней части в середине, – getWidth() / 2. Я предположил, что этот код помещен внутри класса, производного от Canvas, поэтому я смог воспользоваться методом getWidth() и получить значение ширины экрана. Это положение дополняется анкером, который является комбинацией двух констант Graphics.HCENTER и Graphics.TOP. Это означает, что выводимый текст форматируется по центру в горизонтальном направлении, а также, что верхняя граница текста имеет координату y.
Кроме метода drawString(), есть еще ряд методов, предназначенных для вывода текста. Методы drawChar() и drawChars() используются для вывода отдельных символов:
void drawChar(char character, int x, int y, int anchor)
void drawChars(char[] data, int offset, int length, int x, int y,
int anchor)
Оба метода работают аналогично методу drawString(), они запрашивают координаты точки вывода и анкер. Существует метод drawSubString(), с помощью которого можно выводить часть строки:
void drawSubString(String str, int offset, int len, int x, int y,
int anchor)
Этот метод содержит дополнительные параметры offset и len, определяющие подстроку в строке, передаваемой через параметр str.
Изображения очень важны для программирования игр, если, конечно, речь не идет о текстовых играх или играх, в которых применяется векторная графика. Изображения – это прямоугольные графические объекты, составленные из цветных пикселей. Каждый пиксель изображения описывает цвет определенной части изображения. Пиксели имеют уникальные цвета, описываемые цветовой системой RGB. Цветные изображения в MIDP-графики – это 24-битные изображения, следовательно, каждый пиксель изображения описывается 24 битами. Красный, зеленый и синий компоненты хранятся внутри этих четырех битов как самостоятельные 8-битовые значения.
В копилку Игрока
Вы, вероятно, слышали о 32-битной графике, где дополнительные 8 бит используются альфа-компонентами цвета, определяющих прозрачность пикселя. MIDP поддерживает прозрачность, а, следовательно, может использовать дополнительные 8 бит для альфа-компонентов на телефонных аппаратах, поддерживающих 8-битовую альфа-прозрачность. Если вы вспомните, мидлет Skeleton, который мы создали в предыдущей главе, сообщал о количестве альфа-уровней, поддерживаемых телефоном. При работе с книгой вы научитесь эффективно использовать 1-битовую альфа-прозрачность, т. е. простую прозрачность цвета изображения.
Перед тем как вернуться к подробному изучению вывода изображений, вы должны научиться загружать картинки. Поскольку обычно изображения хранятся во внешних файлах, для начала их необходимо загрузить. Для этого используется специальный статический метод createImage() класса Image, его объявление выглядит так:
public static Image createImage(String name) throws IOException
Чтобы создать изображения, используя метод createImage(), необходимо передать название файла с изображением:
Image img = Image.createImage(«Explosion.png»);
Метод createImage() возвращает объект класса Image, который впоследствии можно использовать для работы с изображением в MIDP API. Также можно создать пустое изображение, вызвав другую версию метода createImage(), который принимает ширину и высоту изображения. Класс Image представляет графические изображения (файлы форматов PNG, GIF или JPEG) и предоставляет ряд методов для определения размеров изображения. Также этот класс реализует метод, с помощью которого вы можете создать объект Graphics для картинки и рисовать на существующем изображении.
В копилку Игрока
Если вы не знаете, формат изображений PNG (Portable Network Graphics – Переносимая сетевая графика) – это улучшение формата GIF, постепенно распространяющегося как альтернатива GIF. Изображения в формате PNG сжимаются лучше GIF-изображений, в результате их файлы имеют меньший размер. Также, PNG-изображения удобнее использовать при создании игр, поскольку они поддерживают переменные альфа-уровни. Класс Image полностью поддерживает формат PNG.
В классе Graphics есть единственный метод для вывода изображения на экран – drawImage():
boolean drawImage(Image img, int x, int y, int anchor)
Вероятно, этот метод покажется вам знакомым, поскольку в нем, также как и в методе drawString(), используются анкеры. Подобно drawString() метод drawImage() выводит изображение в точке с координатами (x,y) и с учетом параметра anchor. Для вывода изображений можно использовать те же константы, что и при выводе текста.
Итак, чтобы нарисовать изображение, сначала необходимо вызвать статический метод Image.createImage() и создать и загрузить изображение. Затем следует вызвать метод drawImage() и вывести изображение на экран. Ниже приведен код, который загружает и выводит изображение:
public void paint(Graphics g) {
//очистить экран
g.setColor(255,255,255); //белый
g.FillRect(0, 0, getWidth(), getHeight());
//создать и загрузить изображение
Image img = Image.createImage("Splash.png");
//вывести изображение
g.drawImage(img, getWidth() / 2, getHeight() / 2, //Поскольку используются атрибуты HCENTER и VCENTER,
Graphics.HCENTER | Graphics.VCENTER); //изображение выводится в центре экрана
}
В этом примере сначала очищается дисплей, для чего выбирается белый цвет, которым заполняется весь экран. Перед рисованием необходимо получить чистую поверхность. Затем с помощью метода createImage() загружается и создается изображение Splash.png. После того как изображение создано, вызывается метод drawImage(), и картинка выводится на дисплей, константы HCENTER и VCENTER определяют анкер.
Создание программы OlympicsТеперь у вас есть представление о MIDP-графике и, вероятно, вам не терпится посмотреть, как это все работает в контексте мидлета. Вы узнали, что графика обрабатывается методом paint(). Однако класс мидлета не содержит этого метода, поэтому для выполнения операций с графикой вы должны использовать класс Canvas. Класс Canvas представляет собой абстрактную поверхность и должен быть включен в класс мидлета. Графические операции выполняются с помощью класса Graphics. Класс, производный от Canvas, можно использовать для вывода изображений на экран.
Чтобы начать работу над графическим мидлетом, вы должны выполнить следующее:
1. создать класс, производный от Canvas и ассоциированный с мидлетом;
2. создать объект класса Canvas как член-переменную класса мидлета;
3. установить объект класса Canvas как текущий экран мидлета, для чего вызвать метод setCurrent().
Самый лучший способ понять этот процесс – создать пример программы. Хороший пример простой программы с MIDP-графикой – программа, рисующая олимпийский символ, состоящий из пяти пересекающихся колец.
Давайте начнем с класса OCanvas, который создаст холст для использования в мидлете Olympics. Код класса OCanvas приведен в листинге 4.1.
Листинг 4.1. Класс OCanvas служит настраиваемым холстом для мидлета Olympics
import javax.microedition.lcdui.*;
public class OCanvas extends Canvas {
private Display display;
public OCanvas(Display d) {
super();
display = d;
}
void start() {
display.setCurrent(this);
repaint();
}
public void paint(Graphics g) {
// Clear the display
g.setColor(255, 255, 255); // White
g.fillRect(0, 0, getWidth(), getHeight());
// Draw the first row of circles //Дуги выводятся как идеальные окружности, если задать одинаковые значения высоты и ширины, а также изменение угла от 0° до 360°
g.setColor(0, 0, 255); // Blue
g.drawArc(5, 5, 25, 25, 0, 360);
g.setColor(0, 0, 0); // Black
g.drawArc(35, 5, 25, 25, 0, 360);
g.setColor(255, 0, 0); // Red
g.drawArc(65, 5, 25, 25, 0, 360);
// Draw the second row of circles
g.setColor(255, 255, 0); // Yellow
g.drawArc(20, 20, 25, 25, 0, 360);
g.setColor(0, 255, 0); // Green
g.drawArc(50, 20, 25, 25, 0, 360);
}
}
Этот класс расширяет класс Canvas и устанавливает себя как экран мидлета. Конструктор вызывает конструктор родительского класса Canvas и инициализирует переменную display. Метод start() устанавливает холст текущим экраном мидлета и обновляет изображение. Наиболее важный код содержится в методе paint(), он вызывает функции setColor() и drawArc() и рисует олимпийский символ. Обратите внимание, что все аргументы углов в функциях drawArc() равны 0 0 и 360°, в результате чего будут нарисованы полные эллипсы.
Когда вы определили класс OCanvas, можно объявить член-переменную класса мидлета OlympicsMIDlet:
private OCanvas canvas;
Член-переменная класса должна быть инициализирована конструктором класса:
canvas = new Ocanvas (Display.getDisplay(this));
Это весь код для обработки графики, который необходим в мидлете Olympics. В листинге 4.2 представлен полный код класса OlympicsMIDlet.
Листинг 4.2. Код класса OlympicsMIDlet содержится в файле OlympicsMIDlet.java
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class OlympicsMIDlet extends MIDlet implements CommandListener {
private OCanvas canvas;
public void startApp() {
if (canvas == null) {
canvas = new OCanvas(Display.getDisplay(this));
Command exitCommand = new Command("Exit", Command.EXIT, 0);
canvas.addCommand(exitCommand);
canvas.setCommandListener(this);
}
// инициализация
canvas.start(); //Метод start() холста запускает мидлет
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command c, Displayable s) {
if (c.getCommandType() == Command.EXIT) {
destroyApp(true);
notifyDestroyed();
}
}
}
По мере прочтения книги вы поймете, что приведенный код можно рассматривать как шаблон для построения любого игрового мидлета. Большая часть специального игрового кода размещается в классе, наследованном от Canvas, или в других поддерживающих классах.
Чтобы собрать и протестировать мидлет Olympics, скопируйте папку Olympics в папку apps, расположенную в папке установки J2ME Wireless Toolkit. Чтобы собрать мидлет, щелкните по кнопке Build (Собрать), а чтобы запустить эмулятор J2ME, щелкните по кнопке Run (Запустить). На рис. 4.5 показан мидлет The Olympics MIDlet.
Рис. 4.5. Мидлет The Olympics MIDlet демонстрирует построение основных геометрических фигур
Создание слайд-шоуХотя мидлет Olympics очень интересен и полезен для знакомства с программированием графики мидлета, вы, вероятно, хотите большего. Например, увидеть, как выводится текст и графика в контексте мидлета. В этой части книги вы разработаете слайд-шоу, что поможет вам попрактиковаться в выводе изображений и текста. Очевидно, слайд-шоу – не игра, однако этот мидлет поможет вам изучить основные приемы создания графики игр, например, комбинирование изображений и текста, а также обработку пользовательского ввода.
Мидлет Slideshow загружает несколько изображений и текст, после чего выводится на экран. Пользователь может пролистывать страницы слайд-шоу, используя клавиши со стрелками. Поскольку слайд-шоу относится к графической части мидлета, большая часть кода сосредоточена в классе SSCanvas, который хранит изображения и подписи под ними:
private Display display;
private Image[] slides;
private String[] captions = { "Love Circle Bowl", "Double Wide Spine",
"Flume Zoom Over-vert", "Kulp De Sac Bowl",
"Louie's Ledge" };
private int curSlide = 0;
Переменная sliders – это массив объектов Image, она инициализируется в конструкторе класса SSCanvas. Ниже приведен код этого конструктора:
public SSCanvas(Display d) {
super();
display = d;
// загрузить изображения слайд-шоу
try {
slides = new Image[5];
slides[0] = Image.createImage("/LoveCircle.jpg");
slides[1] = Image.createImage("/DoubleWide.jpg");
slides[2] = Image.createImage("/FlumeZoom.jpg");
slides[3] = Image.createImage("/KulpDeSac.jpg");
slides[4] = Image.createImage("/LouiesLedge.jpg");
}
catch (IOException e) {
System.err.println("Failed loading images!");
}
}
Этот код понять очень просто, он создает массив объектов Image и инициализирует каждый элемент, загружая с помощью метода Image.createImage() соответствующее изображение. Важно отметить, что имя каждого файла изображения начинается с символа / (Косой слеш), обозначающего, что файл расположен в корневой директории мидлета. Это важно, так как эти изображения упакованы в JAR-архив вместе с классом мидлета, а следовательно, они должны быть доступны для чтения.
В копилку Игрока
Если вам интересно, что за изображения помещены в слайд-шоу, то это фотографии общественного скейт-парка, строящегося в моем городе, Нэшвилле, штат Теннеси. Я помогал с оформлением документов на строительство парка нашему сообществу скейтбордистов.
Основные действия выполняются в методе paint() класса SSCanvas, который выводит текущее изображение слайд-шоу и подпись на экран. Ниже приведен код метода paint():
public void paint(Graphics g) {
// очистить экран
g.setColor(255, 255, 255); // белый
g.fillRect(0, 0, getWidth(), getHeight());
// вывести текущее изображение
g.drawImage(slides[curSlide], getWidth() / 2, getHeight() / 2, //Текущее изображение выводится в центре экрана
Graphics.HCENTER | Graphics.VCENTER);
// настроить шрифт
Font f = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD,
Font.SIZE_MEDIUM);
g.setFont(f);
// вывести текущее изображение
g.setColor(0, 0, 0); // Черный //Текущая надпись центрирована и выводится вдоль верхней границы экрана
g.drawString(captions[curSlide], getWidth() / 2, 0,
Graphics.HCENTER | Graphics.TOP);
}
Метод paint() сначала очищает экран, тем самым удаляя то, что осталось от предыдущего слайда. Затем в центре экрана выводится текущее изображение. Далее выполняется настройка шрифта – полужирный, среднего размера, пропорциональный. Наконец, устанавливается черный цвет, и по центру у нижнего края экрана выводится текст.
Когда вывод графики завершен, последняя часть кода класса SSCanvas занимается обработкой пользовательского ввода. Нажимая клавиши со стрелками влево и вправо, пользователь может перелистывать слайды. Технически до главы 6 вы не научитесь обрабатывать ввод в играх, но здесь я познакомлю вас с основами. Ниже приведен код метода keyPressed():
public void keyPressed(int keyCode) {
// Get the game action from the key code
int action = getGameAction(keyCode);
// Process the left and right buttons
switch (action) {
case LEFT:
if (–curSlide < 0) //Перейти к последнему слайду, если первый уже показан
curSlide = slides.length – 1;
repaint();
break;
case RIGHT:
if (++curSlide >= slides.length) //Перейти к первому слайду, если последний уже показан
curSlide = 0;
repaint();
break;
}
}
Метод keyPressed() открывает новые горизонты программирования игровых мидлетов – обработку игровых событий. Игровое событие – это особое событие, которое ассоциировано с клавишами, обычно используемыми в играх. Смысл заключается в том, что вы можете привязать действия к определенным клавишам, чтобы настроить пользовательский интерфейс. В методе keyPressed() с помощью метода getGameAction() определяется игровое событие, ассоциированное с клавишами. Константы LEFT и RIGHT используются для описания нажатий клавиш со стрелками влево и вправо. Если значение action совпадает со значением одной из констант, то номер текущего слайда увеличивается или уменьшается, а затем отображается новый слайд.
Листинг 4.3. Так выглядит класс SSCanvas, который выполняет большую часть работы мидлета Slideshow. В листинге 4.3 приведен полный код этого класса:
import javax.microedition.lcdui.*;
import java.io.*;
public class SSCanvas extends Canvas {
private Display display;
private Image[] slides;
private String[] captions = { "Love Circle Bowl", "Double Wide Spine", //Индексы массива соответствуют изображениям
"Flume Zoom Over-vert", "Kulp De Sac Bowl",
"Louie's Ledge" };
private int curSlide = 0;
public SSCanvas(Display d) {
super();
display = d;
// загрузить изображения
try {
slides = new Image[5];
slides[0] = Image.createImage("/LoveCircle.jpg");
slides[1] = Image.createImage("/DoubleWide.jpg");
slides[2] = Image.createImage("/FlumeZoom.jpg");
slides[3] = Image.createImage("/KulpDeSac.jpg");
slides[4] = Image.createImage("/LouiesLedge.jpg");
}
catch (IOException e) {
System.err.println("Failed loading images!");
}
}
void start() {
display.setCurrent(this);
repaint();
}
public void keyPressed(int keyCode) {
// получить игровое событие
int action = getGameAction(keyCode);
// обработать нажатия клавиш
switch (action) {
case LEFT:
if (–curSlide < 0)
curSlide = slides.length – 1;
repaint();
break;
case RIGHT:
if (++curSlide >= slides.length)
curSlide = 0;
repaint();
break;
}
}
public void paint(Graphics g) {
// очистить экран
g.setColor(255, 255, 255); // белый
g.fillRect(0, 0, getWidth(), getHeight());
// вывести текущее изображение
g.drawImage(slides[curSlide], getWidth() / 2, getHeight() / 2,
Graphics.HCENTER | Graphics.VCENTER);
// установить шрифт
Font f = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD,
Font.SIZE_MEDIUM);
g.setFont(f);
// вывести текущее содержание
g.setColor(0, 0, 0); // черный
g.drawString(captions[curSlide], getWidth() / 2, 0,
Graphics.HCENTER | Graphics.TOP);
}
}
Чтобы интегрировать холст в мидлет, необходимо создать объект класса SSCanvas в классе SlideshowMIDlet:
private SSCanvas canvas;
Затем в конструкторе класса SlideshowMIDlet эта переменная инициализируется. Полный код мидлета Slideshow приведен в листинге 4.4.
Листинг 4.4. Код мидлета Slideshow
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class SlideshowMIDlet extends MIDlet implements CommandListener {
private SSCanvas canvas; //Использование настраиваемого холста – это уникальный фрагмент кода мидлета
public void startApp() {
if (canvas == null) {
canvas = new SSCanvas(Display.getDisplay(this));
Command exitCommand = new Command("Exit", Command.EXIT, 0);
canvas.addCommand(exitCommand);
canvas.setCommandListener(this);
}
// Start up the canvas
canvas.start();
}
public void pauseApp() {}
public void destroyApp(boolean unconditional) {}
public void commandAction(Command c, Displayable s) {
if (c.getCommandType() == Command.EXIT) {
destroyApp(true);
notifyDestroyed();
}
}
}
Как вы, вероятно, заметили, этот код практически идентичен классу мидлета Olympics, созданного нами ранее. Это подтверждает, что большая часть функций возложена на класс, производный от Canvas.