Воздушный змей четверг
14 Февраля
Нарисованный художником красивый воздушный змей — это хорошо, но живой воздушный змей — это еще лучше! Давай попробуем сделать такого и запустить в небо.
Итак, в нашем случае змей состоит из «основы» и хвоста, в свою очередь состоящего из разноцветных ленточек, которые колышатся на ветру. Мы займемся созданием последних, так как анимировать игру света и тени на «основе» змея слишком просто.
Генерировать колебания и рисовать ленты конечно же будем программно используя одну единственную функцию — прототип муви-клипа. Почему прототип, а не класс? Мне показалось, что именно так будет удобнее.
Создадим основу прототипа, на входе будем получать цвет ленты (величину каждой цветовой составляющей) и длину сегмента ленты.
MovieClip.prototype.lenta = function(r, g, b, len) {
var x = new Array();
var y = new Array();
var count = 20
this.onEnterFrame = function() {
...
}
}
Чтобы обеспечить сложные изгибы ленты нам придется разделить ее на несколько сегментов — нам вполне хватит двадцати (переменная count). Создадим в прототипе два массива, которые будут хранить координаты точек ленты.
Соеденить точки в непрерывную линию нам поможет следующий алгоритм рисования:
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 );
}
Это классический вариант рисования сплайна (кривой) с использованием массива координат точек. Алгоритм давно известен и прост, не будем останавливаться на нем.
Самое интересное — это рассчет координат точек. Про него расскажу подробнее. Начнем с простого (см. рис. ниже):
for (var i = 0; i <= count; i++) {
y[i] = i * 2;
x[i] = i * len;
}
Получили прямые линии с наклоном, для начала неплохо :)
Давайте добавим к ним простые синусоидальные колебания с помошью следующей строки кода:
var y1 = 5 * Math.sin( phase1i += phaseAdd1 );
где 5 — это амплитуда колебаний, а на вход функции синуса передадим начальную фазу колебаний phase1i , которая будет наращиваться в цикле на величину phaseAdd1, генерируя колебания определенной частоты. Чтобы амплитуда плавно возрастала умножим получненное значение на коэффициент i/count.
Смотрится лучше. Но движения линий слишком предсказуемые. Надо добавить еще одну синусоидальную составляющую с другой амплитудой и частотой — движения станут интереснее.
Код для вторго синуса и визуальное отображение на рисунке ниже:
var y0 = 14 * Math.sin( phase0i += phaseAdd0 );
После сложения значений обоих колебаний внешний вид ленты значительно улучшился и стал менее предсказуемым как в реальной жизни (см. рис. и код ниже).
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;
}
Вроде отлично, но давайте добавим изгиб всем лентам. Опять используем синус, но в данном случае только его часть (рис. ниже а. и б., результат в.), похожую на нужный нам изгиб.
После сложени всех трех синусоидальных составляющих ленты приобрели реалистичную форму. Осталось добавить маленький штрих — освещение лент.
Сделаем освещение следующим способом. Будем регулировать яркость каждого сегмента в зависимости от его угла наклона отностительно горизонта. Получить угол можно с помошью функции Math.atan2(dy,dx). Код ниже поможет оперировать яркостью сегментов линии исходя из угла наклона:
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 );
Нам еще нехватает рассчета начальной фазы для каждой синусоидальной составляющей. Используем функцию Math.random() — это придаст лентам реалистичности, так как они будут выглядеть по разному при каждом запуске муви-клипа.
Соберем все части кода. Конечный вариант прототипа будет выглядеть вот так:
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 );
}
}
}
Амплитуды, величины фаз и коэффициенты для освещения подобраны для конкретной ситуации. Вы можете поэксперементировать с ними, чтобы лучше понять работу прототипа.
Теперь создадим на сцене несколько пустых муви-клипов для каждой ленты, расположим их на конце змея :) В каждом муви-клипе сделаем вызов прототипа, в параметрах зададим цвет линии и длину.
onClipEvent (load) {
lenta(220,20,0,13) // красная лента
}
Змей готов.
Как это выглядит на сайте
Исходник
(файл .fla, 1.6 Мб)
- Gilios
15 Февраля - Суперски!
- Вадим
21 Февраля - Молодцы, спасибо за скриптик!
- Кирилл
16 марта - Человеку не знакомому с веб-разработками может показаться, что такая картинка довольно просто делается, а на деле не самый простой код. Кстати, сколько по времени у одного программиста, заняла данная задача?
- dsasha
16 апреля - Спасибки, очень интересно
- sas
22 августа - Супер
Вопрос почему вы используете AS2?
- Olga
25 апреля - Супер конечно!!! а интересно, сколько времени займет змей с реалистичными лентами?
14 Февраля