Trzecia gra na konkurs by Kaz 2008-12-09 23:19:25

Myślałem, że to mnie i Scalakowi się uda opublikować jedną z pierwszych gier w konkursie, a tu proszę - klops. Już trzecia gra staje w szranki, a my dalej w lesie... dosłownie i w przenośni :). Autorem dzisiejszej gry pod tytułem ATASCII Squash jest Paweł "Sikor" Sikorski.

Gra polega na odbijaniu piłki o ścianę za pomocą paletki, czyli nawiązuje do największych hitów lat 80-tych jak "Pong" czy "Arkanoid". Oczywiście tu mamy do czynienia ze znacznie prostszą wersją odbijania, ale na starcie też nie jest łatwo i trzeba się wykazać refleksem. Program zlicza liczbę odbić i pozwala ustanawiać rekordy. Plik z grą w katalogu.



Parę słów od autora: "Niestety, jestem na wyjeździe, więc nic dopieszczonego. Ale skoro ma być zabawa, to czemu nie podesłać? Troszkę informacji technicznych:

Kaz 2008-12-10 00:01:44

Oraz zalaczam opis Larka w sprawie uzywania we wlasnej grze duszkow (grafiki PMG), moze sie komus przyda:

"Turbo Basic nie ma niestety specjalnych instrukcji związanych z grafiką PMG, ale jest to naprawdę bardzo łatwe. Choć łatwe nie oznacza, że proste ;-)

Co trzeba zrobić, żeby ustawić duszki i zmieniać im kolor? Wpisać w rejestry położenia poziomego odpowiednie wartości, a w inne rejestry, odpowiedzialne za kolor duszków, odpowiednie wartości kolorów. Jak już duszki (statyczne) ustawisz, to one sobie będą tam stać. Nie ma potrzeby ciągłej ich kontroli.

Po pierwsze proponuję skorzystać z książki ATARI BASIC Miguta. Masz taką książkę w swojej bibliotece. Na stronie 46 i 47 wydania w niebieskiej okładce (w innych nie wiem, ale pewnie to samo) jest krok po kroku wyjaśnione, co należy zrobić, aby na ekranie pojawił się duszek. Jest tam przedstawionych osiem prostych kroków, które należy uczynić i naprawdę jest to tak opisane, że nawet ja kiedyś to pojąłem. Wybaczcie, że odsyłam do lektury, ale tłumaczenie, jak się to robi polegałoby po prostu na przepisaniu tego, co jest w tej książce.

Postaram się oczywiście bardzo ogólnie zarysować sposób postępowania, a bardziej zwrócić uwagę na pewne rzeczy.

1. Trzeba zarezerwować w pamięci specjalny obszar dla PMG. W przypadku wysokiej rozdzielczości (wysokość poziomej 1 linii duszka = wysokość 1 linii skaningowej /1 linia w gr.15/) to musimy mieć na to aż 2KB. Przy niższej rozdzielczości (wysokość linii duszka = 2 linie skaningowe /2 linie gr.15
lub inaczej - 1 linia gr.7/) wystarczy nam obszar wielkości 1KB
Adres początku tego obszaru musi być podzielny przez 256.

2. Duszki na ekranie są wyświetlane w postaci paska z góry na dół o szerokości 8 pikseli, tj. 1 bajt (szerokość można, jak pewnie wiesz, zwiększyć poprzez rozciągnięcie tych ośmiu pikseli w poziomie). Dane duszka są ułożone w pamięci RAM jeden po drugim, czyli np. dane pierwszej linii duszka są w pamięci pod adresem X. Pod adresem X+1 jest bajt reprezentujący drugą linię duszka. X+2 - trzecia linia duszka, itd., aż do 255, bo każdy z duszków (wysokiej rozdzielczości) może mieć wysokość 256 linii, czyli zajmuje w pamięci 256 bajtów. I tak kolejno 4 duszki. Są jeszcze pociski, ale na razie pomijamy to.

Rejestry Atari kontrolują tylko położenie poziome duszka, czyli poprzez wpisanie do jednej komórki pewnej wartości (0-255) możemy przesuwać ten nasz pionowy pasek w lewo lub w prawo (nawet poza ekran). Nie ma czegoś takiego, jak położenie pionowe duszka. Jego fizyczne umieszczenie na ekranie (w pionie) zależy od tego, w którym miejscu pamięci pojawią się dane. Jeśli dane umieścimy w początkowych komórkach, to obraz pojawi się wyżej. Jeśli dane będą w środkowej części tych 256 bajtów, to obraz pojawi się w środku. Jeśli w końcowej części, to na dole ekranu. Ruch w pionie polega na przerzucaniu danych duszka bliżej lub dalej od początku obszaru duszków (w pamięci RAM), co objawia się na ekranie tym, że dane pojawiają się wyżej lub niżej (bo pamiętamy, że duszek to pasek od góry do dołu o szerokości 1 bajta). W programie, jeśli duszki miałyby mieć wysokość np. 80 linii, dane zajmowałyby 80 bajtów i byłyby umieszczone w pewnej odległości (=wysokości) od początku obszaru duszka(-ów). Jeśli ma to być pełne wypełnienie (bez specjalnych kształtów), to będzie to kolejne 80 bajtów zapełnione wartością 255 (=obsadzone wszystkie bity/piksele/ w danym bajcie). Jest tu dokładnie to samo, co mamy w znakach (fontach) - każdy bit w tym bajcie odpowiada za 1 piksel na ekranie. Tyle, że znak ma 8 linii (bajtów) wysokości, a duszek 256 (lub 128).

Ustawienie kolorów i położenia poziomego jest realizowane przez zwykłe POKE. I jest to robione tylko raz, chyba że chcemy zmienić pozycję duszka. Zmianę kolorów duszka robi się dokładnie w ten sam sposób, co zmianę koloru np. ramki ekranu.

Jak już ustalimy obszar i kształt duszków, to wystarczy poinformować system o tym, że chcemy włączyć duszki, podajemy ich rozdzielczość, priorytet i takie tam inne ważne dane i w zasadzie to wszystko.

Bez obaw zajrzyjcie do książki, tam naprawdę jest to bardzo prosto opisane. Gdybyście mieli już jakieś konkretne pytania, to pytajcie. Chętnie odpowiem. Ogólny opis uruchomienia duszków wymaga sporo pisania i tak, jak wspomniałem musiałbym przepisać dwie strony z książki...

Jako ciekawostkę dodam, że w grze PARACHUTE drzewa i ląd (to, co na ekranie jest brązowe), to właśnie takie statyczne duszki. Dlatego w grze mamy 5 kolorów, a nie tylko 4 (gra jest w graficznym trybie 15). W sumie, to mogłem lewe drzewo i ziemię zrobić w innym kolorze, a prawe w innym i byłoby 6
kolorów. Że też o tym nie pomyślałem... :-). O ile pamiętam, to są wykorzystane cztery duszki i jeden pocisk. Ustawienie parametrów następuje zaraz na początku programu. Ja akurat wybrałem metodę wczytania kształtów duszków z pliku zewnętrznego (PMG.PMG), ale na początku dane te wprowadzałem ręcznie poprzez pętle FOR-NEXT i READ, POKE z danych w liniach DATA. Później zapisałem cały 2KB obszar pamięci PMG do pliku i było po sprawie. Możecie podejrzeć :-)

Rzuciłem okiem na kod do PARACHUTE. Cała obsługa duszków znajduje się liniach 1019-1023. Do tego obszar 2KB od adres $9000, do którego wczytywane są dane (kształt) dyszków z pliku zewnętrznego (w linii 1014 - plik PMG.PMG) i to cała tajemnica duszków. Jedynie kilka Poke'ów ;-)

Acha, jeszcze jedno. Przy włączonych duszkach, jeśli program wykona instrukcję GRAPHICS, to mamy kaszankę na ekranie. Po zmianie trybu graficznego (nawet na ten sam) musimy wskazać jeszcze raz systemowi nasz obszar pamięci duszków i chyba kilka innych danych."

Tyle z maili od Arka - dokonalem ogolnej kompilacji.

Kaz 2008-12-10 00:15:01

I link od Scalaka:
http://www.atariarchives.org/mmm/appendix_two.php

larek 2008-12-10 00:40:06

@Sikor - udało Ci się wywołac na mojej twarzy, pomimo zmęczenia całodniową pracą, uśmiech! Prawdziwy i szczery uśmiech. Dziękuję! Piszę całkiem poważnie.
Przy pierwszym uruchomieniu gry - to był najszybszy game over, jaki w życiu doświadczyłem... no, może po The Last V8. Nie wiedziałem co się dzieje. Raz piłka w lewo, raz piłka w prawo, powtórka w lewo i game over. Rewelacja! :-D
Gra prosta jak mało która i wciągneła mnie jak cholera. Byłem ciekawy czy uda mi się zmienić tor piłki. Niestety, chyba się tego nie da zrobić. Nabiłem ponad 100 pkt, a piłeczka dalej swoje pim, puk, pim, puk, pim...
Naprawdę świetne!

@Kaz - jak bym wiedział, że chcesz opublikować tego mojego maila w sprawie PMG, to bym się bardziej przyłożył... a tak, co ludzie pomyślą? Takie to trochę bez ładu i składu, pisane na żywca ;-)

Kaz 2008-12-10 01:37:33

Ostrzegalem, ze wszystko co napiszesz, moze byc wykorzystane przeciwko Tobie... :) Bo to cenne informacje.

sikor 2008-12-10 06:44:22

@larek: cieszę się, że się podoba. Niestety, nic większego w tej chwili nie spłodzę (delegacja i emulator only, a pod tym nie umiem pisać). Najśmieszniejsze jest to, że gra działa szybko (nawet jest opóźnienie 1/50 sekundy, bo po kompilacji działała zbyt szybko ;) ), a wszystkie kolizje (aż 4 warunki) predefiniowane ;) I działa - a chodzi przecież o zabawę tylko. Dziwię się, że chciało Ci sie dojśc >100 punktów ;)

Jazon 2008-12-10 08:16:46

hahaha najlepszy jest początek - jak rzuty karne, potem już idzie.

Scalak 2008-12-10 08:26:16

@Kaz - nie takim znowu głębokim ;)

Kaz 2008-12-10 09:40:19

Scalak - jak to nie, skoro w grze napisane, ze "jestes w glebokim lesie" :D

Scalak 2008-12-10 09:48:55

no tak :D

Caco 2008-12-10 11:07:14

Kurcze, trzeba się pośpieszyć bo Mikołaj nadchodzi i termin konkursowy minie :(

Kaz 2008-12-10 12:43:50

Zgromadzilem w jednym miejscu wszystki porady, jakie udalo mi sie znalezc w komentarzach i mailach, a mogace sie przydac konkursowiczom:
http://atarionline.pl/forum/comments.php?DiscussionID=33&page=1#Item_31

xeen 2008-12-10 18:04:34

lamerskie pytanie, nie zwiazane z tematem:
czy da się sprawdzić w basicu jaki klawisz (lub kombinacja np. ctr+cos tam) został ostatnio wcisniety na klawiaturze?

sikor 2008-12-10 18:09:54

Da się. GET KEY w turbo basicu na przykład lub PEEK 764 - o ile dobrze pamiętam.

xeen 2008-12-10 18:15:01

dzieki Sikor , to jest to (trzeba wiedzieć co wpisac w google :)))
znalazłem, czego szukalem
10 A = PEEK(764)
20 IF A = 63 THEN GOTO 100
30 GOTO 10
40 END
100 PRINT "AN 'A' WAS HIT!"

mono 2008-12-10 21:45:10

Z komórką 764 jest pewien problem :) Systemowe procedury odczytu klawisza przyjmują, że wartość 255 ($FF) oznacza brak wciśniętego klawisza. Co nie do końca jest prawdą, bo POKEY wartością 255 sygnalizuje wciśniętą kombinację SHIFT+CONTROL+A. Można to łatwo zaobserwować wciskając tę kombinację klawiszy podczas trybu przyciągania uwagi (POKE 77,128).
Gdyby ta kombinacja nie była obsługiwana, wtedy komputer nie zareagowałby na wciśnięcie, jak to się dzieje w przypadku np. SHIFT+CONTROL+Z.
Typowa procedura odczytu klawisza czyta wartość z KBCODES, po czym zapisuje do niego wartość 255:
10 K=PEEK(764): IF K=255 THEN 10
20 POKE 764,255
Gdybyśmy jednak chcieli rozpoznać wszystkie dostępne kombinacje klawiszy należałoby zachować ostatnio odczytaną wartość klawisza i porównywać świeżo czytaną zawartość KBCODES z tąże wartością np tak:
10 L=-1: REM peek daje liczby [0..255]
20 K=PEEK(764): IF K=L THEN 20
30 L=K
Nie da się jednak w ten sposób odczytać wciśnięcia tego samego klawisza raz po raz.
Jest jednak na to metoda, ponieważ w całym zestawie możliwych kombinacji POKEY ma dziury. Brakuje np kombinacji SHIFT+CONTROL+[Z,X,C,V,B] i jeszcze kilku innych. Jakie kody one przyjmują? Zasada jest prosta - do kodu pojedynczego klawisza dodaje się 64 w przypadku dołączenia klawisza SHIFT i 128 w przypadku CONTROL. Jeśli więc normalny Z ma kod 23 ($17) to SHIFT+CONTROL+Z będzie mieć 128+64+23=215 ($D7) :)
Po co to wszystko? Jeśli zamiast 255 założymy, że kod dla braku wciśniętego klawisza to właśnie zabroniona kombinacja SHIFT+CONTROL+Z, to będzie można rozpoznać również wciśnięcie SHIFT+CONTROL+A! Zmodyfikowana procedura może wyglądać tak:
10 K=PEEK(764): IF K=215 THEN 10
20 POKE 764,215
Na koniec działania naszego programu tuż przed wyjściem do systemu operacyjnego warto do rejestru-cienia KBCODES wpisać wartość braku klawisza, której system się spodziewa, czyli 255.
Zamiast sztuczki z zabronioną kombinacją można próbować testowania bitu wciśnięcia klawisza w rejestrze SKSTAT ($D20F) np. tak:
10 S=PEEK(53775): SI=INT(S/8)*8: IF (S-SI)>=4 THEN 10
20 K=PEEK(53769): REM KBCODE ($D209)
Dokładnie tak samo można testować wciśnięcie klawisza SHIFT, który jest obsługiwany samodzielnie;
10 S=PEEK(53775): SI=INT(S/16)*16: IF (S-SI)>=8 THEN 10
CONTROL niestety takich możliwości nie daje :(
Na koniec warto chyba wspomnieć jeszcze o jednej sprawie. Klawisze konsoli HELP, START, SELECT, OPTION I RESET mimo, że są umieszczone w jednym bloku, to nie są traktowane jednorodnie (analogicznie, jak SHIFT i CONTROL) - START, SELECT i OPTION bowiem są rozpoznawane przez GTIA w rejestrze CONSOL ($D01F)
10 C=PEEK(53279)
20 CI=INT(C/8)*8: ST=(C-CI)>=4
30 CI=INT(C/4)*4: SE=(C-CI)>=2
40 CI=INT(C/2)*2: OP=(C-CI)>=1
natomiast wciśnięcie HELP'a jest testowane właśnie przez POKEY'a i powoduje pojawienie się w KBCODE wartości 17 ($11). Można sprawdzać co da wciśnięcie go z SHIFT i/lub CONTROL :D Konsekwencją takiej obsługi klawiatury jest również to, że klawisze START, SELECT i OPTION można wciskać równocześnie z dowolnymi kombinacjami klawiszy na klawiaturze. Możliwa jest więc kombinacja START+OPTION+SHIFT+CONTROL+N (kto pamięta w jakiej grze co ta kombinacja powodowała? ;)). Możliwe jest też testowanie wciśnięcia START+SHIFT (jaki "użytek" pana JBW się tak aktywowało?). Nie można jednak wciskać HELP'a równocześnie z klawiszami klawiatury.
Można też wciśnięcie klawisza HELP testować odczytując rejestr cień HLPFLG (732 = $2DC):
10 H=PEEK(732): IF H<>255 THEN 10
20 POKE 732,255
Rejestr ten zawiera wartość odczytaną z KBCODE a więc w kombinacji z SHIFT i/lub CONTROL.
Osobiście jako kod braku wciśnięcia klawisza wykorzystuję kombinację SHIFT+CONTROL+HELP (209 = $D1), która nie jest sygnalizowana przez POKEY.

mono 2008-12-10 21:49:24

Poprawka dla HELPa - oczywiście:
10 H=PEEK(732): IF H=255 THEN 10

Kaz 2008-12-10 22:15:48

Mono - no dobra, teraz rozwiaz wlasny konkurs: "Możliwa jest więc kombinacja START+OPTION+SHIFT+CONTROL+N (kto pamięta w jakiej grze co ta kombinacja powodowała? ;)). Możliwe jest też testowanie wciśnięcia START+SHIFT (jaki "użytek" pana JBW się tak aktywowało?)."

:)

xeen 2008-12-10 22:46:45

jeszcze się muszę duuuużo nauczyć:))

Kaz 2008-12-10 22:57:46

Jak kazdy z nas :).

mono 2008-12-10 23:04:51

@xeen: Ee nie przesadzajmy - to są tylko takie sztuczki :) Niemniej jednak parę możliwości mamy.
Nie potrafiłem za to wymyślić, jak testować w basicu bity i stąd te konstrukcje typu "CI=INT(C/8)*8: ST=(C-CI)>=4"; może ktoś zna lepszy, szybszy, łatwiejszy sposób?
@Kaz: dokładnie - jak każdy z nas :)

vdl 2008-12-10 23:07:49

START+OPTION+SHIFT+CONTROL+N - jak sie nie myle to chodzi o Operation Blood ale co ta kombinacja robila to nie pamietam

Kaz 2008-12-10 23:24:10

Vdl - tutaj znalazlem takie cos:
http://www.tnt.easy9912.easyisp.pl/szukaj.php?tytulgry=Operation%20Blood

mono 2008-12-11 00:06:48

@vdl: Nie :) Dla zawężenia zbioru możliwych rozwiązań powiem, że chodziło o jedną z pierwszych gier firmy Avalon.

vdl 2008-12-11 00:38:44

lasermania?
http://atari.fandal.cz/detail.php?files_id=2288

mono 2008-12-11 00:44:53

Ha! Dokładnie!
To teraz SHIFT+START :)

xeen 2008-12-11 09:50:17

jeszcze jedna rzecz nie daje lamerowi spokoju
np. dla klawisza "A" z peek 764 dostaje 63,
podczas gdy chr$(63) to znak "?" (dla 65 to A)
czy istnieje jakiś wzór na konwersję w drugą stronę, czy też może funkcja.
aż się boje, że mono to opisał, tylko nie zrozumiałem, ale jednak spytam.

xeen 2008-12-11 09:55:26

coś takiego znalazłem, w pracy sprawdzić nie mogę;)
10 OPEN #1,4,0,"K:" REM Opens IOCB #1 for input from the keyboard
20 Y = PEEK(764): IF Y = 255 THEN GOTO 20: REM. Check for keypress
30 GET #1,X: REM Obtain ATASCII value
40 PRINT "YOU PRESSED ";CHR$(X)", PEEK(764)= ";Y
50 POKE 764, 255: REM Clear register
60 GOTO 20: REM Do it again

larek 2008-12-11 11:00:18

