Чтение онлайн

ЖАНРЫ

Создание игр для мобильных телефонов
Шрифт:

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

roidSprite = new Sprite(Image.createImage(«/Roid.png»), 42, 35);

Этот код определяет размер фрейма в изображении – 42х35 пикселей. Фреймы в изображении могут располагаться вертикально, горизонтально или в двух направлениях. Если вы располагаете фреймы по сетке, то нумерация происходит слева направо и сверху вниз (рис. 6.3).

Рис. 6.3. Располагая фреймы изображения в сетке, проход по сетке осуществляется слева направо и сверху вниз

Чтобы создать фреймовую анимацию спрайта, необходимо вызвать методы nextFrame и prevFrame:

roidSprite.nextFrame;

Этот метод отображает следующий фрейм анимации спрайта. По умолчанию эта последовательность соответствует порядку следования фреймов в изображении, однако вы можете изменить эту последовательность. При достижении конца последовательности воспроизведения фреймов начинается воспроизведение с противоположного конца. В любое время вы можете узнать индекс текущего фрейма, вызвав метод getFrame. Этот метод возвращает индекс фрейма в последовательности, а не реальный индекс в изображении. Рассмотрим в качестве примера следующую последовательность фреймов:

int [] sequence = {0, 1, 2, 3, 3, 3, 2, 1};

Пятый элемент этой последовательности – это третий фрейм изображения. Если сейчас отображается пятый фрейм анимации, то метод getFrame возвратит значение 4 (отсчет ведется от 0), а не номер фрейма в изображении. Метод setFrame позволяет назначить текущий индекс фрейма. Выполнив setFrame(6), вызовите фрейм с номером 6, поскольку 2 – это номер фрейма, который стоит шестым по счету. Помните, что эти номера соответствуют местам фреймов в изображении. Последовательности фреймов можно использовать для имитации взмахов крыльев, взрывов и т. п. С помощью метода setFrameSequence вы можете изменить последовательность отображения фреймов. Этот метод принимает в качестве параметра целочисленный массив. Ниже приведен пример вызова этой функции для спрайта с птицей:

birdSprite.setFrameSequence(sequence);

Если вы уже близки к пониманию анимации спрайтов, то можно двинуться дальше! Оставшаяся часть главы посвящена совершенствованию мидлета UFO, рассмотренного в предыдущей главе. Вы добавите анимацию спрайта и пользовательский ввод. Ведь лучше один раз увидеть, чем сто раз услышать!

Создание программы UFO 2

Пример программы UFO из предыдущей главы поможет на практике освоить анимацию спрайтов. Теперь вы можете перевести мидлет на новый уровень, добавив управление, а также астероиды – препятствия на пути НЛО. Я буду называть эту программу UFO 2.

Мидлет UFO 2 содержит следующие изменения по отношению к исходной программе:

► пользовательский ввод, обеспечивающий управление летающим объектом;

► анимационные астероиды, летающие по экрану;

► детектирование столкновений летающего объекта с астероидами.

Вы уже достаточно хорошо подготовлены, чтобы сделать это!

Написание программного кода

Класс мидлета в примере UFO 2 не изменился по сравнению с предыдущим приложением, поэтому давайте перейдем непосредственно к изменению класса UFOCanvas. Первое изменение – это добавление трех спрайтов астероидов, которые хранятся в массиве типа Sprite:

private Sprite[] roidSpace = new Sprite[3];

В методе start выполняется инициализация спрайтов астероида следующим кодом:

Image img = Image.createImage(«/Roid.png»);

roidSprite[0] = new Sprite(img, 42, 35);

roidSprite[1] = new Sprite(img, 42, 35);

roidSprite[2] = new Sprite(img, 42, 35);

Как вы видите, изображение астероида (Roid.png) создается один раз, а затем передается каждому конструктору спрайта. Также при инициализации изменилось и начальное положение НЛО:

ufoSprite.setPosition((getWidth – ufoSprite.getWidth) / 2, (getHeight – ufoSprite.getHeight) / 2);

Хотя этот код может показаться странным, но он не делает ничего особенного, просто выводит спрайт в центре экрана, чтобы НЛО сразу не столкнулся с астероидом, который стартует из точки (0,0). В методе update находятся наиболее интересные новые строки кода. Вся обработка пользовательского ввода сосредоточена в следующем фрагменте кода:

