Динамический фон для баннеров и flash-игр

Урок от Акимова Олега

Привет!

Представляю вниманию очередную хитрость для флеш девелоперов — «Создание динамических текстур во флеш». О чем это я? Частенько в играх, в анимации, существует необходимость использовать качественные текстуры для различных заливок (для фонов, объектов и т.д.) Однако, найти такую текстурку достаточно сложно, подготовить ее к использованию в ролике — тоже сложно (если вы к тому же не владеете растровыми редакторами — это еще проблематичней), импортируемая текстура имеет вес (и не малый) а для вашей игры, например, необходимо много текстур. Но самое важное — это найти ту текстуру, которая бы гармонировала бы с вашей графикой.

Что же делать? Какие пути выхода?

  1. Нарисовать всю картинку целиком — ну тут даже не каждый художник сможет справиться, даже профи 🙂
  2. Предлагаю воспользоваться моим способом — динамически создавать свою текстуру прямо во Flash!

[kml_flashembed movie="http://xitri.com/wp-content/uploads/2009/06/grounds.swf" height="200" width="550" /]

Начнем. Читать далее Динамический фон для баннеров и flash-игр

Простой платформенный движок для флеш-игр

Чтобы сделать игру как на примере (слайд 12), не нужны продвинутые знания в физике и математике. По сути, мало что нужно вообще, потому как флеш располагает всем необходимым, чтобы сделать неплохую платформенную игру без особых усилий.

Обычно для определения столкновений фигур используются непростые математические расчёты проекции векторов. И это часто оправдано, так как позволяет сделать игры с реальным поведением объектов. Но если реальной физикой в вашей игре можно пренебречь, то организовать прогулки по платформенному миру — не проблема. Герой дня — популярная функция hitTest(), которая и будет решать все вопросы определения столкновений.

[kml_flashembed movie="http://xitri.com/wp-content/uploads/2009/06/platformengine.swf" height="200" width="550" /]

У этого движка есть свои плюсы и минусы. К недостаткам можно отнести:

  1. отсутствие физики (есть только гравитация)
  2. отсутствие наклонных поверхностей
  3. платформы только прямоугольной формы
  4. в сложных уровнях, во избежание глюков, требует дополнительной настройки персонажа
  5. есть небольшая «дерганность» персонажа при контакте с платформами (связано с тем, что пересечение с платформой компенсируется не по направлению движения, а с раскладкой на оси X и Y).

К достоинствам:

  1. прост в разработке
  2. простое и быстрое построение уровней сложной формы
  3. персонаж автоматически шагает по невысоким ступенькам
  4. несёт в себе определённую стилизацию и хорошо подходит для пиксельной графики.

Пошаговая инструкция:

  1. Для начала создадим персонажа. Я делаю это так: рисование в виде контурных линий (с раскладкой частей тела по слоям), разукрашивание и конвертация в символы всех значимых элементов. Слайд 1.
    (Полезная деталь — кисти рук обрамляются неразрывной контурной линией, а сама рука без линий на запястье и в плече. Это позволяет крутить символы под разными углами с правильным наложением. Так же с ногами.)
  2. Создаём структуру символов как на слайде 2. В главном клипе героя hero есть 3 кадра в которых расположены символы с одинаковым именем man, содержащие разные состояния героя: 1) стоит на месте; 2) идёт; 3) в прыжке. Если в вашей игре персонаж может погибнуть, это состояние можно поместить в 4-й кадр.
  3. Добавим нашему персонажу возможность атаковать. В этом примере нельзя бить на ходу или в прыжке, поэтому анимацию удара сделаем только в положении «стоит на месте» — hero.man (1 кадр). Внутри символа man оборачиваем руки, туловище и голову в клип body и внутри него делаем анимацию удара. Слайд 3.На слайде обозначены 4 кадра с кодом, которые требуют пояснения. Превый кадр — stop() — остановить персонажа в состоянии готовности (покоя). Второй кадр — play() — не знаю природу этого глюка, но иначе при ударе на ходу персонаж «зависает». 3-й скриптовый кадр — примерно тут должна быть проверка на попадание по противникам (там где происходит удар). Последний кадр сбрасывает флаг отвечающий за состояние удара (дальше в коде он будет включаться при нажатии клавиши ПРОБЕЛ).
    Если вы захотите сделать, чтобы персонаж мог атаковать на ходу и в прыжке, позаботьтесь чтобы в этих состояниях были клипы body с соответствующим кодом и анимацией.
  4. Персонаж готов и пора его проверить. Этот временный код позволяет попробовать персонажа в действии и на раннем этапе поправить анимацию, если это необходимо.
    //считаем что изначально персонаж стоит на поверхности
    var groundY = hero._y;
    //сила гравитации
    var grav = 1;
    //начальная скорость прыжка
    var jumpSpeed = -11.9;
    //шаг по Х
    var speedX = 5;
    //шаг по Y
    var speedY = 0;
    
    //максимальная скорость падения
    var maxSpeedY = 14;
    //отображает состояние прыжка
    var jump = false;
    //отображает состояние удара
    var shoot = false;
    
    Key.addListener(this);
    onKeyDown = function () {
    	//прыгаем
    	if (Key.isDown(Key.UP) && !jump && !shoot) {
    		jump = true;
    		speedY = jumpSpeed;
    	}
    	//бъём
    	if (Key.isDown(Key.SPACE) && !jump && !shoot) {
    		shoot = true;
    		hero.gotoAndStop(1);
    		hero.man.body.gotoAndPlay(2);
    	}
    };
    
    function stepHero() {
    	if (!shoot) {
    		//смещаемся влево/вправо
    		if (Key.isDown(Key.LEFT)) {
    			hero._xscale = -100;
    			hero._x -= (jump) ? speedX * .8 : speedX;
    		} else if (Key.isDown(Key.RIGHT)) {
    			hero._xscale = 100;
    			hero._x += (jump) ? speedX * .8 : speedX;
    		}
    	}
    	//применяем по цепочке: гравитация -> скорость -> _y
    	speedY += grav;
    	speedY = (speedY > maxSpeedY) ? maxSpeedY : speedY;
    	hero._y += speedY;
    
    	if(hero._y > groundY) {
    		hero._y = groundY;
    		speedY = 0;
    		jump = false;
    	}
    
    	if (!shoot) {
    		if (jump) {
    			//в позу полёта
    			hero.gotoAndStop(3);
    		} else {
    			if (oldX != hero._x) {
    				//если позиция по Х изменилась - в позу хотьбы
    				hero.gotoAndStop(2);
    			} else {
    				//иначе - по стойке смирно
    				hero.gotoAndStop(1);
    			}
    		}
    	}
    	oldX = hero._x;
    }
    
    onEnterFrame = function () {
    		stepHero();
    };
  5. Получаем примерно то что на слайде 5. В коде текущее положение персонажа считается поверхностью земли, так что для наглядности можно нарисовать опору у него под ногами.
  6. Главная хитрость. Добавляем в клип hero 4 символа и располагаем их по сторонам света (начиная с «восточного» и дальше по часовой стрелке называем их p1, p2, p3, p4). Назовём их условно «маркеры». По этим зонам мы будем проверять пересечение с платформами методом hitTest(). Также эти маркеры определяют расчётные габариты персонажа (ширину и высоту). Поэтому размещать их нужно не «от балды», а так чтобы они совпадали со ступнями, головой и, как минимум,  туловищем. Чтобы не было сползания графики, суммарно эти 4 символа должны располагаться строго по центру клипа hero. Самый простой способ сделать это — сгруппировать их и отцентрировать с привязкой к stage. После этого двигайте (корректируйте)  графику под них. У нашего героя голова вылазит за маркер и может накладываться на платформу — для мультяшных стилей это приемлемо. Чтобы маркеры участвовали в программных расчётах, но при этом не отображались на сцене, внутри каждого (или только внутри одного, если это один и тот же символ) пишем:
    _visible = false;

    Выглядеть будет примерно как на слайде 6.

  7. Пришло время заняться платформами. Создаём прямоугольный клип и внутри него в кадре пишем одну простую строчку:
    _parent.addBox(this);

    В этом вся прелесть. В этой строке вызывается функция, которая добавит этот блок в массив платформ и в дальнейшем при проверке мы будем в цикле проходить по этому массиву.Слайд 7.

  8. Теперь можно со скоростью звука создавать уровень из этих клипов, совершенно не заботясь каких они размеров, как перекрываются, как их зовут и где у них центр — всё это никак не повлияет на наш расчёт. Одно но — поворачивать нельзя — платформы должны сохранять горизонтальные и вертикальные линии. Строим уровень примерно как на слайде 8. Не забудьте стенки и опору под ногами.
  9. Пишем сам код платформера. Суть проста, проверяем каждую платформу на пересечение с маркерами персонажа. Если есть контакт, то смещаем персонажа в противоположную сторону до отсутствия пересечения.
    //массив с платформами
    var boxes = [];
    //каждая платформа при старте вызывает эту функцию и передаёт себя как параметр
    addBox = function (obj) {
    	boxes.push(obj);
    }
    
    //--------------------------------
    //сила гравитации
    var grav = 1;
    //начальная скорость прыжка
    var jumpSpeed = -11.9;
    //шаг по Х
    var speedX = 5;
    //шаг по Y
    var speedY = 0;
    
    //максимальная скорость падения
    var maxSpeedY = 14;
    //половина ширины персонажа
    var hhx = (hero.p1._x - hero.p3._x + hero.p1._width / 2 + hero.p3._width / 2) / 2;
    //половина высоты персонажа
    var hhy = (hero.p2._y - hero.p4._y + hero.p2._height / 2 + hero.p4._height / 2) / 2;
    //отображает состояние прыжка
    var jump = false;
    //отображает состояние удара
    var shoot = false;
    
    Key.addListener(this);
    onKeyDown = function () {
    	//прыгаем
    	if (Key.isDown(Key.UP) && !jump && !shoot) {
    		jump = true;
    		speedY = jumpSpeed;
    	}
    	//бъём
    	if (Key.isDown(Key.SPACE) && !jump && !shoot) {
    		shoot = true;
    		hero.gotoAndStop(1);
    		hero.man.body.gotoAndPlay(2);
    	}
    };
    
    function stepHero() {
    	if (!shoot) {
    		//смещаемся влево/вправо
    		if (Key.isDown(Key.LEFT)) {
    			hero._xscale = -100;
    			hero._x -= (jump) ? speedX * .8 : speedX;
    		} else if (Key.isDown(Key.RIGHT)) {
    			hero._xscale = 100;
    			hero._x += (jump) ? speedX * .8 : speedX;
    		}
    	}
    	//применяем по цепочке: гравитация -> скорость -> _y
    	speedY += grav;
    	speedY = (speedY > maxSpeedY) ? maxSpeedY : speedY;
    	hero._y += speedY;
    
    	//проверка на пересечение с платформами
    	checkHitPlatform();
    
    	//если закомментировать строку ниже, то можно будет свалиться с платформы 
    	//и прыгнуть оттолкнувшись от воздуха. Этой строкой устраняем этоот глюк
    	jump = (speedY < 5 && speedY > 0)? true : jump;
    
    	if (!shoot) {
    		if (jump) {
    			//в позу полёта
    			hero.gotoAndStop(3);
    		} else {
    			if (oldX != hero._x) {
    				//если позиция по Х изменилась - в позу хотьбы
    				hero.gotoAndStop(2);
    			} else {
    				//иначе - по стойке смирно
    				hero.gotoAndStop(1);
    			}
    		}
    	}
    	oldX = hero._x;
    }
    
    checkHitPlatform = function(){
    	//перебираем все платформы и проверяем пересечение с персонажем с учётом того,
    	//в какую сторону он смотрит
    	var i = boxes.length;
    	while (i--) {
    		var curB = boxes[i];
    		if (curB.hitTest(hero.p1)) {
    			//контакт справа
    			if (hero._xscale > 0) {
    				hero._x = curB._x - curB._width / 2 - hhx;
    			} else {
    				hero._x = curB._x + curB._width / 2 + hhx;
    			}
    		} else if (curB.hitTest(hero.p2)) {
    			//контакт снизу
    			jump = false;
    			speedY = 0;
    			hero._y = curB._y - curB._height / 2 - hhy;
    		} else if (curB.hitTest(hero.p3)) {
    			//контакт слева
    			if (hero._xscale > 0) {
    				hero._x = curB._x + curB._width / 2 + hhx;
    			} else {
    				hero._x = curB._x - curB._width / 2 - hhx;
    			}
    		} else if (curB.hitTest(hero.p4)) {
    			//контакт сверху
    			speedY = 0; // как вариант: speedY *= -1;
    			hero._y = curB._y + curB._height / 2 + hhy;
    		}
    	}
    }
    
    onEnterFrame = function () {
    		stepHero();
    };
  10. Совмещаем наш код, платформы и персонажа вместе и получаем рабочий вариант как на слайде 10.

