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

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 :)

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

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

  1. Murejib

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

  2. Stormit

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

  3. Keeper

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

  4. Murejib

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

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

  5. KUSAKA

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

  6. whoiskoss

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

  7. Эх...

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

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