int keyState = getKeyStates;

if ((keyState & LEFT_PRESSED) != 0)

ufoXSpeed–;

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

ufoXSpeed++;

if ((keyState & UP_PRESSED) != 0)

ufoYSpeed–;

else if ((keyState & DOWN_PRESSED) != 0)

ufoYSpeed++;

ufoXSpeed = Math.min(Math.max(ufoXSpeed, -8), 8); //Скорость НЛО устанавливается случайно из диапазона от -8 до 8

ufoYSpeed = Math.min(Math.max(ufoYSpeed, -8), 8);

Этот код просто проверяет нажатия четырех клавиш управления и в соответствии с этим изменяет скорость НЛО. Обратите внимание, что и в этом случае скорость ограничена 8 вне зависимости от того, сколько раз была нажата та или иная клавиша. После того как скорость изменена, НЛО обновляется следующим кодом:

ufoSprite.move(ufoXSpeed, ufoYSpeed); checkBounds(ufoSprite);

Известный метод move перемещает спрайт, а метод checkBounds проверяет, не вышел ли НЛО за границы экрана. Проверка не изменилась, но ее код оформлен отдельным методом. Это очень важно, поскольку вам необходимо выполнить аналогичную проверку и для астероидов. Для этого нецелесообразно копировать код, если можно использовать существующий. Обновление спрайтов астероидов производится в цикле, который выполняет несколько функций. Ниже приведено начало цикла:

for (int i = 0; i < 3; i++) {

Первое, что нужно выполнить в цикле, – это переместить астероиды и проверить, не вышли ли они за границы экрана:

roidSprite[i].move(i + 1, 1 – i); checkBounds(roidSprite[i]);

Единственная хитрость в этом коде – перемещения астероидов. Чтобы каждый астероид двигался со своей особой скоростью, для перемещения используется индекс каждого из них. Аналогичный код используется для изменения очередности следования фреймов анимации:

if (i == 1)

roidSprite[i].prevFrame;

else

roidSprite[i].nextFrame;

Идея этого кода заключается в том, что второй астероид вращается в направлении, противоположном остальным. Для этого достаточно пролистывать фреймы спрайта в противоположном направлении относительно других. Оставшаяся часть кода в цикле обновления астероидов проверяет их столкновение с НЛО:

if (ufoSprite.collidesWith(roidSprite[i], true)) {

// воспроизвести предупреждающий звук

AlertType.ERROR.playSound(display);

// вернуть спрайт в исходное положение и обнулить скорости

ufoSprite.setPosition((getWidth – ufoSprite.getWidth) / 2, //Спрайт выводится в центре игрового экрана, его скорость равна 0

(getHeight – ufoSprite.getHeight) / 2);

ufoXSpeed = ufoYSpeed = 0;

for (int j = 0; j < 3; j++)

roidSprite[j].setPosition(0, 0);

// нет необходимости обновлять спрайты астероидов

break;

}

}

Если столкновение произошло, то воспроизводится стандартный звук возникновения ошибки (он зависит от конкретной модели телефона), для чего используется объект AlertType. В главе 8 вы узнаете, как использовать разнообразные звуки в играх. В этой программе столкновение возвратит НЛО в исходное положение и обнулит его скорость. Если бы вы создавали полноценную игру, то в этом месте вы бы уменьшили число жизней и проверили, не закончена ли игра. Но в этой программе вы просто изменяете положение спрайтов, и анимация продолжается. По сравнению с мидлетом UFO в методе draw есть только одно незначительное изменение – код, рисующий астероиды:

for (int i = 0; i < 3; i++) roidSprite[i].paint(g);

На этом весь новый код мидлета UFO 2 завершен. В листинге 6.1 приведен полный код нового класса UFOCanvas. Листинг 6.1. Класс UFOCanvas, выполняющий роль холста для мидлета UFO 2

import javax.microedition.lcdui.*;

import javax.microedition.lcdui.game.*;

import java.util.*;

import java.io.*;