Устранение глюков

Если у кого-то движок начнёт глючить, убедитесь что соблюдается одно важное условие — с каждой платформой не должны одновременно  пересекаться несколько маркеров. Чтобы этого избежать, нужно корректировать размеры и положение маркеров, а также скорость персонажа. В частности есть такие правила, слайд 11 (всё в пикселах):

  • скорость по оси X не должна быть больше W1, иначе персонаж может «уйти» под платформу.
  • скорость падения не должна превышать H1 (в коде она ограничивается переменной maxSpeedY)
  • начальная скорость прыжка не должна превышать H2
  • чтобы персонаж, стоя на краю платформы не висел в воздухе, подгоните нижний маркер (W2) под ширину ног когда персонаж стоит
  • высота коридоров (с учётом ступенек, если они есть) не должна быть меньше чем высота персонажа. Если перс откажется переступать невысокую ступеньку в узком коридоре — вам сюда.
  • чтобы ступенька была проходимой, её высота не должна превышать H1

Теперь можно добавить графику для уровня и сделать платформенную flash-игру, например как на слайде 12.

При желании можно добавить лифты (подвижные платформы). Вертикальные должны заработать автоматически, а к горизонтальным нужно «привязывать» персонажа на время езды. Также можно добавить плавное торможение персонажа, если завести переменную для скорости по X, но мне кажется что рисунок уровня и специфическая «дёрганность» движка, требуют резкого пиксельного смещения.