...
В копилку Игрока
Если вы создаете игру с возможностью изменения настроек, или вам необходимо получить информацию от пользователя, используйте класс javax.microedition.lcdui.Screen. Экран (screen) – это настраиваемый GUI-компонент мидлета, который служит базовым классом для других важных компонентов. Значимость экранов заключается в том, что они отображают всю экранную информацию. Несколько экранов не могут отображаться одновременно. Вы можете представить несколько экранов как карты, которые берете одну за другой. Большинство мидлетов используют классы javax.miroedition.lcdui.Form, javax.miroedition.lcdui.TextBox, или javax.miroedition.lcdui.List, поскольку они предоставляют широкие возможности. Экраны можно использовать в совокупности с объектами класса Canvas, в результате чего для игрового мидлета можно создать полноценный GUI. Нельзя отображать экран и холст одновременно, однако вы можете переключаться между отображениями.
Основы разработки мидлетов
Прежде чем приступить к разработке мидлетов, необходимо установить J2ME Wireless Toolkit, который находится на прилагаемом компакт-диске. Вы также можете использовать инструменты для разработки мобильных приложений, выпускаемые другими компаниями, если вашей целью является написание приложения для конкретной модели телефона. Но если вы хотите эмулировать мобильные телефоны с поддержкой Java на компьютере, можно использовать J2ME Wireless Toolkit.
Чтобы воплотить концепцию мидлета в реальность, необходимо:
1. разработать файлы кода;
2. скомпилировать файлы с исходным кодом в классы байт-кода;
3. выполнить предварительную верификацию классов байт-кода;
4. упаковать файлы байт-кода в файл JAR, добавить необходимые ресурсы и файлы манифеста (подробнее об этом чуть позже);
5. разработать JAD-файл (описатель приложения), сопровождающий JAR-файл;
6. протестировать и отладить мидлет.
Шаг 1 выполняется в обычном текстовом редакторе. Если у вас нет специального редактора кода, можно воспользоваться, например, текстовым редактором Notepad. Шаг 2 подразумевает использование стандартного компилятора Java для компиляции файлов мидлета с исходным кодом. На шаге 3 необходимо выполнить предварительную верификацию скомпилированного кода, для чего используйте специальный инструмент предварительной верификации. На шаге 4 выполняется упаковка файлов кода мидлета в Java-архив (JAR). Шаг 5 требует создания специального файла описания – текстового файла, содержащего информацию о вашем мидлете. И, наконец, на шаге 6 вы можете протестировать мидлет в эмуляторе J2ME.
Хотя вы можете выполнить каждый из этих шагов, используя инструменты J2ME Wireless Toolkit, вызываемые из командной строки, в предыдущей главе вы увидели, как просто собираются и тестируются приложения в среде Sun KToolbar.
Создание примера игры Skeleton
Я бы хотел рассказать вам о создании трехмерной игры в реальном времени для нескольких игроков, однако из-за большой сложности это затруднит понимание того, как устроен и написан мидлет. Для начала вы узнаете, как построить простейший мидлет, который называется Skeleton. Этот мидлет отображает текстовую информацию о мобильном телефоне. Поскольку эта информация содержит очень важные параметры телефона (размер игрового экрана и глубина цвета), полезно выполнять такую проверку на реальных устройствах.
При построении мидлета Skeleton вы пройдете через последовательность шагов, обозначенную в предыдущем разделе. Этот процесс практически идентичен для построения любых мидлетов. Ниже перечислены этапы построения мидлета Skeleton:
1. написание кода мидлета;
2. компиляция мидлета;
3. предварительная верификация мидлета;
4. упаковка мидлета;
5. тестирование мидлета.
В последующих разделах подробно рассматривается каждый из этапов, а кульминацией будет полная разработка первого J2ME-мидлета.
Написание программного кода
В этом разделе вы создадите код мидлета Skeleton. Первая часть создаваемого кода – это импорт нескольких важных пакетов J2ME. Вы можете не импортировать пакеты, а ссылаться на них через полное имя (например, javax.microedition.midlet.MIDlet), но это очень неудобно и делает код плохо читаемым. Поэтому первые две строки кода вашего мидлета импортируют два главных пакета, необходимых для разработки:
import javax.microedition.midlet.*; import javax.microedition.lcdui.*;
...
Совет Разработчику
Многие Java-программисты не одобряют импортирование целых пакетов с использованием группового символа * (звездочка), поскольку при этом не содержится информация об особых классах, которые вы импортируете. Однако это очень простой и быстрый способ импортировать все классы пакета, а для целей этой книги я буду использовать самый простой подход, чтобы сделать код как можно более понятным. Не бойтесь импортировать классы при написании собственного кода, это поможет вам сделать код более ясным.
Пакет javax.microedition.midlet включает поддержку класса MIDlet, в то время как пакет javax.microedition.lcdui включает поддержку классам и интерфейсам GUI, которые используются для создания GUI-мидлета, например, класс Display. Импортировав эти два пакета, вы можете объявить класс SkeletonMIDlet, производный от MIDlet:
public class SkeletonMIDlet extends MIDlet implements CommandListener }
Не удивительно, что класс SkeletonMIDlet расширяет MIDlet, но вот реализация интерфейса CommandListener может показаться весьма странной. Этот интерфейс необходим для создания команды Exit, которая позволяет пользователю выходить из мидлета. Если говорить более подробно, то интерфейс CommandListener реализован таким образом, чтобы мидлет мог отвечать на командные события. Единственная переменная, член класса SkeletonMIDlet, – это объект SCanvas, который представляет главный экран:
private SCanvas canvas;
Класс SCanvas – это особый класс мидлета, производный от класса Canvas. Холст инициализируется в методе startApp():
public void startApp() {
if (canvas == null) {
canvas = new SCanvas(Display.getDisplay(this));
Command exitCommand = new Command("Exit", Command.EXIT, 0); //Создаем команду EXIT и добавляем ее в
canvas.addCommand(exitCommand); //класс Canvas. Теперь canvas сможет отвечать на эту команду
canvas.setCommandListener(this);
}
// Start up the canvas
canvas.start();
}
Метод startApp() вызывается при переходе мидлета в состояние Active, первым шагом является создание холста. Объект Display мидлета создается и передается при создании холста. Команда Exit создается путем передачи конструктору трех параметров: названия команды, ее типа и приоритета. Имя команды определяется пользователем и появляется как экранная кнопка на дисплее устройства в зависимости от приоритета и количества доступных кнопок. Тип команды должен быть определен одной из трех предопределенных констант – EXIT, OK или CANСEL. Команда добавлена на холст, поэтому она становится активной. Но все еще необходимо настроить приемник команд для перехвата и обработки командных событий. Для этого вызывается метод setCommandListener(), которому передается параметр this, в результате класс мидлета (SkeletonMIDlet) становится приемником команд. Это замечательно, потому как ранее вы указали для имплементации класса интерфейс CommandListener().
...
Совет Разработчику
Приоритет команды используется для определения доступности команды пользователю. Это необходимо из-за того, что большинство устройств имеет ограниченный набор клавиш для использования мидлетами. Следовательно, только самые важные команды могут быть связаны с экранными кнопками. Другие команды используются через меню, доступ к которому мидлету получить не так уж и просто. Чем важнее команда, тем меньше номер ее приоритета. Например, значение 1 соответствует команде с наивысшим приоритетом, а в примере Skeleton команде Exit присвоен приоритет 2, что соответствует высокой важности команды. Конечно, значения приоритетов относительны, и поскольку в рассматриваемом примере не используются другие команды, то численное значение приоритета в данном случае не существенно.
Команда Exit мидлета Skeleton обрабатывается методом commandAction():
public void commandAction(Command c, Displayable s) {
if (c.getCommandType() == Command.EXIT) {
destroyApp(true);
notifyDestroyed();
}
}
Методу commandAction() передаются два аргумента – команда и экран, на котором будет сгенерирована команда. В рассматриваемом примере интересна лишь команда. Объект Command сравнивается с константой Command.EXIT, таким образом осуществляется проверка, действительно ли выполняется команда Exit. Если да, то вызывается метод destroyApp() и мидлет разрушается. Аргумент true означает, что разрушение безусловно, то есть мидлет разрушается в любом случае, даже если возникла ошибка. Затем вызывается метод notifyDestriyed(), который сообщает менеджеру приложений о том, что мидлет перешел в состояние Destroyed. Мидлет Skeleton не работает с методами pauseApp() и destroyApp(), но вы должны реализовать их в любом случае:
public void pauseApp() {} public void destroyApp(boolean unconditional) {}
Хотя вы уже видели все фрагменты кода, полное содержимое файла SkeletonMIDlet.java представлено в листинге 3.1. Листинг 3.1. Код класса SkeletonMIDlet, расположенный в файле SkeletonMIDlet.java
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class SkeletonMIDlet extends MIDlet implements CommandListener {
private SCanvas canvas;
public void startApp() {
if (canvas == null) {
canvas = new SCanvas(Display.getDisplay(this));
Command exitCommand = new Command("Exit", Command.EXIT, 0);
canvas.addCommand(exitCommand);
canvas.setCommandListener(this);
}
// инициализация холста
canvas.start();
}
public void pauseApp() {} //В данном примере эти методы не используются вовсе, однако все равно
public void destroyApp(boolean unconditional) {} //необходимо предоставить пустые реализации, чтобы удовлетворить требованиям класса MIDLET
public void commandAction(Command c, Displayable s) {
if (c.getCommandType() == Command.EXIT) {
destroyApp(true); //В конце следует вызвать метод destroyApp(), хотя на самом деле
notifyDestroyed(); //работу мидлета завершает метод notifyDestroyed()
}
}
}
Оставшаяся часть кода мидлета Skeleton связана с классом SCanvas и представлена в листинге 3.2. Листинг 3.2. Класс SCanvas служит как настраиваемый холст мидлета Skeleton
import javax.microedition.lcdui.*;
public class SCanvas extends Canvas {
private Display display;
public SCanvas(Display d) {
super();
display = d;
}
void start() {
display.setCurrent(this); //Это весьма важный код, так как он устанавливает текущий холст для мидлета
repaint();
}
public void paint(Graphics g) {
// очистить холст
g.setColor(0, 0, 0); // черный //Прежде чем начинать
g.fillRect(0, 0, getWidth(), getHeight()); //рисование на холсте,
g.setColor(255, 255, 255); // белый //необходимо очистить фон
// вывести размер экрана
int y = 0;
String screenSize = "Screen size: " + Integer.toString(getWidth()) + " x " + Integer.toString(getHeight());
g.drawString(screenSize, 0, y, Graphics.TOP | Graphics.LEFT);
// вывести число цветов дисплея
y += Font.getDefaultFont().getHeight();
String numColors = "# of colors: " + Integer.toString(display.numColors());
g.drawString(numColors, 0, y, Graphics.TOP | Graphics.LEFT);
// вывести число доступных альфа-уровней
y += Font.getDefaultFont().getHeight();
String numAlphas = "# of alphas: " + Integer.toString(display.numAlphaLevels());
g.drawString(numAlphas, 0, y, Graphics.TOP | Graphics.LEFT);
// вывести полный объем памяти и объем свободной памяти
Runtime runtime = Runtime.getRuntime();
y += Font.getDefaultFont().getHeight();
String totalMem = "Total memory: " + Long.toString(runtime.totalMemory() / 1024) + "KB";
g.drawString(totalMem, 0, y, Graphics.TOP | Graphics.LEFT);
y += Font.getDefaultFont().getHeight();
String freeMem = "Free memory: " + Long.toString(runtime.freeMemory() / 1024) + "KB";
g.drawString(freeMem, 0, y, Graphics.TOP | Graphics.LEFT);
}
}
Класс SCanvas – производный от класса Canvas, его конструктор принимает единственный параметр Display. Конструктор просто определяет переменную display, после чего дисплей мидлета доступен в любом месте кода холста. Метод start() вызывает метод setCurrent() объекта Display и устанавливает холст в качестве экрана. Мидлет может иметь несколько экранов, в этом случае для переключения между ними вы можете использовать метод setCurrent(). Метод start() вызывает метод repaint(), выполняющий перерисовку холста.
...
Совет Разработчику
Несмотря на то что класс SCanvas мидлета Skeleton произведен от класса Canvas, в большинстве примеров, рассматриваемых в книге, этот класс является производным от GameCanvas, который предоставляет специальные возможности, как дважды буферизованная графика и эффективная обработка ввода с клавиатуры. Эти возможности не нужны для создания приложения Skeleton.
Рисование на холсте – это большая часть кода мидлета Skeleton, выполняется методом paint(). Сейчас не очень важно внедряться во все тонкости этого кода, потому как следующая глава посвящена мобильной графике. Тем не менее я сделаю небольшое описание на тот случай, если вы хотите заглянуть немного вперед.
Метод начинается с очистки холста и заполнения его черным цветом. Затем изменяется цвет точки на белый и выводится текст. Сначала определяется размер экрана, этот параметр выводится по центру в верхней части экрана. Далее определяется число доступных цветов и альфа-уровней, эта информация тоже выводится на экран. И, наконец, выводится информация об общем количестве памяти и объеме свободной памяти.
...
В копилку Игрока
Число альфа-уровней, поддерживаемых телефоном, определяет возможность управления прозрачными областями изображений. Например, телефоны поддерживают как минимум два альфа-уровня, поэтому пиксель может находиться в двух состояниях: прозрачном и непрозрачном.
Теперь, когда написание кода завершено, вы почти готовы к сборке и тестированию мидлета Skeleton. Вам осталось только создать пару важных файлов поддержки, необходимых для упаковки мидлета для его распространения.
Подготовка мидлета для распространения
Подготовка игрового мидлета включает в себя сжатие нескольких файлов, используемых мидлетом, в JAR-файл. Кроме включения предварительно верифицированного файла класса в JAR-архив, вы также должны включить файлы ресурсов, ассоциированных с мидлетом, а также файл манифеста, который описывает содержимое JAR-файла.
В нашем примере единственным ресурсом является пиктограмма, отображаемая рядом с мидлетом на экране устройства. Чуть позже я поясню все, что касается пиктограмм. А пока давайте рассмотрим файл манифеста. Файл манифеста – это специальный текстовый файл, который содержит перечень свойств мидлета и их относительных значений. Эта информация очень важна, поскольку определяет название, пиктограмму и классовое имя каждого мидлета из JAR-файла, а также особые версии CLDC и MIDP, используемыми мидлетом. Помните, что в одном JAR-файле может храниться несколько мидлетов, при этом такой JAR-файл называется пакетом мидлетов.