Программирование мобильных телефонов на Java



8.9. Столкновение объектов



Практически во всех играх приходится обрабатывать события связанные со столкновением двух объектов или с препятствием. В профиле MIDP 2.0 существует три отличных метода, отслеживающих факт столкновения. Все три метода определены в классе Sprite.

  •  collidesWith(Image image, int x, int y, Boolean pixelLevel) - определяет факт столкновения со статическим изображением;
  •  collidesWith (Sprite s, Boolean pixelLevel) - определяет столкновение между двумя объектами класса Sprite;
  •  collidesWith(TiledLayer t. Boolean pixelLevel) -отслеживает столкновение между объектом класса Sprite и объектом класса TiledLayer.


Используя эти методы можно обрабатывать всевозможные ситуации, связанные со столкновением. Как правило, при столкновении должны произойти какие-то события, чаще всего связанные с изменением первоначального состояния объекта. Это может быть уничтожение объекта, его перемещение, уменьшение или увеличение. В основе изменения состояния объекта положен перебор имеющихся фреймов в анимационной последовательности или перерисовка изображения на новом месте. Для этих операций в классе Sprite имеется несколько методов. С одним из методов nextFrame (), вы уже познакомились в разделе 8.8, рассмотрим оставшиеся методы.

  •  prevFrame () - с помощью этого метода происходит переход к предыдущим фреймам изображения. Этот метод подобен методу nextFrame (), только переход осуществляется в обратном порядке;
  •  setFrame() - производит установку при помощи индекса заданного фрейма из всей имеющейся последовательности фреймов.
  •  setFrameSequence () - устанавливает определенную фреймовую последовательность при помощи массива индексов в анимационной последовательности;
  •  getFrame () - узнает, какой из фреймов исходного изображения используется в текущий момент;
  •  set Image () - заменяет исходное изображение на новое. Можно использовать этот метод, например в том случае, если объект уничтожен, и его необходимо перерисовать заново.


Набора этих методов вполне достаточно для обработки различных ситуаций возникающих в игровом процессе. Однако необходимо очень тщательно разобраться в действии всех вышеперечисленных методов. Для этого был написан исходный код примера, иллюстрирующего работу методов nextFrame (), prevFrame (), setFrame () и setFrameSequence (). В этой программе на экран выводятся четыре бомбы и синий шарик, перемещая который в разные стороны можно произвести столкновение с четырьмя бомбами. Все бомбы являются объектами класса MySprite, являющимся подклассом класса Sprite. Метод, обрабатывающий столкновение мяча и бомбы, использует один из четырех методов nextFrame (), prevFrame.(), setFrame () и setFrameSequence () для каждой из бомб, что красочно иллюстрирует работу каждого метода. Исходные, изображения бомбы и мяча выполнены в виде последовательности четырех фреймов размером 23x23 пикселя. Первый фрейм мяча и бомбы является исходным изображением, а три последующих фрейма реализованы в виде взрыва. Переход по этим фреймам создает иллюзию взрыва мячика или бомбы. Но оттого, что для каждой бомбы как мы договорились, используются различные методы, то результат может оказаться неожиданным. Поэтому внимательно посмотрите на код примера в листинге 8.4 и запустите программу  с помощью любого эмулятора, поддерживающего профиль MIDP 2.0. Либо откомпилируйте исходный код из листинга 8.4 и внимательно ознакомьтесь с работой этой программы. Я уверен, что вы без труда разберетесь, что именно нужно сделать для логического завершения взрывов бомб и мяча. Алгоритм действий очень прост и может быть следующим после столкновения мяча с одной из мин. Необходимо произвести взрыв, последовательно переходя по всем фреймам, после чего, например, нарисовать бомбу и мячик заново. В листинг 8.4 предложен исходный код примера.

/ * *
листинг 8.4
класс MainGame
*/
import javax.microedition.Icdui.*;
import javax.microedition.midlet.*;
public class MainGame extends MIDlet implements
CommandListener
{
// команда выхода
private Command
exitMidlet = new Command(«Выход»,Command.EXIT, 0);
// объект класса MyGameCanvas
private MyGameCanvas mr;
public void startApp()
{
// обрабатываем исключительную ситуацию.
try{
// инициализируем'объект класса MyGameCanvas
mr = new MyGameCanvas();
// запускаем поток
mr.start{};
// добавляем команду выхода
mr.addCommand(exitMidlet) ;
 mr.setCommandListener(this);
// отражаем текущий дисплей
Display.getDisplay(this).setCurrent(mr) ;
}
catch (Java . io. lOException zxz) {} ;
 }
public void pauseApp() {}
public void destroyApp(boolean unconditional)
{
// останавливаем поток
if(mr != null) mr.stopf);
 }
