Текст книги "Создание игр для мобильных телефонов"
Автор книги: Майкл Моррисон
Жанр: Зарубежная компьютерная литература, Зарубежная литература
сообщить о неприемлемом содержимом
Текущая страница: 11 (всего у книги 35 страниц)
Тестировать мидлет UFO 2 намного интереснее, чем все предыдущие приложения, поскольку теперь вы можете управлять летающим объектом. На рис. 6.4 показан мидлет UFO 2.
Рис. 6.4. В мидлете UFO 2 вы можете управлять летающим объектом и сталкиваться с астероидами
Нетрудно заметить в этом примере отголоски игры вроде Asteroids. Поиграйте в игру, обратите внимание на то, как детектируются столкновения. Удивительно, как, используя данные изображений, можно определять столкновения с большой степенью точности. Вы можете аккуратно огибать астероиды на очень небольших расстояниях.
РезюмеЭффективное взаимодействие игрока с приложением – это критический фактор при разработке игр. Разработчику очень важно тщательно проработать пользовательский ввод, чтобы он был максимально эффективным в условиях ограниченного пользовательского интерфейса мобильного устройства. В этой главе шла речь о том, как обрабатывать ввод с клавиатуры мобильного устройства – это удивительно просто. Вы также узнали, как детектировать столкновения спрайтов и использовать фреймовую анимацию. Хотя, несомненно, пример UFO 2, созданный в этой главе, можно улучшить, он сочетает в себе все элементы разработки мобильной игры.
В следующей главе вы непосредственно окунетесь в мир программирования мобильных игр и создадите первую мобильную игру Henway, которая во многом похожа на классическую аркаду Frogger.
Еще немного об играхПора сделать что-то действительно креативное! Я хочу, чтобы вы добавили еще один анимационный спрайт в мидлет UFO 2. Это может быть спутник, странный пришелец или комета, бороздящая космические просторы, – все, что захотите. Ниже приведено пошаговое описание, как это сделать:
1. нарисуйте или получите каким-либо другим способом изображение, состоящее из нескольких фреймов;
2. создайте член-переменную класса UFOCanvas, в которой будут храниться несколько экземпляров спрайта;
3. в методе start() мидлета загрузите изображение;
4. в методе start() создайте объекты типа Sprite новых спрайтов, не забудьте при инициализации каждого передать соответствующее изображение;
5. создайте код, обновляющий положение новых спрайтов, в методе update() проверяйте их положение на экране. Если вы хотите определять столкновения между созданными спрайтами и любыми другими спрайтами, то смело делайте это в методе update().
6. в методе draw() добавьте код, выполняющий рисование спрайтов.
Хотя для добавления спрайта в мидлет требуется выполнить несколько шагов, это не очень сложно. Уверен, что это упражнение послужит вам на пользу, потому как вам придется более глубоко проникнуть в код и поэкспериментировать, изменяя его.
Глава 7
Henway: дань игре Frogger
Архив Аркад
Выпущенная в 1980 году компанией Midway игра Rally-X была очень популярной, она представляла собой гонки в лабиринте. Вы управляете небольшим автомобилем по лабиринту, избегая столкновения с другими автомобилями. Интересный элемент игры Rally-X – это дымовая завеса, которую может выпускать автомобиль и временно нейтрализовать автомобили противника. Однако такая завеса требует дополнительного расхода топлива, поэтому, применяя ее, надо быть осторожным, – очень интересная находка в игре. Другая особенность X-Rally – это уменьшенная карта, на которой вы можете отслеживать свое положение, положение других автомобилей, а также флаги, которые необходимо собрать.
Вы потратили много времени, изучая программирование мидлетов и методов создания игр. Но пока вы еще не создали ни одной полноценной игры. В этой главе вы создадите свою первую игру, для этого вам потребуется применить все полученные знания о спрайтах. Игра Henway использует несколько спрайтов и все их возможности, о которых вы узнали в предыдущих главах. Эта игра во многом похожа на классическую игру Frogger, она является значительным этапом на пути постижения искусства программирования мобильных игр. Эту игру вы можете использовать как основу для создания собственных.
В этой главе вы узнаете:
► почему полезно моделировать мобильные игры на основе классических аркад;
► как разработать игру Henway, аналог классической игры Frogger;
► как написать код игры Henway;
► почему тестирование игры – это неотъемлемая часть процесса проектирования.
Об игре HenwayВ оригинальной аркаде Frogger целью было перевести лягушку через шоссе и реку. На пути лягушки встречались преграды: автомобили, волны и крокодилы – это лишь часть из них. Лягушка должна была перейти целой и невредимой из нижней части экрана в верхнюю. С увеличением числа переправленных лягушек, возрастает и сложность игры – автомобили ускоряются, добавляются новые препятствия. Несмотря на то что по современным игровым стандартам Frogger – очень простая игра, это хороший пример классической игры, в которую весело играть. Поэтому эта игра как нельзя лучше подходит для создания собственной, немного фантазии – и готов новый игровой шедевр!
В копилку Игрока
Если вы удивлены моим подходом, то поймите, что я вовсе не призываю к тому, чтобы создавать игры на основе старых. Просто я обнаружил, что популярные игры прошлого могут дать хорошие идеи для создания новых. Сегодня многие пытаются создавать игры по мотивам популярных фильмов, но не каждый захочет разыгрывать драматический сюжет, особенно на экране мобильного телефона. В большинстве случаев люди предпочитают играть в простые игры, а следовательно, для этого как нельзя лучше подходят проверенные временем классические игры.
Популярность игры Frogger привела к тому, что по ее подобию был создан ряд игр в надежде на успех. Одна из этих игр называется Freeway, в ней цыпленок должен был перейти через загруженное шоссе. Freeway была создана компанией Activision для игровой системы Atari 2600, которая была первой игровой консолью, достигшей большого успеха на рынке. Как обладатель и поклонник игры Freeway я подумал, что было бы неплохо создать в этой главе ее аналог. Игра будет называться Henway.
В отличие от Frogger в игре Henway главным героем является цыпленок, которому во что бы то ни стало необходимо перебраться на другую сторону шоссе. Также, в отличие от Frogger, в этой игре цыпленка необходимо перевести из левой половины экрана в правую. Экран выглядит приблизительно так, как показано на рис. 7.1.
Рис. 7.1. Игра Henway состоит из старта, шоссе и финиша, а также спрайтов цыпленка и автомобилей
Как вы видите, препятствия на пути цыпленка – это четыре автомобиля, проезжающие вверх и вниз по шоссе. Автомобили движутся с изменяющимися скоростями, что делает игру более привлекательной. В отличие от Frogger, в которой вам необходимо провести лягушку в определенные места на экране, в Henway вы просто должны перевести цыпленка через дорогу.
В копилку Игрока
Обратите внимание, что все рассматриваемые в книге примеры очень просты. Хотя есть множество способов усовершенствовать игры, я к этому не стремлюсь, оставляя вам поле для деятельности. Моя цель – показать простой для понимания, работающий пример мобильной игры, которую вы сможете дополнить интересными для вас деталями. В конце большинства глав я подкину вам несколько идей, как сделать игры более привлекательными.
Вы начинаете игру с тремя цыплятами. Как только вы переведете всех цыплят через шоссе, игра закончится. Важно где-нибудь на экране показать, сколько цыплят осталось переправить через дорогу. Кроме того, при потере цыпленка или удачном преодолении шоссе следует вывести какое-нибудь сообщение. Не помешает и система подсчета очков, чтобы вознаградить вас за хорошую игру.
Анализ игрыОбзор игры, приведенный чуть ранее, уже определил некоторые элементы игры, даже если вы этого и не заметили. Например, вы уже догадались, сколько спрайтов нужно для игры? В игре будет пять спрайтов: четыре автомобиля и цыпленок. Но можно и увеличить число автомобилей, чтобы усложнить игру, однако в этом примере будет лишь четыре машины.
А теперь вы сможете догадаться, сколько растровых изображений вам понадобится? Если вы сказали шесть, то вы очень близки к правильному ответу. Ниже перечислены семь спрайтов, необходимые в игре:
► фоновое изображение шоссе;
► изображение цыпленка (рис. 7.2);
Рис. 7.2. Изображение цыпленка состоит из двух фреймов, на которых цыпленок идет вправо
► четыре изображения автомобилей (рис. 7.3);
Рис. 7.3. Все изображения автомобилей ориентированы вертикально, потому что они будут перемещаться вверх или вниз
► маленькое изображение головы цыпленка (рис. 7.4).
Рис. 7.4. Маленькая голова цыпленка символизирует число оставшихся жизней
Вы, вероятно, посчитали все эти изображения за исключением последнего. Маленькое изображение головы цыпленка используется, чтобы сообщить игроку, сколько жизней осталось. Например, в начале игры в нижнем правом углу экрана отображаются три маленьких головы цыпленка. Если цыпленок погибает под колесами автомобиля, то число жизней уменьшается на одну; игра продолжается до тех пор, пока хоть один цыпленок жив.
Теперь, когда вы имеете представление о графических объектах, используемых в игре, давайте рассмотрим, что еще нужно для игры. Во-первых, очевидно, понадобится отслеживать число жизней. Также, вероятно, вы захотите увеличивать число очков, если цыпленок удачно преодолел шоссе. Булевская переменная будет следить, завершена игра или нет.
Есть еще одна переменная, о необходимости которой можно сказать, прежде чем приступить к разработке и тестированию. Я говорю о задержке считывания ввода, эта величина поможет улучшить реакцию на пользовательский ввод. Если в каждом игровом цикле будет реакция на нажатия клавиш, то цыпленок будет перемещаться по экрану с неимоверной скоростью. Чтобы ограничить скорость его перемещения и понизить частоту обработки ввода, вы можете ввести специальную переменную и проверять нажатые клавиши, например, на каждом третьем игровом цикле.
В копилку Игрока
Точно определить задержку ввода нельзя, ее можно установить методом проб и ошибок, поэтому вы можете подобрать любое значение. Главное, чтобы обработка пользовательского ввода производилась на частоте, близкой к частоте нажатий клавиш игроком. Также помните, что величина задержки обработки пользовательского ввода может изменяться от одной игры к другой в зависимости от аппаратных ресурсов и скорости выполнения мидлета.
Давайте подведем итог. Первоначальная разработка игры Henway говорит о том, что в процессе игры мы должны управлять следующими элементами:
► числом жизней цыплят;
► счетом;
► булевской переменной окончания игры;
► переменной задержки пользовательского ввода.
Помня об этом, вы готовы перейти дальше к разработке кода игры Henway.
Разработка игрыЯ надеюсь, что к настоящему моменту вы поняли, из чего состоит игра Henway. В следующих разделах речь пойдет о разработке кода этого мидлета. Это достаточно просто, поскольку большая часть кода основана на рассмотренных ранее примерах.
Неудивительно, что код игры Henway начинается с написания класса специального холста, производного от класса GameCanvas. Я говорю о классе Hcanvas, который ответственен за реализацию всей логики игры. Давайте рассмотрим один из его фрагментов. Ниже приведено объявление переменных класса:
private Display display;
private boolean sleeping;
private long frameDelay;
private int inputDelay;
private Random rand;
private Image background;
private Image chickenHead;
private Sprite chickenSprite; //Спрайты в игре Henway – это спрайт цыпленка и 4 спрайта автомобилей
private Sprite[] carSprite = new Sprite[4];
private int[] carYSpeed = new int[4]; //Поскольку автомобили движутся вертикально, не нужно хранить горизонтальные составляющие их скоростей
private boolean gameOver;
private int numLives;
private int score;
Первые две переменные и переменная rand должны быть вам знакомы по созданным ранее программам UFO. Четвертая переменная – новая. Переменная inputDelay контролирует чувствительность пользовательского ввода в игре. Оказывается, что если цыпленок сможет перемещаться очень быстро с одной стороны шоссе на другую, то в игру будет не так уж и весело играть. Чтобы ограничить скорость цыпленка, просто используйте переменную inputDelay.
Две переменные – объекты класса Image – используются для хранения фонового изображения и изображения головы цыпленка, жизни. Хотя эти изображения очень важны в игре, все же сердце Henway – это спрайты. Переменная chickenSprite – это спрайт цыпленка, а массив carSprite хранит спрайты всех четырех автомобилей. Поскольку при нажатиях на клавиши цыпленок перемещается на равные расстояния, то нет необходимости создавать переменную скорости. Однако автомобили движутся с различными скоростями, поэтому необходим массив carYSpeed – массив скоростей автомобилей вдоль оси Y.
Последние три переменные есть практически во всех играх, они хранят текущее состояние игры, число оставшихся жизней и счет. Переменная gameOver используется в нескольких фрагментах кода для проверки конца игры.
Переменная numLives хранит число оставшихся жизней цыпленка и используется для проверки окончания игры, а также определяет число изображений головы цыпленка, выводимых на экран. Наконец, переменная score хранит число очков, набранных игроком, ее значение будет отображаться в конце игры.
Как вы знаете, при создании объекта холста класса HCanvas вызывается конструктор. Кроме того, что он устанавливает частоту кадров игры, в нем выполняется очистка экрана и обнуление задержки ввода:
// установить частоту кадров (30 кадров/с)
frameDelay = 33;
// обнулить задержку ввода
inputDelay = 0;
Совет Разработчику
Помните, что частота кадров рассчитывается как обратная величина времени между кадрами в секундах. Поэтому, если перевести 33 мс в секунды, то получится 0.033 с. Если разделить 1 на 0.033, то получится приблизительно 30, то есть частота равна 30 кадров/с.
Смысл переменной inputDelay станет ясен чуть позже, когда вы узнаете, как она применяется для контролирования пользовательского ввода.
Метод start() игры Henway очень важен, поскольку выполняет ряд особых инициализаций в игре. Например, следующий код инициализирует три основные глобальные переменные:
gameOver = false;
numLives = 3;
score = 0;
Метод start() также загружает изображения и создает игровые спрайты:
try {
background = Image.createImage("/Highway.png");
chickenHead = Image.createImage("/ChickenHead.png");
chickenSprite = new Sprite(Image.createImage("/Chicken.png"), 22, 22);
chickenSprite.setPosition(2, 77); //Спрайт цыпленка помещается на траве в зоне старта, в левой части экрана
carSprite[0] = new Sprite(Image.createImage("/Car1.png"));
carSprite[0].setPosition(27, 0); //Этот автомобиль, также как и другие, помещается на дороге
carYSpeed[0] = 3;
carSprite[1] = new Sprite(Image.createImage("/Car2.png"));
carSprite[1].setPosition(62, 0);
carYSpeed[1] = 1;
carSprite[2] = new Sprite(Image.createImage("/Car3.png"));
carSprite[2].setPosition(93, 67);
carYSpeed[2] = -2;
carSprite[3] = new Sprite(Image.createImage("/Car4.png"));
carSprite[3].setPosition(128, 64);
carYSpeed[3] = -5;
}
catch (IOException e) {
System.err.println("Failed loading images!");
}
Этот код сначала создает фон и изображения голов цыпленка, после чего переходит к созданию спрайтов. Обратите внимание, каждый спрайт привязан к определенной области экрана. Хотя вы можете использовать выражения, вычисляющие положение спрайтов на основании данных о высоте и ширине экрана, можно непосредственно указать нужные координаты, что я и сделал. После того как начальные координаты установлены, инициализируется скорость спрайтов так, чтобы автомобили двигались в разных направлениях.
Совет Разработчику
Этот код приводит к вопросу о том, как будет вести себя приложение в зависимости от модели мобильного телефона. В нашем случае игра Henway создана для эмулятора J2ME, размер экрана которого 180 177, поэтому все координаты графики относятся именно к этому экрану. Если вы хотите, чтобы вашу игру можно было запускать на различных моделях телефонов, то вы должны вычислять положения спрайтов и изображений.
Хотя метод start() очень важен, все же большую роль играет метод update(), который обеспечивает работу всех игр, рассматриваемых в книге. В игре Henway метод update() выполняет ряд очень важных задач, например, обработку пользовательского ввода, перемещение спрайта цыпленка и проверку, попал ли цыпленок под колеса автомобиля или в сохранности преодолел шоссе. Перед выполнением любой задачи, осуществляется проверка, запущена ли игра:
if (gameOver) {
int keyState = getKeyStates();
if ((keyState & FIRE_PRESSED) != 0) {
// запустить новую игру
chickenSprite.setPosition(2, 77); //Чтобы начать игру заново, необходимо установить спрайт цыпленка в исходное положение и установить значения ряда переменных
gameOver = false;
score = 0;
numLives = 3;
}
// игра закончена, поэтому не нужно выполнять обновление
return;
}
Этот код проверяет, закончена ли игра – в единственном месте, где имеет смысл перезапустить игру. Клавиша стрельбы используется для перезапуска игры. В разных телефонах эта клавиша называется по-разному, но в эмуляторе J2ME это клавиша Select, связанная с клавишей Enter персонального компьютера. Код перезапуска игры в методе update() восстанавливает исходное положение спрайта цыпленка, обнуляет переменную gameOver, счет, а также восстанавливает исходное количество жизней. Это все, что требуется для перезапуска игры.
Метод update() также обрабатывает пользовательский ввод и перемещает цыпленка по экрану. Ниже приведен код, перемещающий спрайт цыпленка в соответствии с нажатыми клавишами:
if (++inputDelay > 2) {
int keyState = getKeyStates();
if ((keyState & LEFT_PRESSED) != 0) {
chickenSprite.move(-6, 0); //Кроме того, что спрайт цыпленка перемещается, с каждым нажатием клавиши изменяется номер фрейма анимации
chickenSprite.nextFrame();
}
else if ((keyState & RIGHT_PRESSED) != 0) {
chickenSprite.move(6, 0);
chickenSprite.nextFrame();
}
if ((keyState & UP_PRESSED) != 0) {
chickenSprite.move(0, -6);
chickenSprite.nextFrame();
}
else if ((keyState & DOWN_PRESSED) != 0) {
chickenSprite.move(0, 6);
chickenSprite.nextFrame();
}
checkBounds(chickenSprite, false); //Значение false, передаваемое вторым параметром, говорит о том, что цыпленок не должен выйти за границы экрана
// обнулить задержку ввода
inputDelay = 0;
}
Этот код объясняет, как работает переменная inputDelay: она увеличивается на каждой итерации игрового цикла и обрабатывает нажатия клавиш на каждой третьей итерации. Иначе говоря, реакция на нажатия клавиш снижена в три раза, это делает игру Henway более интересной и захватывающей, особенно на сложных уровнях. После того как детектировано нажатие клавиши, спрайт цыпленка перемещается на определенное расстояние и вызовом метода nextFrame() изменяется фрейм. Поскольку изображение цыпленка состоит из двух фреймов, то они сменяют друг друга при движении цыпленка. В результате такой простой анимации создается иллюзия того, что цыпленок идет.
Совет Разработчику
Порог для переменной inputDelay, несомненно, варьируется от одной игры к другой. В некоторых играх вам может понадобиться молниеносная реакция при нажатии на клавиши, в этом случае переменная inputDelay становится ненужной.
Другой фрагмент кода, представляющий интерес, – это метод checkBounds(), который проверяет, что цыпленок остается на экране. Если вы вспомните программу UFO 2, то одноименный метод использовался для проверки того, что астероиды находятся на экране. В игре Henway новая версия этого метода, второй параметр говорит о том, следует ли ограничить перемещение спрайта (значение false) или вернуть его на противоположную сторону (значение true). Чуть позже будет приведен код этого метода.
В методе update() очень важно по окончании каждого перемещения проверять, перешел ли цыпленок через шоссе. Ниже приведен код, проверяющий, перебрался ли цыпленок через шоссе:
if (chickenSprite.getX() > 154) { //Число 154 получено исходя из того, что ширина дороги равна 154 пикселям
// воспроизвести звук, если цыпленок удачно перебрался через шоссе
AlertType.WARNING.playSound(display);
// вернуть цыпленка в исходное положение и увеличить счет
chickenSprite.setPosition(2, 77);
score += 25;
}
Число 154 обозначает горизонтальную координату на игровом экране, где заканчивается шоссе. Если цыпленок находится дальше этой координаты, то вы знаете, что он благополучно перешел через шоссе. В этом случае воспроизводится звук, спрайт цыпленка возвращается в исходное положение, а счет увеличивается на 25 очков.
Но спрайт цыпленка – это не единственный спрайт, который перемещается по экрану. Метод update() также проверяет и движущиеся спрайты автомобилей:
for (int i = 0; i < 4; i++) {
// переместить спрайты автомобилей
carSprite[i].move(0, carYSpeed[i]);
checkBounds(carSprite[i], true);
// проверить столкновение спрайта цыпленка и спрайтов автомобилей
if (chickenSprite.collidesWith(carSprite[i], true)) {
// воспроизвести звук в случае гибели цыпленка
AlertType.ERROR.playSound(display);
// Check for a game over
if (–numLives == 0) {
gameOver = true;
} else {
// восстановить исходное положение цыпленка
chickenSprite.setPosition(2, 77); //Если игра не закончена, цыпленок возвращается в исходное положение, чтобы еще раз попытаться перейти дорогу.м
}
// не нужно обновлять спрайты автомобилей
break;
}
}
Все спрайты автомобилей перемещаются в вертикальном направлении, их скорости хранятся в массиве carYSpeed. Затем выполняется проверка, достиг ли автомобиль противоположной стороны экрана, для чего вызывается метод checkBounds() со вторым параметром true. Наиболее важный код – это детектирование столкновений спрайтов цыпленка и автомобилей. Если они столкнулись, то воспроизводится звук «ошибка» и переменная numLives уменьшается на 1. Если значение переменной равно 0, то игра закончена, значение переменной gameOver приравнивается true. Если нет, положение спрайта цыпленка обнуляется, а игра возобновляется. Важно отметить, что при столкновении спрайтов цикл прерывается, потому что нет необходимости проверять, был ли сбит цыпленок еще раз.
Поскольку в игре используется не так много графики, метод draw() класса HCanvas очень прост. Первое, что он выполняет, – выводит фоновое изображение:
g.drawImage(background, 0, 0, Graphics.TOP | Graphics.LEFT);
После этого выводится число оставшихся жизней цыпленка:
for (int i = 0; i < numLives; i++)
g.drawImage(chickenHead, 180 – ((i + 1) * 8), 170, Graphics.TOP |
Graphics.LEFT);
Проще всего нарисовать, вероятно, самый важный спрайт игры – спрайт цыпленка. Для этого необходима единственная строка кода:
chickenSprite.paint(g);
Спрайты автомобилей нарисовать также несложно, просто вызывайте метод paint() внутри цикла:
for (int i = 0; i < 4; i++)
carSprite[i].paint(g);
И, наконец, последнее, что остается вывести, – это сообщение «game over» (игра закончена), но его необходимо отображать только в случае, если игра закончена. Ниже приведен код, выполняющий это:
if (gameOver) {
// вывести сообщение о конце игры и счет
g.setColor(255, 255, 255); // white
g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,
Font.SIZE_LARGE));
g.drawString("GAME OVER", 90, 40, Graphics.TOP | Graphics.HCENTER);
g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,
Font.SIZE_MEDIUM));
g.drawString("You scored " + score + " points.", 90, 70, Graphics.TOP |
Graphics.HCENTER);
}
Для каждой строки текста используется шрифт разного размера, поэтому сообщение «game over» больше, нежели набранное число очков. Больше ничего особенного в этом коде нет.
Последний фрагмент кода, который я хотел бы выделить, – это код нового улучшенного метода checkBounds(), который или возвращает спрайт в исходное положение, или ограничивает его дальнейшее перемещение:
if (wrap) {
// перемесить спрайт в исходное положение
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());
}
else {
// остановить спрайт у края экрана
if (sprite.getX() < 0) //Код предохраняет спрайты от выхода за границы экрана
sprite.setPosition(0, sprite.getY());
else if (sprite.getX() > (getWidth() – sprite.getWidth()))
sprite.setPosition(getWidth() – sprite.getWidth(), sprite.getY());
if (sprite.getY() < 0)
sprite.setPosition(sprite.getX(), 0);
else if (sprite.getY() > (getHeight() – sprite.getHeight()))
sprite.setPosition(sprite.getX(), getHeight() – sprite.getHeight());
}
Первая часть этого кода идентична коду метода chackBounds() мидлета UFO 2. Второй блок – новый, он ограничивает перемещение спрайта. По мере работы с книгой вы обнаружите, что метод checkBounds() очень полезен и широко применяется в играх.
Хотя я не хочу приводить большие листинги, стоит посмотреть на класс HCanvas целиком. В листинге 7.1 приведен полный код класса HCanvas.
Листинг 7.1. Класс HCanvas – это специальный холст мидлета Henway
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.util.*;
import java.io.*;
public class HCanvas extends GameCanvas implements Runnable {
private Display display;
private boolean sleeping;
private long frameDelay;
private int inputDelay;
private Random rand;
private Image background;
private Image chickenHead;
private Sprite chickenSprite;
private Sprite[] carSprite = new Sprite[4];
private int[] carYSpeed = new int[4];
private boolean gameOver;
private int numLives;
private int score;
public HCanvas(Display d) {
super(true);
display = d;
// Set the frame rate (30 fps)
frameDelay = 33;
// обнулить задержку ввода
inputDelay = 0;
}
public void start() {
// установить холст как текущий экран
display.setCurrent(this);
// инициализировать генератор случайных чисел
rand = new Random();
// инициализация переменных
gameOver = false;
numLives = 3;
score = 0;
// инициализация фонового изображения и спрайтов
try {
background = Image.createImage("/Highway.png");
chickenHead = Image.createImage("/ChickenHead.png");
chickenSprite = new Sprite(Image.createImage("/Chicken.png"), 22, 22);
chickenSprite.setPosition(2, 77);
carSprite[0] = new Sprite(Image.createImage("/Car1.png"));
carSprite[0].setPosition(27, 0);
carYSpeed[0] = 3;
carSprite[1] = new Sprite(Image.createImage("/Car2.png"));
carSprite[1].setPosition(62, 0);
carYSpeed[1] = 1;
carSprite[2] = new Sprite(Image.createImage("/Car3.png"));
carSprite[2].setPosition(93, 67);
carYSpeed[2] = -2;
carSprite[3] = new Sprite(Image.createImage("/Car4.png"));
carSprite[3].setPosition(128, 64);
carYSpeed[3] = -5; //Последний автомобиль – самый быстрый
}
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() {
// проверить, перезапущена ли игра
if (gameOver) {
int keyState = getKeyStates();
if ((keyState & FIRE_PRESSED) != 0) {
// Start a new game
chickenSprite.setPosition(2, 77);
gameOver = false;
score = 0;
numLives = 3;
}
// игра окончена, нет необходимости обновления
return;
}
// обработать пользовательский ввод
if (++inputDelay > 2) {
int keyState = getKeyStates();
if ((keyState & LEFT_PRESSED) != 0) {
chickenSprite.move(-6, 0);
chickenSprite.nextFrame();
}
else if ((keyState & RIGHT_PRESSED) != 0) {
chickenSprite.move(6, 0);
chickenSprite.nextFrame();
}
if ((keyState & UP_PRESSED) != 0) {
chickenSprite.move(0, -6);
chickenSprite.nextFrame();
}
else if ((keyState & DOWN_PRESSED) != 0) {
chickenSprite.move(0, 6);
chickenSprite.nextFrame();
}
checkBounds(chickenSprite, false);
// обнулить задержку ввода
inputDelay = 0;
}
// проверить, перешел ли цыпленок через шоссе
if (chickenSprite.getX() > 154) {
// воспроизвести звук, если цыпленок преодолел шоссе
AlertType.WARNING.playSound(display);
// вернуть спрайт цыпленка в исходное положение и увеличить счет
chickenSprite.setPosition(2, 77);
score += 25;
}
// обновить спрайты автомобилей
for (int i = 0; i < 4; i++) {
// переместить спрайты автомобилей
carSprite[i].move(0, carYSpeed[i]);
checkBounds(carSprite[i], true); //Значение true, передаваемое вторым параметром, говорит о том, что автомобили при достижении границы экрана появятся у противоположного края
// проверить столкновения между спрайтами автомобилей и спрайтом цыпленка
if (chickenSprite.collidesWith(carSprite[i], true)) {
// воспроизвести звук при гибели цыпленка
AlertType.ERROR.playSound(display);
// проверить, не закончена ли игра
if (–numLives == 0) {
gameOver = true;
} else {
// вернуть спрайт цыпленка в исходное положение
chickenSprite.setPosition(2, 77);
}
// нет необходимости обновлять спрайты автомобилей
break;
}
}
}
private void draw(Graphics g) {
// вывести фоновое изображение
g.drawImage(background, 0, 0, Graphics.TOP | Graphics.LEFT);
// вывести число оставшихся жизней
for (int i = 0; i < numLives; i++) //Ряд маленьких изображений цыплят отражает число оставшихся жизней
g.drawImage(chickenHead, 180 – ((i + 1) * 8), 170, Graphics.TOP |
Graphics.LEFT);
// нарисовать спрайт цыпленка
chickenSprite.paint(g);
// нарисовать спрайт автомобиля
for (int i = 0; i < 4; i++)
carSprite[i].paint(g);
if (gameOver) {
// вывести сообщение о конце игры и счет
g.setColor(255, 255, 255); // белый
g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,
Font.SIZE_LARGE));
g.drawString("GAME OVER", 90, 40, Graphics.TOP | Graphics.HCENTER);
g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,
Font.SIZE_MEDIUM));
g.drawString("You scored " + score + " points.", 90, 70, Graphics.TOP |
Graphics.HCENTER);
}
// вывести содержимое буфера на экран
flushGraphics();
}
private void checkBounds(Sprite sprite, boolean wrap) {
// переместить/остановить спрайт
if (wrap) {
// переместить спрайт в исходное положение
if (sprite.getX() < -sprite.getWidth())
sprite.setPosition(getWidth(), sprite.getY());
else if (sprite.getX() > getWidth())
Правообладателям!
Это произведение, предположительно, находится в статусе 'public domain'. Если это не так и размещение материала нарушает чьи-либо права, то сообщите нам об этом.