Падал новогодний снег

Дата публикации: 21 декабря 2004 года
Адрес статьи в интернете: http://www.cherry-design.spb.ru/news/article.php?id=101


Однажды, для создания новогодней открытки на Flash, мне понадобилось сделать снег, и я стал смотреть, а как же это сделано у других. И обнаружил, что реалистичного снега нет практически ни у кого. Большинство либо анимируют большие снежинки по одной, либо сразу рисуют фоном снег, а потом плавно двигают его вниз. Ни то ни другое меня не устраивало, и тогда я написал свой вариант снегопада, который мне очень нравится.


Часть I :::

Предварительная подготовка и создание заготовки открытки на Flash

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

Ниже я буду подробно объяснять, как написать этот скрипт, используя "Flash MX 2004", но общие принципы остаются неизменными, и Вы можете достаточно легко модифицировать его для других языков, скажем для JavaScript. Для облегчения программирования и переносимости кода программа на ActionScript будет вынесена в отдельный файл и подключена к основному мультфильму при помощи директивы #include.

Рисуем новогоднюю открытку


Создайте три слоя-дорожки на временной шкале
Перед тем как переходить непосредственно к программированию, давайте нарисуем нашу открытку. Запустите Flash и создайте новый документ с размерами 640x300 пикселей. В настройках мультфильма выберите версию ActionScript 1.0. Теперь на временной шкале (Timeline) создайте следующие слои для нашего мультфильма:

Выделив первый кадр в слое actions и открыв панель "Actions", напишите в нем следующий код:

// Подключаем внешние скрипты
#include "snow.as"

// Инициализируем снежинки
init_snowflakes();

// Останавливаем проигрывание мультфильма
stop();

Перетащите снежинку точно в левый верхний угол мультфильма и дайте экземпляру имя "snow"
Теперь переходим в первый кадр слоя snow. Сейчас нам нужно будет нарисовать снежинку и сохранить ее в библиотеке как мувиклип с именем snowflake. Проще всего нарисовать снежинку кистью от руки - просто нарисуйте неровный круг белым цветом (снежные хлопья у нас ведь белые, правда?) и уменьшите его до размеров 10x10 пикселов. Границы у снежинки быть не должно. После того как снежинка сохранена в библиотеке, удалите ее со сцены.

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

Теперь перейдите в основную сцену и перетащите снежинку точно в левый верхний угол мультфильма. После этого дайте данному экземпляру снежинки имя "snow" и добавьте в панели "Actions" следующий код:

onClipEvent(enterFrame) {

    // Перерисовываем снежинки
    _root.draw_snowflakes();
}

Наконец, займемся последним слоем, который у нас называется card. В нем Вы можете нарисовать любой зимний сюжет или просто вставить подходящую зимнюю фотографию. Я нарисовал звездное небо, немного сугробов, елочные игрушки, веточку ели, светящееся окошко и симпатичного Деда Мороза (по крайней мере, мне так кажется :). В итоге получилась вот такая новогодняя открытка:

Вот такая вот открытка получилась у меня

Осталось ее сохранить. Так как у нас будет несколько файлов в проекте, то лучше всего создать отдельную папку "snow" и уже в ней сохранить открытку под именем snow.fla. Ура, готово! С предварительной подготовкой закончено, и в следующей части переходим к сути, т.е. к программированию и разбору алгоритма поведения снежинок.


Часть II :::

Алгоритм поведения снежинок

Перед тем как заняться непосредственно физикой снегопада, давайте создадим внешний файл, в котором и будут находиться все необходимые функции. Запустите ваш любимый текстовый редактор (например, UltraEdit, но сойдет и Блокнот) и сохраните пока еще пустой файл под именем snow.as. Помните, мы уже подсоединили этот файл в первом фрейме нашей открытки? Далее мы начнем постепенно заполнять этот файл необходимыми функциями и переменными.

Физика поведения снежинок


Начнем с самого простого - подумаем, а как же ведет себя снежинка? В самом простом случае, когда нет никаких дополнительных влияющих факторов, она, как и все остальные предметы на планете, падает вниз под действием силы тяжести. Давайте учтем это, введя постоянную составляющую силу (Gravity), направленную вниз:

// Постоянная составляющая скорости падения снежинки
_global.snowflake_y_gravity = 15;

В данном коде я определил глобальную переменную snowflake_y_gravity. Заметьте, что для определения глобальной переменной во флеше необходимо создавать ее как переменную объекта _global. Тогда данная переменная у нас будет доступна в любом месте нашего мультфильма.

Даже в очень спокойном воздухе постоянно возникают разнообразные небольшие вихри. Попробуйте подкинуть в воздух любой очень легкий предмет - перышко или шелковый платок - и Вы увидите, что горизонтально вниз он никогда не упадет. Наша снежинка даже легче перышка, и поэтому за счет этих микровихрей она может смещаться не только влево или вправо относительно вертикального падения, но даже иногда и немножко вверх. Мы учтем подобные микровихри, введя переменные смещения по вертикали (Yshift) и горизонтали (Xshift). А саму величину смещения будем вычислять как случайное число между нулем и максимально возможным смещением.

// Максимальный сдвиг снежинки по оси X и по оси Y
_global.snowflake_x_shift = 10;
_global.snowflake_y_shift = 10;

Но, кроме вихрей, случаются и настоящие ветры. Особенно это чувствуется в метель! Ветер мы учтем подобно гравитационной силе - как постоянно действующую силу в горизонтальном направлении (Wind). Знак этой силы будет указывать направление ветра.

// Скорость ветра
_global.wind_x_speed = 0;

Снежные хлопья бывают как маленькими, так и большими - за счет слипания нескольких снежинок вместе. Мы учтем этот фактор как случайное изменение размера снежинки (Sizeshift) относительно некоего базового размера.

// Ширина и высота снежинки
_global.snowflake_width = 11;
_global.snowflake_height = 10;

// Максимальное изменение размеров снежинки
_global.snowflake_size_shift = 2;

Для большего реализма давайте учтем также и пространство вглубь. Когда снежные хлопья близко к нам, то мы видим их более четкими, а когда они вдалеке, то кажутся размытыми. Мы учтем этот эффект как случайное изменение прозрачности снежинки (Alphashift) - чем она более прозрачна, тем дальше находится.

// Максимальное изменение прозрачности снежинки
_global.snowflake_alpha_shift = 10;

Пожалуй, мы учли все основные факторы, действующие на снежинку. Давайте подытожим и напишем окончательную функцию расчета нового положения снежинки (X0, Y0) относительно ее предыдущего местонахождения (X1, Y1):

Размер новой снежинки и ее прозрачность мы также будем рассчитывать, основываясь на предыдущем состоянии снежинки, по следующим формулам:

Во всех формулах при расчете случайных величин я удваивал значения, т.к. сдвиг может происходить как в положительную, так и в отрицательную сторону. А для того чтобы затем выровнять положение, размер или прозрачность снежинки - вычитал величину сдвига. Этот способ как раз и позволяет нам учесть сдвиг значения в обе стороны.

Инициализация снегопада


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

// Количество падающих снежинок
_global.num_snowflakes = 30;

// Массив, хранящий описание снежинок
_global.snowflakes = new Array(num_snowflakes);
for (i=0; i<num_snowflakes; i++) {
    snowflakes[i] = new Array(0,0,0,0,0);
}

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

// Ширина и высота снегопада
_global.snow_width = 640;
_global.snow_height = 300;

Перед тем как мы начнем отрисовывать снегопад, нам необходимо заполнить массив снежинок начальными данными, а также программно сгенерировать мувиклипы для всех снежинок. Мы оформим данную процедуру в виде функции init_snowflakes():

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                      Функция инициализации снежинок                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

function init_snowflakes() {

    for (i=0; i<num_snowflakes; i++) {

        // Создаем новый экземпляр снежинки
        snow.attachMovie("snowflake", "snowflake_"+i, i);

        // Инициализируем снежинку начальными значениями
        snowflakes[i][0] = random(snow_width);
        snowflakes[i][1] = random(snow_height);
        snowflakes[i][2] = snowflake_width;
        snowflakes[i][3] = snowflake_height;
        snowflakes[i][4] = random(30);
    }
}

В процессе инициализации мы создаем новые мувиклипы, на основе нашей снежинки, помещенной в библиотеке. Помните, при сохранении мы назвали ее snowflake? Теперь именно этот идентификатор используется для создания новых экземпляров снежинок. Каждый новый экземпляр должен быть прикреплен к уже существующему объекту, и именно точка привязки этого объекта будет началом координат для нового мувиклипа. В качестве родительского объекта мы поместили саму же снежинку в верхний левый угол открытки, дав ей имя snow. И таким образом обеспечили совмещение координат снегопада с координатами открытки.

Для создания нового экземпляра мувиклипа в ActionScript используется метод attachMovie(), вызываемый из родительского объекта. Синтаксис данной функции следующий:

my_mc.attachMovie(idName:String, newName:String, depth:Number [, initObject:Object]),

где

Для генерации случайного целого числа в ActionScript используется функция random(). В качестве параметра ей передается целое число N, а в качестве результата возвращается случайное число R в диапазоне 0 < R < N-1.


Часть III :::

Функции расчета и отрисовки снежинок

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

Как Вы, наверное, знаете, любой объект обладает свойствами, изменяя которые мы можем влиять на его внешний вид и расположение. В случае создания мультфильма вручную Вы указываете значения этих параметров в панели "Properties". Если же нам необходимо изменять значения программно, то для этих случаев в ActionScript существует функция setProperty(), синтаксис которой следующий:

setProperty(target:Object, property:Object, value/expression:Object),

где

Эта функция нам понадобятся, когда мы приступим к отрисовке нашего снегопада.

Функция расчета нового местоположения и размеров снежинок


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

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                Функция расчета нового положения снежинок                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

function calculate_snowflakes() {

    for (i=0; i<num_snowflakes; i++) {

        // Вычисляем новый размер снежинки
        size = random(2*snowflake_size_shift)-snowflake_size_shift;
        
        // Корректируем параметры снежинки
        snowflakes[i][0] += wind_x_speed + random(2*snowflake_x_shift)
                          - snowflake_x_shift;
        snowflakes[i][1] += snowflake_y_gravity + random(2*snowflake_y_shift)
                          - snowflake_y_shift;
        snowflakes[i][2] += size;
        snowflakes[i][3] += size;
        snowflakes[i][4] += random(2*snowflake_alpha_shift)-snowflake_alpha_shift;

        // Корректируем снежинки, которые вышли за пределы снегопада
        if (snowflakes[i][0] < 0 || snowflakes[i][0] > snow_width || 
            snowflakes[i][1] < 0 || snowflakes[i][1] > snow_height ||
            snowflakes[i][4] < snowflake_alpha_shift ||
            snowflakes[i][2] < snowflake_size_shift) {
        
            // Генерируем новые параметры для снежинок
            snowflakes[i][0] = random(snow_width);
            snowflakes[i][1] = random(snow_height);
            snowflakes[i][2] = snowflake_width+size;
            snowflakes[i][3] = snowflake_height+size;
            snowflakes[i][4] = 30 + random(70);
        }
    }
}

Кроме снежинок, которые достигли земли или были унесены за пределы снегопада, нам необходимо также учитывать и те снежинки, размеры которых уменьшились до минимального размера. То же самое касается и снежинок, прозрачность которых стала меньше минимально допустимой. Поэтому функция проверки снежинок получилась более сложной, чем просто проверка границ снегопада.

Функция отрисовки снежинок


Как вы помните, когда мы создали объект "snow", мы повесили на него обработку события onClipEvent(enterFrame). Данное событие вызывается каждый раз, когда передается управление данному мувиклипу. Мы используем настройки мультфильма по умолчанию, поэтому данное событие будет вызываться 12 раз в секунду и запускать функцию отрисовки снежинок.

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
//                        Функция отрисовки снежинок                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

function draw_snowflakes() {

    // Рассчитываем новое положение снежинок
    calculate_snowflakes();

    for (i=0; i<num_snowflakes; i++) {

        // Меняем параметры снежинки
        setProperty(snow["snowflake_"+i],_x,snowflakes[i][0]);
        setProperty(snow["snowflake_"+i],_y,snowflakes[i][1]);
        setProperty(snow["snowflake_"+i],_width,snowflakes[i][2]);
        setProperty(snow["snowflake_"+i],_height,snowflakes[i][3]);
        setProperty(snow["snowflake_"+i],_alpha,snowflakes[i][4]);
    }
}

Первым делом мы рассчитываем новое положение снежинок, а затем производим корректировку параметров, используя функцию setProperty(), которую я описал выше. Все встроенные в объект атрибуты начинаются со знака подчеркивания "_". Полный список атрибутов может быть очень большим и зависит от конкретного объекта. В нашем же случае достаточно знать атрибуты, описывающие координаты объекта (_x, _y), его ширину и высоту (_width, _height), а также степень прозрачности (_alpha). Именно их мы и корректируем при отрисовке снежинок.


Часть IV :::

Подбираем параметры снегопада

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


Название эффекта Число снежинок Ширина Высота Gravity Wind Xshift Yshift Sizeshift Alphashift
По умолчанию 30 11 10 15 0 10 10 2 10
Метель 100 11 10 40 50 30 30 4 10
Снежная крупа 80 3 3 15 5 30 30 0 20
Дождь 50 1 150 40 0 30 10 0 10
Дискотека 10 110 100 2 2 30 30 5 10
Пузырьки 30 11 10 -15 0 10 10 4 20


Поэкспериментировать с этими параметрами, а также попробовать изменить форму и цвет снежинки, я предоставляю вам самим. Возможно, вы найдете новые интересные эффекты. Тогда можете прислать мне Ваши параметры - и я добавлю эффект в таблицу.

Ура, новогодняя открытка готова!


Ну что же, мы сделали все что нужно! Осталось только собрать все функции воедино и сгенерировать SWF-файл. Если вы все делали аккуратно, то увидите замечательный снегопад. Вот какая открытка получилась у меня:



Вы можете взять данный скрипт за основу и создать свою собственную новогоднюю открытку! Добавить более сложную анимацию или нарисовать свою собственную картинку. А потом послать ее своим друзьям и любимым. Ну разве не здорово? Им наверняка понравится! :)

Архив с файлами к статье


скачать архив с файлами к статьескачать архив с файлами к статье

Нажав на изображение дискеты, вы сможете скачать ZIP-архив с рабочими файлами, упомянутыми в статье. Ниже приведен полный список файлов, входящих в состав архива:

  1. snow.fla - исходник Flash-ролика новогодней открытки в формате FLA;
  2. snow.as - внешний файл с описанными функциями на ActionScript;
  3. snow.swf - скомпилированный Flash-ролик в формате SWF.


Copyright © 2001-2008 Михаил Мельников / Перепечатка без разрешения запрещена