QUICK Programmierkurs Teil 8 Ja ja, diese Packprogramme !!! Manchmal sind sie eben etwas übereifrig, und packen schon mal mehr Text weg, als Sie später entpacken. So kam es, daß beim letzten Teil die 2te Programmversion des Playerdemos nicht mehr so richtig vorhanden war. Da dieser Teil des Kurses aber sowieso auf das zweite Demoprogramm zurückgreift, macht das gar nichts. Da wir beim ersten Programm den Player nicht im Interrupt bewegen, hat dieses 2 Nachteile: -Es könnte sein, daß der Player doch mal flackert (obwohl ich das nie bemerkt habe). -Wenn wir das Hauptprogramm um einen Spieleteil erweitern, könnte es sein, daß der player nicht mehr regelmäßig bewegt wird, weil zwischen 2 Aufrufen von WAIT zuviel Zeit vergeht. Deshalb nutzen wir nun die Fähigkeiten von QUICK noch mehr aus, und legen das Unterprogramm MOVE, das den Player darstellt, als VBI fest: INTER MOVE ... ENDVBI Damit wird es automatisch jede 50tel Sekunde einmal aufgerufen, egal was wir im Hauptprogramm tun! Natürlich darf MOVE nur sehr kurz sein, sonst bekommen wir größte Schwierigkeiten. Aber ein paar PLAYER kann man ohne weiteres bewegen. (Das Spiel RUBBERBALL ist fast ein einziger großer VBI!). Damit wird natürlich die Prozedur WAIT überflüssig, und der Hauptteil des Programms sieht nun so aus: ... MAIN * "leerer Player" DATA(FREE) (+ 0,0,0,0,0,0,0,0,0,0 )+ VADR(FREE) FREEADR=VARADR * Playerform DATA(SHAPE) (+ 8,8,28,28,28,62,127,107,107,65 )+ VADR(SHAPE) SHAPEADR=VARADR X=48 Y=32 .INIT VBI(MOVE) * Hier koennte ein weiteres * Programm stehen, das nun voellig * unabhaengig von der Playerbewegung * laufen kann ENDMAIN * VBI zur Playerbewegung, der jede * 50/tel Sekunde aufgerufen wird. * Dabei wird 1 mal der Stick abgefragt * und 1 mal der PLAYER entspr. gesetzt INTER MOVE BEGIN .STICK .PLAYER ENDVBI ... Um den VBI auszuschalten, schaltet man einen DUMMY-VBI ein: INTER NOTHING BEGIN ENDVBI Es könnte ja schließlich sein, daß man den Player irgendwann nicht mehr bewegen will... So. soweit zum letzten Mal. Das Gesamtprogramm, mit den heutigen Erweiterungen folgt am Textende. Worum geht es diesesmal? Nun, man benötigt natürlich eine Spielfigur (Player), um ein Spiel zu spielen, aber das alleine reicht ja nicht. Deshalb bauen wir nun noch einen Schuss und ein paar (noch unbewegte) Ziele ins Programm ein. Player 2 als Schuss Da wir bisher nur einen der 4 Player benötigten, ist es kein Problem, Player 1 (Zählung ab 0) als Schuss zu verwenden. Die Schussform legen wir analog zur Schifform im Feld SHOT ab, die Löschbytes in NOSHOT. Praktischerweise ist der Schuss einfach ein kurzer Strich. Ausgelöst wird der Schuss durch Drücken des Trigger. Dadurch steht in der OS-Variablen STRIG0 der Wert 0, sonst 1. Im Fall 0, setzen wir also die X-Position (SX und HPOSP1) des Schusses auf die X-Position des Schiff-Players. Die Y-Position, ist um 5 erniedrigt, denn der Schuss soll ja vor dem Schiff erscheinen. Die Bewegung des Schusses besorgt die Routine SHOOT, die ja vom VBI MOVE in regelmäßigen Abständen aufgerufen wird. Dort wird der Schuss solange nach oben bewegt, bis er den Bildschirm verläßt (Y-Pos kleiner 32). Um das Ganze akustisch zu untermalen, setzen wir den SOUND-Befehl ein.Er funktioniert fast so wie im BASIC: SOUND(KANAL,HOEHE,VERZERRUNG,VOLUME) Der einzige Unterschied ist, daß die Verzerrung nur Werte von 0 bis 7 annehmen darf (Wert im BASIC durch 2). Das macht aber nichts, da die ungeraden Werte im BASIC ja keinen TOn nach sich ziehen! KANAL ist die Nummer des zuverwendenden Soundkanals 0 bis 3, VOLUME ist die Lautstärke von 0 bis 15 und die Tonhöhe ist von 0 bis 255 regelbar. Der Sound wird durch 2 Parameter gesteuert. Die Tonhöhe wird ständig von 128 bis 0 erniedrigt (usw.). Außerdem verringern wir die Lautstärke vom Startwert (7 bis 15) auf den Wert 0. Der Startwert wird beim Drücken des Triggers gesetzt, und ist um so kleiner, je näher der Schuß bereits am oberen Bildschirmrand ist. Der Sound wird ebenfalls im Interrupt erzeugt. Die Farbe des Schusses setzt sich aus 2 Werten zusammen: Die Helligkeit wird vom Volumen des Sounds bestimmt. Die Farbe selbst wird auf 240 (Gelb) festgelegt. Um das Endresultat zu sehen, addiert man Volumen und 240, und erhält so einen dunkler werdenden Schuss. Schließlich möchten wir ja auch irgendwas treffen. Dazu setzen wir zunächst ganz einfache Scheibchen (ASCII-Zeichen 20 == Internes Zeichen 84) auf den Bildschirm. Das erledigt die Routine SCREEN, die 20 Zufallszahlen im Bereich von 0 bis 2*255 erzeugt (Die Speicherstelle RANDOM enthält nämlich jeweils einen Wert von 0 bis 255) und diese zur Bildschirmanfangsadresse SAVMSC (=88/89) zählt. SAVMSC wird am Anfang des Programms als OS-Variable definert. An die berechnete Stelle im Speicher poken (igitt!) wir dann den Wert. PRINT können wir in der Grafikstufe 12 ja nicht ohne weiteres beutzen. Um festzustellen, ob der Schuß ein Objekt des Anzeigenfeldes (Scheibchen) getroffen hat, brauchen wir nur das Kollisionsregister P1PF ("Player1 - Playfield") abfragen, das einen Wert ungleich Null in diesem Fall aufweist. Dieses OS-Register wird jede 50tel Sekunde vom Grafikchip aktualisiert und speichrt solche Überlappungen. Erst durch Einschreiben eines beliebigen Wertes in HITCLR werden diese Kollisionsregister wieder gelöscht. Das tun wir auch im Falle eines Treffers, und außerdem löschen wir den Schuß vom Bildschirm und setzten SY auf den Wert 34, so daß der Schuß auc nicht mehr erscheint. Wie das alles zusammen dann aussieht, zeigt der folgende erweitere Quellcode. Anmerkung: Runde Klammern mit + Zeichen stellen eckige Klammern dar. -US- steht für den Unter- strich (SHIFT -). Quick-Sourcetext D8:PLAY.QIK ---------------- Length: $14CB Free : $62A0 ---------------- * PLAYER-DEMO in QUICK Folge 2 * (c) H.Schoenfeld '91 * Grafik-Variablen Header * (c) RAINDORF SOFT '90 * aus QUICKmagazin III BYTE (+ ATRACT=77 *Farbwechsel LMARGN=82 *Linker Rand RMARGN=83 *Rechter Rand ROWCRS=84 *Cursorzeile DINDEX=87 *Graphics Nr SDMCTL=559 *DMA Zugriff GPRIOR=623 *Farbkontrolle/Prioritaet PCOLR0=704 *Playerfarben PCOLR1=705 PCOLR2=706 PCOLR3=707 COLOR0=708 *Farbregister COLOR1=709 COLOR2=710 COLOR3=711 COLOR4=712 CRSINH=752 *Cursorsteuerung CHAT=755 *Zeichendarstellung CHBAS=756 *ZS-Anfangsadr.Highbyte HPOSP0=53248 *Hor. Playerpos. HPOSP1=53249 HPOSP2=53250 HPOSP3=53251 M0PF=53248 *Koll.reg. Missile/Playfi. M1PF=53249 M2PF=53250 M3PF=53251 HPOSM0=53252 *Hor. Missilepos. HPOSM1=53253 HPOSM2=53254 HPOSM3=53255 P0PF=53252 *Koll.reg. Player/Playfie. P1PF=53253 P2PF=53254 P3PF=53255 SIZEP0=53256 *Playerbreiten SIZEP1=53257 SIZEP2=53258 SIZEP3=53259 M0PL=53256 *Koll.reg. Missile/Player M1PL=53257 M2PL=53258 M3PL=53259 SIZEM=53260 *Missilebreiten P0PL=53260 *Koll.reg. Player/Player P1PL=53261 P2PL=53262 P3PL=53263 COLPM0=53266 *Farbregister Player + Mis COLPM1=53267 COLPM2=53268 COLPM3=53269 COLPF0=53270 *Farbregister Anzeigenfeld COLPF1=53271 COLPF2=53272 COLPF3=53273 COLPF4=53274 PRIOR=53272 *Prioritaet VDELAY=53276 *2-zeilige Aufloesung GRACTL=53277 *Grafikkontrolle HITCLR=53278 *Kollision loeschen PBCTL=54019 DMACTL=54272 *DMA-Kontrolle CHACTL=54273 *Zeichendarstellungkontr. HSCROL=54276 *Horiz. Scrolling VSCROL=54277 *Vert. Scrolling PMBASE=54279 *Anfang der PM's CHBASE=54281 *Anfang des Zeichensatzes WSYNC=54282 *Zeilensynch. )+ WORD (+ COLCRS=85 *Cursorspalte SAVMSC=88 *BS-Adresse SDLSTL=560 *Zeiger auf DPL DLIST=54274 *Zeiger auf DPL )+ BYTE (+ X,Y,AX,AY SND1,SND2,SNDZ SX,SY )+ WORD (+ SHAPEADR,FREEADR,VARADR=208 SHOTADR,NOSHOTADR )+ ARRAY (+ FREE(10),SHAPE(10),SHOT(5),NOSHOT(5) )+ * Hauptprogramm * Setzt Playerformen fest * Initialisiert * Zeichnet Screen * Bewegt Player * Bewegt Schuss * Erzeugt Schuss-Sound MAIN * "leerer Player" DATA(FREE) (+ 0,0,0,0,0,0,0,0,0,0 )+ VADR(FREE) FREEADR=VARADR * Playerform DATA(SHAPE) (+ 8,8,28,28,28,62,127,107,107,65 )+ VADR(SHAPE) SHAPEADR=VARADR * Schussform DATA(SHOT) (+ 8,8,8,8,8 )+ VADR(SHOT) SHOTADR=VARADR * Kein Schuss DATA(NOSHOT) (+ 0,0,0,0,0 )+ VADR(NOSHOT) NOSHOTADR=VARADR X=48 Y=32 .INIT .SCREEN VBI(MOVE-US-AND-US-SHOOT) * Hier koennte ein weiteres * Programm stehen, das nun voellig * unabhaengig von der Playerbewegung * laufen kann ENDMAIN * VBI zur Playerbewegung, der jede * 50/tel Sekunde aufgerufen wird. * Dabei wird 1 mal der Stick abgefragt * und 1 mal der PLAYER entspr. gesetzt INTER MOVE-US-AND-US-SHOOT BEGIN .STICK .PLAYER .SHOOT ENDVBI * Stick abfragen und * Playerkoordinaten entspr. aendern PROC STICK LOCAL BYTE (+ STICK=632,STRIG=644,TST )+ BEGIN IF STICK<>15 AY=Y AX=X AND(STICK,1,TST) IF TST=0 IF Y>37 Y- ENDIF ELSE AND(STICK,2,TST) IF TST=0 IF Y<224 Y+ ENDIF ENDIF ENDIF AND(STICK,4,TST) IF TST=0 X- ELSE AND(STICK,8,TST) IF TST=0 X+ ENDIF ENDIF ENDIF * Schuss ausloesen IF STRIG=0 IF SY<=32 * Y Pos. SUB(Y,5,SY) * X Pos. HPOSP1=X SX=X * Sound ausloesen SND1=128 *Tonhoehe DIV(Y,32,SND2) *Volume ADD(SND2,7,SND2) SNDZ=0 ENDIF ENDIF ENDPROC * Player darstellen PROC PLAYER BEGIN * Player an alter Pos. loeschen PLAYER($74,AY,10,FREEADR) * Player an neue Pos. kopieren HPOSP0=X PLAYER($74,Y,10,SHAPEADR) ENDPROC PROC SHOOT BYTE (+ HSY,COL )+ BEGIN * Schuss noch auf Schirm? IF SY>32 * alten Schuss loeschen PLAYER($75,SY,5,NOSHOTADR) SY- SY- IF SY>32 * neuer Schuss PLAYER($75,SY,5,SHOTADR) * Tonhoehe aendern HSY=SY * SY/4 in HSY schreiben LSRB(HSY) * (schnell!) LSRB(HSY) SUB(SND1,HSY,SND1) IF SND1>128 SND1=128 ENDIF * Lautstaerke aendern ADD(SNDZ,16,SNDZ) IF SNDZ>=SY IF SND2<>0 SND2- SNDZ=0 ENDIF ENDIF SOUND(0,SND1,4,SND2) ADD(240,SND2,PCOLR1) ELSE * kein Sound SOUND(0,0,0,0) ENDIF ENDIF * Kollision Schuss-Objekt? IF P1PF<>0 * Schuss loeschen PLAYER($75,SY,5,NOSHOTADR) SY=34 * Schuss auf Endposition HITCLR=0 * Kollision loeschen ENDIF ENDPROC * Grafik und Player initialisieren PROC INIT BEGIN CLOSE(6) OPEN(6,8,12,"S:") *Grafik 12 HPOSP0=0 *player ganz rechts PCOLR0=136 *Playerfarbe Blau HPOSP1=0 *Schussplayer rechts PCOLR1=252 *Schussfarbe Gelb SY=0 *Schuss aus CLR($70,8) *Playerbereich loeschen SDMCTL=62 *Player an PMBASE=$70 *Anfang der PM's GRACTL=2 *Grafikkontrolle Player SETCOL(0,3,6) * Rote Ziele SETCOL(1,3,6) SETCOL(2,3,6) ENDPROC * Setzt bis zu 20 Ziele auf * den Bildschirm PROC SCREEN LOCAL BYTE (+ I,RANDOM=53770 *Zufallszahl 0 bis 255 )+ WORD (+ BSADR )+ BEGIN I=0 REPEAT ADD(SAVMSC,RANDOM,BSADR) ADD(BSADR,RANDOM,BSADR) I+ POKE(BSADR,84) UNTIL I=20 ENDPROC