Создание игр для мобильных телефонов - Майкл Моррисон 34 стр.


Последний метод класса LHServer – это метод sendMessage(), приведенный в листинге 14.2. Этот метод отправляет сообщения клиенту.

Листинг 14.2. Метод sendMessage() класса LHServer отправляет строковое сообщение как пакет датаграммы

public void sendMessage(String message) {

// отправить сообщение

try {

// преобразовать текстовое сообщение в массив байтов

byte[] bytes = message.getBytes(); //Строковое сообщение должно быть преобразовано в массив байтов

// отправить сообщение

Datagram dg = null; //Упаковка данных в датаграмму и отправка клиенту

dg = dc.newDatagram(bytes, bytes.length, address);

dc.send(dg);

}

catch (Exception e) {

}

}

В этом коде строковое сообщение преобразуется в массив байтов, а затем отправляется клиенту как датаграммный пакет. Обратите внимание, что адрес, сохраненный ранее в методе run(), теперь используется при создании объекта Datagram отправляемого сообщения. Этот адрес необходим, чтобы отправить сообщение клиенту. Однако, как вы увидите позже, этот адрес не обязателен при отправке сообщения клиентом серверу. Другая часть сетевого кода мидлета Lighthouse – это класс LHClient, который очень похож на класс LHServer. Так же, как и LHServer, класс LHClient также реализует интерфейс Runnable:

public class LHClient implements Runnable {

Ниже приведен список членов класса LHClient.

private LHCanvas canvas;

private DatagramConnection dc;

private boolean connected;

Переменные класса должны быть вам знакомы, поскольку они точно такие же, как и в классе LHServer, за исключением отсутствия переменной address. Ниже приведен код конструктора класса LHClient, который выполняет инициализацию некоторых переменных:

public LHClient(LHCanvas c) {

canvas = c;

connected = false;

}

Метод start() класса LHClient точно такой же, как и аналогичный метод класса LHServer, поэтому давайте перейдем к методу run(). В листинге 14.3 приведен код метода run() класса LHClient. Листинг 14.3. Метод run() класса LHClient отвечает на сообщения, полученные от сервера

public void run() {

try {

// соединиться с серверным устройством

canvas.setStatus("Connecting to peer server..."); //Клиент отображает начальное соединение, что говорит о том, что он пытается соединиться с сервером

dc = null;

while (dc == null)

dc = (DatagramConnection)Connector.open("datagram://localhost:5555"); //Номер порта клиента должен совпадать с номером порта сервера

while (true) {

// попытаться отправить сообщение //Если соединение не установлено, отправить клиентское сообщение об установлении соединения серверу

if (!connected)

sendMessage("Client");

// попытаться принять пакет датаграммы

Datagram dg = dc.newDatagram(32);

dc.receive(dg);

// проверить, что датаграмма содержит данные

if (dg.getLength() > 0) {

String data = new String(dg.getData(), 0, dg.getLength());

if (data.equals("Server")) { //Ответить на сообщение сервера о соединении

// оповестить пользователя об установлении соединения

canvas.setStatus("Connected to peer server.");

connected = true;

}

else {

// отправить данные

canvas.receiveMessage(data); //Сообщение содержит символы азбуки Морзе, поэтому его следует передать холсту

}

}

}

}

catch (ConnectionNotFoundException cnfe) {

System.err.println("The network server is unavailable.");

}

catch (IOException ioe) {

}

}

Метод run() класса клиента очень похож на аналогичный метод класса сервера за исключением того, что в классе клиента отсутствует переменная адреса при отправлении датаграммы. Кроме того, URL немного отличается от того, который использовался в классе сервера. И снова важно отметить, что номер порта (5555) должен быть одинаковым для клиента и сервера.

Класс LHClient также реализует метод sendMessage(), который также очень похож на аналогичный метод сервера. В листинге 14.4 приведен код метода sendMessage() клиента.

Листинг 14.4. Метод sendMessage() класса LHClient отправляет строковое сообщение серверу как пакет датаграммы

public void sendMessage(String message) {

// отправить сообщение

try {

// преобразовать строку в массив байтов

byte[] bytes = message.getBytes();

// отправить сообщение

Datagram dg = null; //Упаковка данных в датаграмму и отправка клиенту

dg = dc.newDatagram(bytes, bytes.length);

dc.send(dg);

}

catch (Exception e) {

}

}

Единственное отличие методов sendMessage() сервера и клиента – это то, что версия класса клиента не использует адреса при отправлении пакета серверу. Это незначительное, но очень важное отличие. Холст мидлета Lighthouse Когда классы сервера и клиента созданы, перейдем к созданию холста класса Lighthouse. Класс называется LHCanvas, он выводит на экран информацию о ходе подключения, а также отображает нужную картинку с маяком в соответствии с получаемой информацией.

private Display display;

private boolean sleeping;

private long frameDelay;

private Image[] background = new Image[2]; //Эта переменная говорит о том, является данное приложение образом сервера или клиента

private LHClient client;

private LHServer server;

private boolean isServer;

private String status = "";

private int mode; // 0 = none, 1 = dot, 2 = dash

private int morseTimer;

Переменная background содержит два изображения маяка: с включенным и погашенным огнем. Переменные server и client – это объекты сервера и клиента соответственно, они отвечают за работу мидлета с сетью. Переменная isServer очень важна, она определяет сетевой режим работы мидлета. Эта переменная определяет, как работает мидлет – как клиент или как сервер.

Текст статуса хранится в переменной status. временная mode используется для контроля вывода изображений маяка, а также интервалов времени. Помните, что точка в азбуке Морзе по длительности в три раза меньше тире, поэтому в мидлете Lighthouse используется таймер вывода изображения маяка, который задерживает изображение с включенным огнем в соответствии с отображаемым сигналом. За задержку отвечают переменные mode и morseTimer.

Переменные холста широко используются в методе start(), код которого приведен в листинге 14.5.

Листинг 14.5. Метод start() класса LHCanvas запускает сервис Клиент-Сервер

public void start() {

// установить экран как холст

display.setCurrent(this);

// инициализация фонового изображения

try {

background[0] = Image.createImage("/LighthouseOff.png");

background[1] = Image.createImage("/LighthouseOn.png");

}

catch (IOException e) {

System.err.println("Failed loading images!");

}

// инициализация режима и таймера

mode = 0;

morseTimer = 0;

// запуск сетевого сервиса

if (isServer) { //Начиная с этой точки, мидлет работает в режиме сервера или клиента

server = new LHServer(this);

server.start();

}

else {

client = new LHClient(this);

client.start();

}

// начало потока анимации

sleeping = false;

Thread t = new Thread(this);

t.start();

}

После того как фоновые изображения инициализированы, метод start() инициализирует таймер и режим. Переменная mode инициализируется 0, что соответствует погашенному огню маяка (нет сообщения), в то время как переменная morseTimer обнуляется, несмотря на то что она не используется в отсутствие сообщения.

Наиболее важная часть кода метода start() реализует режим сервера или клиента. В зависимости от значения переменной isServer создается экземпляр класса LHServer или LHClient. После этого создается сетевой объект, вызывается метод старт, запускающий поток соединения.

Метод start() инициализирует мидлет Lighthouse, а метод update() обрабатывает пользовательский ввод и позволяет вам отправлять сообщения, закодированные азбукой Морзе. В листинге 14.6 приведен код метода update().

Листинг 14.6. Метод update() класса LHCanvas отправляет сообщения, закодированные азбукой Морзе, в соответствии с нажатыми клавишами

private void update() {

// обработка пользовательского ввода

int keyState = getKeyStates();

if ((keyState & LEFT_PRESSED) != 0) {

if (isServer) //Передать точку на другой телефон

server.sendMessage("Dot");

else

client.sendMessage("Dot");

status = "Dot";

}

else if ((keyState & RIGHT_PRESSED) != 0) {

if (isServer) //Передать тире на другой телефон

server.sendMessage("Dash");

else

client.sendMessage("Dash");

status = "Dash";

}

// обновить таймер кода Морзе

if (mode != 0) {

morseTimer++;

// тайм-аут точки

if (mode == 1 && morseTimer > 3)

mode = 0;

// тайм-аут тире

else if (mode == 2 && morseTimer > 9)

mode = 0;

}

}

Метод update() проверяет нажатие клавиш и в соответствии с нажатыми клавишами отправляет нужный знак. Клавиша влево соответствует точке, а клавиша вправо – тире. Чтобы отправить код азбуки Морзе, метод update() просто вызывает метод sendMessage() сетевого объекта (клиента или сервера).

После проверки нажатий клавиш и отправки сообщения, если необходимо, метод update() обновляет таймер Морзе. Если значение переменной mode равно 1, то выводится точка, затем значение таймера увеличивается до 3, после чего выводится изображение маяка с погашенным огнем. Если значение переменной mode равно 2, выводится тире, при этом счетчик будет увеличиваться до 9. И наконец, если значение переменной mode равно 0, то следует вывести изображение маяка с погашенным огнем, таймер не изменяется.

Метод draw() выводит изображение маяка на экран (листинг 14.7).

Листинг 14.7. Метод draw() класса LHCanvas выводит на экран нужное изображение маяка

private void draw(Graphics g) {

// вывести фоновое изображение

if (mode == 0) //В зависимости от режима маяк светится или нет

g.drawImage(background[0], 0, 0, Graphics.TOP | Graphics.LEFT);

else

g.drawImage(background[1], 0, 0, Graphics.TOP | Graphics.LEFT);

// вывести сообщение о статусе

g.setColor(255, 255, 255); // white

g.setFont(Font.getFont(Font.FACE_SYSTEM,Font.STYLE_BOLD, Font.SIZE_MEDIUM));

g.drawString(status, getWidth() / 2, 5, Graphics.TOP | Graphics.HCENTER); // В верхней части экрана выводится статусное сообщение

// вывести содержимое буфера на экран

flushGraphics();

}

Метод draw() начинается с проверки значения переменной mode, которая определяет, какое изображение маяка следует вывести. Если значение этой переменной равно 0, то выводится изображение маяка с погашенным огнем, в противном случае выводится изображение маяка с включенным огнем. Остальной код выводит в нижней части экрана сообщение о статусе соединения. Сообщение о текущем статусе выводится функцией setStatus():

public void setStatus(String s) {

// установить текущий статус

status = s;

}

Последний метод класса LHCanvas – это метод receiveMessage(), принимающий сообщение, закодированное азбукой Морзе, и в соответствии с сообщением настраивает холст. В листинге 14.8 приведен код этого метода: Листинг 14.8. Метод receiveMessage() класса LHCanvas получает сообщение, отправленное по сети

public void receiveMessage(String message) {

// установить режим

if (message.equals("Dash")) // Изменить режим в соответствие с сообщением

mode = 2;

else if (message.equals("Dot"))

mode = 1;

else

mode = 0;

// обнулить таймер

morseTimer = 0;

// очистить сообщение о статусе

status = ""; // При отображении символов азбуки Морзе нет необходимости выводить статусное сообщение

}

Параметр метода receiveMessage() – это отправленное сообщение. Это сообщение всегда точка (Dot) или тире (Dash). В последнем случае значение переменной mode становится равным 2, в то время как в первом случае – 1. Если по какой-либо причине сообщение не содержит ни один из возможных вариантов, его значение переменной mode становится равным 0. После этого обнуляется таймер, обеспечивающий правильную работу мидлета. Мидлет Lighthouse

Последний функциональный представляющий интерес фрагмент кода мидлета Lighthouse – это сам класс мидлета, в котором есть код, отличающий его от всех созданных ранее в книге мидлетов. В листинге 14.9 приведен код класса LighthouseMIDlet.

Листинг 14.9. Класс LighthouseMIDlet при загрузке приложения позволяет пользователю выбрать режим работы: клиент или сервер

public class LighthouseMIDlet extends MIDlet implements CommandListener {

private Form initForm; // Объект типа Form используется для реализации интерфейса

private ChoiceGroup choices;

private LHCanvas gameCanvas;

public void startApp() {

// создать стартовую форму

initForm = new Form("Connect 4");

// добавить выбор устройства

String[] peerNames = { "Server", "Client" }; // Создать группу выбора с двумя опциями: Клиент и Сервер

choices = new ChoiceGroup("Please select peer type:", Choice.EXCLUSIVE,

peerNames, null);

initForm.append(choices);

// добавить команды Play и Exit

Command exitCommand = new Command("Exit", Command.EXIT, 0);

initForm.addCommand(exitCommand);

Command playCommand = new Command("Play", Command.OK, 0);

initForm.addCommand(playCommand);

initForm.setCommandListener(this);

// вывести форму на экран

Display.getDisplay(this).setCurrent(initForm);

}

public void pauseApp() {}

public void destroyApp(boolean unconditional) {

gameCanvas.stop();

}

public void commandAction(Command c, Displayable s) {

if (c.getCommandType() == Command.EXIT) {

destroyApp(true);

notifyDestroyed();

}

else if (c.getCommandType() == Command.OK) {

// определить тип функционирования приложения

String name = choices.getString(choices.getSelectedIndex()); // Когда выполняется команда Play, отображается холст игры, в который передается тип работы (клиент или сервер)

// создать новый игровой холст

if (gameCanvas == null) {

gameCanvas = new LHCanvas(Display.getDisplay(this), name);

Command exitCommand = new Command("Exit", Command.EXIT, 0);

gameCanvas.addCommand(exitCommand);

gameCanvas.setCommandListener(this);

}

// запустить игровой холст

gameCanvas.start();

}

}

}

Класс LighthouseMIDlet реализует пользовательский интерфейс в дополнение к основному холсту, в результате пользователь может выбрать режим функционирования приложения (клиент или сервер). Класс MIDP, который называется Form, используется для создания этого интерфейса в виде формы. На формах вы можете размещать стандартные элементы управления, например, кнопки и группы выбора. В мидлете Lighthouse используется группа выбора, предлагающая два варианта ("Server" или "Client"), которая очень хорошо подходит в данном случае.

Группа выбора создается как объект типа ChoiceGroup, инициализируемый строковым массивом peerNames. Созданная группа применяется к форме и становится ее неотъемлемой частью. Затем к форме добавляются две команды Play (Играть) и Exit (Выход). Команда Exit завершает работу мидлета, а команда Play запускает мидлет, применяя выбранный режим работы. Команда Play связана с константой Command.OK, в результате выполнения команды создается объект класса LHCanvas. Это основной разработанный вами холст.

Важно отметить, что выбранный режим функционирования передается в качестве второго параметра конструктору класса LHCanvas. Именно так холст узнает, в каком режиме он должен функционировать, в соответствии с этим переменной isServer присваивается нужное значение.

Тестирование приложения

Мидлет Lighthouse – это, вероятно, самое мудреное приложение, которое вы тестировали, поскольку оно требует наличия двух мобильных телефонов, или двух запущенных эмуляторов J2ME. Несмотря на то что я настоятельно рекомендую тестировать приложение на реальных устройствах, J2ME позволяет наглядно протестировать мидлет на двух расположенных рядом виртуальных телефонах. Чтобы протестировать мидлет Lighthouse, запустите два раза эмулятор J2ME. На одном из виртуальных телефонов выберите режим функционирования "сервер", а на другом – "клиент". На рис. 14.1 на виртуальном телефоне выбран режим сервера.

Назад Дальше