ООО "МСК" Очистка поверхностейУборка после пожара
ООО "МСК" Очистка поверхностейОчистка после пожара
ООО "МСК" Очистка поверхностейОчистка от краски

Эффект пламени

Автор: kaldirishe вкл. . Опубликовано в Блог

Lightbox Image

Эффект "Пламя"

В новой версии библиотеки был добавлен новый эффект – пламя. Эффект сделан на вполне тривиальном алгоритме сглаживания. Работает он следующим образом – создается палитра цветов, создается массив значений размерностью в изображение, в массив вносятся возмущения и далее усредняются. У эффекта несколько свойств: 5-ть цветов, 3-и позиции переходов цветов (между 2-м и 3-м цветом, 3-м и 4-м, 4-м и 5-м), интенсивность возмущений, степень и тип прозрачности, координаты. Все свойства кроме типа прозрачности можно анимировать. У цветов можно анимировать ARGB составляющие. В ходе экспериментов по выбору свойств наиболее реалистичное пламя получается при следующем наборе:

  • цвета: белый, белый, желтый, красный, черный;
  • позиции перехода: 60, 30, 10 (в сумме должны быть равны 100);
  • интенсивность возмущения: 1 (эту величину я буду анимировать)
  • степень прозрачности: 1;
  • тип прозрачности: квадратичный. 

Разжигание и потухание реализуется анимированием интенсивности возмущения от 0 до 1 и обратно.
Помимо самого класса эффекта присутствуют два вспомогательных класса настроек – инициализация и очистка. Первый задает размеры изображения пламени, второй очищает изображение. Пару слов о самой анимации, у эффекта предусмотрено свойство «Count» типа byte, то есть количество шагов, за которые пламя будет гореть. 255-ть шагов «прогорят» за несколько секунд, поэтому эффект я буду зацикливать. Возможно два варианта, первый по таймеру, второй – используя эффект генерации события. Работают оба, но с таймером как то нагляднее. Ну и последнее, я специально добавил компоненту несколько служебных свойств «OnSystem» для того чтобы на них можно было вешать эффекты и по необходимости переключаться между ними.

Итак приступим Smile

Шаг 1

Создаем проект, кидаем на форму: TlfeLayerForm, TlfeBitmapsList, TlfeLayerEffectList, 4-е TlfeLayerEffectControl, TTimer, TPopupMenu.

Lightbox Image

lfeLayerEffectControl1 будет камином (размер 237, 239 и DragAble=True), lfeLayerEffectControl2 и lfeLayerEffectControl3 - свечками (размер 25, 60), lfeLayerEffectControl4 иконкой. Ставлю Timer.Enable в False и Interval в 10. Создаю у PopupMenu1 - TMenuItem, и в событии OnClick пишу Close. Присваиваю lfeLayerEffectControl1.PopupMenu - PopupMenu1. Связываю компоненты библиотеки между собой: lfeLayerEffectList1.Images - lfeBitmapsList1, lfeLayerEffectList1.LayerForm - lfeLayerForm1; для всех TlfeLayerEffectControl - lfeLayerEffectControl.LayerEffectList - lfeLayerEffectList1, lfeLayerEffectControl.LayerForm - lfeLayerForm1. В lfeBitmapsList1 загружаю изображения камина и иконку (выбираю свойство Images в инспекторе, далее в диалоговом окне "Добавить"), можно скачать здесь.

Шаг 2

Начинаю настраивать анимацию (выбираю свойство Collection у lfeLayerEffectList1 в инспекторе). Привязываю эффект к lfeLayerEffectControl1, привязываю событие к "OnDesigned", привязываю шаг, добавляю к шагу: "Очистить холст", "Очистить компонент", "Рисовать на холсте" и "Рисовать компонент". У первых двух выставляю lfataInit в True, у третьего выбираю изображение камина, у четвертого выставляю lfetaAnimate в True.

Lightbox Image 

Шаг 3

Копирую событие на событие "OnLoaded" и "OnSystem1". В "Step 1" у события "OnSystem1" добавляю "Добавить 'пламя'", перемещаю его на оду позицию вверх и настраиваю: 

Lightbox Image  Lightbox Image  Lightbox Image

свойства Color2 .. Color5 настраиваются аналогично Color1, но с учетом выбора цвета (соответственно как порядок составляющих в свойствах цвета ABGR: (255,255,255,255) (255,0,255,255) (255,0,0,255) (255,0,0,0)). Intensity пока не трогаю. Работа утомительная, но делается один раз. Копирую событие "OnSystem1" на "OnSystem2", "OnSystem3", "OnSystem4". Возвращаюсь к  "OnSystem1" и добавляю еще три шага. В первый добавленный добавляю "Инициализировать 'Пламя'" и указываю размеры Width=120 Height=100, во второй добавляю "Удалить 'Пламя'" и вставляю lfataInit в True, в третьем добавляю эффект "Обработать". Теперь перемещаю "Step 1" вниз на две позиции (должен стать третьим):

Lightbox Image

 

Настаиваю Intensity у эффекта события "OnSystem1": начальное значение 0, конечное 1, тип инициализации tivValue, с анимацией за 200 шагов (пламя будет разгораться):

Lightbox Image 

