Rozdział 8
GRAFIKA GRACZY I POCISKÓW
Jedną z najważniejszych czynności wykonywanych przez
komputery domowe jest animacja. Stosowana jest ona nie tylko w
grach komputerowych, lecz także w wielu programach edukacyjnych
i użytkowych. Uzyskanie szybkiej i płynnej animacji jest jednak
bardzo trudne ze względu na sposób przechowywania danych
obrazu.
We wszystkich komputerach pamięć obrazu jest zorganizowana
liniowo. Oznacza to, że dane dla kolejnych linii obrazu są
umieszczone w pamięci jedne po drugich. Utworzenie na ekranie
niewielkiego obiektu polega na wpisaniu informacji o tym
obiekcie w różne miejsca pamięci, oddalone od siebie (rys. 18).
Przesunięcie obiektu wymaga usunięcia jego danych z pamięci
obrazu, wpisania w to miejsce danych tła i umieszczenie obiektu
w nowym miejscu. Ponieważ poszczególne fragmenty danych są od
siebie oddalone, to jest to pracochłonne i stosunkowo powolne.
Dodatkowym utrudnieniem jest to, że w trybach bitowych każdy
bajt zawiera dane dla kilku punktów obrazu i trzeba zmieniać
tylko określone bity tego bajtu. Taki sposób animacji wymaga
więc od procesora dużej liczby obliczeń, a na dodatek nie
zapewnia płynności ruchu.
pamięć obrazu
............|......XXX.......|......XXX.......|............
| |
| |
+-------+ |
| |
+----------------+ | |
|................| | |
|................| | |
|......XXX.......|<----+ |
|......XXX.......|<-------------+
|................|
|................|
+----------------+
Rys.18. Rozmieszczenie danych obrazu w pamięci
Stosowane są różne sposoby rozwiązywania tego problemu.
Najczęściej stosuje się specjalny układ scalony, który
umożliwia utworzenie tzw. ruchomych obiektów ekranowych. W
komputerach Atari XL/XE układem tym jest GTIA, a technika
uzyskiwania obiektów nazywa się grafiką graczy i pocisków
(Player/Missile Graphics - P/MG). Umożliwia ona jednoczesne
przedstawienie na ekranie czterech dużych obiektów zwanych
graczami oraz czterech małych zwanych pociskami. Większa liczba
obiektów jest dostępna poprzez zmiany parametrów P/MG przez
procedury przerwania DLI.
Zasada działania P/MG polega na umieszczeniu danych obiektu
obok siebie w pamięci, dzięki czemu ich przemieszczanie staje
się bardzo proste i, co ważniejsze, szybkie (rys. 19). Podczas
tworzenia obrazu GTIA do danych aktualnej linii ekranu dodaje
dane P/MG i w ten sposób obiekt pojawia się na ekranie.
Oczywiście jest to znacznie bardziej skomplikowane i wymaga od
komputera wykonania wielu operacji, lecz są one wykonywane
przez układy komputera i nie angażują programisty.
pamięć obrazu
............|......ooo.......|......ooo.......|............
| |
| |
pamięć P/MG | |
....XXX.... +-------+ |
| | |
+---------+ | |
| | |
v | |
+----------------+ | |
|................| | |
|................| | |
|......ooo...X...|<----+ |
|......ooo...X...|<-------------+
|............X...|
|................|
+----------------+
Rys.19. Umieszczenie danych P/MG na obrazie
Podczas tworzenia obrazu ANTIC pobiera z pamięci, oprócz
danych dla linii, także dane grafiki P/M i umieszcza je w
rejestrze grafiki GRAFP lub GRAFM znajdującym się w GTIA. Gdy
GTIA przenosi na ekran dane linii, to dodaje do nich dane z
rejestrów grafiki. W ten sposób na ekranie uzyskujemy sumę
danych obrazu i danych P/MG.
Grafika graczy i pocisków nie jest wykorzystywana przez
system operacyjny. Pomimo, iż działa ona sprzętowo, to jednak
wszystkie czynności przygotowawcze oraz poruszanie obiektów
muszą być zaprogramowane. Do tego celu konieczna jest znajomość
działania układów ANTIC i GTIA, a przede wszystkim ich
rejestrów służących do sterowania grafiką P/M.
8.1. Tworzenie P/MG
Grafika graczy i pocisków, tak jak obraz, musi posiadać
obszar pamięci, w którym będą przechowywane dane do
umieszczenia na ekranie. Wybór tego obszaru nie jest dowolny,
lecz zależy od sposobu odczytu danych przez ANTIC. Ponieważ
dostęp ANTIC-a do pamięci jest kontrolowany poprzez rejestr
DMACTL, to trzeba zacząć od niego.
Wcześniej było już opisane znaczenie bitów 0-2 i 5-7
rejestru DMACTL (str. 166). Pozostałe trzy bity sterują
dostępem do danych P/MG.
Bit 3 kontroluje dostęp ANTIC-a do obszaru pamięci dla
pocisków. Gdy jest ustawiony, to dane te są przepisywane z
pamięci do rejestru GRAFM (GRAphics For Missile).
Bit 4 kontroluje dostęp ANTIC-a do obszaru pamięci dla
graczy. Gdy jest ustawiony, to dane te są przepisywane z
pamięci do rejestrów GRAFP0-3 (GRAphics For Player 0-3).
Bit 5 ustala rozdzielczość grafiki P/MG. Gdy jest
skasowany, to dane P/MG są pobierane z pamięci podczas
tworzenia co drugiej linii ekranu. Każdy pixel obiektu ma więc
wysokość dwóch linii ekranu. Po ustawieniu bitu 5 dane są
odczytywane przy tworzeniu każdej linii, więc pixel ma wysokość
jednej linii ekranu. Nazywa się to odpowiednio rozdzielczością
dwuliniową lub jednoliniową.
8.1.1. Pamięć P/MG
Bazowy adres pamięci graczy i pocisków jest umieszczany w
rejestrze PMBASE (Player/Missile BASE - $D407). Całkowity adres
danych obiektu dla konkretnej linii ekranu jest tworzony z
trzech elementów: zawartości PMBASE, numeru gracza lub pocisku
i licznika linii ekranu. Ponieważ częstość pobierania danych z
pamięci zależy od wybranej rozdzielczości, to różny jest sposób
zestawienia adresu. Różnice te wyjaśniają rysunki 20 i 21.
PMBASE numer obiektu licznik linii
+-+-+-+-+-+-+-+-+ +-+-+-+ +-+-+-+-+-+-+-+-+
|7|6|5|4|3|2|1|0| |2|1|0| |7|6|5|4|3|2|1|0|
+-+-+-+-+-+-+-+-+ +-+-+-+ +-+-+-+-+-+-+-+-+
| | | | | |
+----+----+ +--+--+ +-------+-------+
| | |
| | |
| | |
| | |
+------+ +--+ +------+
| | |
v v v
+----+----+--+--+-------+-------+
| | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|D|E|C|B|A|9|8|7|6|5|4|3|2|1|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
adres danych obiektu
Rys.20. Adres obiektu przy rozdzielczości jednoliniowej
PMBASE numer obiektu licznik linii
+-+-+-+-+-+-+-+-+ +-+-+-+ +-+-+-+-+-+-+-+-+
|7|6|5|4|3|2|1|0| |2|1|0| |7|6|5|4|3|2|1|0|
+-+-+-+-+-+-+-+-+ +-+-+-+ +-+-+-+-+-+-+-+-+
| | | | | |
+-----+-----+ +--+--+ +------+------+
| | |
| | |
| | |
| | |
+------+ ++ +----+
| | |
v v v
+-----+-----+--+--+------+------+
| | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|E|D|C|B|A|9|8|7|6|5|4|3|2|1|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
adres danych obiektu
Rys.21. Adres obiektu przy rozdzielczości dwuliniowej
Numer obiektu jest wybierany przez ANTIC na drodze
sprzętowej według następującego schematu:
000 - kombinacja niewykorzystana
001 - kombinacja niewykorzystana
010 - kombinacja niewykorzystana
011 - odczyt danych dla pocisków
100 - odczyt danych dla gracza 0
101 - odczyt danych dla gracza 1
110 - odczyt danych dla gracza 2
111 - odczyt danych dla gracza 3
Wynika z tego, że dane poszczególnych obiektów muszą być
umieszczone w pamięci następująco:
rozdz. dwuliniowa rozdz. jednoliniowa
pociski PMBASE+$0180-PMBASE+$01FF PMBASE+$0300-PMBASE+$03FF
gracz 0 PMBASE+$0200-PMBASE+$027F PMBASE+$0400-PMBASE+$04FF
gracz 1 PMBASE+$0280-PMBASE+$02FF PMBASE+$0500-PMBASE+$05FF
gracz 2 PMBASE+$0300-PMBASE+$037F PMBASE+$0600-PMBASE+$06FF
gracz 3 PMBASE+$0380-PMBASE+$03FF PMBASE+$0700-PMBASE+$07FF
Pozostawia to wolny obszar pamięci o wielkości 384 lub 768
bajtów. Zwykle pamięć P/MG jest zabezpieczana przed ingerencją
systemu operacyjnego (przez zmianę RAMTOP), więc ten obszar
można przeznaczyć na umieszczenie procedur w języku maszynowym.
Jeżeli zostanie ustawiona rozdzielczość jednoliniowa i nie będą
wykorzystywane pociski, to pozostaje wolny obszar 1 KB, w
którym można umieścić dodatkowy zestaw znaków.
Widoczne jest także ograniczenie dla wartości PMBASE.
Ponieważ nie wszystkie bity tego rejestru są wykorzystywane do
tworzenia adresu obiektu, to PMBASE musi wskazywać początek
bloku 1 KB przy rozdzielczości dwuliniowej i 2 KB przy
jednoliniowej.
Obiekty mogą też być tworzone na ekranie bez pośrednictwa
ANTIC-a i bez korzystania z opisanego wyżej obszaru pamięci.
Trzeba w tym celu wpisać bajt wzoru bezpośrednio do rejestru
grafiki GRAFM lub GRAFP. Każdy rejestr GRAFP odpowiada bajtowi
danych gracza, zaś w rejestrze GRAFM każdemu pociskowi
odpowiadają dwa bity. Umieszczone w ten sposób dane będą
wyświetlane na całym ekranie, otrzymamy więc pionowy pas.
Zmienić zawartość rejestrów grafiki, tak aby uzyskać widoczny
efekt na ekranie, można tylko podczas tworzenia obrazu, czyli
przy pomocy procedury przerwania DLI.
Niezależnie od sposobu zapisywania danych w rejestrach
grafiki (bezpośrednio lub przez ANTIC) należy jeszcze
zasygnalizować układowi GTIA, że dane z tych rejestrów mają być
przeniesione na ekran. Do tego celu służą dwa najmłodsze bity
rejestru PMCTL (Player/Missile ConTroL - $D01D). Bit 0 steruje
pociskami, a bit 1 graczami. Ustawienie tych bitów powoduje
dodawanie danych gracza lub pocisku do danych tworzonej linii.
8.1.2. Wielkość i kolor obiektów
Kolejny wniosek dotyczy efektu uzyskiwanego na ekranie.
ANTIC przenosi po jednym bajcie danych obiektu dla każdej
tworzonej linii ekranu. Oznacza to, że można uzyskać obiekt o
wysokości całego ekranu i szerokości jednego bajtu (gracz) lub
dwóch bitów (pocisk). Szerokość wyrażona w bitach nic jeszcze
nie mówi o rzeczywistej szerokości obiektu. Każdy bit
reprezentuje jeden pixel obrazu. Pixel ten jest niezależny od
aktualnego trybu obrazu.
Wysokość pixela jest wyznaczana przez wybraną rozdzielczość
i jest jednakowa dla wszystkich obiektów. Szerokość pixela jest
ustalana przez zawartość rejestru SIZEM lub SIZEP. W rejestrach
SIZEP0-3 (SIZE Player 0-3) szerokość określają dwa najmłodsze
bity (pozostałe są niewykorzystane). W rejestrze SIZEM (SIZE
Missiles) każda para bitów określa szerokość pixeli
odpowiedniego pocisku: bity 0 i 1 - pocisk 0, bity 2 i 3 -
pocisk 1, bity 4 i 5 - pocisk 2 oraz bity 6 i 7 - pocisk 3.
Szerokość pixeli jest wyznaczana według schematu:
para bitów 00 - pixel o szerokości 1 cyklu koloru
para bitów 01 - pixel o szerokości 2 cykli koloru
para bitów 10 - pixel o szerokości 1 cyklu koloru
para bitów 11 - pixel o szerokości 4 cykli koloru
Maksymalna szerokość gracza wynosi więc 6*4=32 cykle koloru.
Jest to 1/5 normalnej szerokości obrazu. Przy użyciu czterech
graczy i czterech pocisków można całkowicie pokryć obraz.
Wygląd obiektu na ekranie jest określany przez jego dane.
Każdy bajt danych określa jedną linię pixeli. Natomiast wygląd
poszczególnych pixeli określają bity tego bajtu. Skasowany bit
oznacza przezroczysty pixel, czyli pixel w takim kolorze jaki
znajduje się w tym miejscu na ekranie. Bit ustawiony powoduje
nadanie odpowiadającemu mu pixelowi koloru z rejestru COLPM0-3
(COLor of Player/Missile 0-3). Kolory te są identyczne parami,
to znaczy gracz 0 i pocisk 0 mają kolor z rejestru COLPM0,
gracz 1 i pocisk 1 z rejestru COLPM1 itd.
Gdy obiekt znajduje się na tle obrazu, nie ma żadnych
kłopotów. Problem pojawia się dopiero wtedy, gdy obraz zawiera
jakąś treść. Chodzi o to, czy obiekt będzie zasłaniał zawartość
obrazu, czy odwrotnie. Nazywamy to priorytetem - kolor o
wyższym priorytecie będzie zasłaniał kolory o niższym
priorytecie. Do określenia kolejności kolorów służy rejestr
GTIACTL (GTIA ConTroL - $D01B). Jego bity 0-3 ustalają
2następujące priorytety kolorów:
bit 0 bit 1 bit 2 bit 3
COLPM0 COLPM0 COLPF0 COLPF0
COLPM1 COLPM1 COLPF1 COLPF1
COLPM2 COLPF0 COLPF2 COLPM0
COLPM3 COLPF1 COLPF3 COLPM1
COLPF0 COLPF2 COLPM0 COLPM2
COLPF1 COLPF3 COLPM1 COLPM3
COLPF2 COLPM2 COLPM2 COLPF2
COLPF3 COLPM3 COLPM3 COLPF3
COLBAK COLBAK COLBAK COLBAK
Ustawienie jednego z tych bitów ustala określone przez
niego priorytety. Ustawienie więcej niż jednego bitu powoduje
niejednoznaczne określenie priorytetów. W takim przypadku, gdy
obszary o jednakowym priorytecie będą się pokrywały, to
uzyskają one kolor czarny. Przy ustalaniu priorytetów trzeba
pamiętać, że w niektórych trybach ANTIC-a (2, 3 i F) z rejestru
COLPF1 jest pobierana tylko jasność pixela. W tych trybach
wszystkie pixele obrazu mają priorytet taki jak COLPF2,
niezależnie od aktualnego priorytetu COLPF1.
Przy tworzeniu P/MG wykorzystywane są jeszcze dwa bity
rejestru GTIACTL (4 i 5). Ustawienie bitu 4 powoduje nadanie
wszystkim pociskom koloru określonego rejestrem COLPF3. Można w
ten sposób uzyskać piątego gracza. Każda część takiego gracza
jest jednak niezależna - może być poruszana oddzielnie i mieć
różną szerokość.
Bit 5 wywołuje po ustawieniu zmianę priorytetów par graczy
0-1 i 2-3. Każdy gracz z takiej pary ma kolor określony
odpowiednim rejestrem. Gdy pixele graczy pokrywają się, to
kolor wynikowy jest otrzymywany poprzez operację OR wykonaną na
bitach kolorów (np. dla graczy 0 i 1 - COLMP0 ORA COLMP1).
Także w tym przypadku pozostałe parametry graczy są ustalane
odrębnie.
8.1.3. Umieszczenie obiektu na ekranie
Teraz nadeszła już pora przeniesienia obiektu na ekran.
Podczas tworzenia kolejnych linii ekranu GTIA odczytuje
zawartość rejestrów grafiki GRAFP0-3 i GRAFM i dodaje ją do
danych linii. W ten sposób pionowa pozycja obiektu na ekranie
odpowiada dokładnie położeniu danych tego obiektu w 128- lub
256-bajtowym obszarze pamięci P/MG.
Wybranie rozdzielczości dwuliniowej uniemożliwia jednak
umieszczenie obiektu na ekranie z dokładnością jednej linii
ekranu. Aby usunąć tą niedogodność w GTIA został przewidziany
rejestr VDELAY (Vertical DELAY - $D01C). Jeżeli zawarty w nim
bit jest skasowany, to odpowiadający mu obiekt jest umieszczany
na ekranie tak, jak to zostało wcześniej opisane. Natomiast
ustawienie bitu powoduje opóźnienie o jedną linię przesyłania
danych z rejestrów grafiki na ekran. Dzięki temu cały obiekt
jest przesunięty o jedną linię ekranu w dół. Bity rejestru
VDELAY odpowiadają kolejno pociskom od 0-3 i graczom 0-3:
bit 0 - pocisk 0
bit 1 - pocisk 1
bit 2 - pocisk 2
bit 3 - pocisk 3
bit 4 - gracz 0
bit 5 - gracz 1
bit 6 - gracz 2
bit 7 - gracz 3
GTIA musi jeszcze wiedzieć, w którym miejscu linii ma
umieścić dane obiektu. Do tego celu służy osiem rejestrów
poziomej pozycji obiektów - HPOSP0-3 (Horizontal POSition of
Player 0-3) i HPOSM0-3 (Horizontal POSition of Missile 0-3).
Każdy z nich zawiera numer cyklu koloru, w którym rozpoczyna
się przenoszenie na ekran danych odpowiedniego obiektu.
Aby obiekt był widoczny na ekranie, jego pozycja nie może
być dowolna, lecz musi mieścić się w pewnych granicach. Dla
pionowej pozycji granice te zależą od przyjętej rozdzielczości.
Przy rozdzielczości jednoliniowej w obrębie obrazu mieszczą się
pozycje od 32 do 224. Dla rozdzielczości dwuliniowej są to
pozycje od 16 do 112.
Znacznie większe jest zróżnicowanie pozycji poziomych -
zależą one bowiem od szerokości obiektów. Wartości te zawiera
poniższa tabela.
obiekt całkowicie: na obrazie poza obrazem
granica od strony: lewej prawej lewej prawej
szerokość:
pojedyncza - gracz 48 200 40 208
- pocisk 48 206 46 208
podwójna - gracz 48 192 32 208
- pocisk 48 204 44 208
poczwórna - gracz 48 176 16 208
- pocisk 48 200 40 208
Na zakończenie tego opisu krótka procedura umieszczająca na
ekranie gracza 0.
0100 ;Player 0 routine
0110 ;
0120 COLPM0S = $02C0
0130 DMACTLS = $022F
0140 GTICTLS = $026F
0150 HPOSP0 = $D000
0160 PMBASE = $D407
0170 PMCTL = $D01D
0180 PMSTRT = $9000
0190 SIZEP0 = $D008
0200 ;
0210 *= $9000
0220 ;
0230 LDX #$00
0240 LOOP LDA SHAPE,X
0250 STA PMSTRT+$0400+VPOS,X
0260 INX
0270 CPX #$19
0280 BNE LOOP
0290 LDA # >PMSTRT
0300 STA PMBASE
0310 LDY #$02
0320 STY PMCTL
0330 DEY
0340 STY GTICTLS
0350 DEY
0360 STY SIZEP0
0370 LDA HPOS
0380 STA HPOSP0
0390 LDA #$0E
0400 STA COLPM0S
0410 LDA #$3A
0420 STA DMACTLS
0430 RTS
0440 ;
0450 VPOS .BYTE $78
0460 HPOS .BYTE $7A
0470 SHAPE .BYTE $00,$00,$0C,$18
0480 .BYTE $38,$7C,$54,$7C
0490 .BYTE $28,$38,$38,$3C
0500 .BYTE $7E,$FB,$B9,$38
0510 .BYTE $38,$3C,$1C,$1C
0520 .BYTE $0E,$06,$03,$00,$00
8.2. Przemieszczanie obiektów
Po tym, co zostało napisane w poprzednim rozdziale, łatwo
już zrozumieć sposób przemieszczania obiektu po ekranie. Trzeba
jednak wspomnieć o kilku ograniczeniach i dodatkowych
możliwościach.
Rejestry pozycji poziomej obiektów, jak prawie wszystkie
rejestry GTIA, są jednokierunkowe - w tym przypadku można
jedynie do nich zapisywać. Próba odczytu da nam w efekcie
wartość zupełnie bez sensu. Jeśli zatem zachodzi potrzeba
zmiany pozycji poziomej obiektu o pewną wartość, to trzeba
aktualną zawartość rejestru HPOS... przechowywać w dodatkowym
rejestrze RAM. Jest to uwidocznione w zamieszczonych
przykładach.
Istnienie rejestrów pozycji poziomej umożliwia uzyskanie
praktycznie dowolnej liczby obiektów na ekranie. Jest tylko
jedno ograniczenie - w jednej linii ekranu nigdy nie można
umieścić więcej niż czterech graczy i cztery pociski.
Zwielokrotnienie obiektu wykonuje się przez zapisanie w jego
polu pamięci danych dla kilku obiektów. Wszystkie te obiekty
będą przy tym położone jeden nad drugim i będą się poruszały
razem. Do ich usamodzielnienia należy wykorzystać najczęściej
wymienianą w tej książce procedurę - przerwanie DLI. Podczas
procedury, która je obsługuje można wpisać do rejestru pozycji
nową wartość i następny obiekt znajdzie się w zupełnie innym
miejscu. Trzeba jednak pamiętać o jednym - rejestry HPOS... nie
są odnawiane podczas przerwania VBLK. Należy więc użyć tej
procedury dwukrotnie, a w przypadku większej liczby obiektów -
tyle razy, ile obiektów znajduje się w jednym polu danych P/MG.
Więcej kłopotu sprawia przemieszczanie w pionie. Tu
konieczne jest przepisywanie danych wewnątrz obszaru pamięci
dla danego obiektu. Ponieważ obszar taki nigdy nie przekracza
jednej strony pamięci, to napisanie odpowiedniej procedury jest
bardzo proste. Dla przesunięcia zawartości całego pola pamięci
o jeden bajt w górę przy rozdzielczości jednoliniowej wystarczy
taka procedura:
0100 ;Move Up
0110 ;
0120 LDX #$01
0130 LOOP LDA PLAYER,X
0140 STA PLAYER-1,X
0150 INX
0160 BNE LOOP
Podobna procedura przesuwa pole pamięci o jeden bajt w dół.
0100 ;Move Down
0110 ;
0120 LDX #$FF
0130 LOOP LDA PLAYER-1,X
0140 STA PLAYER,X
0150 DEX
0160 BNE LOOP
Dla rozdzielczości dwuliniowej należy tylko zamienić
rozkazy BNE LOOP na BPL LOOP i w drugiej procedurze rozkaz
LDX #$FF na LDX #$7F.
Gdy przemieszczenie ma dotyczyć tylko części pola (np. przy
wykorzystywaniu go dla kilku obiektów), to trzeba wprowadzić
ograniczenia pętli LOOP. Zwiększa to długość procedury, lecz
skraca czas jej wykonywania. Przykład takiej procedury jest
pokazany dalej.
Przy wykorzystywaniu rozdzielczości dwuliniowej
przesunięcie obiektu o jeden bajt powoduje przemieszczenie
obiektu o dwie linie ekranu. Czasami może to wywoływać wrażenie
niezbyt płynnego ruchu. Można to ominąć poprzez naprzemienne
kasowanie i ustawianie odpowiedniego bitu w rejestrze VDELAY
oraz przesuwanie obiektu. Uzyskuje się wtedy jeszcze większą
płynność ruchu.
Teraz dalszy ciąg procedury pokazanej w poprzednim
rozdziale (należy to po prostu dopisać do niej usuwając rozkaz
RTS). Przesuwa ona utworzony obiekt w zależności od położenia
joysticka (w celu praktycznego wykorzystania trzeba ją dołączyć
do programu i nieco zmienić zakończenie, aby nie działała w
pętli bez końca).
0530 ;Player 0 Movement
0540 ;
0550 JOY0 = $0278
0560 ;
0570 MOVE LDA JOY0
0580 EOR #$FF
0590 AND #$0F
0600 BEQ MOVE
0610 CLC
0620 ROR A
0630 BCC E1
0640 LDX #$01
0650 LP1 LDA PMSTRT+$0400,X
0660 STA PMSTRT+$03FF,X
0670 INX
0680 BNE LP1
0690 BEQ MOVE
0700 E1 ROR A
0710 BCC E2
0720 LDX #$FF
0730 LP2 LDA PMSTRT+$03FF,X
0740 STA PMSTRT+$0400,X
0750 DEX
0760 BNE LP2
0770 BEQ MOVE
0780 E2 ROR A
0790 BCC E3
0800 DEC HPOS
0810 JMP HP
0820 E3 INC HPOS
0830 HP LDA HPOS
0840 STA HPOSP0
0850 JMP MOVE
8.3. Zderzenia między obiektami
Dodatkowym zyskiem z zastosowania grafiki graczy i pocisków
jest możliwość sprzętowego wykrywania kolizji. Są to przypadki
zetknięcia się poszczególnych obiektów między sobą oraz z
elementami obrazu (zwanego tu polem gry), które mają kolor inny
niż kolor tła. Ponieważ jest to działanie sprzętowe (wbudowane
w strukturę układu GTIA), to użytkownik musi jedynie sprawdzić
w odpowiednim rejestrze, czy takie zetknięcie wystąpiło i
odpowiednio zareagować.
Można w ten sposób wykrywać wszelkie spotkania obiektów,
bez przeprowadzania żmudnych obliczeń sprawdzających, czy dwa
pixele należące do różnych obiektów zajmują to samo miejsce na
ekranie. W tym celu należy jedynie zbadać zawartość jednego z
rejestrów kolizji.
Tych rejestrów kolizji jest w układzie GTIA aż szesnaście.
Można je podzielić na cztery grupy: rejestry kolizji pocisków z
polem gry (KOLMPF), graczy z polem gry (KOLPPF), pocisków z
graczami (KOLMP) i graczy z graczami (KOLPP). Niemożliwe jest w
ten sposób jedynie wykrycie kolizji pocisku z innym pociskiem.
We wszystkich wymienionych rejestrach do wykrywania kolizji
służą bity 0-3, a bity 4-7 są niewykorzystane. Normalnie
wszystkie bity są skasowane. Ustawienie bitu oznacza
zaistnienie kolizji obiektu, do którego należy ten rejestr, z
odpowiednim obiektem lub kolorem pola gry.
Znaczenie poszczególnych bitów w rejestrach wskazujących
kolizje obiektów z polem gry (KOLM0-3PF - KOLlision Missile 0-3
to Play Field i KOLP0-3PF - KOLlision Player 0-3 to Play Field)
jest następujące:
bit 0 - kolizja z obszarem o kolorze z COLPF0
bit 1 - kolizja z obszarem o kolorze z COLPF1
bit 2 - kolizja z obszarem o kolorze z COLPF2
bit 3 - kolizja z obszarem o kolorze z COLPF3
Podobnie w rejestrach wskazujących kolizje obiektów z
innymi obiektami (KOLM0-3P - KOLlision Missile 0-3 to Player i
KOLP0-3P - KOLlision Player 0-3 to Player) poszczególne bity
oznaczają:
bit 0 - kolizja z graczem 0
bit 1 - kolizja z graczem 1
bit 2 - kolizja z graczem 2
bit 3 - kolizja z graczem 3
W przypadku rejestrów kolizji graczy z graczami (KOLPP) bit
odpowiadający graczowi, do którego należy ten rejestr jest
zawsze skasowany. Na przykład w rejestrze KOLP0P zawsze
skasowany jest bit 0, a w KOLP1P bit 1.
Wykrycie kolizji i ustawienie odpowiedniego bitu następuje
zawsze dopiero po wyświetleniu linii na ekranie. Aby uniknąć
pomyłek najlepiej wszystkie kolizje sprawdzać dopiero po
utworzeniu całego obrazu, czyli podczas przerwania VBLK. Jeżeli
procedura wywoływana przez zderzenie obiektów jest bardzo
długa, to można zastosować przepisywanie zawartości rejestru
kolizji do określonego miejsca w pamięci RAM. Jeżeli nie jest
istotne natychmiastowe wykrycie kolizji, to sprawdzenie można
wykonać w dowolnej chwili, gdyż rejestry kolizji nie są
kasowane po przerwaniu zetknięcia.
No tak! Rejestry kolizji można tylko odczytywać, a bity
wskazujące kolizje nie są kasowane. Z tego wynika, że są to
rejestry jednorazowego użytku. Po wykryciu kolizji trzeba
wyłączyć komputer i uruchomić ponownie, aby wykryć następne
spotkanie tych samych obiektów. To jakie to ułatwienie? Na
szczęście nie jest tak źle. Wszystkie rejestry kolizji są
kasowane przez wpisanie dowolnej wartości do rejestru HITCLR
(HIT CLeaR - $D01E). Rejestr ten nie służy ponadto do niczego,
więc jest obojętne, co zostanie w nim zapisane.
Procedura wykrywania kolizji między obiektami powinna
kolejno sprawdzać wszystkie konieczne rejestry kolizji i
wywoływać odpowiednie części programu lub odczytane wartości
przepisywać do innych komórek. Następnie musi zapisać dowolną
wartość do HITCLR, aby umożliwić wskazanie następnych kolizji i
już... może się powtarzać od początku. A oto przykład:
0100 ;Player 0 Collisions Detection
0110 ;
0120 HITCLR = $D01E
0130 KOLP0PF = $D004
0140 KOLP0P = $D00D
0150 ;
0160 *= $0600
0170 ;
0180 LDA #$00
0190 LDX #$07
0200 LOOP STA PF0,X
0210 DEX
0220 BPL LOOP
0230 LDY #$FF
0240 LDX #$00
0250 LDA KOLP0PF
0260 AND #$0F
0270 CLC
0280 LP1 ROR A
0290 BCC NX1
0300 STY PF0,X
0310 NX1 INX
0320 CPX #$04
0330 BNE LP1
0340 LDA KOLP0P
0350 AND #$0F
0360 CLC
0370 LP2 ROR A
0380 BCC NX2
0390 STY PF0,X
0400 NX2 INX
0410 CPX #$08
0420 BNE LP2
0430 STY HITCLR
0440 RTS
0450 ;
0460 PF0 .BYTE $00,$00,$00,$00
0470 PL0 .BYTE $00,$00,$00,$00
|