public void commandAction(Command c, Displayable d)
{
if (c = = exitMidlet)
{
destroyApp (false);
notifyDestroyed(); }
 }
  }
}
/**
файл MyGameCanvas.Java
класс MyGameCanvas
*/
import java.io.*;
import javax.microedition.Icdui.*;
import javax.microedition.Icdui.game.*;
public class MyGameCanvas
extends GameCanvas implements
Runnable
{
// создаем объект класса MySprite
private MySprite bol;
// создаем объект класса LayerManager
private LayerManager lm;
// создаем бомбы
private MySprite bombal, bomba2, bombaB, bomba4;
// логическая переменная
boolean z;
public MyGameCanvas() throws lOException
{
// обращаемся к конструктору суперкласса Canvas
super(true);
// загружаем изображение мяча
linage bollmage = Image.createlmage («/bol.png»)
// инйциалтзируем объект bol
bol = new MySprite(bollmage, 23, 23);
// выбираем позицию в центре экрана
bol.setPosition(getWidth() /2, getHeight. () /2) ;
// загрузка изображения бомбы
Image bombalmage = Image.createlmage(«/bomba.png»);
// инициализируем первую бомбу
bombal = new MySprite(bombalmage, 23, 23);
// выбираем позицию для первой бомбы
bombal.setPosition(10, 10);
// инициализируем вторую бомбу
bomba2 = new MySprite(bombalmage, 23, 23);
// выбираем позицию для второй бомбы
bomba2 . setPosition( getwidth()-30, 10);
// инициализируем третью-бомбу
bоmbа3 = new MySprite(bombalmage, 23, 23);
// выбираем позицию для третьей бомбы
bоmbа3.setPosition(10, getHeight()-40);
// инициализируем четвертую бомбу
bomba4 = new MySprite(bombalmage, 23, 23);
// выбираем позицию для четвертой бомбы
bomba4.setPosition(getWidth()-30, getHeight 0-40);
// инициализируем менеджер уровней
lm = new LayerManager();
// добавляем мяч к уровню
lm.append(bol);
// добавляем бомбы к уровню
lm.append(bombal);
lm.append(bomba2);
lm.append(ЬотЬаЗ);
lm.append(bomba4); }
// обрабатываем столкновение
public void stolknovenie()
{
// столкновение с первой бомбой
if (bol. collidesWith (bombal, true))
{
bol.nextFrame();
bombal.nextFrame(); }
// столкновение со второй бомбой
if(bol.collidesWith(bomba2, true))
{
bol.prevFrame(); bomba2.prevFrame();
}
// столкновение с третьей бомбой
if (bol .collidesWith(bоmbа3 , true))
{
bol.setFrame (2) ;
vbomba3.setFrame(0);
}
// столкновение с четвертой бомбой
if(bol.collidesWith(bomba4, true))
{
int[] i = {2,3};
bol.setFrame(0);
bomba4 . setFrameSequence (i) ;
}
 }
public void start()
{
z = true;
// создаем и запускаем поток
 Thread t = new Thread(this);
t.start();
}
// останавливаем поток
public void stop()
{ z = false; }
public void run()
{
// получаем графический контекст
Graphics g = getGraphics(); while (z)
 {
// столкновение с препятствием
stolknovenie ();
// обрабатываем события с клавиш телефона
inputKey();
// рисуем графические элементы init(g);
// останавливаем цикл на 20 миллисекунд
 try { Thread.sleep(20);
}
catch (Java.lang.InterruptedException zxz) {};
}
}
private void inputKey()
{
//-определяем нажатую клавишу
int keyStates = getKeyStates();
// код обработки для левой нажатой клавиши
if ((keyStates & LEFT_PRESSED) != 0) bol.moveLeft();
// код обработки для правой нажатой клавиши
if ((keyStates & RIGHT_PRESSED) != 0) bol.moveRight();
// код обработки для клавиши вверх
if ((keyStates & UP_PRESSED) != 0) bol.moveUp();
// код обработки для клавиши вниз
if ({keyStates & DOWN_PRESSED) !=-0) bol.moveDown();
}
private void init(Graphics g)
 {
// желтый цвет фона
g.setColor(0xffff00);
// перерисовываем экран
g.fillRect(0, 0, getWidth(), getHeight());
// рисем уровень в точке 0,0
lm.paint(g, 0, 0);
// двойная буферизация
flushGraphics();
}
}
/**
файл MySprite.Java класс MySprite
*/
import javax.microedition.Icdui.*;
import javax.microedition.Icdui.game.*;
public class MySprite extends Sprite
// констоуктор
oublic MySprite(Image image, int fw, int fh)
// обращаемся к конструктору суперкласса
super( image , fw, fh);
}
//метод для левой нажатой клавиши
public void moveLeft()
//передвигаем спрайт
move(-1,0);
//метод  для правой нажатой клавиши
publiс void moveRight()
//передвигаем спрайт
move(1,0);
}
// метод для клавиши вверх
 public void rnoveUp ()