У событий "OnSystem2", "OnSystem3": начальное значение 1, конечное 1, тип инициализации tivValue, без анимации (пламя горит). У событий "OnSystem4": начальное значение 1, конечное 0, тип инициализации tivValue, с анимацией в 200 шагов (пламя тухнет). И выглядит структура так:

Lightbox Image

Ну и наконец зациклю анимацию. Выбираю на форме lfeLayerEffectControl1 и создаю отклики на "OnMouseUp" и "OnSystem1" (попутно подключаю PopupMenu1):

Lightbox Image 

и соответственно заполняю их:

procedure TForm1.lfeLayerEffectControl1MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if Button = mbRight then lfeLayerEffectControl1.PopupAtCursor;
end;

procedure TForm1.lfeLayerEffectControl1System1(Sender: TObject);
begin
Timer1.Enabled := True;
end;

Шаг 4

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

  • Копирую все дерево дерево эффектов компонента lfeLayerEffectControl1 на компонент lfeLayerEffectControl2. Выбираю lfeLayerEffectControl2. Так как кроме пламени никаких изображений не подразумевается - удаляю привязки к событиям "OnDesigned" и "OnLoaded". Выбираю "OnSystem1", в первом шаге у эффекта инициализации пламени меняю размер на 25,60, а в третьем шаге удаляю эффект "Рисовать на холсте". У эффекта пламени увеличиваю конечную интенсивность с 1 до 3 (то есть будет разгораться с 0 до 3) и выставляю X и Y в 0,0 (начальные и конечные значения). Удаляю четвертый шаг с обработкой события. 
  • В событиях "OnSystem2", "OnSystem3" аналогично в первом шаге удаляю "Рисовать на холсте",  меняю интенсивности с 1 на 3 и выставляю X и Y в 0,0 (начальные и конечные значения).
  • В "OnSystem4" аналогично удаляю "Рисовать на холсте", меняю начальную интенсивность с 1 на 3, то есть будет затухнуть с 3 до 0 и выставляю X и Y в 0,0 (начальные и конечные значения). И выглядит структура так:

Lightbox Image   

  • Копирую все дерево дерево эффектов компонента lfeLayerEffectControl2 на компонент lfeLayerEffectControl3. 

Теперь настрою lfeLayerEffectControl4. Копирую все дерево эффектов компонента lfeLayerEffectControl1 на компонент lfeLayerEffectControl4. Выбираю lfeLayerEffectControl4 и удаляю привязки к событиям "OnSystem1" .. "OnSystem4". В событиях "OnDesigned" и "OnLoaded" у эффекта "Рисовать на холсте" выбираю изображение иконки пламени. Копирую событие "OnDesigned" на события "OnMouseEnter" и "OnMouseLeave". Добавляю в "Step 1" события "OnMouseEnter" - "Сбросить матрицу цветов" и "Изменить цвет". Для первого выставляю lfataInit в True. Для второго выставляю красный цвет (255,0,0,255). Добавляю в "Step 1" события "OnMouseLeave" - "Сбросить матрицу цветов". Выставляю lfataInit в True.

Lightbox Image

 

Создам и заполню отклик на событие "OnMouseUp" в инспекторе компонент:

procedure TForm1.lfeLayerEffectControl4MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Timer1.Enabled := False;

with lfeLayerEffectControl1 do
if (CurrentEventName = 'OnLoaded') or
(CurrentEventName = 'OnSystem4') then
GenerateEvent('OnSystem1')
else
GenerateEvent('OnSystem4');

with lfeLayerEffectControl2 do
if (CurrentEventName = 'OnLoaded') or
(CurrentEventName = 'OnSystem4') then
GenerateEvent('OnSystem1')
else
GenerateEvent('OnSystem4');

with lfeLayerEffectControl3 do
if (CurrentEventName = 'OnLoaded') or
(CurrentEventName = 'OnSystem4') then
GenerateEvent('OnSystem1')
else
GenerateEvent('OnSystem4');
end;

Заполню обработчик таймера и менюшки:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
if lfeLayerEffectControl1.CurrentEventName = 'OnSystem2' then
begin
lfeLayerEffectControl1.GenerateEvent('OnSystem3');
lfeLayerEffectControl2.GenerateEvent('OnSystem3');
lfeLayerEffectControl3.GenerateEvent('OnSystem3');
end
else
begin
lfeLayerEffectControl1.GenerateEvent('OnSystem2');
lfeLayerEffectControl2.GenerateEvent('OnSystem2');
lfeLayerEffectControl3.GenerateEvent('OnSystem2');
end;
end;

procedure TForm1.Close1Click(Sender: TObject);
begin
Close;
end;

В качестве постскриптума: зациклить анимацию можно было также следующим способом - в событиях "OnSystem2" и "OnSystem3" (всех компонентов с анимацией пламени) нужно добавить шаг, а в него эффект "Сгенерировать" у которого выставить  lfataInit в True и EventName = OnSystem3 для события "OnSystem2" и наоборот. У эффекта пламени выставить Count в 100..255. И заменить эффект "Обработать" (событие "OnSystem1", "Step 4") у lfeLayerEffectControl1 на "Сгенерировать" с указанием события "OnSystem2".

Скачать пример можно здесь

Добавить комментарий


Защитный код
Обновить