public class UFOCanvas extends GameCanvas implements Runnable {

private Display display;

private boolean sleeping;

private long frameDelay;

private Random rand;

private Sprite ufoSprite;

private int ufoXSpeed, ufoYSpeed;

private Sprite[] roidSprite = new Sprite[3]; //В игре UFO 2 есть 3 спрайта астероида

public UFOCanvas(Display d) {

super(true);

display = d;

// установить частоту кадров (30 fps)

frameDelay = 33;

}

public void start {

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

display.setCurrent(this);

// инициализировать генератор случайных чисел

rand = new Random;

// инициализировать спрайты НЛО и астероидов

ufoXSpeed = ufoYSpeed = 0;

try {

ufoSprite = new Sprite(Image.createImage("/Saucer.png"));

ufoSprite.setPosition((getWidth – ufoSprite.getWidth) / 2,

(getHeight – ufoSprite.getHeight) / 2);

Image img = Image.createImage("/Roid.png");

roidSprite[0] = new Sprite(img, 42, 35);

roidSprite[1] = new Sprite(img, 42, 35);

roidSprite[2] = new Sprite(img, 42, 35);

}

catch (IOException e) {

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

}

// запустить поток анимации

sleeping = false;

Thread t = new Thread(this);

t.start;

}

public void stop {

// остановить анимацию

sleeping = true;

}

public void run {

Graphics g = getGraphics;

// игровой цикл

while (!sleeping) {

update;

draw(g);

try {

Thread.sleep(frameDelay);

}

catch (InterruptedException ie) {}

}

}

private void update {

// обработка пользовательского ввода, изменение скорости НЛО

int keyState = getKeyStates;

if ((keyState & LEFT_PRESSED) != 0) //Клавиши со стрелками изменяют скорость НЛО по всем четырем направлениям

ufoXSpeed–;

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

ufoXSpeed++;

if ((keyState & UP_PRESSED) != 0)

ufoYSpeed–;

else if ((keyState & DOWN_PRESSED) != 0)

ufoYSpeed++;

ufoXSpeed = Math.min(Math.max(ufoXSpeed, -8), 8);

ufoYSpeed = Math.min(Math.max(ufoYSpeed, -8), 8);

// переместить спрайт НЛО

ufoSprite.move(ufoXSpeed, ufoYSpeed);

checkBounds(ufoSprite);

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

for (int i = 0; i < 3; i++) {

// переместить спрайты астероидов

roidSprite[i].move(i + 1, 1 – i);

checkBounds(roidSprite[i]); //Эта строка кода отвечает за отрисовку астероида при достижении границ экрана

// изменить отображаемый фрейм астероида

if (i == 1) //Индекс астероида определяет направление анимации

roidSprite[i].prevFrame;

else

roidSprite[i].nextFrame;

// проверить столкновение НЛО с астероидом

if (ufoSprite.collidesWith(roidSprite[i], true)) { //Поскольку второй параметр метода collidesWith равен true, то выполняется пиксельное детектирование столкновения

// воспроизвести предупреждающий звук

AlertType.ERROR.playSound(display);

// восстановить исходные положения и скорости объектов

ufoSprite.setPosition((getWidth – ufoSprite.getWidth) / 2,

(getHeight – ufoSprite.getHeight) / 2);

ufoXSpeed = ufoYSpeed = 0;

for (int j = 0; j < 3; j++)

roidSprite[j].setPosition(0, 0);

// нет необходимости обновлять спрайты астероидов

break;

}

}

}

private void draw(Graphics g) {

// Clear the display

g.setColor(0x000000);

g.fillRect(0, 0, getWidth, getHeight);

// нарисовать спрайт НЛО

ufoSprite.paint(g);

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

for (int i = 0; i < 3; i++)

roidSprite[i].paint(g);

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

flushGraphics;

}

private void checkBounds(Sprite sprite) {

// проверить положение спрайта

if (sprite.getX < -sprite.getWidth)

sprite.setPosition(getWidth, sprite.getY);

else if (sprite.getX > getWidth)

sprite.setPosition(-sprite.getWidth, sprite.getY);

if (sprite.getY < -sprite.getHeight)

sprite.setPosition(sprite.getX, getHeight);

else if (sprite.getY > getHeight)

sprite.setPosition(sprite.getX, -sprite.getHeight);

}

}

Поделиться с друзьями: