на главную почта поиск
 
поиск
Диона Холдинг
   
   
FLASH-версия 
 Версия для печати 
  Новости
 

   
Новости  
   

Глава 9. Глупый враг

Мы тяжело и много работали, чтобы научить Героя перемещаться по карте, но он один, кругом тишь, да гладь, да божья благодать. Нужен кто-то еще. Нет, не еда (выпивка, девушки, хотя и не помешали бы), что нам нужно, так это несколько врагов.

Враги, как соль в супе, без них все так пресно. Хорошая игра содержит злобных и умных врагов, но мы начнем с очень простых и глупых. Все, что они будут делать – ходить взад – вперед и проверять, столкнулись ли они с Героем.

Не так давно, у нас было всего два типа объектов: плитки и Герой. Герой двигался под управлением игрока, плитки были неподвижны. Враги будут, как Герой. И главное, Враги будут с думателем. Мы создадим два различных типа врагов: враги первого типа будут перемещаться вверх-вниз, а второго типа - влево-вправо. Враги будут останавливаться около препятствий (стен) и опять двигаться, но в противоположном направлении.

Прежде, чем создавать очень сложных врагов, давайте подумаем. Множество игр сейчас не используют сложных врагов, а, если даже и есть враги, то они не такие яркие. У нас нет неограниченных ресурсов во Flash и 100 шикарных врагов, использующих сложный поиск пути, будут полностью вырубать нашу игру. Если можно, создайте несколько глупых (простых) врагов и несколько шикарных (сложных). И, уверяю вас, пользователь не найдет различия. Поэтому, дайте и игроку возможность почувствовать себя королем.

Подготовка Врага

Создайте клипы Врага тем же путем, как и Героя (посмотрите главу «Герой» нашего учебника, если забыли). Каждый клип будет иметь 4 ключевых кадра с анимацией лево/право/верх/низ. Они (клипы) также должны быть экспортированы из библиотеки с именами "enemy1" и "enemy2". Теперь добавим массив Врагов:

myEnemies = [ [0], // Массив пустышка, для счета
[[1, 6, 1]],
[[2, 1, 3]] ];

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

Ого! У нас болше одного глупых Врага. Когда карта будет построена, Враг размещается на плитке x=6, y=1. Второй массив описывает Врага на карте 2, только задаем Врага другого типа и в другой позиции. Можно добавить других Врагов, но размещать их нужно, конечно, на проходных плитках.

(Хотя здесь есть пища для фантазии, скелеты, мумии и т.п. ужасная интересная живность.)

Объявляем шаблон объекта для Врагов:

// Первый тип Врага – перемещается вертикально, не очень быстр, но свиреп, как оса
game.Enemyp1= function () {};
game.Enemyp1.prototype.xMove=0;
game.Enemyp1.prototype.yMove=1;
game.Enemyp1.prototype.speed=2;

// Второй тип Врага – перемещается горизонтально, тихоход, кусается только
game.Enemyp2= function () {};
game.Enemyp2.prototype.xMove=1;
game.Enemyp2.prototype.yMove=0;
game.Enemyp2.prototype.speed=2;

Они выглядят одинаково, но поведение их различно. Враг Enemyp1 будет перемещаться вертикально, поэтому его свойство yMove имеет значение 1, а Враг Enemyp2 будет гулять влево/вправо, поэтому свойство хMove - 1. Можно установить значения этих свойств – 1 / -1 / 0. Правда, теже самые значения мы использовали для определения направления движения в функции moveChar. Можно установить значения свойств xMove/yMove отличным от 0, для того, чтобы Враги могли перемещаться по диагонали.

Если оба свойства xMove / yMove равны 0, значит Враг встал, как вкопанный (то ли груш объелся, то ли мешком стукнули). Пока дотумкали до этого, дальше видно будет.

Свойство speed определяет скорость перемещения Врага. Разные Враги и перемещаются по разному.

Размещение Врага

В функции buildMap добавим строки после определения «дверей» и до того, как размещается Герой.

// Блок кода для Врагов
var enemies = myEnemies[game.currentMap];
game.currentEnemies = enemies.length;
// Цикл для создания Ворогов и присоединения их клипов в общую структуру
for (var i = 0; i<game.currentEnemies; ++i) {

// Имя Ворога
var name = "enemy"+i;
// Включение в структуру объектов
game[name]= new game["Enemyp"+enemies[i][0]]();
// Присоединяем клип Ворога под созданным именем и кладем его на уровень 10001+i
game.clip.attachMovie("enemy"+enemies[i][0], name, 10001+i);
// Запомним клип в общей структуре клипов
game[name].clip=game.clip[name];
// Запомним позицию Ворога
game[name].xtile = enemies[i][1];
game[name].ytile = enemies[i][2];
// Запомним его размеры
game[name].width = game.clip[name]._width/2;
game[name].height = game.clip[name]._height/2;
// Вычислим координаты Х / У Ворога
game[name].x = (game[name].xtile*game.tileW)+game.tileW/2;
game[name].y = ((game[name].ytile+1)*game.tileH)-game[name]. height;
// Запишем их в свойства клипа
game[name].clip._x = game[name].x;
game[name].clip._y = game[name].y;
}

Что здесь случилось? Первое, мы получаем массив описания Врагов для текущей карты. Затем устанавливаем значение переменной currentEnemies (количество Врагов) по размеру массива описания Врагов, тем самым, мы всегда знаем сколько врагов создано. Затем обрабатываем массив описания и создаем Ворогов (не забудьте, сейчас, мы будем использовать только 1-ого Врага на каждой карте, потом их число можно будет увеличить).
Переменная name будет содержать имя нашего нового Врага, которые называются "enemy0", "enemy1", "enemy2"... Затем мы создадим новый объект Враг на основе шаблона, объявленного ранее:

game[name]= new game["Enemyp"+enemies[i][0]]();

Из массива описания Врагов возьмем первый элемент первого врага. В нашем примере, для первой карты враг будет типа 1, а для второй карты, враг типа 2 (так написано в массиве описания Врагов).

Следующие строки задают начальную позицию, ширину и высоту объекта Враг. Затем вычисляются координаты Х/У и объект корректно устанавливается.
Но, пока Враг не движим! Не волнуйся, ща побежит.

Перемещение Врага

Как и у большинства людей, у Врагов должна работать соображалка. Добавим функцию enemyBrain:

//****************
// Функция: enemyBrain
// Назначение: Вражья соображалка – опеределяет поведение Врага в непростых условиях игры
// Параметры:
// Возвращает:
// ****************************
function enemyBrain () {

// Цикл по всем Врагам
for (var i = 0; i<game.currentEnemies; ++i) {

// Формируем имя Ворога
var name = "enemy"+i;
// Запомним текущего Врага
var ob = game[name];
// Определим его припятствия в текущей позиции
getMyCorners (ob.x+ob.speed*ob.xMove, ob.y+ob.speed*ob.yMove, ob);
// Проверим (Препятствий НЕТ)?
if (ob.downleft and ob.upleft and ob.downright and ob.upright) {

// ДА – Ворог движется
moveChar(ob, ob.xMove, ob.yMove);

} else {

// НЕТ – Ворог начинает двигаться в обратном направлении
ob.xMove = -ob.xMove;
ob.yMove = -ob.yMove;
}

// Вычислим расстояние по Х / У до Героя
var xdist = ob.x - char.x;
var ydist = ob.y - char.y;
// Проверим (Ворог столкнулся с Героем)?
if (Math.sqrt(xdist*xdist+ydist*ydist) < ob.width+char.width) {

// ДА – конец Героя и конец игре
removeMovieClip(_root.tiles);
_root.gotoAndPlay(1);
}

}

}

Как видите, мы снова просматриваем всех врагов, используя переменную ob и game.currentEnemies. Переменная ob содержит текущего Врага из списка.

Далее вызовем функцию getMyCorners - проверки препятствия для объекта (в данном случае Врага). Если рядом нет стен или препятствий вокруг, то нужно вызвать функцию moveChar, используя свойства объекта xMove / yMove . Таким образом, для движущихся объектов используется одна и таже функция moveChar.

Посмотрим на трюк со стеной для Врага, мы меняем знак направления перемещения xMove / yMove, как только Враг достигает препятствия. Если yMove была 1, то станет -1, но если был 0, то и останется 0 (Вот почему направление перемещения для Врага не должно быть 0).

Последняя часть соображалки – проверка столкнрвения Врага с Героем. Столкнулись, значит Враг победил и игре конец. Конечно, обычно игра не может так легко закончиться, можно уменьшить жизнь Героя или сделать еще какой трюк, но пока это все. Для определения столкновения Врага с Героем используем простой способ, называемый «Теоремой Пифагора», - расчет расстояния между Героем и Врагом. Если вам хочется поискать другой способ решения окончания игры, то, пожалуйста, здесь можно придумать вызов функции hitTest для этого, но пока резона нет. Пока – «Не подходи, а то смерть!».

Для вызова соображалки Врага на каждом шаге, добавим строку в конец функции detectKeys:

_root.enemyBrain();

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

Можно загрузить исходный код и клипы вот отсюда.

Исправление ошибок. Наше с кисточкой kuRTko нашедшему ошибку в коде врагов. Я обновил учебник и исходные коды, но если вы увидите код, в котором Враги объявлены как:

game.Enemy1= function ()

А нужно game.Enemyp1= function (), пожалуйста, замените.

Примечание, ошибка была в том, что свойства объекта enemy использовали тоже самое имя "enemy+number", как и имя текущего объекта. Прототипы должны называться чуть по другому - "enemyp+number". Не забудьте проверить, также строку функции buildMap , после обновления она должна выглядеть так:

game[name]= new game["Enemyp"+enemies[i][0]]();

Враг на платформе

Возможно, при взгляде на игру со стороны, вам захочется, чтобы Враги также прыгали, как и Герой.

(На счет прыгать не обещают, а вот определять границу стены, Врага научить надо. Надо только быть очень внимательным и смотреть за У координатой Врага.)

Нужно изменить несколько строк кода. Враг будет ходить по платформе и определять конец платформы. В конце платформы Враг развернется и пойдет обратно. Эти возможности Врага требуют проверки наличия платформы (плитки) ниже плитки, на которой он (Враг) сейчас стоит. Изменения в функции enemyBrain:

getMyCorners (ob.x+ob.speed*ob.xMove, ob.y+ob.speed*(ob.yMove+1), ob);

if (!ob.downleft and !ob.downright and ob.upright and ob.upleft) {

Важное замечание, при вызове функции проверки препятствий в параметре для У – формула ob.y+ob.speed*(ob.yMove+1) и особенно +1 задает положение следующей плитки ниже текущей.

По результатам определения препятствий, проверяется наличие плиток внизу и их отсутствие вверху.

Можно загрузить исходный код и клипы отсюда.

Обучение Врага некоторым трюкам

Что, если Враг научится менять направление движение не только на противоположное.

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

} else {

// Вычисляем случайное число для движения по горизонтали
ob.xMove = random(3)-1;
// Проверим (Движение есть)?
if (ob.xMove) {

// ДА – значит по вертикали не двигаемся
ob.yMove = 0;

} else {

// НЕТ – Вычислим случайное число для движения по вертикали
ob.yMove = random(2)*2-1;
}

}

Когда Враг встанет около стены и задумается, xMove получит случайное число. Функция random(2) вернет значение 0 или 1. Если значение xMove будет равно 0, то определим значение yMove путем задания случайного числа 1 или -1.

random(2) - 0 или 1.
random(2)*2 - 0 или 2.
random(2)*2-1 - -1 или 1.

В случае, когда xMove – 1, устанавливаем значение yMove – 0 и получим случайное число 1 или -1 для xMove.

Можно загрузить исходный код отсюда.

Это лучше, но, если хотим еще больше улучшить соображалку Врага, то, при определении направления движения, нужно убрать из рассмотрения последнее направление движения (оно все одно ведет к стене).

