Воздушный змей

Февраль 14, 2008
|

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

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

Генерировать колебания и рисовать ленты конечно же будем программно используя одну единственную функцию — прототип муви-клипа. Почему прототип, а не класс? Мне показалось, что именно так будет удобнее.

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

<code class="actionscript">
MovieClip.prototype.lenta = function(r, g, b, len) {
	var x = new Array();
	var y = new Array();
	var count = 20
	this.onEnterFrame = function() {
		...
	}
}
</code>

Чтобы обеспечить сложные изгибы ленты нам придется разделить ее на несколько сегментов — нам вполне хватит двадцати (переменная count). Создадим в прототипе два массива, которые будут хранить координаты точек ленты.

Соеденить точки в непрерывную линию нам поможет следующий алгоритм рисования:

<code class="actionscript">
this.clear();
this.moveTo(0,0);
for (var i = 0; i < count; i++) {
	this.lineStyle( line_width, color );
	this.curveTo( x[i], y[i], (x[i] + x[i+1]) * 0.5, (y[i] + y[i+1]) * 0.5 );
}
</code>

Это классический вариант рисования сплайна (кривой) с использованием массива координат точек. Алгоритм давно известен и прост, не будем останавливаться на нем.

Самое интересное — это рассчет координат точек. Про него расскажу подробнее. Начнем с простого (см. рис. ниже):

<code class="actionscript">
for (var i = 0; i <= count; i++) {
	y[i] = i * 2;
	x[i] = i * len;
}
</code>

Получили прямые линии с наклоном, для начала неплохо :)
Давайте добавим к ним простые синусоидальные колебания с помошью следующей строки кода:

<code class="actionscript">
var y1 = 5 * Math.sin( phase1i += phaseAdd1 );
</code>

где 5 — это амплитуда колебаний, а на вход функции синуса передадим начальную фазу колебаний phase1i , которая будет наращиваться в цикле на величину phaseAdd1, генерируя колебания определенной частоты. Чтобы амплитуда плавно возрастала умножим получненное значение на коэффициент i/count.

Смотрится лучше. Но движения линий слишком предсказуемые. Надо добавить еще одну синусоидальную составляющую с другой амплитудой и частотой — движения станут интереснее.

Код для вторго синуса и визуальное отображение на рисунке ниже:

<code class="actionscript">
var y0 = 14 * Math.sin( phase0i += phaseAdd0 );
</code>

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

<code class="actionscript">
for (var i = 0; i <= count; i++) {
	var y0 = 14 * Math.sin( phase0i += phaseAdd0 );
	var y1 = 5 * Math.sin( phase1i += phaseAdd1 );
	y[i] = i * 2 + i/count * ( y0 + y1 );
	x[i] = i * len;
}
</code>

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

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

Сделаем освещение следующим способом. Будем регулировать яркость каждого сегмента в зависимости от его угла наклона отностительно горизонта. Получить угол можно с помошью функции Math.atan2(dy,dx). Код ниже поможет оперировать яркостью сегментов линии исходя из угла наклона:

<code class="actionscript">
var light = 1.5 + 0.6 * ( Math.atan2(y[i+1] - y[i], x[i+1] - x[i]) );
var rNew = Math.min( light * r, 255 ) &lt;&lt; 16;
var gNew = Math.min( light * g, 255 ) &lt;&lt; 8;
var bNew = Math.min( light * b, 255 );

</code>

Суммируя новые величины цветовых составляющих можно получить конечный цвет сегмента и использовать его для рисования — this.lineStyle( 2, rNew + gNew + bNew );

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

Соберем все части кода. Конечный вариант прототипа будет выглядеть вот так:

<code class="actionscript">
MovieClip.prototype.lenta = function(r, g, b, len) {

	var x = new Array();
	var y = new Array();
	var count = 22

	var phase0 = 0;
	var phaseSub0 = 0.05 + 0.1 * Math.random();
	var phaseAdd0 = 0.3 + 0.1 * Math.random();

	var phase1 = 0;
	var phaseSub1 = 0.05 + 0.1 * Math.random();
	var phaseAdd1 = 0.4 + 0.2 * Math.random();

	var phase2 = Math.PI * 0.3;
	var phaseAdd2 = Math.PI / count;	

	this.onEnterFrame = function() {
		var phase0i = phase0 -= phaseSub0;
		var phase1i = phase1 -= phaseSub1;
		var phase2i = phase2;
		for (var i = 0; i <= count; i++) {
			var y0 = 14 * Math.sin( phase0i += phaseAdd0 );
			var y1 = 5 * Math.sin( phase1i += phaseAdd1 );
			var y2 = 80 * Math.sin( phase2i += phaseAdd2 );
			y[i] = i * 2 + i/count * ( y0 + y1 + y2 );
			x[i] = i * len;
		}
		this.clear();
		this.moveTo(0,0);
		for (var i = 0; i < count; i++) {
			var light = 1.5 + 0.6 * ( Math.atan2(y[i+1] - y[i], x[i+1] - x[i]) );
			var rNew = Math.min( light * r, 255 ) << 16;
			var gNew = Math.min( light * g, 255 ) << 8;
			var bNew = Math.min( light * b, 255 );
			this.lineStyle( 2, rNew + gNew + bNew );
			this.curveTo( x[i], y[i], (x[i] + x[i+1]) * 0.5, (y[i] + y[i+1]) * 0.5 );
		}
	}
}
</code>

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

Теперь создадим на сцене несколько пустых муви-клипов для каждой ленты, расположим их на конце змея :) В каждом муви-клипе сделаем вызов прототипа, в параметрах зададим цвет линии и длину.

<code class="actionscript">
onClipEvent (load) {
	lenta(220,20,0,13)       // красная лента
}
</code>

Змей готов.

Исходник
(файл .fla, 1.6 Мб)

Tags:

Автор записи: Михаил Востриков

Twitter Facebook Flickr