Из журнала Virtual Worlds #1 Дзержинск, 2000 +----------------------------+ | -------- 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.: Приведенный исходник рабо- тает очень медленно. Как сделать быстрее , а это можно сделать, вам придется думать самим!