Циклическая анимация фона - Часть третья, или делаем игровой баннер

28.04.2008, автор Stormit, рубрики: ActionScript, Flash игры, Игровые баннеры

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

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

Сценарии и gameplay как для flash-игр, так и для баннеров могут быть самые разнообразные. Я выбрал для примера классический: есть объекты, которых нужно избегать и которые наоборот необходимо собирать. Если нет такой “борьбы противоположностей”, то обычно и играть не интересно. Такой вот метод “кнута и пряника”.

Теперь по порядку, как это сделано. Не буду говорить о чистоте кода, его можно оптимизировать 10тыс. раз. Наоборот, постараюсь внести как можно меньше изменений в примеры из предыдущих уроков. Так будет понятнее, а гуру flash-программирования легко могут переписать код под себя.

Пример и описание:

  1. Все начинается со сценария (или идеи, которая потом детально описывается). Обычно всегда есть главный герой и противники (препятствия). Могут быть разные виды бонусов, предметы помогающие в игре (оружие, трамплины, аптечки). Нужны индикаторы состояния игры - количество жизней, бонусов, врагов, сила удара, пройденный путь, таймер и т.д. Определяются условия выигрыша и поражения.От задумки напрямую зависит оформление и анимация всех персонажей и окружающей среды. В моем примере главный герой - мячик. Что может представлять для него угрозу? Наверное, все, что может его проколоть, ведь кроме оболочки и воздуха у него и нет ничего. Поэтому в игре нужно избегать всех наземных опасностей (гвозди, кактус и ежик). Сложнее придумать для мяча что-то полезное. Думал, сделать облачко из воздуха, но на линии горизонта, на светлом фоне оно смотрится не эффектно ( да и маленькие насосы как-то в антураж не вписываются). Поэтому выбор пал на звездочки.Если идей нет, тема сложная или “не тот день”, для бонусов можно использовать звездочки, подарки, монетки, изумруды или просто желтые шарики - очевидно, что это полезные штуки и их нужно собирать (наверное потому, что все это связано с деньгой и имеет ценность для людей). Для победы нужно собрать 5 звезд.
  2. Это то, что у нас есть от предыдущего урока. Дорога и передний план являются отдельными символами и смещаются влево на половину своей ширины, затем возвращаются в начальное положение (для кустов на переднем плане скорость задана чуть больше, чем для дороги - они к нам ближе и движутся быстрее). Задний фон создается путем дублирования клипа эталона (куст).
  3. По такому же принципу как и фоновые кустарники, делаются и препятствия с бонусами, только частота дублирования гораздо ниже.
    Создадим новый символ (enemy), внутри него нарисуем 3 вида препятствий, каждое в своем кадре. Это будут наши противники. Ежик живой, поэтому для него я буду дополнительно смещать клип влево (как будто он бежит навстречу).
  4. Клип 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;//часть от общей скорости
    }
  5. То же самое делаем для бонусов. Создаем символ bonus. Рисуем в нем звездочку. Оборачиваем ее в символ и делаем анимацию для случая, когда мяч ее ловит (у меня она улетает вверх).
  6. Так же, как и в третьем шаге, делаем для звездочки программное дублирование и анимацию, только с чуть большим интервалом (так препятствия будут появляться чаще, чем призы). Обратите внимание: скорость движения у всех объектов на дороге одинаковая, как и у самой дороги (еж дополнительно смещается). И также ведем учет символов. Код почти не меняется, и при желании его можно вынести во внешние функции.
    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();
    	}
    }
  7. Вот так препятствия и бонусы работают вместе.
  8. Для определения пересечений используем простой, но действенный способ - функцию hitTest(). Она работает с прямоугольными формами, поэтому рисуем прямоугольник и создаем из него символ hit. Если точнее, то hitTest() определяет пересечение не по самому символу, а по прямоугольнику, в который вписан “bounding box” этого символа. Это важно, если надумаете эти символы вращать.Так как форма у объекта не всегда совпадает с областью пересечения, то лучше создать отдельный символ, специально для проверки пересечений и его уже изменять индивидуально для каждого объекта (масштабировать и смещать его можно, а вращать нет).Внутри символа hit пишем простой код, символ теперь невидим, но доступен для скриптов:
    _visible = false;

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

    Проверка на пересечение осуществляется каждый кадр. Если есть столкновение с бонусом, он удаляется из списка и в проверках больше не учавствует, если с препятствием, то останавливаем основной скрипт и все проверки и запускаем анимацию взрыва.

  9. Для мяча символ hit кладем в символ ball. Так он будет двигаться и деформироваться вместе с мячом. Также кладем его в символы bonus и enemy (для каждого вида препятствий корректируем форму масштабированием).
  10. Еще нужно сделать анимацию поражения. В символе pers создадим символ с именем boom. Я предпочел прорисовать анимацию руками, но можно воспользоваться для этого эффектом взрыва, изменив нужным образом цвет частиц. В последнем кадре символа boom:
    stop();
    _parent._parent.lose.play();//запускает сообщение о проигрыше (описано ниже)
  11. Индикатор выигрыша - 5 звезд. При каждом собирании звезды, переходим в символе на один кадр вперед и соответствующая звезда в нем заменяется на анимацию исчезновения. После собирания всех звезд, функция play(), проигрывает небольшую задержку и после вызывает окно с поздравлениями (описано ниже).
  12. Создаю 2 клипа с сообщениями выиграл/проиграл. Называю их won и lose соответственно. Чтобы они не перекрывали экран и не загромождали собой сцену, можно в первом кадре нарисовать условную фигуру, разместить его над рабочей сценой, а анимацию начинать со второго кадра.
  13. Теперь нужно сделать сброс игры. Выиграли мы или проиграли, баннер нужно сбросить в начальное состояние. У нас сейчас достаточно много динамически созданных объектов и удалять их программно - непростое занятие. Можно что-то упустить и при повторной игре пойдут накладки. Проще сделать так, чтобы символ исчез с линейки и появился снова, тогда все внутри него удалится автоматически. Мы это сделаем для всего баннера целиком.Все, что я делал до шага 11, нужно обернуть в символ - лужайка со всеми объектами. А слоем выше создать 2 новых слоя для клипов lose и won. Во втором кадре баннера уже нет. Теперь, если внутри символов lose и won (когда они закроют собой экран) прописать код
    _parent.play();//фактически _root.play();

    это сбросит игру, но плашки сверху останутся и спокойно доиграют анимацию.

  14. Весь управляющий код будет лежать на главном действующем лице - символе 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();//переводим индикатор в следующий кадр
    			}
    		}
    	}
    }
  15. Вот так выглядит баннер, если для символов hit временно отключить _visible = false; Сейчас можно отработать играбельность - усложнить или упростить задачу, масштабируя hit.
  16. Снова делаем символ hit невидимым и баннер готов!

