Еще год назад подавляющее большинство игр для телефонов использовалименю на основе форм - базового интерфейса телефона. Эти менюшкисмотрелись убого и явно выбивались из игрового дизайна. Сейчастребования к играм существенно возросли, и пользователи требуютинтересных графических решений. В этой статье я хочу рассказать вам ометоде, который позволит вам разбить программу на два класса - игру именю, и использовать только один класс 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 архиве, чемреализованное на основе форм.
На мой взгляд, преимущества, которое дает этот подход, болеезначимы. Вы получаете красивое меню, органично сочетающееся с Вашейигрой и дополняющее игровую атмосферу. Кроме того, телефоны с каждомгодом оснащаются все большим объем памяти.
Несомненно, современные разработчики игр должны снабжать своипродукты качественными меню. Требования к памяти в последнее времястановятся менее жесткими, так что не стоит экономить на удобствепользователя и атмосфере игры. Надеюсь, данная статья пригодится Вам.
Автор оригинальный статьи: civax
Перевод: aRix