// передвигаем спрайт
// метод для клавиши вниз
public void moveDown()
//передвигаем спрайт
 move(0,1);
}
}


В листинге 8-4 содержатся три класса MainGame, MyGameCanvas и My Sprite. Основной код обработки столкновений бомб и мяна находится в классе Мус игпйСа n vas - этому классу мы и уделим особое внимание при разборе - листинга.

В конструкторе класса MyGameCanvas происходит загрузка изображения мяча из файла ресурса bol.png, инициализация объекта bol, класса MySprite и устанавливается позиция прорисовки на экране объекта bol.

ImagebolImage = Image.createlmage(«/bol.png») ;
bol= new KySprioe(bollmage, 23, 23);
bol.setPosition(getWidth()/2 ,getHeight()/2 ) ;


Заметьте, что позиция вывода мяча на экран установлена в центре экрана, но эта позиция определена для левой верхней точки мяча, поэтому именно левый верхний угол изображения спрайта мяча будет находиться в центре экрана. Для того чтобы нарисовать сам спрайт в центре экрана, нужно переопределить опорную позицию мяча с помощью метода def ineRef erencePixel ().

Затем в конструкторе класса MyGameCanvas загружается изображение бомбы.

Image bombalmage=Image.createlmage(«/bomba.png»);


После этого происходит инициализация четырех объектов bombal, bomba2, bomba3 и bomba4 класса MySprite и устанавливается позиция для вывода всех четырех бомб на экран телефона.

bombal = new MySprite(bombalmage, 23, 23);
bombal.setPosition(10,10);
bomba2 = new MySprite(bombalmage, 23, 23);
bomba2.setPosition( getwidth()-30, 10);
bomba3 = new MySprite(bombalmage, 23, 23);
bonba3.setPosition(10, getHeight()-40);
bomba4 = new MySprite(bombalmage, 23, 23);.
bomba4.setPosition(getWidth()-30, getHeight()-40);


Все четыре бомбы рисуются в разных углах экрана слева направо и сверху вниз.

Произведя загрузку необходимых изображений и инициализируя все объекты класса MySprite, можно добавить их к уровню с помощью менеджера уровней.

lm.append(bol);
lm.append(bombal);
lm.append(bomba2);
lm.append(bomba3);
 lm. append (bomba4);


Наша задача в этом примере - это определение столкновения бомб и мячика, для этого создан метод stolknovenie (), где при помощи конструкции if/ else происходит обработка столкновения объектов bol и bombalbomba4. Сейчас было бы очень хорошо, если бы вы могли запустить  программу из листинга 8.4 и попробовали осуществить столкновение мяча с четырьмя бомбами. Как мы договорились, при столкновении будет обсуждаться работа четырех разных методов.

В столкновении с первой бомбой, находящейся в левом верхнем углу экрана, используется метод next Frame (). Если вы переместите мячик по экрану, то при наезде на первую бомбу произойдет взрыв бомбы и мяча. То есть начнется перебор всех имеющихся фреймов обоих объектов в цикличном порядке. Как только вы уберете мячик с бомбы, взрыв обоих объектов прекратится, потому что закончится перебор фреймов изображений. А состояние бомбы и мяча будет соответствовать одному из фреймов всей анимационной последовательности, при этом возможности повлиять на остановку- перехода по фреймам в методе nextFrame () нет.

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

Третья бомба рисуется в нижнем углу экрана и для обработки столкновения мяча с бомбой используется метод setFrame (). Этот метод производит переход по заданным фреймам всей анимационной последовательности по номеру или индексу фрейма. В этом примере используется следующий код при столкновении мяча и третьей бомбы.

bol, setFrame (2);
bomba3.setFrame(0);


Когда вы передвинете мячик на третью бомбу, то увидите, что изображение мячика изменится, и будет соответствовать третьему фрейму всей анимационной последовательности мячика. Состояние бомбы останется неизменным, потому что используется индекс 0, а это первый фрейм бомбы.

Четвертая бомба при столкновении задействует метод setFrameSequenсе (), благодаря которому можно использовать фреймовые последовательности в виде массива индексов.

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

Назад Начало