Легко бросаться именами символов, но так же легко и запутаться в структуре. Поэтому привожу дерево основных клипов в сцене. Все остальные, если и присутствуют, то уже не важны и на результат не влияют.
ball_banner_struct
Кстати, если кто встречал утилиту для флэша (или jsfl-скрипт), которая делает подобное дерево, оставьте ссылку в коментах, буду благодарен.

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

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

Эта часть самая запутанная из всех, поэтому не факт, что сразу ВСЕ заработает корректно, но информации здесь достаточно, чтобы довести баннер до ума. Кому лень это делать, специально для Вас: этот баннер ждет своего покупателя по условной цене $100 :)

Интересно на 24%

(17) Хитрых на тему «Циклическая анимация фона - Часть третья, или делаем игровой баннер»

  1. Murejib

    Статья чудная, но!
    Я задам личный вопрос не по теме ))
    А чем Вы пользуетесь для подсветки кода? )))

  2. Stormit

    есть плагин для WordPress, называется wp-syntax. Нужно только стили настроить.

  3. Keeper

    Супер!Спасибо=)

  4. Murejib

    По какой то причине wp-syntax у меня не заработал ((
    И это не первый раз, когда плагин не работает…
    Или руки у меня не там…. или не у меня…

    В любом случае спасибо за наводку.

  5. KUSAKA

    Серия статей просто супер! Много интересного для себя почерпнул, хотя не новичек во флеше уже.

  6. whoiskoss

    Класс ) Только иногда на переднем плане деревца. те что дальше сползают ниже ближних…

  7. Эх...

    Всё вроде хорошо, но
    onClipEvent (load) так уже давно никто не пишет …

  8. RuStep

    Здорово! Только косяк есть - “Спробуй ще”, при проигрыше!

  9. Kinu

    Исходник бы выложил.

  10. kingslayer

    у меня анимация противника меняется на месте но никуда не едет, почему?

  11. Stormit

    Если символы противника не плодятся, то скорее всего вы забыли его назвать или не вызывается функция placeEnemy(). Если плодятся и не идут, наверное не определена скорость, нужно пробовать трейсом что теряется.

  12. Nrjwolf

    я не понял как заставить двигаться мяч,посмотрите пожалуйста эту флешку http://vkontakte.ru/app744049_16246915 , как мне скорость изменять,при разных направлениях,напишите пожалуйста код,заранее спасибо.

  13. Роман

    ппц,для какого языка тут скрипты?АС1-2 или АС3?
    предыдуший урок сделал на ура,а тут как вставил первый скрипт в мувик enemys сразу поднял тревогу =(аж 5 ошибок указал =(

  14. boris1029

    А как при изменении высоты курсора изменять высоту прыжка?

  15. Сашка

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

  16. ButuzGOL

    а скажите как происходит связь между hit как выполняются 2 события взрыв и сбор звезды ?

  17. sever

    АбАлдеть !
    мастерам огромное спасибо =)

Оставить комментарий