|
19.10.2009, 12:48
|
#1
|
Дэвелопер
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,376
Написано 491 полезных сообщений (для 886 пользователей)
|
Разрывание рисунка на куски и растворение
Здравствуйте!
Здесь пример того, как можно спецэффектно рвать рисунки на куски или растворять их не просто альфой.
Реализовано два типа "деформации" рисунка - взрыв и растворение.
Создавать куски можно как заблаговременно, например, в момент загрузки уровня, так и "на лету". Но создание на лету тормозит систему на время своей работы.
Куски получаются посредством попиксельного копирования цветов из исходного рисунка в рисунок куска. Медленно? Ещё бы! (для ускорения и есть предварительная загрузка в кеш)
Управление:
-левая кнопка мыши - создать взрыв
-правая кнопка мыши - создать растворение
-Esc - выход
Псевдокод использования:
1. создаем заранее:
взрыв_растворение.СоздатьВКеш(исходныйРисунок, ширина куска, высота куска, тип{Взрыв,Растворение})
а затем там где нужно создать эффект:
взрыв_растворение.Запуск(Х, У)
2. создаем в реальном времени:
взрыв_растворение.СоздатьПрямЩас(исходныйРисунок, ширина куска, высота куска, тип{Взрыв,Растворение}, Х, У)
Для примера возьмем такой рисунок:
Что есть в примере:
-класс TParticle - для хранения информации о кусках и их рисования
-класс TExplosion - для создания и управления кусками
-код, показывающий как этим воспользоваться
Скрины из данного примера:
исходный рисунок:
взрыв:
растворение:
Теперь - непосредственно к исходнику.
В коде имеются комментарии для увеличения степени понятности; их вполне может хватить. Итак,
Strict
AppTitle = "Разрывание и растворение рисунка"
'загружаем наш подопытный рисунок
Local img:TImage = LoadImage("pointer.png")
'создаем окно
Graphics(800, 600, 0, 60)
HideMouse()
'создаем экземпляр объекта "взрыв"
Local expl:TExplosion = New TExplosion
'заранее создаем будущие осколки из исходного рисунка
'параметры: рисунок, ширина осколков, высота осколков, тип осколков
expl.fnPreCreate(img, Rand(5, 15), Rand(5, 15), TParticle.KIND_EXPLOSION)
SetClsColor 100, 100, 200
SetBlend ALPHABLEND
'главный цикл
While Not KeyHit(KEY_ESCAPE)
'сбрасываем в исходное состояние альфу, поворот и масштаб
SetAlpha 1
SetTransform
'по щелчку левой кнопки мыши запускаем наш ранее кешированный взрыв
If(MouseHit(1) > 0)
'указываем координаты левого верхнего угла
expl.fnStart(MouseX(), MouseY())
EndIf
'по щелчку правой кнопки - "на лету" создаем "растворение" рисунка
'на время разбиения на куски прога подвисает, поэтому рекомендуется заранее (на этапе загрузки) создавать нарезку
If(MouseHit(2) > 0)
'параметры: исходный рисунок, ширина кусков, высота кусков, тип "растворение", координаты левого верхнего угла
expl.fnCreate(img, Rand(5, 15), Rand(5, 15), TParticle.KIND_RASTVORENIE, MouseX(), MouseY())
EndIf
'обновление взрыва/растворения
'если идет анимация, то функция вернет true - исходный рисунок не рисуем
'если анимации нет, то вернет false - и будет нарисован исходный рисунок
If(expl.fnUpdate() = False) DrawImage (img, MouseX(), MouseY())
Flip
Cls
Wend
End
'класс КУСОК, содержит картинки и параметры рисования этих картинок
Type TParticle
Field img:TImage 'картинка
Field x:Float, y:Float 'координаты
Field dx:Float, dy:Float 'смещение по X и Y (скорость)
Field alpha:Float, scale:Float, angle:Float 'альфа, масштаб и поворот
Field dAlpha:Float, dScale:Float, dAngle:Float 'их приращение
Field dir 'направление вращения картинки
Field kind 'тип куска
Global KIND_EXPLOSION = 1 'для взрыва
Global KIND_RASTVORENIE = 2 'для растворения
'создание экземпляра
'параметры: указатель на рисунок, координаты х-у, тип, скорость по х-у,начальные значения альфы, масштаба и поворота
Function fnCreate:TParticle(img:TImage, x:Float, y:Float, knd, dx:Float = 0, dy:Float = 0, alpha:Float = 1.0, scale:Float = 1.0, angle:Float = 0)
Local p:TParticle = New TParticle
p.img = img
p.x = x
p.y = y
p.dx = dx
p.dy = dy
p.fnSetKind(knd) 'эта функция ставит параметры в зависимости от типа
p.alpha = alpha
p.scale = scale
p.angle = angle
p.dir = 1 - 2 * (Rand(100) < 50) 'случайное направление вращения (dir = -1 или 1)
Return p
End Function
'устанавливаем параметры в зависимости от типа
Method fnSetKind(knd)
kind = knd
'для взрава значения побольше, для растворения - поменьше
If(kind = KIND_EXPLOSION)
dx = Rnd(- 2.0, 2.0)
dy = Rnd(- 2.0, 2.0)
dAlpha = -0.015
dScale = 0.03
dAngle = 3.5
Else 'If(kind = KIND_RASTVORENIE)
dx = Rnd(- 0.02, 0.02)
dy = Rnd(- 0.02, 0.02)
dAlpha = -0.007
dScale = 0.01
dAngle = 0.3
EndIf
End Method
'обновления куска
'здесь все значения нужно умножать на коэффициент,
'равный koef#=fpsEtalon#/fpsReal#
'чтобы на разномощных компах одинаково выглядело
Method fnUpdate()
x:+dx
y:+dy
alpha:+dAlpha
scale:+dScale
angle:+dAngle * dir
End Method
'рисование картинки с ее альфой, масштабом и поворотом
'а также с внешним смещением по х-у
Method fnDraw(x0:Float = 0, y0:Float = 0)
SetAlpha alpha
SetTransform angle, scale, scale
DrawImage img, x0 + x, y0 + y
End Method
End Type
'класс ВЗРЫВ (он же и растворение)
Type TExplosion
Field list:TList = New TList 'список кусков рисунка
Field listCashed:TList = New TList 'список кешированных кусков рисунка
Field x:Float, y:Float 'координаты левого верхнего угла, относительно которого рисовать
Field image:TImage 'указатель на исходный рисунок
Field cashed 'флаг - кешировали или нет
Field animate 'флаг - идет анимация или нет
'"врЕменная" переменная для циклов for ... eachin ...
Global part:TParticle
'кеширование кусков
Method fnPreCreate(pimage:TImage, sizeX:Float, sizeY:Float, kind)
image = pimage
'создаем куски из указанного рисунка, указанного размера и типа
listCashed = fnCreateParticles(image, sizeX, sizeY, kind)
cashed = True 'устанавливаем флаг, ибо сделали дело
End Method
'запуск анамации - используется для старта из кешированного списка
Method fnStart(px:Float, py:Float)
'если не кешировали или уже идет анимация - выходим
If(cashed = False Or animate = True) Return
Local p:TParticle 'это для кусков-копий кешированных
'позицию запоминаем
x = px
y = py
'проходим по списку кешированных кусков
'создаем их копиии и добавляем в список
For part = EachIn listCashed
p = New TParticle
p.x = part.x
p.y = part.y
p.dir = part.dir
p.dx = part.dx
p.dy = part.dy
p.alpha = part.alpha
p.angle = part.angle
p.scale = part.scale
p.dAlpha = part.dAlpha
p.dAngle = part.dAngle
p.dScale = part.dScale
p.img = part.img
list.AddLast(p)
Next
animate = True '"включаем" анимацию
End Method
'создание кусков "на лету"
'даем функции рисунок, ширину, высоту и тип кусков, и координаты откуда все рисовать
Method fnCreate(pimage:TImage, sizeX:Float, sizeY:Float, kind, px:Float, py:Float)
'если уже идет анимация - выходим
If(animate = True) Return
image = pimage
x = px
y = py
'создаем куски
list = fnCreateParticles(image, sizeX, sizeY, kind)
animate = True 'поехали!
End Method
'создание кусков - возвращает список с кусками
'даем функции исходный рисунок + ширину, высоту и тип кусков
Function fnCreateParticles:TList(img1:TImage, sizeX:Float, sizeY:Float, kind)
Local lst:TList = New TList
Local imgW, imgH, ix, iy, px, py
Local pix1:TPixmap, pix2:TPixmap 'через пиксмапы будем попиксельно копировать точки из исходного в куски
Local img2:TImage 'это для указывания на очередной кусок
'реальная ширина и высота создаваемого куска
'нужна для того, чтобы правильного размера крайние куски сделать
Local wd, hg
'запомним размеры исходного рисунка в переменные, для удобства
imgW = img1.width
imgH = img1.height
'блокируем исходный рисунок для работы с его точками
'и получаем пиксмап
pix1 = LockImage(img1)
'пробегание по координате Y
'iy - хранит номер строки
While(iy * sizeY < imgH)
'проверка - выйдем ли мы следующим проходим за границы исходного рисунка
'если да, то значит кусок нужно делать не на всю высоту sizeY, а поменьше
'чтоб он как раз до края получился
If((iy + 1) * sizeY > imgH)
hg = imgH - iy * sizeY
Else
hg = sizeY
EndIf
'обнуляем номер столбца
ix = 0
'пробегание по координате Х
'iх - хранит номер столбца
While(ix * sizeX < imgW)
'проверка - выйдем ли мы следующим проходим за границы исходного рисунка
'если да, то значит кусок нужно делать не на всю ширину sizeХ, а поменьше
'чтоб он как раз до края получился
If((ix + 1) * sizeX > imgW)
wd = imgW - ix * sizeX
Else
wd = sizeX
EndIf
'создаем рисунок для очередного куска размером (wd x hg) точек
img2 = CreateImage(wd, hg, 1, DYNAMICIMAGE | FILTEREDIMAGE)
'блокируем рисунок и получаем его пиксмап
pix2 = LockImage(img2)
'(столько циклов, аж дух захватывает!)
'здесь мы попиксельно копируем инфу о цветах из исходного рисунка в рисунок куска
For py = 0 Until hg
For px = 0 Until wd
'в пиксмап куска пишем в позицию px,py
'а из исходного читаем со смещением ix*sizeX,iy*sizeY
WritePixel(pix2, px, py, ReadPixel(pix1, ix * sizeX + px, iy * sizeY + py))
Next
Next
'разблокируем рисунок куска
UnlockImage(img2)
'ставим ему смещения рисования в центр
MidHandleImage(img2)
'добавляем кусок в список
'отнимать от х координаты (sizeX - wd) * 0.5
'и от у координаты (sizeY - hg) * 0.5
'нужно для того, чтобы крайние куски не съехали в сторону
'когда их размер меньше чем sizeX*sizeY - из-за midhandl'а
'и еще 0.5 от размера прибавляем, опять же компенсируя сдвиг midhandl'a
lst.AddLast(TParticle.fnCreate(img2, ix * sizeX - (sizeX - wd) * 0.5 + sizeX * 0.5, iy * sizeY - (sizeY - hg) * 0.5 + sizeY * 0.5, kind))
ix:+1
Wend
iy:+1
Wend
'разблокируем исходный рисунок
UnlockImage(img1)
'возвращаем список
Return lst
End Function
'обновление
Method fnUpdate()
'если анимация не включена, то выходим, возвращая false
If(animate = False) Return False
'пробегаем по всем кускам из списка
For part = EachIn list
'обновляем и рисуем каждый
part.fnUpdate()
part.fnDraw(x, y)
'проверяем - не пора ли удалить кусок?
If(part.alpha <= 0)
list.Remove(part)
part = Null
EndIf
Next
'если кусков больше нет, то останавливаем анимацию
If(list.IsEmpty())
animate = False
EndIf
'возвращает true, а после удаления последнего - false
'это не столь важно, здесь можно просто return true поставить
Return animate
End Method
End Type
Во вложении исходник и подопытный рисунок.
Вот и всё!
|
(Offline)
|
|
Эти 8 пользователя(ей) сказали Спасибо Жека за это полезное сообщение:
|
|
20.10.2009, 00:33
|
#2
|
Ференька
Регистрация: 26.01.2007
Адрес: улица Пушкина дом Колотушкина
Сообщений: 10,742
Написано 5,461 полезных сообщений (для 15,675 пользователей)
|
Ответ: Разрывание рисунка на куски и растворение
Нельзя ли выложить скомпиленное? Так заинтересовало, что даже посмотреть захотелось, а БМакса нет-с За урок спасибо. Идея оригинальная, использует преимущества аппаратного рендера БМакса и не сложна в реализации.
__________________
Мои проекты:
Анальное Рабство
Зелёный Слоник
Дмитрий Маслов*
Различие**
Клюква**
* — в стадии разработки
** — в стадии проектирования
Для проектов в стадии проектирования приведены кодовые имена
|
(Offline)
|
|
Сообщение было полезно следующим пользователям:
|
|
20.10.2009, 04:45
|
#3
|
.
Регистрация: 05.08.2006
Сообщений: 10,429
Написано 3,454 полезных сообщений (для 6,863 пользователей)
|
Ответ: Разрывание рисунка на куски и растворение
Очень хороший пример, и интересная реализация.
Поддерживаю предыдущего оратора - скомпиленную ехе'шку хочется.
Если указать фрэймворк, и заинклудить что нада, будет очень лёгкая ехе.
|
(Offline)
|
|
20.10.2009, 07:04
|
#4
|
Дэвелопер
Регистрация: 04.09.2005
Адрес: Красноярск
Сообщений: 1,376
Написано 491 полезных сообщений (для 886 пользователей)
|
Ответ: Разрывание рисунка на куски и растворение
Прицепил архив с exe-шником и новым исходником.
В новом коде небольшие изменения: в класс TParticle добавил поле deep, эта переменная аналогична переменной dir, только та для направления вращения, а эта для направления масштабирования, т.е. задает - увеличивать картинку или уменьшать при анимации.
Так же принимает случайное значение - или 1, или -1.
deep = 1 - 2 * (Rand(100) < 50)
Для получения результата в функции обновления куска приращение масштаба умножается на значение этой переменной:
scale:+dScale * deep
Теперь часть осколков летит к смотрящему, приближаясь, а часть - отдаляется, тем самым объёмность прослеживается чётче.
Отдаление только для взрыва, для растворения некрасиво с ним.
Спасибо за беседу
|
(Offline)
|
|
Эти 3 пользователя(ей) сказали Спасибо Жека за это полезное сообщение:
|
|
Ваши права в разделе
|
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 14:52.
|