J2ME. Реализация Игры и графического Меню с использованием одного Canvas класса

Автор: content Вторник, Апрель 10th, 2012 Нет комментариев

Рубрика: Java Mobile

Еще год назад подавляющее большинство игр для телефонов использовали меню на основе форм — базового интерфейса телефона. Эти менюшки смотрелись убого и явно выбивались из игрового дизайна. Сейчас требования к играм существенно возросли, и пользователи требуют интересных графических решений. В этой статье я хочу рассказать вам о методе, который позволит вам разбить программу на два класса — игру и меню, и использовать только один класс canvas.

Приведенный ниже метод с успехом применяется во всех наших играх. Он действительно работает.

Для начала давайте посмотрим как выглядит типичный canvas класс игры:

import javax.microedition.lcdui.*;

public class MyCanvas extends Canvas implements Runnable {

public MyCanvas(Midlet midlet) { // Конструктор
}

public void run(){
try {
while (running) {
//Главный игровой цикл
}
}
catch(InterruptedException ie) { System.out.println(ie.toString()); }
}
protected void paint(Graphics g){ /* code */ }

synchronized void start() { /* code */ }

synchronized void stop() { /* code */ }

public void keyPressed(int keyCode) { /* code */ }
}

Это довольно простое и распространенное переопределение canvas. Вероятно что-то подобное Вы уже использовали в своих играх. Давайте разобьем этот файл на два различных canvas класса — один для экранов меню и один для игры.

Создадим два класса Menu.java и Game.java. Они осуществляют все те же функции, что и приведенный выше код. Заполним функции, необходимые для Canvas. Обратите внимание, и в ходе выполнения игры и в ходе работы с меню, программа выполняет одни и те же базовые функции, но с различным содержанием. Это позволяет нам оставить только один Canvas класс, и экспортировать различающиеся реализации извне.

public class Menu{
boolean isActive;

public static boolean isActive() { return isActive; }

public static void destroy() { /* удаляет все ненужное из памяти */ }

public static void initMenu() { /* инициализация первого меню*/ }

private static void initRest(){ /* инициализация остальной части меню */ }

public static void paint(Graphics g) {
isActive = true;

drawMenuBackground(g);

switch (menuType) {
case 1: showMenuA(g); break;
case 2: showMenuB(g); break;
}

}

public static void processKey(int keyCode, int GameActionKey)
{ /* обработка клавиш */   }
}

public class Game {

boolean isActive;

public static boolean isActive() { return isActive; }

public static void destroy() { /* удаляем все лишнее */ }

public static void initGame() { /* инициализация игры */ }

public static void paint(Graphics g) {
// игровое действие
isActive = true;
}

public static void processKey(int keyCode, int GameActionKey)
{ /* Обработка клавиш*/ }
}

Функция любого из этих классов может корректно заменить функции MyCanvas.

Теперь нам необходим код, который сообщит canvas-у какой из классов должен контролировать дисплей. Введем переменную типа boolean, которая будет индикатором текущего режима (ИГРА или МЕНЮ).

Вот часть кода MyCanvas, которую мы использовали в нашей первой игре MotorDuels: Outcast. Этот класс переопределяет Canvas. Если Вы ориентируетесь на Nokia, то необходимо переопределять FullCanvas, что позволит вам использовать DirectGraphics. Это означает, что Вы должны будете передавать dg наряду с g в Game.paint() и Menu.paint().

import javax.microedition.lcdui.*;

public class MyCanvas extends Canvas implements Runnable {

private volatile Thread animationThread=null;

private static Graphics graphics;
private static boolean running;

public static boolean inMenu = true;
public static boolean doInit = true;

private static final int SLEEP = 5;

public MyCanvas(MyGame mygame) {
buffer=Image.createImage(MyGame.canvasWidth, MyGame.canvasHeight);
graphics=buffer.getGraphics();
}

public void run(){ //main_game_loop
try {
while (running) {

if (doInit){
doInit=false;
if (inMenu) Menu.initMenu();
else Game.initGame();
}

if (inMenu) {
if (!Menu.isActive()) {
doInit=true;
inMenu=false;
Menu.destroy();
Sounds.destroyMenu();
}
} else {
if (!Game.isActive()) {
inMenu = true;
doInit = true;
Game.destroy();
}

}

repaint(0,0,MyGame.canvasWidth, MyGame.canvasHeight);
serviceRepaints();
Thread.sleep(SLEEP);
}
}
catch(InterruptedException ie) { System.out.println(ie.toString()); }
}

protected void paint(Graphics g){

if (inMenu) Menu.paint(g);
else Game.paint(g);

}

synchronized void start()
{
running=true;
animationThread=new Thread(this);
animationThread.start();
}

synchronized void stop() { running=false; }

public void keyPressed(int keyCode) {
if (allowKeys) {
if (inMenu) {
Menu.processKey(keyCode, getGameAction(keyCode));
}
else {
Game.processKey(keyCode, getGameAction(keyCode));
}
}

}

}

Как видите, наш класс не только проверяет какой из методов paint() должен использоваться, но и следит за своевременным завершением метода init(). Мы также разделяем обработку клавиш keypressed(), так что игра и меню используют собственные обработчики.
Стоит ли применять этот метод?

К недостаткам этого подхода можно отнести необходимость следить за правильностью размещения элементов меню на экране. Кроме того, графическое меню занимает намного больше места в jar архиве, чем реализованное на основе форм.

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

Несомненно, современные разработчики игр должны снабжать свои продукты качественными меню. Требования к памяти в последнее время становятся менее жесткими, так что не стоит экономить на удобстве пользователя и атмосфере игры. Надеюсь, данная статья пригодится Вам.

Источник: http://www.javaportal.ru/mobiljava/articles/canvasmenu.html
Автор оригинальный статьи: civax
Перевод: aRix

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты