┌────────────────────────────┐
│ ░▒▓▒▓█▓█ RIPPLES █▓█▓▒▓▒░░ │
└────────────────────────────┘
(C) Reaper/ZX-MANIACS
В этой статье я решил описать
эффект, известный на других
платформах как дождь на воде. Он
представляет из себя расходя-
щиеся концентрические круги
( как след на воде от капель
дождя ). Этот эффект особенно
похож на воду, если под кругами
есть какая-нибудь картинка.
Впрочем, данную модификацию
эффекта на Спектруме сделать
красиво довольно сложно ( хотя,
например, в демке Emergency этот
эффект есть и смотрится он очень
даже неплохо ). Как бы то ни бы-
ло, я буду описывать обычный эф-
фект, без фоновой картинки. В
конце, я, пожалуй, скажу пару
слов об алгоритме работы с фоно-
вой картинкой, но пример приво-
дить не буду.
Эффект битмаповский, поэтому
делать его можно в атрибутах или
в чанках.
Итак, для создания эффекта Вам
понадобятся два буфера, равных
по размеру экрану. Так, напри-
мер, в примере используются чан-
ки. Выводятся они на 2/3 экрана,
так что размер обоих буферов
должен быть 64*32. Но здесь есть
одна тонкость. Желательно остав-
лять нетронутой ( постоянно рав-
ной нулю ) полоску толщиной в 1
чанк по всему периметру буфера.
Поэтому у меня в примере исполь-
зуются буфера размером 66*34, но
выводится на экран лишь внутрен-
няя их часть размером 64*32.
Можно оставить буфера размером
64*32, но тогда обрабатывать
следует только внутреннюю об-
ласть 62*30 ( это, кстати, даст
увеличение скорости из-за умень-
шения обрабатываемой области и
из-за упрощения конвертации в
формат 2 чанка на байт ( или ус-
корения вывода чанков, если вы
пользуетесь процедурой вывода
чанков в формате 1 чанк на
байт ) ).
Один из буферов считается те-
кущим ( т. е. в нем строится
изображение ), а второй - преды-
дущим ( то, что было на экране
после предыдущего прохода эффек-
та ). Основной смысл эффекта
заключается в следующей последо-
вательности действий:
1) Выставляются указатели в
верхнюю левую ячейку текущего и
предыдущего буферов ( а лучше в
ячейку на 1 строку ниже и на 1
столбец правее верхней левой, т.
к. это даст возможность не зат-
рагивать крайние строку и стол-
бец );
2) Берем значения четырех яче-
ек вокруг текущей в 'предыдущем'
буфере и складываем их;
3) Полученное число делим на 2
и вычитаем из него значение те-
кущей ячейки в 'текущем' буфере;
4) Если полученное значение
меньше нуля, приравниваем его к
нулю. Обрубаем его также и по
максимуму ( в моем примере, мак-
симальное значение, которое
воспринимают чанки - 31, поэтому
полученное число обрубается
просто по AND 31 );
5) Ну и теперь то, что получи-
лось, кладем в текущую ячейку в
'текущем' буфере;
6) Переставляем указатели на
следующие ячейки в обоих буферах
и завершаем циклы ( сначала по
столбцам, а потом по строкам );
7) Меняем местами текущий и
предыдущий буфера. Точнее, прос-
то переставляем указатели, и те-
перь текущий буфер будет счи-
таться предыдущим и наоборот.
Вот, в общем-то, и весь внут-
ренний цикл эфекта. Теперь можно
зацикливаться на пункт 1. Если в
'предыдущий' буфер поставить
точку, то она разойдется круга-
ми.
Для большей наглядности приве-
ду алгоритм в неком псевдокоде,
в основном смахивающем на Спе-
ковский Бейсик.
Предположим, что у нас есть
два буфера: COLD(M,N) и
NEW(M,N). Так будет выглядеть
процедура 'запуска кругов' в них
( для упрощения опущена проверка
значений на выход за пределы до-
пустимого диапазона ).
DO
X = INT(RND * (N - 1)) + 1
Y = INT(RND * (M - 1)) + 1
OLD (Y,X) = 15
FOR I = 2 TO M - 1
FOR J = 2 TO N - 1
A = OLD(I-1,J) + OLD(I+1,J) +
+ OLD (I,J-1) + OLD(I,J+1)
A = A/2 - NEW(I,J)
NEW(I,J) = A
NEXT J,I
NEW(M,N) -> SCREEN
SWAP OLD(M,N), NEW(M,N)
LOOP WHILE NOT KEYPRESSED
Здесь NEW(M,N) -> SCREEN - вывод
на экран ( в данном примере под-
разумеваются чанки, принимающие
значения от 0 до 15. Именно
поэтому в 'предыдущий' буфер в
качестве 'источника' кругов кла-
дется точка со значением 15 ). В
этом примере новый источник кру-
гов ставится в буфер перед каж-
дым внутреним циклом. Это дает
очень большое количество кругов
на экране и получается мельтеше-
ние... но все-равно довольно
занимательно.
У вас может возникнуть вопрос,
почему исходник работает со зна-
чениями ячеек, равными 0..31,
если чанки воспринимают только
значения 0..15 и приходится до-
полнительно конвертировать. Все
дело в том, что размер кругов
зависит от величины числа-
источника. Если ставить в ка-
честве источника число 15, то
круги получатся маленькими и не-
красивыми. Можно ставить даже 63
( так, имхо, сделано в демке
BlAME ), но мне больше всего
нравится вариант с источником,
равным 31.
Вот, вроде, и все. Напоследок,
как и обещал, расскажу, как пус-
кать круги поверх картинки. Для
этого вам, естественно, понадо-
бится буфер-картинка. Для прос-
тоты будем считать, что он равен
по размеру 'текущему' и
'предыдущему' буферам ( к тому
же, именно так обычно и бывает).
Алгоритм таков:
1) Проделываете все обычные
действия для создания кругов, но
не выводите 'текущий' буфер на
экран;
2) Теперь рассматриваются 'те-
кущий' буфер и буфер-картинка.
Для каждого элемента буфера-
картинки проводятся следующие
действия:
3) Берется значение из соот-
ветствующей ячейки из 'текущего'
буфера и из него вычитается зна-
чение ячейки справа от этой
( все так же в 'текущем'
буфере ). Получается смещение по
X;
4) Теперь берется значение
соответствующей точки из 'теку-
щего' буфера и вычитается значе-
ние точки на строку ниже. Полу-
чается смещение по Y;
5) Оба смещения делятся на ка-
кое-нибудь число. На PC в 256-
цветном эффекте лучше всего де-
лить на 8. На Спеке в чанках,
наверно, подойдет деление на 4,
хотя это все подбирается опытным
путем;
6) Берется точка в буфере-
картинке, находящаяся от текущей
на расстоянии рассчитанных сме-
щений, и ставится в текущую по-
зицию.
7) И так цикл по всем точкам.
Стоит еще упомянуть, что при
расчете позиции новой точки, ко-
торую надо ставить на место те-
кущей, необходимо проверять
координаты на выход за пределы
буфера.
Ну а теперь алгоритм в псевдо-
коде. В этом алгоритме, для
упрощения, нет вышеуказанной
проверки на выход координат за
пределы буфера. Итак, NEW(M,N) -
'текущий' буфер, SCR(M,N) - бу-
фер-картинка, OUT(M,N) - буфер,
который будет выводиться на эк-
ран.
FOR I = 2 TO M - 1
FOR J = 2 TO N - 1
XS = NEW(I,J) - NEW(I,J + 1)
YS = NEW(I,J) - NEW(I + 1,J)
XS = XS / 4
YS = YS / 4
X = J + XS
Y = I + YS
OUT(I,J) = SCR(Y,X)
NEXT J,I
OUT(M,N) -> SCREEN
Как несложно заметить, расчет
смещений очень смахивает на рас-
чет псевдо-нормали в
bumpmapping'е. Так что, здесь
мы, в некотором смысле, делаем
бампоподобное освещение картин-
ки.
Алгоритм всего эффекта кругов
поверх картинки в общих чертах
выглядит примерно так:
1) Ставится точка в 'предыду-
щий' буфер;
2) На основе 'предыдущего' и
'текущего' буферов рассчитывает-
ся изображение в 'текущем' буфе-
ре;
3) На основе 'текущего' буфера
и буфера-картинки рассчитывается
изображение в выходном буфере;
4) Выходной буфер выводится на
экран;
5) И опять на п.1 ( или п.2,
если не нужно ставить новый ис-
точник кругов).
Теперь совсем все. Надеюсь, вы
хоть что-нибудь поняли из моего
параноидального бреда. В прило-
жении вы можете найти исходник
данного эффекта на асме. В нем
вы можете управлять курсором
клавишами 6/7/8/9 и пускать кру-
ги клавишей 0. В исходнике есть
пояснения, так что у Вас еще
есть шанс разобраться на практи-
ке, если Вы до сих пор не все
поняли.
P.S.: Приведенный исходник рабо-
тает очень медленно. Как сделать
быстрее , а это можно сделать,
вам придется думать самим!