Создание игр для мобильных телефонов - Майкл Моррисон
Шрифт:
Интервал:
Закладка:
private TiledLayer landLayer;
private int waterDelay;
private int[] waterTile = { 1, 3 }; //В слое воды используются два различных анимационных изображения
private Image infoBar;
private Sprite playerSprite;
private DriftSprite[] pirateSprite = new DriftSprite[2];
private DriftSprite[] barrelSprite = new DriftSprite[2];
private DriftSprite[] mineSprite = new DriftSprite[5];
private DriftSprite[] squidSprite = new DriftSprite[5];
private Player musicPlayer;
private Player rescuePlayer;
private Player minePlayer;
private Player gameoverPlayer;
private boolean gameOver;
private int energy, piratesSaved;Первые несколько переменных используются для хранения менеджера слоев, положения окна вида, слоя воды и слоя суши. Переменные waterDelay и waterTile контролируют анимацию воды в замощенном слое водного слоя. Поскольку в игре два различных анимационных элемента воды, переменная waterTile – это массив целых чисел, состоящий из двух элементов.
Переменная infoBar хранит растровое изображение, используемое как фон информационной строки, в которой отображается энергия корабля и число спасенных пиратов. Затем создаются несколько спрайтов: пиратский корабль, два пирата и пара бочек, пять мин и пять осьминогов. Интересно заметить, что в игре больше не будут создаваться какие-либо спрайты. Позже вы узнаете, как повторно использовать спрайты, чтобы создать иллюзию того, что их число увеличилось.
Звуковые эффекты и музыка в игре воспроизводятся с помощью объектов класса Player. И наконец, состояние игры отражается переменными energy и piratesSaved.
Разработка метода start()
Метод start() в игре High Seas выполняет инициализацию всех переменных класса. Например, следующий код создает изображение для информационной строки, а также замощенные слои воды и суши:try {
infoBar = Image.createImage("/InfoBar.png");
waterLayer = new TiledLayer(24, 24, Image.createImage("/Water.png"), 32, 32);
landLayer = new TiledLayer(24, 24, Image.createImage("/Land.png"), 32, 32);
}
catch (IOException e) {
System.err.println("Failed loading images!");
}Если вы вспомните, то в водном слое используются два анимационных элемента, имитирующих движение воды. Ниже приведен код, создающий эти элементы:
waterLayer.createAnimatedTile(1); waterLayer.createAnimatedTile(3);
Два анимационных элемента имеют различные индексы (1 и 3), это важно, поскольку элементы при создании анимации будут отображать различные картинки. Если использовались одинаковые инициализирующие значения, то вы не увидите никакой разницы. Также в этой главе вы разработали водный слой, результатом которого является массив целых чисел waterMap, содержащий карту слоя. Ниже приведен код, инициализирующий замощенный слой значениями из этого массива:
for (int i = 0; i < waterMap.length; i++) {
int column = i % 24;
int row = (i – column) / 24;
waterLayer.setCell(column, row, waterMap[i]);
}Чтобы завершить инициализацию водного слоя, необходимо установить начальное значение для переменной waterDelay, которая используется как счетчик, регулирующий скорость анимации:
waterDelay = 0;
Подобно водному слою, слой суши описывается картой индексов landMap, содержание которой вы видели ранее. Следующий код выполняет инициализацию слоя суши:
for (int i = 0; i < landMap.length; i++) {
int column = i % 24;
int row = (i – column) / 24;
landLayer.setCell(column, row, landMap[i]);
}После того как слои суши и воды были успешно созданы, можно перейти к спрайтам. Если вы вспомните, в игре есть пиратский корабль, управляемый игроком, два пирата, две бочки, пять мин и пять осьминогов. Спрайт игрока – это объект класса Sprite, поскольку ему не требуется выполнять особых функций. В то же время остальные спрайты – это объекты класса DriftSprite, нового класса, ранее созданного в этой главе. Ниже приведен код, создающий эти спрайты:
try {
playerSprite = new Sprite(Image.createImage("/PlayerShip.png"), 43, 45);
int sequence2[] = { 0, 0, 0, 1, 1, 1 };
int sequence4[] = { 0, 0, 1, 1, 2, 2, 3, 3 };
for (int i = 0; i < 2; i++) {
pirateSprite[i] = new DriftSprite(Image.createImage("/Pirate.png"), //Четвертый и пятый параметры конструктора DriftSprite() – это скорость спрайта и слой-барьер соответственно
29, 29, 2, landLayer);
pirateSprite[i].setFrameSequence(sequence2);
placeSprite(pirateSprite[i], landLayer);
barrelSprite[i] = new DriftSprite(Image.createImage("/Barrel.png"),
24, 22, 1, landLayer);
barrelSprite[i].setFrameSequence(sequence4);
placeSprite(barrelSprite[i], landLayer); //Метод placeSprite () случайным образом размещает спрайт на карте
}
for (int i = 0; i < 5; i++) {
mineSprite[i] = new DriftSprite(Image.createImage("/Mine.png"),
27, 23, 1, landLayer);
mineSprite[i].setFrameSequence(sequence2);
placeSprite(mineSprite[i], landLayer);
squidSprite[i] = new DriftSprite(Image.createImage("/Squid.png"),
24, 35, 3, landLayer);
squidSprite[i].setFrameSequence(sequence2);
placeSprite(squidSprite[i], landLayer);
}
}
catch (IOException e) {
System.err.println("Failed loading images!");
}Спрайт игрока создается в тот момент, когда размер фрейма передается конструктору. Остальные спрайты – это объекты класса DriftSprite, они имеют различные скорости. Например, спрайты пиратов имеют скорость 2, а спрайты мин и бочек перемещаются со скоростью 1. Смысл в том, что пираты могут плавать, поэтому они должны перемещаться быстрее бочек и мин, которые на самом деле просто дрейфуют. Аналогично, спрайты осьминогов имеют скорость 3. Важно отметить, что переменная landLayer выполняет функции барьера для всех спрайтов.
...Совет Разработчику
Скорость спрайтов – это один из самых интересных моментов в мобильных играх. Поскольку спрайты осьминогов не дрейфуют, а плывут, попробуйте увеличить их скорость и посмотрите, как это отразится на игре. Хотя осьминоги по-прежнему двигаются хаотично, они стали более грозными противниками, потому что быстро перемещаются по игровому экрану.
Замощенные слои и игровые спрайты объединяются менеджером слоев, который заботится об их упорядочивании и создании. Следующий код добавляет спрайты в менеджер слоев:
layers = new LayerManager();
layers.append(playerSprite);
for (int i = 0; i < 2; i++) {
layers.append(pirateSprite[i]);
layers.append(barrelSprite[i]);
}
for (int i = 0; i < 5; i++) {
layers.append(mineSprite[i]);
layers.append(squidSprite[i]);
}
layers.append(landLayer);
layers.append(waterLayer); //Последним добавляется слой воды, он будет выведен под остальными элементамиНе забудьте, что порядок, в котором вы добавляете спрайты в менеджер слоев, очень важен, первый добавленный спрайт будет выводится на экран поверх остальных объектов. Поэтому фоновые слои добавляются в последнюю очередь. Звуковые эффекты и музыка играют большое значение в оформлении большинства игр, и High Seas – не исключение. Приведенный ниже код устанавливает проигрыватели:
try {
InputStream is = getClass().getResourceAsStream("Music.mid");
musicPlayer = Manager.createPlayer(is, "audio/midi");
musicPlayer.prefetch();
musicPlayer.setLoopCount(-1);
is = getClass().getResourceAsStream("Rescue.wav");
rescuePlayer = Manager.createPlayer(is, "audio/X-wav");
rescuePlayer.prefetch();
is = getClass().getResourceAsStream("Mine.wav");
minePlayer = Manager.createPlayer(is, "audio/X-wav");
minePlayer.prefetch();
is = getClass().getResourceAsStream("GameOver.wav");
gameoverPlayer = Manager.createPlayer(is, "audio/X-wav");
gameoverPlayer.prefetch();
}
catch (IOException ioe) {
}
catch (MediaException me) {
}Как видно, для музыки создается один MIDI-проигрыватель, а также три проигрывателя – по одному на каждый из воспроизводимых в игре звуков (звук спасения пирата, звук подрыва на мине и звук окончания игры). Последний фрагмент метода start() начинает новую игру, для чего вызывается метод newGame():
newGame();
Чуть позже вы узнаете, как работает этот метод. А пока давайте перейдем к рассмотрению метода update(), который выполняет всю основную работу мидлета.
Разработка метода update()
Как вы знаете, метод update() вызывается один раз за игровой цикл, он отвечает за обновление спрайтов, слоев, проверяет столкновения, именно он обеспечивает работу приложения. В игре High Seas этот метод начинается с проверки окончания игры. Если результат положительный, начинается новая игра, для чего пользователь должен нажать клавишу «огонь»:
if (gameOver) {
int keyState = getKeyStates();
if ((keyState & FIRE_PRESSED) != 0)
// Start a new game
newGame();
// игра окончена, обновление не требуется
return;
}Для начала игры вызывается метод newGame(), о котором упоминалось ранее. Обратите внимание, что метод update() заканчивает свою работу сразу после вызова этого метода, потому что нет необходимости обновлять только что запущенную игру. Следующая функция метода update() – это обработка пользовательского ввода. Следующий код обрабатывает нажатие четырех клавиш со стрелками и перемещает окно вида:
int keyState = getKeyStates();
int xMove = 0, yMove = 0;
if ((keyState & LEFT_PRESSED) != 0) {
xMove = -4;
playerSprite.setFrame(3);
}
else if ((keyState & RIGHT_PRESSED) != 0) {
xMove = 4; //Чтобы корабль игрока передвигался быстрее, нужно изменить это значение
playerSprite.setFrame(1);
}
if ((keyState & UP_PRESSED) != 0) {
yMove = -4;
playerSprite.setFrame(0);
}
else if ((keyState & DOWN_PRESSED) != 0) {
yMove = 4;
playerSprite.setFrame(2);
}
if (xMove != 0 || yMove != 0) { //Изменить положение окна вида и переместить спрайт игрока в соответствии с нажатой клавишей
layers.setViewWindow(xView + xMove, yView + yMove, getWidth(),
getHeight() – infoBar.getHeight());
playerSprite.move(xMove, yMove);
}Если вы вспомните, в игре High Seas пиратский корабль остается неподвижным в центре экрана, а остальные элементы перемещаются. Код обработки пользовательского ввода достигает этого эффекта, перемещая окно вида в соответствии с нажатыми клавишами. Сначала определяется, на какое расстояние необходимо переместить изображение, а затем окно перемещается вызовом метода setViewWindow(). Спрайт игрока перемещается на это расстояние, чтобы оставаться в центре экрана. Класс DriftSprite проверяет столкновение со слоем-барьером всех спрайтов, кроме спрайта пиратского корабля. Приведенный далее код выполняет проверку столкновения корабля игрока со слоем-барьером: