Электронная библиотека » Майкл Моррисон » » онлайн чтение - страница 20


  • Текст добавлен: 14 ноября 2013, 04:34


Автор книги: Майкл Моррисон


Жанр: Зарубежная компьютерная литература, Зарубежная литература


сообщить о неприемлемом содержимом

Текущая страница: 20 (всего у книги 35 страниц) [доступный отрывок для чтения: 12 страниц]

Шрифт:
- 100% +
Разработка метода 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 проверяет столкновение со слоем-барьером всех спрайтов, кроме спрайта пиратского корабля. Приведенный далее код выполняет проверку столкновения корабля игрока со слоем-барьером:


if (playerSprite.collidesWith(landLayer, true)) {

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

layers.setViewWindow(xView, yView, getWidth(),

getHeight() – infoBar.getHeight());

playerSprite.move(-xMove, -yMove);

}

else {

// если столкновение не произошло, изменить координаты окна вида

xView += xMove;

yView += yMove;

}


Если столкновение произошло, то окно вида возвращается в исходное положение, которое было сохранено в переменных xView, yView. Спрайт игрока также возвращается в исходное положение таким образом, что он остается в центре игрового экрана. Если столкновения нет, то окно вида перемещается в новое положение, определяемое переменными xView и yView.

Обновление спрайтов игры High Seas – это та часть кода, в которой выполняется большее число действий. Вот как это делается:


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

// обновить спрайты пиратов, бочек и мин

pirateSprite[i].update();

barrelSprite[i].update();

// проверить столкновение спрайта корабля и спрайта пирата

if (playerSprite.collidesWith(pirateSprite[i], true)) {

// воспроизвести звук спасения пирата

try {

rescuePlayer.start();

}

catch (MediaException me) {

}

// увеличить число спасенных пиратов

piratesSaved++; //Увеличить счетчик пиратов, потому что был спасен пират

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

placeSprite(pirateSprite[i], landLayer); //Использовать спрайт пирата снова, поместив его в новое место

}

// проверить столкновение спрайта корабля со спрайтом бочки

if (playerSprite.collidesWith(barrelSprite[i], true)) {

// воспроизвести звук пополнения энергии

try {

Manager.playTone(ToneControl.C4 + 12, 250, 100);

}

catch (MediaException me) {

}

// увеличить энергию игрока

energy = Math.min(energy + 5, 45); //Увеличить энергию игрока, потому что была подорвана бочка

// поместить бочку в новое положение

placeSprite(barrelSprite[i], landLayer); //Использовать спрайт бочки снова, поместив его в новое положение

}

}


После обновления обоих спрайтов этот код проверяет столкновение между спрайтом корабля и спрайтом пирата. Если столкновение определено, то воспроизводится звук спасения, означающий, что пират спасен. Число спасенных пиратов, определяемое переменной piratesSaved, увеличивается на 1. Спрайт пирата помещается в новое случайное положение, для чего вызывается метод placeSprite(). Для игрока пират исчез, а в реальности он просто переместился в другое место на карте. Это удобный способ убрать пирата и создать нового простым перемещением спрайта. Наконец, в этом фрагменте кода показано, как применять метод placeSprite().

После того, как было определено столкновение между спрайтами корабля и пирата, проверяется столкновение корабля с бочкой. В этом случае воспроизводится тоновый сигнал, а не wav-файл. Энергия игрока увеличивается, а бочка перемещается в новое место на карте.

Совет Разработчику

Максимальный объем энергии игрока в игре High Seas равен 45, поэтому код, изменяющий энергию игрока при столкновении с бочкой, восстанавливает уровень энергии до 45. Если бы не было этого ограничения, то индикатор энергии мог бы бесконечно расти, загородив индикатор спасенных пиратов.

Спрайты мины и осьминога обновляются в методе update() так же, как и спрайты бочки и пирата. Но этот код отделен от обновления бочек и пиратов, потому что число мин и осьминогов больше числа бочек и пиратов. Именно поэтому необходим другой цикл for:


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

// Update the mine and squid sprites

mineSprite[i].update();

squidSprite[i].update();

// проверить столкновение спрайта игрока и спрайта мины

if (playerSprite.collidesWith(mineSprite[i], true)) {

// воспроизвести звук подрыва на мине

try {

minePlayer.start();

}

catch (MediaException me) {

}

// уменьшить энергию игрока

energy -= 10; //Уменьшить энергию игрока, потому что он подорвался на мине

// поместить мину в новое случайное положение

placeSprite(mineSprite[i], landLayer); //Использовать спрайт мины снова, поместив его в новое положение

}

// проверить столкновение спрайта игрока и спрута

if (playerSprite.collidesWith(squidSprite[i], true)) {

// воспроизвести звук столкновения со спрутом

try {

Manager.playTone(ToneControl.C4, 250, 100);

}

catch (MediaException me) {

}

// уменьшить энергию игрока

energy -= 5; //Уменьшить энергию игрока, потому что он попал в щупальца спрута

}

}


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

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

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


if (energy <= 0) {

// остановить музыку

try {

musicPlayer.stop();

}

catch (MediaException me) {

}

// воспроизвести звук тонущего корабля

try {

gameoverPlayer.start();

}

catch (MediaException me) {

}

// спрятать корабль игрока

playerSprite.setVisible(false); //Спрятать корабль игрока, потому что игра окончена

gameOver = true;

}


При окончании игры сначала останавливается музыка, затем воспроизводится булькающий звук тонущего корабля. Затем спрайт игрока скрывается, для чего вызывается метод setVisible(). Это означает, что корабль затонул. Наконец, переменной gameOver присваивается значение true, что говорит о том, что игра закончена.

Последний фрагмент кода метода update() создает анимацию водного слоя:


if (++waterDelay > 3) {

if (++waterTile[0] > 3)

waterTile[0] = 1;

waterLayer.setAnimatedTile(-1, waterTile[0]);

if (–waterTile[1] < 1)

waterTile[1] = 3;

waterLayer.setAnimatedTile(-2, waterTile[1]);

waterDelay = 0;

}


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

Вывод игрового экрана

Благодаря менеджеру слоев вывод игрового экрана весьма прост. В листинге 12.3 приведен код метода draw() класса HSCanvas.

Листинг 12.3. Метод draw() класса HSCanvas выводит информационную строку, игровые слои и строку «Game Over» при необходимости

private void draw(Graphics g) {

// вывести информационную строку, оставшуюся энергию и число спасенных пиратов

g.drawImage(infoBar, 0, 0, Graphics.TOP | Graphics.LEFT);

g.setColor(0, 0, 0); // черный

g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM));

g.drawString("Energy:", 2, 1, Graphics.TOP | Graphics.LEFT);

g.drawString("Pirates saved: " + piratesSaved, 88, 1, Graphics.TOP |

Graphics.LEFT);

g.setColor(32, 32, 255); // синий //Справа от текста Energy вывести оставшуюся энергию как синий прямоугольник

g.fillRect(40, 3, energy, 12);

// вывести слои

layers.paint(g, 0, infoBar.getHeight());

if (gameOver) {

// вывести сообщение об окончании игры и набранные очки

g.setColor(255, 255, 255); // white

g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_LARGE));

g.drawString("GAME OVER", 90, 40, Graphics.TOP | Graphics.HCENTER);

g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD,

Font.SIZE_MEDIUM)); //Вывести число спасенных пиратов

if (piratesSaved == 0)

g.drawString("You didn't save any pirates.", 90, 70,

Graphics.TOP | Graphics.HCENTER);

else if (piratesSaved == 1)

g.drawString("You saved only 1 pirate.", 90, 70,

Graphics.TOP | Graphics.HCENTER);

else

g.drawString("You saved " + piratesSaved + " pirates.", 90, 70,

Graphics.TOP | Graphics.HCENTER);

}

// вывести графику

flushGraphics();

}


Первая часть кода выводит информационную строку – фоновое растровое изображение, индикатор энергии и число спасенных пиратов. Индикатор энергии рисуется с помощью метода fillRect(), а текст выводится методом drawString().

Слои выводятся в середине метода draw(), для этого нужна лишь одна строка кода, за которой следует сообщение об окончании игры. Если игра закончена, то выводится сообщение о конце игры – «GAME OVER», после чего появляется число спасенных пиратов – счет игры.

Начало новой игры

В разъяснениях я несколько раз упоминал о методе newGame(). Пришла пора увидеть, как он работает. Листинг 12.4 содержит код этого метода, начинающего новую игру.

Листинг 12.4. Метод newGame() класса HSCanvas инициализирует переменные игры, изменяет положение пиратского корабля и начинает воспроизведение музыки

private void newGame() {

// инициализировать переменные игры

gameOver = false;

energy = 45;

piratesSaved = 0;

// показать спрайт пиратского корабля

playerSprite.setVisible(true); //В начале игры важно вывести на экран корабль игрока

// поместить игрока и переместить окно вида

placeSprite(playerSprite, landLayer); //При запуске игры корабль игрока помещается на карте случайно

xView = playerSprite.getX() – ((getWidth() – playerSprite.getWidth()) / 2);

yView = playerSprite.getY() – ((getHeight() – playerSprite.getHeight()) / 2);

layers.setViewWindow(xView, yView, getWidth(),

getHeight() – infoBar.getHeight());

// начать воспроизведение музыки

try {

musicPlayer.setMediaTime(0);

musicPlayer.start();

}

catch (MediaException me) {

}

}


Метод newGame() начинается с инициализации трех основных игровых переменных. Обратите внимание, что значение переменной energy равно максимально возможному значению 45. Затем спрайт игрока становится видимым, для чего вызывается метод setVisible(). Это необходимо потому, что при окончании игры спрайт пиратского корабля исчезает с экрана. Спрайт игрока помещается в случайное место на карте, для чего вызывается метод placeSprite(). В соответствии с этим изменяется положение окна вида таким образом, чтобы спрайт оказался в центре окна. В конце вызовом методов setMediaTime() и start() начинается воспроизведение музыки.

Безопасное размещение спрайтов

Я могу понять, если вы устали, но я обещаю, что это последний фрагмент кода игры High Seas, который мы посмотрим. В листинге 12.5 приведен полный код метода placeSprite(), который отвечает за размещение спрайта в произвольной точке игровой карты.

Листинг 12.5. Метод placeSprite() класса HSCanvas помещает спрайт в произвольную точку карты так, чтобы он не совпадал со слоем – барьером

private void placeSprite(Sprite sprite, TiledLayer barrier) {

// попробовать поместить в произвольную точку

sprite.setPosition(Math.abs(rand.nextInt() % barrier.getWidth()) – //Спрайт помещается случайным образом

sprite.getWidth(), Math.abs(rand.nextInt() % barrier.getHeight()) -

sprite.getHeight());

// перемещать, пока не будет столкновения

while (sprite.collidesWith(barrier, true)) { //Проверить столкновение спрайта со слоем-барьером, продолжать перемещение спрайта до тех пор, пока он не столкнется со слоем-барьером

sprite.setPosition(Math.abs(rand.nextInt() % barrier.getWidth()) -

sprite.getWidth(), Math.abs(rand.nextInt() % barrier.getHeight()) -

sprite.getHeight());

}

}


Вы можете подумать, что разместить спрайт в произвольном месте на игровой карте – это просто получить несколько случайных чисел и более ничего. Помните, что на карте есть области, в которых не имеет смысла размещать бочку или осьминога… Я говорю о суше! Иначе говоря, важно разместить спрайт не только произвольно, но и грамотно. В результате, размещая спрайт, вы должны проверять, не попадает ли он на фрагмент суши.

Чтобы «безопасно» разместить спрайт случайным образом, необходимо проверить его столкновение со слоем-барьером. Если столкновение произошло, то необходимо попробовать другое положение. Такую проверку и размещение удобно выполнять в цикле до тех пор, пока не будет найдено подходящее место. Вы можете сказать, что этот код небезопасен, потому что выполнение цикла может не закончиться. Однако на карте достаточно свободного места, поэтому такой проблемы не возникнет.

