Циклическая анимация фона - Часть третья, или делаем игровой баннер
28.04.2008, автор Stormit, рубрики: ActionScript, Flash игры, Игровые баннерыПора заканчивать с этой темой, а то это начинает казаться сложным.
Для лучшего понимания нижеизложенного материала стоит почитать предыдущие части.
Чтобы мне не переделывать работу заново, а у вас была возможность проверить как все это применять на практике, за основу я возьму результат второй части программной анимации фона. Добавлю к этому препятствия, бонусы, индикаторы, немного Actionscript и получу самый настоящий игровой баннер, вот такой:
Сценарии и gameplay как для flash-игр, так и для баннеров могут быть самые разнообразные. Я выбрал для примера классический: есть объекты, которых нужно избегать и которые наоборот необходимо собирать. Если нет такой “борьбы противоположностей”, то обычно и играть не интересно. Такой вот метод “кнута и пряника”.
Теперь по порядку, как это сделано. Не буду говорить о чистоте кода, его можно оптимизировать 10тыс. раз. Наоборот, постараюсь внести как можно меньше изменений в примеры из предыдущих уроков. Так будет понятнее, а гуру flash-программирования легко могут переписать код под себя.
Пример и описание:
- Все начинается со сценария (или идеи, которая потом детально описывается). Обычно всегда есть главный герой и противники (препятствия). Могут быть разные виды бонусов, предметы помогающие в игре (оружие, трамплины, аптечки). Нужны индикаторы состояния игры - количество жизней, бонусов, врагов, сила удара, пройденный путь, таймер и т.д. Определяются условия выигрыша и поражения.От задумки напрямую зависит оформление и анимация всех персонажей и окружающей среды. В моем примере главный герой - мячик. Что может представлять для него угрозу? Наверное, все, что может его проколоть, ведь кроме оболочки и воздуха у него и нет ничего. Поэтому в игре нужно избегать всех наземных опасностей (гвозди, кактус и ежик). Сложнее придумать для мяча что-то полезное. Думал, сделать облачко из воздуха, но на линии горизонта, на светлом фоне оно смотрится не эффектно ( да и маленькие насосы как-то в антураж не вписываются). Поэтому выбор пал на звездочки.Если идей нет, тема сложная или “не тот день”, для бонусов можно использовать звездочки, подарки, монетки, изумруды или просто желтые шарики - очевидно, что это полезные штуки и их нужно собирать (наверное потому, что все это связано с деньгой и имеет ценность для людей). Для победы нужно собрать 5 звезд.
- Это то, что у нас есть от предыдущего урока. Дорога и передний план являются отдельными символами и смещаются влево на половину своей ширины, затем возвращаются в начальное положение (для кустов на переднем плане скорость задана чуть больше, чем для дороги - они к нам ближе и движутся быстрее). Задний фон создается путем дублирования клипа эталона (куст).
- По такому же принципу как и фоновые кустарники, делаются и препятствия с бонусами, только частота дублирования гораздо ниже.
Создадим новый символ (enemy), внутри него нарисуем 3 вида препятствий, каждое в своем кадре. Это будут наши противники. Ежик живой, поэтому для него я буду дополнительно смещать клип влево (как будто он бежит навстречу). - Клип enemy помещаем в символ enemies. Дальше препятствия создаются и двигаются путем создания дубликатов клипа enemy, так же как и кусты на заднем фоне, но без разброса по _y.Еще мне нужно определять пересечение мяча с врагами, поэтому я ввожу простую систему учета символов. Для этого я завожу массив all, где будут храниться все текущие символы и 2 функции addObj() и removeObj() (только удаляет объект из списка - сам клип остается в сцене). Полный код для символа enemies будет такой:
lev = 0; speed = 5; var dist = right._x - left._x; all = [];//массив для дубликатов //функция добавляет элемент в массив function addObj(obj) { all.push(obj); } //функция удаляет элемент из массива function removeObj(obj) { var i = all.length; while(i--) { if(all[i] == obj) { all.splice(i, 1); } } } //функция создает дубликат препятствием placeEnemy = function () { lev++; var d = enemy.duplicateMovieClip("e" + lev, lev); d._x = right._x + Math.random() * 50; //случайно выбираем вид препятствия if(Math.random() < .33) { d.gotoAndStop(1); } else if(Math.random() < .66) { d.gotoAndStop(2); } else { d.gotoAndStop(3); } d.onEnterFrame = function() { this._x -= speed; if(this._x < left._x){ removeObj(this);//удалить из списка this.removeMovieClip();//удалить со сцены } } addObj(d); return d; }; var d = placeObj(); d._x = 600; onEnterFrame = function() { if(lev++ % 70 == 0){//каждый 70-й кадр placeEnemy(); } }
И специально для ежика - в символе enemy засовываем ежа в новый мувик и на нем пишем код:
onClipEvent (enterFrame) { _parent._x -= _parent._parent.speed * .7;//часть от общей скорости }
- То же самое делаем для бонусов. Создаем символ bonus. Рисуем в нем звездочку. Оборачиваем ее в символ и делаем анимацию для случая, когда мяч ее ловит (у меня она улетает вверх).
- Так же, как и в третьем шаге, делаем для звездочки программное дублирование и анимацию, только с чуть большим интервалом (так препятствия будут появляться чаще, чем призы). Обратите внимание: скорость движения у всех объектов на дороге одинаковая, как и у самой дороги (еж дополнительно смещается). И также ведем учет символов. Код почти не меняется, и при желании его можно вынести во внешние функции.
lev = 0; speed = 5; var dist = right._x - left._x; all = []; function addObj(obj) { all.push(obj); } function removeObj(obj) { var i = all.length; while(i--) { if(all[i] == obj) { all.splice(i, 1); } } } placeBonus = function () { lev++; var d = bonus.duplicateMovieClip("b" + lev, lev); d._x = right._x + Math.random() * 50; if(Math.random() < .33) { d.gotoAndStop(1); } else if(Math.random() < .66) { d.gotoAndStop(2); } else { d.gotoAndStop(3); } d.onEnterFrame = function() { this._x -= speed; if(this._x < left._x){ removeObj(this); this.removeMovieClip(); } } addObj(d); return d; }; onEnterFrame = function() { if(++lev % 120 == 0){//каждый 120-й кадр placeBonus(); } }
- Вот так препятствия и бонусы работают вместе.
- Для определения пересечений используем простой, но действенный способ - функцию hitTest(). Она работает с прямоугольными формами, поэтому рисуем прямоугольник и создаем из него символ hit. Если точнее, то hitTest() определяет пересечение не по самому символу, а по прямоугольнику, в который вписан “bounding box” этого символа. Это важно, если надумаете эти символы вращать.Так как форма у объекта не всегда совпадает с областью пересечения, то лучше создать отдельный символ, специально для проверки пересечений и его уже изменять индивидуально для каждого объекта (масштабировать и смещать его можно, а вращать нет).Внутри символа hit пишем простой код, символ теперь невидим, но доступен для скриптов:
_visible = false;
Мяч, каждый бонус и препятствие, будут иметь внутри себя копию этого символа.
Проверка на пересечение осуществляется каждый кадр. Если есть столкновение с бонусом, он удаляется из списка и в проверках больше не учавствует, если с препятствием, то останавливаем основной скрипт и все проверки и запускаем анимацию взрыва.
- Для мяча символ hit кладем в символ ball. Так он будет двигаться и деформироваться вместе с мячом. Также кладем его в символы bonus и enemy (для каждого вида препятствий корректируем форму масштабированием).
- Еще нужно сделать анимацию поражения. В символе pers создадим символ с именем boom. Я предпочел прорисовать анимацию руками, но можно воспользоваться для этого эффектом взрыва, изменив нужным образом цвет частиц. В последнем кадре символа boom:
stop(); _parent._parent.lose.play();//запускает сообщение о проигрыше (описано ниже)
- Индикатор выигрыша - 5 звезд. При каждом собирании звезды, переходим в символе на один кадр вперед и соответствующая звезда в нем заменяется на анимацию исчезновения. После собирания всех звезд, функция play(), проигрывает небольшую задержку и после вызывает окно с поздравлениями (описано ниже).
- Создаю 2 клипа с сообщениями выиграл/проиграл. Называю их won и lose соответственно. Чтобы они не перекрывали экран и не загромождали собой сцену, можно в первом кадре нарисовать условную фигуру, разместить его над рабочей сценой, а анимацию начинать со второго кадра.
- Теперь нужно сделать сброс игры. Выиграли мы или проиграли, баннер нужно сбросить в начальное состояние. У нас сейчас достаточно много динамически созданных объектов и удалять их программно - непростое занятие. Можно что-то упустить и при повторной игре пойдут накладки. Проще сделать так, чтобы символ исчез с линейки и появился снова, тогда все внутри него удалится автоматически. Мы это сделаем для всего баннера целиком.Все, что я делал до шага 11, нужно обернуть в символ - лужайка со всеми объектами. А слоем выше создать 2 новых слоя для клипов lose и won. Во втором кадре баннера уже нет. Теперь, если внутри символов lose и won (когда они закроют собой экран) прописать код
_parent.play();//фактически _root.play();
это сбросит игру, но плашки сверху останутся и спокойно доиграют анимацию.
- Весь управляющий код будет лежать на главном действующем лице - символе pers.
Так как и бонусы, и препятствия хранятся в массивах, нужно только перебрать их все в цикле и проверить на пересечение с мячом:onClipEvent (load) { boom._visible = false;//сразу скрываем взрыв backSpeed = 4; maxSpeed = 5; speedX = 0; onEnterFrame = function() { var dx = _parent._xmouse - _x; speedX += dx * .1; speedX *= .7; speedX = (speedX > maxSpeed)? maxSpeed : (speedX < -maxSpeed)? -maxSpeed : speedX; if(_currentframe < 23) { _x += speedX; } else { _x -= backSpeed*.7;//по просьбам, делаю небольшое проскальзывание } //проверяем пересечение с врагами var i = _parent.enemies.all.length; while(i--) { var curE = _parent.enemies.all[i]; if(curE.hit.hitTest(ball.hit)) { boom._visible = true;//отображаем взрыв boom.gotoAndPlay(1);//запускаем сначала boom._x = ball._x;//выравниваем по мячику boom._y = ball._y; ball._visible = shadow._visible = false;//мяч и тень скрываем delete onEnterFrame;//останавливаем мячик и все проверки } } //проверяем пересечения с бонусами var i = _parent.bonuses.all.length; while(i--) { var curB = _parent.bonuses.all[i]; if(curB.hit.hitTest(ball.hit)) { _parent.bonuses.removeObj(curB);//удаляем бонус из списка curB.star.play();//при пересечении звезда улетает вверх _parent.ind.nextFrame();//переводим индикатор в следующий кадр } } } }
- Вот так выглядит баннер, если для символов hit временно отключить _visible = false; Сейчас можно отработать играбельность - усложнить или упростить задачу, масштабируя hit.
- Снова делаем символ hit невидимым и баннер готов!
Легко бросаться именами символов, но так же легко и запутаться в структуре. Поэтому привожу дерево основных клипов в сцене. Все остальные, если и присутствуют, то уже не важны и на результат не влияют.

Кстати, если кто встречал утилиту для флэша (или jsfl-скрипт), которая делает подобное дерево, оставьте ссылку в коментах, буду благодарен.
Можно сказать что это типизированный пример баннера. Заменяя действующих лиц, он легко превратится в самолет, облетающий грозовые тучи, зайца, прыгающего через пеньки, машиниста на поезде, который пригибается под столбами или конвеер, где нужно проскочить между штампующими роботами.
Назвать это игрой у меня язык не поворачивается. Хотя если добавить сюда вступительный мультфильм, таблицу рекордов, наделать уровней с разными препятствиями (мячик в разных странах например) и добавить в конце босса, то это уже будет игра, каких сейчас много в сети.
Эта часть самая запутанная из всех, поэтому не факт, что сразу ВСЕ заработает корректно, но информации здесь достаточно, чтобы довести баннер до ума. Кому лень это делать, специально для Вас: этот баннер ждет своего покупателя по условной цене $100 ![]()
Интересно на 24%




Статья чудная, но!
Я задам личный вопрос не по теме ))
А чем Вы пользуетесь для подсветки кода? )))
есть плагин для WordPress, называется wp-syntax. Нужно только стили настроить.
Супер!Спасибо=)
По какой то причине wp-syntax у меня не заработал ((
И это не первый раз, когда плагин не работает…
Или руки у меня не там…. или не у меня…
В любом случае спасибо за наводку.
Серия статей просто супер! Много интересного для себя почерпнул, хотя не новичек во флеше уже.
Класс ) Только иногда на переднем плане деревца. те что дальше сползают ниже ближних…
Всё вроде хорошо, но
onClipEvent (load) так уже давно никто не пишет …
Здорово! Только косяк есть - “Спробуй ще”, при проигрыше!
Исходник бы выложил.
у меня анимация противника меняется на месте но никуда не едет, почему?
Если символы противника не плодятся, то скорее всего вы забыли его назвать или не вызывается функция placeEnemy(). Если плодятся и не идут, наверное не определена скорость, нужно пробовать трейсом что теряется.
я не понял как заставить двигаться мяч,посмотрите пожалуйста эту флешку http://vkontakte.ru/app744049_16246915 , как мне скорость изменять,при разных направлениях,напишите пожалуйста код,заранее спасибо.
ппц,для какого языка тут скрипты?АС1-2 или АС3?
предыдуший урок сделал на ура,а тут как вставил первый скрипт в мувик enemys сразу поднял тревогу =(аж 5 ошибок указал =(
А как при изменении высоты курсора изменять высоту прыжка?
у кого есть готовая игра скиньте плиз
а скажите как происходит связь между hit как выполняются 2 события взрыв и сбор звезды ?
АбАлдеть !
мастерам огромное спасибо =)