Plazma (shade bobs)
-- Sebastian Pawlak, 1995.
Jeżeli ktoś chce napisać grę lub zakodować demo musi
liczyć się z tym, że aby jego produkt był naprawdę dobry
powinien zawierać jakieś ciekawe efekty graficzne.
Często stosuje się skomplikowane skrolingi tekstu, rotacje
obrazu, projekcję obiektów 3D oraz wiele innych. Jednak
najczęściej spotkać można efekt tzw. "plazmy". Nie można
tu podać żadnej konkretnej definicji jak powinna wyglądać
plazma. U niektórych autorów przypomina ona mgłę, a u
innych wzburzoną kałużę z olejem samochodowym. To tylko
dwa z prostszych do opisania słowami efektów, których
można stworzyć dziesiątki. Osobiście nie widziałem dwóch
identycznych plazm, chodź bywały one niekiedy podobne.
Program, który napisałem jest bardzo prosty, a plazma
powstająca w wyniku jego działania dość efektowna.
W tym momencie mam wątpliwości czy to coś jest w ogóle
plazmą. Niektórzy stwierdziliby, że tak a inni, że nie.
Zdania na ten temat byłyby podzielone.
Przejdźmy teraz do opisu algorytmu.
Opiera się on na przesuwaniu po ekranie kwadratu, który
podczas kreślenia dla każdego swojego punktu wyświetla
piksel o wartości odpowiadającej mu komórki pamięci ekranu
zwiększonej o jeden. Dla przykładu, gdybyśmy cały czas
odbijali nasz kwadrat w tym samym miejscu to wartość
każdego punktu ekranu w obrębie wyświetlanego obiektu
była by: 0, 1 , 2 , 3 , ... , 255 , 0 , 1 , 2 , ... .
Kwadrat przesuwany jest o losowo wybraną liczbę dla osi
X i Y.
Oto, program do wpisania i uruchomienia:
(* "PLAZMA" *) (* autor: Sebastian Pawlak *) (* marzec 1995 r. *) Program Plazma; Uses Crt; Var X_Kwadr,Y_Kwadr:integer; {Pozycja kwadratu.} Nr_Kol:byte; {Numer ustawianego koloru.} Const Bok:byte=50; {Wielkość boku kwadratu.} {Kreśli specjalny kwadrat do plazmy.} Procedure Kwadrat (Poz_X,Poz_Y:word;Bok:byte); Assembler; Asm Mov ax,0a000h {Ustawienie rejestru ES na segment pamięci} Mov es,ax {ekranu (musi być ustawiany za każdym razem,} {ponieważ np. procedura Delay również wykorzystuje ES i} {po jej wstawieniu bez ustawiania ES komputer może się} {zawiesić).} {Obliczanie adresu odpowiadającego pozycji Poz_X, Poz_Y} {na ekranie, Poz_na_ekr=Poz_Y*Szerokość_ekr+Poz_X.} Mov bx,Poz_Y {Rejestr bx zawiera pozycje Y.} Mov cl,6 {Rejestr cl zawiera wartość 6.} Shl bx,cl {Mnożenie pozycji Y przez 64.} Mov cx,bx {Rejestr cx zawiera wynik mnożenia Y*64.} Shl bx,1 {Mnożymy wysokość Y przez 2, i przez 2, gdyż} Shl bx,1 {jest to szybsze niż mnożenie przez 4.} Add bx,cx {BX=BX+CX lub BX=Poz_Y*64*2*2+Poz_Y*64.} Add bx,Poz_X {BX=BX+Poz_X lub BX=Poz_Y*256+Poz_Y*64+Poz_X.} {Od tej chwili rejestr BX zawiera pozycje w pamięci ekr.} {odpowiadającą lewemu górnemu rogowi kwadratu.} Mov dx,word ptr [Bok] {Rejestr DX zawiera wielkość boku.} Mov cx,dx {CX zawiera DX (Bok).} @Nastepny_wiersz: Push cx {Odkłada na stosie wartość rej. CX.} Mov cx,dx {Rejestr CX zawiera DX (Bok).} @Nastepny_piksel_wiersza: Mov al,es:[bx] {Pobiera wartość piksela z pamięci.} Inc al {Zwiększa wartość tego piksela o jeden.} Mov es:[bx],al {Zapisuje tę wartość na to samo miejsce.} Inc bx {Zwiększa pozycję w pamięci o jeden bajt.} Loop @Nastepny_piksel_wiersza {Następny punkt wiersza.} Sub bx,word ptr [Bok] {Ustawienie pozycji w pamięci} Add bx,320 {dla następnego wiersza, poprzez:} {odjęcie od ostatniej pozycji wartości boku i dodanie} {szerokości ekranu.} Pop cx {Podniesienie ze stosu wartości pozycji Y dla} {kolejnego wiersza kwadratu.} Loop @Nastepny_wiersz {Następny wiersz.} End; {Ustawia barwę dla danego koloru.} Procedure SetRgbPalette (Nr_Kol,R,G,B:byte); Assembler; Asm Mov bx,word ptr [Nr_Kol] {BX zawiera numer koloru.} Mov dh,R {Poszczególne wartości składowych barwy.} Mov ch,G Mov cl,B Mov ah,10h {Funkcja 10h - ustawienie RGB dla palety.} Mov al,10h {Podfunkcja 10h - wartości dla jednego koloru.} Int 10h {Wywołanie przerwania 10h.} End; Begin Asm Mov ax,13h {Inicjuje tryb graficzny 13h, 320x200 256 kol.} Int 10h End; {Ustawianie barw kolorów w palecie.} For Nr_Kol:=0 to 85 do SetRgbPalette (Nr_Kol,0,Nr_Kol div 2,0); For Nr_Kol:=86 to 169 do SetRgbPalette (Nr_Kol,(Nr_Kol-86) div 2,0,0); For Nr_Kol:=170 to 255 do SetRgbPalette (Nr_Kol,0,0,(Nr_Kol-170) div 2); Randomize; {Inicjuje działanie generatora liczb losowych.} Repeat {Pozycja X i Y kwadratu jest zwiększana albo zmniejszana} {o losową wartość.} X_Kwadr:=X_Kwadr+random (20)-10; Y_Kwadr:=Y_Kwadr+random (20)-10; {Jeżeli pozycja kwadratu jest większa niż 200-Bok lub} {mniejsza od 0 wtedy odpowiednio zmniejsza albo zwiększa} {jej wartość.} if X_Kwadr>200-Bok then X_Kwadr:=X_Kwadr-(200-Bok); if Y_Kwadr>200-Bok then Y_Kwadr:=Y_Kwadr-(200-Bok); if X_Kwadr<0 then X_Kwadr:=200-Bok+X_Kwadr; if Y_Kwadr<0 then Y_Kwadr:=200-Bok+Y_Kwadr; {Wyświetla przesunięty od 60 pikseli kwadrat.} Kwadrat (X_Kwadr+60,Y_Kwadr,Bok); Until KeyPressed; {Wyświetla kwadraty do czasu naciśnięcia} {dowolnego klawisza.} Asm Mov ah,0ch {Czyszczenie bufora klawiatury po ostatniej} mov al,0 {funkcji KeyPressed, gdyż dalej też jest} int 21h {testowana zawartość bufora klawiatury.} End; {Teraz wyświetlany jest jeden duży kwadrat, tam gdzie były} {wyświetlane poprzednio małe obiekty, co daje efekt} {"rozchodzenia" się kolorów.} repeat Kwadrat (60,0,200); {Wyświetla wycentrowany duży kwadrat.} until KeyPressed; {Wyświetla duży kwadrat do momentu} {naciśnięcia dowolnego klawisza.} Asm Mov ax,3h {Inicjuje tryb tekstowy 3H, 80x25 16 kol.} Int 10h End; End.
Bez kłopotów można modyfikować program: - zmieniać wielkość boku kwadratu, - trochę zmieniony efekt uzyskuje się zwiększając parametr w funkcji Random, aby ruchy kwadratu stały się bardziej swobodne, - można też ingerować w barwy poszczególnych kolorów, eksperymentując z parametrami procedury SetRgbPalette.
Mam nadzieję, że trud wpisania programu opłaci się, a algorytm znajdzie zastosowanie w jakichś pracach.
Literatura: 1. "Turbo Assembler Biblia użytkownika" - Gary Syck, 2. "Jak pisać Wirusy" - Andrzej Dudek.