Тестирование игры

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

Рис. 12.12. Хорошее начало, пиратский корабль готов к спасательной миссии


После того как пират спасен, его спрайт исчезает с экрана, а счетчик спасенных пиратов увеличивается на 1 (рис. 12.13).

Рис. 12.13. Первый пират спасен – его спрайт исчез с карты, а счетчик спасенных пиратов увеличился на 1


В игре вы рано или поздно столкнетесь с другими спрайтами – минами, бочками и осьминогами (рис. 12.14).

Рис. 12.14. Дрейфующие мины и бочки – это противоположные объекты в игре. Первые отнимают энергию, вторые – прибавляют


В итоге удача может покинуть вас, и мина или осьминог заберут последнюю каплю энергии, и игра будет окончена. На рис. 12.15 показан игровой экран с сообщением об окончании игры на фоне осьминога, потопившего корабль.

Рис. 12.15. В итоге корабль попадает в щупальца осьминога и идет ко дну вместе с тремя спасенными пиратами


Если вы немного поиграете в High Seas, то она, вероятно, не покажется вам сложной. Дело в том, что ни один спрайт не умен достаточно, чтобы соперничать с игроком. О том, как решить эту проблему, речь пойдет в следующей главе, прочитав которую, вы узнаете о том, как использовать искусственный интеллект в играх.

Резюме

В этой главе было продемонстрировано все, что может понадобиться для создания мобильной игры. Несмотря на то что High Seas можно улучшить и сделать более интересной, я должен отметить, что немного увлекся ее созданием. Я не планировал приводить в этой книге весь код. Даже если вы немного утомились от объема приведенного кода, эта глава дала вам все, что может послужить основой для создания собственных игр.

Единственный недостаток игры High Seas – это то, что она не очень увлекательная. Верите или нет, но это был умышленный шаг. В следующей игре вы узнаете, как сделать плохих парней чуточку умнее. Но это еще не все. Также вы добавите еще одного плохого парня, действительно страшного!

В заключение

Возможно самое простое и значительное, что вы можете сделать с игрой High Seas, – это поэкспериментировать с замощенными слоями. Например, вы можете сделать слои больше, изменить острова и сушу так, чтобы получилась более интересная карта. Для этого требуется внести лишь небольшие изменения. Просто выполните следующие шаги для фрагментов кода, работающих с картой:

1. найдите новые интересные элементы, которые можно было бы использовать на карте, например: останки кораблей, рифы и т. п. Эти элементы будут не только служить барьерами, но и украсят игру;

2. выберите новый больший размер карты и добавьте новые ячейки в слой воды и в слой суши. Добавьте новые элементы на карту. Вы можете самостоятельно разработать карту, используя соответствующее программное обеспечение (как рассказывалось в главе 10);

3. измените код инициализации слоев суши и воды в соответствии с новыми размерами карты.

Эти изменения украсят игру High Seas, а также сделают карту больше и интереснее. Конечно, теперь, когда карта стала больше, возможно, потребуется добавить больше осьминогов, мин, пиратов и бочек, но это решать вам!

Глава 13
Учим игры думать

Архив Аркад

Выпущенная в 1982 году компанией Seg игра Pengo – это, вероятно, первая игра, в которой главным героем является пингвин. В ней вы управляете пингвином, который пробирается по ледяному лабиринту. Пингвина преследуют маленькие забавные пятнистые существа – снубы (snoobee). Pengo – это нечто среднее между лабиринтом и игрой в стиле экшн. Поскольку в этой игре по большей части нет жестокости, Pengo – это первая классическая аркада, в которую играла не только мужская аудитория. Интересный факт, но основная музыкальная тема в игре, «Popcorn», – это опус группы «Hot Butter».

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

Из этой главы вы узнаете:

► об основах искусственного интеллекта (ИИ);

► о различных типах ИИ, применяемых в играх;

► как самостоятельно разработать стратегию ИИ;

► как создать спрайты, проявляющие агрессию и способные преследовать друг друга;

► как в мобильной игре создать спрайты с ИИ, которые могут преследовать игрока.

Минимум, что вы должны знать об ИИ

Если вы видели фильмы «ИИ» («A.I.») или «Я, робот» («I, robot»), то вы, вероятно, можете представить на что способен искусственный интеллект. Хорошо или плохо, но идея создания компьютеров, способных думать, как человек, восхищает. Искусственный интеллект (ИИ) определяется как методы, используемые для имитации мышления человека в компьютере. Это самое общее определение искусственного интеллекта. Искусственный интеллект – это обширное пространство для исследования, а игровой ИИ – это очень маленькая часть этого пространства. Цель этой главы – познакомиться с основополагающими концепциями создания искусственного интеллекта и его применения в играх.

Конечно, мышление человека имитировать очень сложно, вот почему ИИ – столь богатая область для исследований. Несмотря на то что есть множество подходов к реализации искусственного интеллекта, все можно свести к попытке имитации человеческого мозга компьютерными средствами. Большинство традиционных систем с ИИ для принятия решений применяют разнообразные информационные алгоритмы, точно так же, как люди используют накопленный опыт и определенные правила. В прошлом информационные алгоритмы были полностью детерминированными: любое решение принималось чисто логически. На рис. 13.1 показана схема чисто логического мышления человека. Очевидно, что человеческое мышление работает несколько иначе. Если бы все было, как на схеме, то этот мир был бы очень скучным! Рациональная скука.

Рис. 13.1. Полностью логическое мышление человека – очевидные доводы, и ничего более


В итоге исследователи ИИ поняли, что детерминированный подход к искусственному интеллекту не подходит для моделирования мышления человека. Интерес ученых переместился в область создания более реалистичных моделей, приближенных к мыслительному процессу человека, например, принятие решения лучшей догадкой (best-guess decision). Люди могут принимать такие решения на основе прошлого опыта, собственных взглядов и/или текущего эмоционального состояния – все это дополняет полностью логический процесс принятия решений. На рис. 13.2 показан пример реального мыслительного процесса. Дело в том, что люди принимают не всегда предсказуемые наукой решения на основании своего опыта и логического вывода. Вероятно, мир был бы лучше, если бы все было правильно, однако он был бы безумно скучным!

Рис. 13.2. Более реалистичный процесс мышления содержит эмоциональную и иррациональную составляющие


Логическая схема, показанная на рис. 13.1, – это идеальный сценарий, в котором каждое решение принимается на основе полностью объективного логического вывода. На рис. 13.2 показан более реалистичный вариант принятия решения, в котором учитываются такие факторы, как эмоциональное состояние человека, а также его материальное состояние (есть ли у него страховка). Если рассмотреть второй вариант с логической точки зрения, то человеку нет смысла бросать молоток, потому что это замедлит работу.

Однако это достаточно распространенная реакция человека на боль. Для ИИ плотничной системы, чтобы эффективно отработать такую ситуацию, необходимо предусмотреть код «бросания молотка»!

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

В последние годы исследователи ИИ сосредоточились на проблемах, аналогичных рассмотренным в примере с молотком. Одна из особенно интересных областей – это нечеткая логика (fuzzy logic), которая пытается принимать решения, не следуя железной логике традиционных систем искусственного интеллекта. Другая интересная область – это генетические алгоритмы (genetic algorithms) в играх, с помощью которых имитируется процесс мышления, подобно тому, как это происходит в природе. Игры, в которых применяются генетические алгоритмы, теоретически были бы обучаемыми, тем самым делая процесс игры интереснее.


Страницы книги >> Предыдущая | 1 2 3 4 5 6 7 8 9 10 11 12
  • 0 Оценок: 0

Правообладателям!

Данное произведение размещено по согласованию с ООО "ЛитРес" (20% исходного текста). Если размещение книги нарушает чьи-либо права, то сообщите об этом.

Читателям!

Оплатили, но не знаете что делать дальше?


Популярные книги за неделю


Рекомендации