Напишем код:

} else {

if (ob.xMove == 0) {

// ДА - Вычисляем случайное число для движения по горизонтали
ob.xMove = random(2)*2-1;
ob.yMove = 0;
// Определим препятствия
getMyCorners (ob.x+ob.speed*ob.xMove, ob.y+ob.speed*ob.yMove, ob);
// Проверим (Препятствий НЕТ)?
if (!ob.downleft or !ob.upleft or !ob.downright or !ob.upright) {

// ДА – Ворог пошел, при этом мы не знаем в какую сторону
// может пойти и в стенку, для этих случаем и меняем знак
// Но в целом, тут надо делать более хитрый расклад
ob.xMove = -ob.xMove;
}

} else {

// НЕТ – Попробуем обойти препятствие
ob.xMove = 0;
// Вычислим направление движение по вертикали
ob.yMove = random(2)*2-1;
// Определим препятствия
getMyCorners (ob.x+ob.speed*ob.xMove, ob.y+ob.speed*ob.yMove, ob);
// Проверим (Препятствий НЕТ)?
if (!ob.downleft or !ob.upleft or !ob.downright or !ob.upright) {

// ДА – Ворог пошел куда-то по вертикали
// может пойти и в стенку, тут надо делать более хитрый расклад
ob.yMove = -ob.yMove;
}

}

}

На этот раз, в первую очередь, проверяется текущее направление. Если, для примера, перемещение шло по вертикали (xMove==0), то случайно выбираем 1 или -1 для xMove и устанавливаем yMove в 0. Но, если Враг пришел в угол, то он может опять пойти в стену. Вот почему мы проверяем угловые препятствия для нового направления движения и если они есть, то меняем его (направление движения) на противоположное.

Можно загрузить исходный код отсюда.

Отлично, Враг лучше двигается и даже разработчику и игроку трудно понять куда же он (Враг) пойдет.

Дадим Врагу возможность изменения направления движения даже, когда он не сталкивается со стеной.

Я никак не мог придумать хорошего слова для описания изменения направления движения в процессе самого движения, давайте добавим новое свойство Врагу и назовем его "turning" (разворот).

game.Enemyp1.prototype.turning=5;
game.Enemyp2.prototype.turning=5;

Свойтсво «разворот» будет давать возможность случайного изменения направления движения на каждом шаге. Значение 0 будет говорить, что Враг прям, как оглобля, и не меняет направление движения. Значение выше 50, показывает что направление движения меняется на каждом шаге.

Вот наконец, код, воплощающий наши замыслы при выборе нового направления движения объекта (Врага):

if (ob.downleft and ob.upleft and ob.downright and ob.upright and random(100)>ob.turning) {

В случае, если случайное число random(100) меньше значения свойства ob.turning, будем менять направление движения.

Можно загрузить исходный код отсюда.

Обновление от 23.06.2004. Благодарности qhwa за предложение простого метода случайного выбора направления движения.

Исправление ошибок. 05.02.2005. Благодарности Эрику Лиобису (Eric Liobis) за найденную ошибку, когда стена была слева ил справа от Врага. Исправленная строка выглядит так:

if (!ob.downleft and !ob.downright and ob.upright and ob.upleft) {

Есть вопросы - пишите мне на semikin@dionaholding.ru

Читаем дальше - Глава 10. Убей его!

Возвращаемся на Начало учебника

 

Всего: 5

Электронные формы от Adobe

14 ноября с.г. компания представила новую версию всем известного средства Adobe Acrobat. 8-ая по счету версия принесла новые возможности по переходу от бумажных документов к электронным.
читать
 

АрмПарк - Универсальная учетная система

Серия материалов о программе - АрмПарк - Универсальная учетная система. Решение для тех кому необходимо быстро создать и внедрить электронную картотеку по учету - документов, матералов, оборудования, ресурсов или зеленых человечков.
читать
 

Программирование игр на основе плиток

Представляется перевод замечательного учебника по созданию движка игры на основе плиток (Tile Based Games). Я благодарен автору Tonypa за разрешение публикации перевода. Если у Вас возникают вопросы, пишите и я постараюсь Вам помочь.
читать
 

Универсальная информационная система-конструктор Учет документов (Доклад на конференции).

Доклад на Третьей практической конференции по Электронному Документообороту - "Программа "Учет документов" ", 9 декабря, Москва, Мэрия Москвы.
читать
 

Интеграция систем с использованием новых технологий

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

Rambler's Top100

   
   

новости

продукты

решения

услуги

о компании

   
   
125315, Москва, Часовая ул., д.30
Телефон: (095)797-32-82; Факс: (095)797-32-81
© 2005 ООО "Диона Холдинг"
Администратор: admin@dionaholding.ru