Всего за 239 руб. Купить полную версию
...
Совет Разработчику
Помните, что частота кадров рассчитывается как обратная величина времени между кадрами в секундах. Поэтому, если перевести 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);
После этого выводится число оставшихся жизней цыпленка: