COLLISION

Aus C64-Wiki
Zur Navigation springenZur Suche springen
COLLISION
Syntax: COLLISION <Kollisionsquelle> [,<Zeilennummer>]
Parameter
<Kollisionsquelle>: numerischer Ausdruck im Wertebereich von 1 bis 3
<Zeilennummer>: numerischer Ausdruck im Wertebereich von 0 bis 65535
Einordnung
Typ: Anweisung
Kontext: Sprites
Aufgabe: Kollisionsbehandlungsroutine aktivieren oder deaktivieren
Abkürzung: coL
Token: $fe $17 (254 23)
Verwandte Befehle
BUMP, PEN, TRAP

Anmerkung: Dieser Artikel beschreibt den BASIC-Befehl COLLISION ab Commodore BASIC V7.0 oder höher.


Die Anweisung COLLISION dient dazu, Unterprogramme zur Kollisionsbehandlung festzulegen oder die Kollisionsbehandlung wieder abzuschalten.

Die Anweisung kennt folgende 2 Parameter:

  • <Kollisionsquelle>: gibt an, welche Kollisionsquelle abgefangen wird. Folgende Werte sind erlaubt:
1: Sprite-Sprite-Kollision
2: Sprite-Bildschirm-Kollision
3: Lightpen-Impuls
Andere Werte führen zu einem ?ILLEGAL QUANTITY ERROR.
  • <Zeilennummer>: die Zeilennummer, an der die Kollisionsbehandlungsroutine beginnt. Erlaubt sind die Werte von 0 bis 65535, auch wenn Werte ab 64000 unsinnig sind, da eine Programmzeile maximal mit Zeilennummer 63999 angelegt werden kann. Fehlt der Parameter, so wird die Kollisionsbehandlung für die angegebene Quelle abgeschaltet. Obwohl beliebige numerische Ausdrücke erlaubt sind, sollte immer eine Konstante als Zeilennummer angegeben werden, da bei einem RENUMBER-Aufruf nur solche berücksichtigt und automatisch anpasst werden.

Tritt das angegebene Kollisionsereignis auf, so wird der augenblicklich ausgeführte BASIC-Befehl noch beendet und dann zur festgelegten Zeilennummer verzweigt. Existiert das Sprungziel nicht, wird die Fehlermeldung ?UNDEF'D STATEMENT ERROR IN <Zeilennummer> ausgegeben, wobei <Zeilennummer> die Zeile des zuletzt ausgeführten Befehls meint.
Treten während der Abarbeitung der Kollisionsbehandlungsroutine weitere Kollisionsereignisse auf, werden diese bis zur Beendigung der Routine zurückgestellt.
Bei mehreren gleichzeitigen Unterbrechungsanforderungen werden diese nacheinander, aufsteigend nach Nummer der Kollisionsquelle, aufgerufen.

Anmerkungen:

  • Um die Unterbrechungsanforderungen schnell abzuarbeiten, sollten die Routinen möglichst am Anfang des Programms stehen. Ebenso sollten sie möglichst geschwindigkeitsoptimiert programmiert sein.
  • Mit hoher Wahrscheinlichkeit wird während der Abarbeitung der Kollisionsbehandlungsroutine die Unterbrechungsanforderung erneut ausgelöst. Dies ist zu berücksichtigen, um mehrfaches Abarbeiten derselben Kollision zu verhindern.
  • Beim Abschalten einer Kollisionsbehandlungsroutine wird die Registrierung neuer Unterbrechungsanforderungen der passenden Quelle abgeschaltet und zusätzlich (eigentlich unnötigerweise) das Sprungziel auf 0 gesetzt. Tritt nun während des Abschaltens das passende Kollisionsereignis auf, wird nach Abschluss des COLLISION-Befehls trotzdem noch die Kollisionsbehandlung aufgerufen, aber mit Zeilennummer 0. Existiert diese nicht, kommt es zu einem ?UNDEF'D STATEMENT ERROR IN <Zeilennummer>, ansonsten, falls dieser Fall nicht abgefangen wird, zu auf den ersten Blick unerklärlichen Fehlern im Programmablauf. Einige Möglichkeiten, diesen Bug zu umgehen, sind:
  1. Vor Abschalten der Kollisionsbehandlung sicherstellen, dass keine entsprechenden Unterbrechungsanforderungen mehr ausgelöst werden (z.B. durch Abschalten der Sprites).
  2. Abfangen des ?UNDEF'D STATEMENT ERROR durch eine passende TRAP-Routine.
  3. Das Programm mit Zeilennummer 0 beginnen und dort eine Wächtervariable überprüfen:
    0 IF XX THEN RETURN:ELSE XX=1:GOTO 1000
  • Wird ein Programm mittels RUN/STOP -Taste oder dem Befehl STOP unterbrochen und dann wieder fortgesetzt, werden alle Kollisionsabfragen abgeschaltet.
  • COLLISION darf auch im Direktmodus aufgerufen werden, hat dann aber keinen Effekt.

Interna[Bearbeiten | Quelltext bearbeiten]

COLLISION verwendet folgende Adressen im RAM:

4726 ($1276) Flag für Sprite-Sprite-Kollision
4727 ($1277) Flag für Sprite-Bildschirm-Kollision
4728 ($1278) Flag für Lightpen-Impuls
4729-4731 ($1279-$127B) Sprungziele Low-Byte
4732-4734 ($127C-$127E) Sprungziele High-Byte
4735 ($127F) Bitmaske für Kollisionsquelle

Im Hauptrasterzeilen-IRQ wird das IRQ-Register des VIC (Register 25, Adresse $D019) ausgelesen und für jede aktive Interruptquelle, bei der das passende Bit in Adresse 4735 ($127F) gesetzt ist, das entsprechende Flag auf 255 ($FF) gesetzt.

In der BASIC-Hauptschleife wird vor dem Einlesen des nächsten BASIC-Befehls überprüft, ob eines der 3 Kollisionsflags gesetzt ist. Falls ja, wird das Kollisionsflag auf 0 gesetzt, Bit 7 der Bitmaske in 4735 ($127F) gesetzt, die Kollisionsbehandlungsroutine wie ein BASIC-Unterprogramm aufgerufen und anschließend Bit 7 der Bitmaske wieder gelöscht.

Beispiele[Bearbeiten | Quelltext bearbeiten]

Einfach[Bearbeiten | Quelltext bearbeiten]

100 COLLISION 1,1000

Das Programm verzweigt zur Zeile 1000, sobald mindestens 2 Sprites miteinander kollidieren.

200 COLLISION 3

Schaltet die Behandlung von Lightpen-Impulsen ab.

Komplex[Bearbeiten | Quelltext bearbeiten]

Ein kleines Spiel: Manövriere das "Raumschiff" (das gedrehte Quadrat) durch die sich bewegenden Bälle. Das Programm ist ähnlich dem Beispiel bei BUMP, verwendet aber COLLISION zusätzlich zu BUMP.

0 IF XX THEN RETURN: ELSE XX=1:GOTO 150: REM BEI XX<>0 UNERWUENSCHTE KOLLISIONSBEHANDLUNG
20 B=BUMP(2): IF B=0 THEN RETURN: REM BEI FEHLALARM SOFORT ZURUECK
30 DO
40 FOR I=1 TO 7
50 IF B AND BM(I) THEN BEGIN
60 Y=RSPPOS(I,1): IF Y<99 THEN MOVSPR I,180#SP(I): MOVSPR I,+0,60: ELSE MOVSPR I,0#SP(I): MOVSPR I,+0,218
70 BEND
80 NEXT
90 GET K$: IN=INSTR(I$,K$): IF IN THEN MOVSPR 8,-(DX(IN)),-(DY(IN)): IF RSPPOS(8,0)<24 THEN GS=2: EXIT
100 B=BUMP(2) AND NOT B: REM NUR NEUE SPRITE-KOLLISIONEN BERUECKSICHTIGEN
110 LOOP WHILE B
120 RETURN
130 GS=1: REM GAMESTATUS = GAME OVER
140 RETURN
150 REM SPRITES UND VARIABLEN INITIALISIEREN
155 XX=1
160 GRAPHIC 1,1: CIRCLE 1,12,10,5: PAINT 1,12,10: SSHAPE S$,0,0,23,20
170 BOX 1,27,5,37,15,45: SSHAPE R$,24,0,47,20: SPRSAV R$,8
180 I$="{LINKS}{RECHTS}{RAUF}{RUNTER}": DIM DX(4),DY(4): DX(1)=5: DX(2)=-5: DY(3)=5: DY(4)=-5
190 DIM BM(8),SP(8): BM=1
200 FOR I=1 TO 7: BM(I)=BM: BM=BM2*2: SP(I)=1+(I AND 3): SPRSAV S$,I: NEXT
210 DO: REM GRAFIK AUFBAUEN UND SPRITES EINSCHALTEN
220 SCNCLR 1: WIDTH 1: COLOR 1,1
230 BOX 1,0,0,319,12,0,1:BOX 1,0,187,319,199,0,1
240 FOR I=1 TO 7: MOVSPR I,40*I+5,200: MOVSPR I,A(I)#SP(I): SPRITE I,1,I,1: NEXT
250 MOVSPR 8,320,150: SPRITE 8,1,1,0: REM "RAUMSCHIFF" EINSCHALTEN
260 B=BUMP(2): B=0: REM VERALTETE KOLLISIONEN LOESCHEN
270 GS=0
280 COLLISION 1,130: REM SPRITE-SPRITE-KOLLISIONEN
290 COLLISION 2,20: REM SPRITE-BILDSCHIRM-KOLLISIONEN
300 REM HAUPTTEIL
310 DO
320 GET K$: IN=INSTR(I$,K$): IF IN THEN MOVSPR 8,-(DX(IN)),-(DY(IN))
330 IF RSPPOS(8,0)<24 THEN GS=2: EXIT: REM GS (GAMESTATUS) = SIEG
340 LOOP UNTIL GS
350 REM SPRITES AUSSCHALTEN
360 COLLISION 1: COLLISION 2
370 FOR I=1 TO 8: SPRITE I,0: MOVSPR I,0#0: NEXT
380 IF GS=1 THEN CHAR 1,18,8,"GAME": CHAR 1,18,10,"OVER!": ELSE CHAR 1,15,8,"GESCHAFFT!"
390 CHAR 1,10,13,"NOCHMAL SPIELEN (J/N)?"
400 DO: GETKEY K$: LOOP UNTIL K$="J" OR K$="N"
410 LOOP WHILE K$="J"
420 GRAPHIC 0

Alarmfunktion/BASIC-Hintergrundverarbeitung[Bearbeiten | Quelltext bearbeiten]

An sich verläuft der Programmablauf in BASIC strikt durchgehend ohne Unterbrechung. Mit COLLISION und sich selbst bewegenden Sprites lässt sich eine zeitlich gesteuerte Unterbrechung eines Programms zu einer BASIC-Routine konstruieren, um etwa einen Alarm oder sonstige im "Hintergrund" laufende Tätigkeiten quasiparallel zum Hauptprogramm abarbeiten zu lassen. Eine Unterbrechung wird dabei erst nach Abarbeitung des aktuellen Kommandos verarbeitet. Somit kann etwa auch nicht eine INPUT-Eingabe per Timeout abgebrochen werden.

Das folgende Programm befindet sich in einer Eingabeschleife und bricht diese ab, sobald der Alarm abgelaufen ist. Etwaige Eingabe setzen den Alarm wieder zurück (eine Art Totmann-Funktion). Der Ablauf des Alarms bewirkt hier nur, dass ein Flag gesetzt wird, das in der Hauptschleife abgefragt wird. In der Alarm-Routine können aber auch aufwändigere Dinge gemacht werden, wie etwa am Bildschirm etwas aktualisieren (siehe Nachfolgende Anpassung für eine "Hintergrunduhr"). Man müsste hier eventuell nur auf die aktuelle Cursorposition achten, die so im Hintergrund immer wieder "entführt" werden könnte, wenn das Hauptprogramm ebenso am Bildschirm Ausgaben tätigt.

Bei einem Timeout endet das Programm und gibt die abgelaufene Zeit (nur zur Kontrolle) aus.

 100 GOTO 1000 START
 110 S1=1: S2=2: VI=51*1: REM 0/1 UNSICHTBAR/SICHTBAR
 120 GRAPHIC 1,1:DRAW 1,0,0: REM SPRITE IMAGE (PUNKT)
 130 SSHAPE A$,0,0,23,20: REM AUS BITMAP ...
 140 SPRSAV A$,S1:SPRSAV A$,S2:GRAPHIC 0: REM ... ZU SPRITES 
 150 SPRITE S1,1,RCLR(0)-(VI>0),0,0,0,0: REM HINTERGRUNDFARBE
 160 SPRITE S2,1,RCLR(0)-(VI>0),0,0,0,0
 170 MOVSPR S1,24,VI: REM VI AB 50 WAERE SICHTBARER BEREICH
 180 MOVSPR S2,24+TT*25,VI
 190 MOVSPR S1,90#1: REM 1 = 1/25 S PRO PIXEL
 200 COLLISION S1,500
 210 RETURN
 220 :
 230 COLLISION S1
 240 MOVSPR S1,0#0: SPRITE S1,0: SPRITE S2,0
 250 RETURN
 498 :
 499 REM HINTERGRUNDAKTION
 500 GOSUB 230 ALARM DEAKTIVIEREN
 510 AL=1: REM ALARMENDE-FLAG
 599 RETURN
 999 :
1000 REM TIMEOUT TT: >0 BIS 13
1010 TT=5: GOSUB 110: TI$="000000"
1020 PRINT CHR$(147)CHR$(17)CHR$(17)CHR$(17)"BITTE UM TASTENEINGABE..."
1030 PRINT "TIMEOUT ="TT"SEKUNDEN"
1040 DO
1050 GET A$: PRINT A$".";
1060 IF A$<>"" THEN TI$="000000": GOSUB 170 ALARM ZURUECKSETZEN
1070 IF AL THEN PRINT: PRINT"TIMEOUT "TI$" S,"TI"TICKS": END
1080 LOOP
  • Einsprungstellen:
    • 110: Alarmumgebung initialisieren und gemäß Variable TT den Alarm setzen
    • 170: Alarm reaktivieren gemäß Zeit in Variable TT
    • 230: Alarm deaktivieren
    • 500: Alarmroutine
    • 1000: Hauptprogramm
  • Parametrisierung:
    • 110: kann bei der VI-Zuweisung mit einer 1 die Sprites sichtbar gemacht werden. Im 2-Pixelabstand unterhalb des oberen Rahmenrandes bewegt sich das 1×1-Pixel-Sprite
    • 190: Das MOVSPR-Kommando gibt die Geschwindigkeit vor. #1 entspricht 1/25 s/Pixel , #2 wäre 1/50. Je größer dieser Geschwindigkeitsparameter, desto geringer die maximal möglich Alarmzeit (das Limit ergibt sich aus der maximalen X-Position eines Sprites).
  • Verwendete Variablen:
    • S1, S2: Sprite-Nummern
    • VI: Vertikale Position der Sprites
    • A$: temporär: Sprite-Image-Daten
  • Parametervariable:
    • TT: Timeout-Wert in Sekunden, > 0, in 1/25-Auflösung (auch nichtganze Zahlen)
  • Anwendungsvariablen:
    • AL: Flag, =1 wenn der Alarm abgelaufen ist.
    • A$: eingelesene Taste


Variante "Hintergrunduhr":

Ab Zeile 499 durch folgenden Teil ersetzt, wird während der Eingabe die TI$-Zeit rechts oben am Schirm dargestellt (im 1-Sekunden-Intervall aktualisiert).

 499 REM HINTERGRUNDAKTION
 500 GOSUB 170 ALARM NEU
 510 FOR I=1 TO 6: POKE 1056+I,ASC(MID$(TI$,I,1)): NEXT
 599 RETURN
 999 :
1000 REM TIMEOUT TT: >0 BIS 13
1010 TT=1: GOSUB 110
1020 PRINT CHR$(147)CHR$(17)CHR$(17)CHR$(17)"BITTE UM TASTENEINGABE..."
1030 DO
1040 GET A$: PRINT A$;
1050 LOOP