Создание телефонной игры на конкретном примере В этой статье мы расскажем, как происходит создание игры для мобильного телефона. В чем-то похоже на извращения игровых кодеров-одиночек конца 80-х, чем-то отличается от них. Главное то, что это популярно и нравится нашим пользователям :). Для начала определимся, какую игру мы хотим написать, какие возможности будем использовать, как взаимодействовать с игроком - в общем, что хочется получить в итоге? Для начала предлагаю выбрать какуюнибудь аркаду, как представителя наиболее популярного жанра мобильных игр. Существует много специальных примеров, которые можно использовать, чтобы создать собственный вариант. Проще всего использовать их для изучения, возможно, сделать что-нибудь базой будущей программы. Если желаешь собственной игре максимальной аудитории, пиши ее на Java 2 Micro Edition (J2ME), которую поддерживают большинство современных телефонов. Жестокие требования Для начала нам необходимо загрузить и установить Java 2 SE SDK 1.4.2 с сайта java.sun.com, а также J2ME MIDP API - оно есть, например, в Sun J2ME WTK. Как вариант, можешь взять Nokia Developers Suite for J2ME последней версии. Кроме того, желательно быть знакомым хотя бы с основами программирования на Java. Больше ничего не требуется, код программы можно писать в любом текстовом редакторе. Структура программы Аркада. Простейшая выглядит так: глубокий космос, командир космического корабля, вооруженного ракетами класса "Космос-Космос", лавирует между астероидам разного размера, разрушая их до мельчайшей пыли и очищая окружающее пространство. Какую структуру программы выбрать? Независимую, готовую к замене отдельных частей. Другими словами, будем работать с блоками. Посмотри на этот листинг: public class Game extends MIDlet implements CommandListener; public class Field extends Canvas implements Runnable; public class Slideshow extends TimerTask; public class Geometry extends Object; public class Pool extends Object; public class Explosion extends Object; public abstract class Mobile extends Object; public class Asteroid extends Mobile; public class Rocket extends Mobile; public class Ship extends Mobile; public class Scores; Здесь: Game - собственно мидлет, меню, формы и т.д. Field - игровое поле, действие. Slideshow - различные экраны игры (например лучшие результаты, управление). Geometry - отдельный класс для различных вычислений. Pool - хранилище всех объектов, находящихся на поле. Explosion - взрыв после разрушения астероида. Mobile - абстрактный класс для перемещаемого объекта, который может оказаться на игровом поле. Asteroid, Rocket и Ship - астероид, запущенная ракета и сам космический корабль. Scores - отдельный класс для сохранения информации о высших результатах, полученных в игре. Теперь информации достаточно - можно начать реализовывать первую версию игры (и не самую элементарную!), основываясь на этом плане. Разберемся в деталях того, что нужно знать в каждом объекте. Любой движущийся элемент на экране космические координаты и скорость движения. Об астероиде нужно знать его размер и положение в пространстве (в двухмерном простейшем представлении - как он наклонен относительно базовой фигуры). О ракете - направление ее движения, а также то, какое расстояние она уже пролетела (возможно, скоро она исчезнет). О космическом корабле – опять же, его положение в космическом пространстве. Пока хватит. Базовая версия Что же делать в мидлете? Определить поведение программы. Запустить таймер, инициализировать генератор случайных чисел, создать все нужные объекты и установить меню программы. И приведу такой жестокий программный код (для краткости инициализация показана в той же строке): public static Random random = new Random(); public static Field field = new Field(); public static Display display = Display.getDisplay(this); private static Command _exitCommand = new Command("Exit", Command.EXIT, 1); field.addCommand(_exitCommand); field.setCommandListener(this); private static Displayable _currentDisplayable = field; public static Timer timer = new Timer(); display.setCurrent(displayable); Переопределяем pauseApp(), destroyApp(boolean unconditional) и startApp() (кстати, не забудь самостоятельно очистить за собой весь мусор в случае выхода из приложения). В основном игровом цикле обязательно определим размеры доступного экрана и отрисуем все пропорционально так, чтобы дисплей всегда был занят изображением полностью. По возможности нужно использовать буферизацию: _buffer = Image.createImage(Mobile.width, Mobile.height), а при прорисовке в paint(Graphics g) сначала выбираем Graphics gr = (_buffer != null) ? _buffer.getGraphics() : g, а после всех действий - g.drawImage(_buffer, 0, 0, Graphics.TOP|Graphics.LEFT). Впрочем, проверка здесь не помешает: не используй offline screen, если телефон сам выполняет все необходимые действия (DoubleBuffer). Изначально нужно стремиться к тому, чтобы мобильные объекты были простейшие – обычные линии. Перейдем к игровому циклу. Вот длинный список того, что нужно проконтролировать: выяснить, попала ли какая-нибудь ракета в астероиды, и осуществить взрыв; отследить перемещение астероидов; сделать то же с перемещением ракет; предусмотреть поворот и движение корабля; проверить, не погиб ли корабль; досмотреть отображение текущего состояния на экране сотового (нужно нарисовать фоновый экран, текущие очки и уровень игры, все астероиды, ракеты и космический корабль или же вообще экран зала славы, например). Для управления просто запоминай нажатую клавишу и обрабатывай ее в главном цикле. Если выполнено все технически необходимое кодирование, то базовая версия игры, которая будет идти на большинстве сотовых телефонов, выполняя заданные тобой функции, уже получена. Постепенное развитие Посмотреть информацию о возможности добавления к игре различных зависимых от устройства приблудов ты можешь увидеть на врезке "Дополнительные возможности". Но вначале лучше оценить и решить, что развивать дальше: общие возможности программы, ее структуру или же различные игровые аспекты. Для начала нужно напрячь внимание и заметить, что практически везде корабль управляется плохо. Что можно сделать для исправления ситуации? Можно сохранять нажатые клавиши во внутренний массив и обрабатывать сразу все нажатые клавиши либо ускорять перерисовку после получения значимого сообщения о произошедшем событии. Если посмотреть еще внимательнее, то обнаружится, что космос в игре никак не обозначен: вакуум, пустота. Лучше добавить какой-нибудь фон. Воспользуйся image = javax.microedition.lcdui.Image.createImage("/splash.png") (взять файл изображения) и при отрисовке просто нарисуй его: Graphics g.drawImage(image, 0, 0, Graphics.TOP | Graphics.LEFT). Точно так же и космический корабль, и астероиды, и ракеты можно заменить на спрайты. Стандартной функции поворота нет, да и наклон может выполняться долго, поэтому самый простой способ - завести несколько картинок для различных степеней поворота. Создание готового продукта Сам процесс компиляции не отличается от обычного для Java-программ - запуск javac с правильным набором библиотек и списком файлов. Готовый JAR-файл отличается от обычной Java-программы тем, что в него добавлен манифест - различная информация о мидлете. Это файл META-INF\MANIFEST.MF, ниже представлены строки, минимально необходимые для него: MIDlet-Name: Example - название программы MIDlet-Version: 1.0 - версия мидлета MIDlet-Vendor: Vendor - разработчик мидлета MicroEdition-Profile: MIDP-1.0 - версия MID-профиля MicroEdition-Configuration: CLDC-1.0 - версия конфигурации MIDlet-1: Example, icon.png, mypackage.Example - Имя, Иконка, Главный класс Все это может быть сделано автоматически из какой-нибудь среды разработки. Обязательно заполняй все данные правильно. Распространение В результате у тебя есть готовая игра, которую ты хочешь сделать доступной для других людей. Продать саму программу целиком, распространять ее бесплатно или за деньги, позволить заплатить за нее с помощью платных SMS или по принципу Shareware – все по желанию. Показ статического экрана Листинг /* пример прорисовки титульного экрана Используемые значения из Field: пытаемся получить шрифты с соответствующими атрибутами (маленький обычный и большой жирный): public static Font smallFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL); public static Font bigFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE); На самом деле в зависимости от реализации J2ME на устройстве, они могут оказаться одним и тем же шрифтом. В текущем объекте вычисляем высоту этих шрифтов (для отрисовки по высоте): private static int _smallFontHeight; private static int _bigFontHeight; static { _smallFontHeight = Field.smallFont.getHeight();; _bigFontHeight = Field.bigFont.getHeight(); } */ public static void drawTitleScreen(Graphics g) { g.setColor(0x00000000); // выставляем черный цвет g.setFont(Field.bigFont); // выбираем толстый шрифт // Рассчитываем x для вывода отцентрированной строки _asteroid int x = (Mobile.width - Field.bigFont.stringWidth(_asteroid)) %26gt;%26gt; 1;// Также рассчитываем положение строки по вертикали int y = ((Mobile.height - ((_bigFontHeight + _smallFontHeight) %26lt;%26lt; 1)) %26gt;%26gt; 1) + _bigFontHeight; // и выводим строки на экран (название программы) g.drawString(_asteroid, x, y, Graphics.BOTTOM|Graphics.LEFT); x = (Mobile.width - Field.bigFont.stringWidth(_field)) %26gt;%26gt; 1; g.drawString(_field, x, y + _bigFontHeight, Graphics.BOTTOM|Graphics.LEFT); // меняем цвет и выводим те же самые строчки со сдвигом на один пиксел влевовниз (фактически оттеняем выведенный текст). g.setColor(0x008080FF); x = ((Mobile.width - Field.bigFont.stringWidth(_asteroid)) %26gt;%26gt; 1) 1; y += 1; g.drawString(_asteroid, x, y, Graphics.BOTTOM|Graphics.LEFT); x = ((Mobile.width - Field.bigFont.stringWidth(_field)) %26gt;%26gt; 1) - 1; y += _bigFontHeight; g.drawString(_field, x, y, Graphics.BOTTOM|Graphics.LEFT); // дальше выводим версию и информацию об авторе программы без "спецэффектов" y += _smallFontHeight; g.setColor(0x00000000); g.setFont(Field.smallFont); x = (Mobile.width - Field.smallFont.stringWidth(_byjfd)) %26gt;%26gt; 1; g.drawString(_byjfd, x, y, Graphics.BOTTOM|Graphics.LEFT); y += _smallFontHeight; x = (Mobile.width - Field.smallFont.stringWidth(_version)) %26gt;%26gt; 1; g.drawString(_version, x, y, Graphics.BOTTOM|Graphics.LEFT); } Дополнительные возможности Желаешь поддержки виброзвонка и прочих наворотов? Заведем отдельный класс для всех специальных эффектов и создадим его в зависимости от типа телефона. Например: class GameEffects с методами boolean hasSoundCapability() и boolean hasVibrationCapability() для проверки с return false по умолчанию. А вот пример для Nokia - для вибры com.nokia.mid.ui.DeviceControl.stopVibra() и DeviceControl.startVibra(100, 300) (100% вибрации на 300 миллисекунд). Для звука же com.nokia.mid.sound.Sound: class NokiaGameEffects extends GameEffects com.nokia.mid.sound.Sound sound; // bytes - набор для проигрыша sound = new Sound(bytes, Sound.FORMAT_TONE); sound.setGain(128); // громкость sound.play(1); // 1 цикл Для того чтобы использовать действительно динамическую инициализацию игры, при запуске нужно будет сделать вот так: try { Class.forName("com.nokia.mid.sound.Sound"); Class.forName("com.nokia.mid.ui.DeviceControl"); // Если нет исключений, можно использовать эти API. Class clas = Class.forName("NokiaGameEffects"); return (GameEffects) clas.newInstance(); } catch (Exception e) { // Использовать заглушку. return new GameEffects(); } Основные модели мобильных телефонов с J2ME Если ты хочешь провести тестирование на эмуляторах или на реальных устройствах, то ниже приведен ряд моделей с различной функциональностью: Nokia 3100 - MIDP1.0/CLDC1.0 (типичный представитель) Nokia 6021 - MIDP2.0/CLDC1.1 (тоже типичный) Nokia 6230i - MIDP2.0/CLDC1.1 (много дополнительных API) Nokia 6630/6680 или Nokia 7710 - MIDP2.0/CLDC1.1/CDC (high-end) Siemens M55 - MIDP1.0/CLDC1.0 (для мидлетов память не ограничивает) Siemens M65 - MIDP2.0/CLDC1.1 (есть новые API, Java 3D) Sony Ericsson T610 - MIDP1.0/CLDC1.0 (довольно старый, как и Siemens M55) Sony Ericsson K500i - MIDP2.0/CLDC1.1 (новые API) Samsung C100, X100 - MIDP1.0/CLDC1.0 (очень распространен, хотя довольно неудобен для программиста) Ссылки по теме Очень полезный сайт по Java (и J2ME) на русском языке: www.juga.ru Основной сайт по Java: java.sun.com Море информации по играм: www.gamasutra.com От Nokia: все для разработчика: forum.nokia.com и Discussion Board Просто неплохой сайт для начинающих и не только: www.developer.com В качестве примера начального приложения используется Asteroid Zone от Jean-Francois Doue, распространяемая по лицезии GNU General Public License версии 2 (или выше). По возможности используй буферизацию вывода на экран. Исходники программы ты можешь найти на нашем диске. Если планируется сделать доступной игру на любых мобильных устройствах, нужно портировать ее для них (Symbian, Windows Mobile, BREW и т.д.). http://www.xakep.ru/magazine/xs/056/064/1.asp