Nie sprawdzaj klawiatury poprzez peek(764), tylko przez funkcję turbo basica INKEY$ i nie będziesz miał problemów. Jeśli już bardzo chcesz, to w książce Miguta Atari Basic (dostępna w dziale z książkami) jest tabelka z odpowiednimi kodami.

DO:? ASC(INKEY$): LOOP

mono 2008-12-11 11:30:26

Ten kod rzeczywiście działa, ponieważ w KBCODES zostanie kod klawisza i handler K: go poprawnie obsłuży :) Bardzo elegancka sztuczka!
Niestety kody klawiszy wystawiane przez POKEY'a nie są kodami ATASCII - trzeba je skonwertować. W systemie jest zdefiniowana odpowiednia tablica, której adres znajduje się w wektorze KEYDEFP (121=$79). Tablica ta zawiera tylko 192 kody (nie są brane kombinacje z SHIFT+CONTROL, choć programiści z Chaos'u się tym zbytnio nie przejmowali) tak więc trzeba to uwzględnić. Poniżej kawałek programu, który konwersji powinien dokonać.
10 KT=PEEK(121)+256*PEEK(122)
20 K=PEEK(764)
30 A=PEEK(KT+K)
40 IF K>=192 THEN REM ignorujemy wciśnięcie klawisza
Nic nie stoi na przeszkodzie, żeby zdefiniować własną tablicę i ustawić ją w systemie. Wtedy obsługa handlera K: będzie ją wykorzystywać.
10 DIM T$(192): T$="ATASCII4KBCODES"
20 TH=INT(ADR(T$)/256): TL=ADR(T$)-TH*256
30 POKE 121, TL: POKE 122, TH
Do własnych potrzeb możemy zdefiniować pełną tablicę konwersji i się nią posłużyć naszym zmodyfikowanym programem:
10 DIM T$(256): T$="ATASCII4KBCODES"
20 TH=INT(ADR(T$)/256): TL=ADR(T$)-TH*256
30 POKE 121, TL: POKE 122, TH
40 KT=PEEK(121)+256*PEEK(122)
50 K=PEEK(764)
60 A=PEEK(KT+K)
Kombinacje z SHIFT+CONTROL zostaną obsłużone, ale handler K: dalej ich nie będzie widział.
Znaki w zmiennej T$ mapują kody odczytywane z KBCODES na znaki ATASCII. Znaki pobrane z tej mapy o kodach ATASCII 128..255 są kodami specjalnymi i we własnej procedurze najprościej je odfiltrować np.:
10 KT=PEEK(121)+256*PEEK(122)
20 K=PEEK(764)
30 A=PEEK(KT+K)
40 IF A>=128 THEN REM ignorujemy wciśnięcie klawisza
Handler K: (jeśli go użyjemy) będzie natomiast próbował wykonać specjalną funkcję przypisaną do takiego znaku (np. przesunięcie kursora do początku wiersza).
Polecam lekturę "Procedur wejścia-wyjścia" Wojciecha Zientary rozdział "3.1.1. Odczyt z klawiatury" - są tam dodatkowo opisane np. kody specjalne (np. kod NU oznaczający znak nieużywany) oraz tablica mapy klawiszy F1..F4 FKDEFP (96=$60) wykorzystywana w 1200XL. Nawet osoby nie znające assemblera śmiało mogą z wielu informacji tam zawartych skorzystać.

xeen 2008-12-11 12:19:24

dzieki

miker 2008-12-11 19:32:48

A to SHIFT+START to nie wiem, czy aby nie XL-Friend aby miał. :)

mono 2008-12-11 20:01:59

XLF miał SHIFT+HELP i CONTROL+HELP :)

mono 2008-12-13 00:21:03

OK. Tytuł brzmi JBW Commander (plik nazywał się COM.COM) :)

homek 2008-12-13 13:15:54

JBW też niezłe zioło palił... ;->

Kaz 2008-12-13 13:48:58

Kiedys sie nie palilo ziola, zeby miec fantazje... :)