C 64
Grafik

Sprites ohne Geheimnisse

Sprites sind für manchen Einsteiger reizvolle, aber schwer zu programmierende Gebilde. Man muß viele Register kennen und mit Binärzahlen arbeiten. Dieser Artikel soll Ihnen helfen, beide Hürden zu meistern.

In Bild 1 finden Sie einen Fahrplan, der in 13 Stationen den Weg zum funktionierenden Sprite enthält. Diesem Schema werden wir nun folgen und anhand von zwei Sprites Abschnitt für Abschnitt ein Sprite-Programm aufbauen.

Bild 1. Fahrplan zum fertigen Sprite

Schritt 1

Der erste Schritt spielt sich im Kopf ab: Wir fällen eine Entscheidung darüber, ob unser Sprite einfarbig (dafür aber feiner strukturiert) oder mehrfarbig werden soll. Von dieser Entscheidung hängt nämlich Schritt 2 ab. Wir werden zweispurig weiterfahren, indem wir sowohl ein einfarbiges als auch ein »Multicolor«-Sprite wählen.

Schritt 2

Hier nehmen wir uns nun ein Blatt kariertes Papier zur Hand und zeichnen darauf ein Rechteck mit 24 Karos horizontaler und 21 Karos vertikaler Ausdehnung. Das wird unser Sprite-Raster. Dann warten wir auf den Kuß der Musen, der uns eine geeignete Sprite-Gestalt eingibt. Sind wir soweit, dann trennen sich die Wege:

  1. Das einfarbige Sprite tragen wir in unser Schema ein wie ein Stickmuster: Überall, wo ein Bildpunkt erscheinen soll, machen wir ein Kreuzchen (eine 1). Dann ergibt sich unser erstes Sprite wie Bild 2 zeigt.
  2. Etwas komplexer ist das mit dem Multicolorsprite. Hier treten vier Farben auf, die sich durch die Zahlenkombinationen 00, 01, 10 und 11 unterscheiden lassen. 00 besitzt die Hintergrundfarbe (ist also durchsichtig). Am besten benutzt man drei verschiedene Farbstifte (je einen für 01, 10 und 11) und trägt in das Schema mit dem jeweiligen Stift die Zweierkombination ein. Das Ergebnis sieht dann so aus wie in Bild 3.
Bild 2. Entwurf eines einfarbigen Sprite
Bild 3. So kann man einen Multicolor-Sprite aufbauen

Schritt 3

Nach diesen — mehr schöpferischen — Tätigkeiten kommt nun die Mathematik zu Wort. Wir müssen nämlich nun das, was wir auf dem Papier vor uns haben, in eine dem Computer verständliche Form bringen, also in Zahlen. Sehen Sie sich dazu nochmals Bild 2 und 3 an. Dort wurden zwei senkrechte Linien gezogen, die jede Zeile in drei Achtergruppen aufteilt. Eine solche Gruppe ist der künftige Inhalt eines Bytes. Jedes einzelne Kästchen entspricht einem Bit, und schon sind wir auf diese Weise bei den Binärzahlen gelandet. Wir werden aber hier keine Erklärung über alle Höhen und Tiefen dieser Zahlenart starten, sondern nur feststellen, auf welche Weise wir das Gebilde, das wir vor uns haben, in eine normale Zahl umrechnen können. Das ist ganz einfach. Sehen Sie sich dazu Bild 4 an.

Bit-Nr. 7 6 5 4 3 2 1 0
mal 128 64 32 16 8 4 2 1
Bild 4. Bitnummern und ihre Werte. Eine kleine Rechenhilfe.

Sie finden darin ein Byte und die Numerierung der Bits. Überall dort, wo an einer Bit-Position eine 1 steht, merkt man sich die im Bild darunter stehende Zahl. Alle auf diese Weise gemerkten Zahlen addiert man schließlich. Das Ergebnis ist die Dezimalzahl, die uns interessiert. Ein Beispiel soll das noch verdeutlichen. In Bild 3 (dem Multicolorsprite) finden Sie in Zeile 2 als erstes Byte: 10100000. Wenden wir nun unser Bild 4 darauf an:

1 0 1 0 0 0 0 0
128 64 32 16 8 4 2 1
Sie sehen, wir müssen addieren:
128 + 32 = 160

Nun wissen Sie, wie es gemacht wird. Üben können Sie das nun bei allen 126 Byte unserer beiden Sprites. Was dabei herauskommt, sehen Sie ebenfalls in Bild 2 und 3 rechts neben dem Raster.

Schritt 4

Nachdem wir nun unsere Sprites dem Computer mundgerecht gemacht haben, überlegen wir uns, in welchen Speicherbereich wir sie plazieren. Zwei Bedingungen sind dabei zu beachten:

  1. Die Sprites müssen im gleichen 16 K-Bereich liegen wie der Bildschirm. (Im Normalfall also von Speicherstelle 0 bis Speicherstelle 16383.)
  2. Die Startadresse der Sprite-Daten muß glatt durch 64 teilbar sein.

Außerdem sollte man natürlich sinnvolle Speicherbereiche auswählen, das heißt solche, die weder der Computer selber braucht (wie die ersten 1023 Byte), noch solche, die für das Basic-Programm benötigt werden. Allerdings gibt es erfreulicherweise Lücken in den ersten 1023 Speicherplätzen, die nicht oder nur recht selten vom Computer benötigt werden. Man kann dort tatsächlich die Daten von vier Sprites ablegen:

704 bis 767 1. Sprite
832 bis 895 2. Sprite
896 bis 959 3. Sprite
960 bis 1023 4. Sprite

Die letzten Bereiche gehören zum Kassettenpuffer. Falls Sie also während eines Sprite-Programmes planen, eine Datasetten-Operation vorzunehmen, müssen Sie dafür sorgen, daß Ihre Sprite-Daten später wieder in den Kassettenpuffer geschrieben werden.

Sollten Sie mehr als vier Sprites benötigen, dann bleibt Ihnen nichts anderes übrig, als dazu den Basic-Speicherraum zu verwenden. Ich lege die Daten in diesem Fall häufig an die obere Grenze, also knapp unter 16383. Dann muß ich diesen Speicherteil vor dem Überschreiben durch das Basic-Programm (und Variable) schützen.

Eine andere Möglichkeit ist es, den Start des Basic-Speichers höher zu legen. All dies soll uns an dieser Stelle aber nicht belasten. Wir gehen hier davon aus, daß wir mit vier Sprite-Mustern auskommen.

Wie in der obigen Aufstellung schon angedeutet, legen wir unsere Sprite-Daten für Sprite 1 (das einfarbige Sprite) nach Speicherstelle 704 und für Sprite 2 nach 832.

Nun geht's ans Programmieren:

Schritt 5

Wir lesen die gewonnenen Sprite-Daten in den Computerspeicher ein mittels zweier simpler Schleifen:

100 FOR I = 704 TO 766:READD :POKE I, D:NEXT I
110 FOR I = 832 TO 894:READD :POKE I, D:NEXT I
115 REM ****** DATAS VON SPRITE 1 ******
120 DATA 0,0,0,0,0,0,3,255,192,1,3,128
130 DATA 1,51,128,1,115,128,1,59,192
140 DATA 129,186,254,255,255,255, 129,2,1
150 DATA 129,2,1,253,254,127,222,0, 157
160 DATA 255,255,190,62,0,62,28,0,28
170 DATA 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
175 REM ****** DATAS VON SPRITE 2 ******
180 DATA 128,8,0,160,40,0,168,168,0
190 DATA 40,168,0,42,170,0,10,234,160
200 DATA 43,250,128,43,250,0,170,232,0
210 DATA 170,168,0,165,168,0,129,90,0
220 DATA 129,86,0,0,80,128,0,80,0
230 DATA 0,16,20,0,16,85,0,17,84,0,21,0
240 DATA 0,16,0,0,16,0

Damit hätten wir den schlimmsten Teil der ganzen Arbeit hinter uns!

Schritt 6

Nun müssen wir unserem Computer mitteilen, an welchen Stellen im Speicher wir die Daten verstaut haben. Dazu gibt es ab Speicherstelle 2040 die sogenannten Sprite-Zeiger. Hier kommt die Startadresse der Daten hinein und zwar eine Kennzahl, die sich so ergibt: Datenstartadresse/64 = Kennzahl

Für das erste Sprite folgt 704/64 = 11 und für das zweite Sprite 832/64 = 13. Gleichzeitig legt man auf diese Weise eine Sprite-Nummer fest, die von 0 (Speicherstelle 2040) bis 7 (Speicherstelle 2047) gehen kann. Unser Sprite 1 machen wir nun zum Sprite 0 und schreiben die Kennzahl 11 in Speicherstelle 2040. Analog dazu wird Sprite 2 zum Sprite 1 und wir schreiben 13 in Byte 2041:

245 REM **** SPRITE-ZEIGER ****
250 POKE2040,11:POKE2041,13

Immer dann, wenn im folgenden von einer Spritenummer N die Rede ist, meinen wir damit die durch die Spritezeiger festgelegte (also 0 für das einfarbige, 1 für das Multicolorsprite).

Schritt 7

Ob wir diesen Schritt jetzt gehen oder später, ist eigentlich gleichgültig. Es dreht sich um das Einschalten der Sprites. Dazu bedienen wir uns des Registers in Speicherstelle 53269. Dies ist das erste einer Reihe von Registern, die bei Sprites eine Rolle spielen und nach einem Schema funktionieren: Von den 8 Bits des Registers gehört zu jedem Bit ein Sprite. Also zu Sprite 0 das Bit 0, zu Sprite 1 das Bit 1 und so weiter, allgemein zu Sprite N das Bit N. Jedes Bit mit dem Inhalt 1 bewirkt, daß das dazugehörige Sprite angeschaltet ist, ein Bit-Inhalt 0 sorgt für das Ausschalten des Sprite

Ohne nun auf mathematische Einzelheiten einzugehen, genügt es zu wissen, daß man einzelne Sprites einschaltet mittels: POKE 53269,PEEK(53269)OR(2↑N) und abschaltet mit: POKE 53269,PEEK(53269)AND(255-2↑N)

Wir schalten nun unsere beiden Sprites an:

255 REM **** EINSCHALTEN ****
260 POKE53269,PEEK(53269)OR(2↑0): POKE53269,PEEK(53269)OR(2↑1)

Schritt 8

Sie werden festgestellt haben, daß kein Sprite auf dem Bildschirm erschienen ist. Wir müssen nämlich noch festlegen, wo das Sprite auftauchen soll, müssen also für jedes Sprite eine X- und eine Y-Position eingeben. Oje, jetzt wird es wieder komplizierter. Sehen Sie sich am besten mal das Bild 5 an. Sie sehen dort ein Koordinatensystem, das X-Werte von 0 bis 511 und Y-Werte von 0 bis 255 zuläßt. Genau das sind die möglichen Sprite-Orte. Dabei zählt die linke obere Ecke des Sprite-Rasters als Anhaltspunkt. Umrahmt ist der Teil der Fläche, der den sichtbaren Bildschirm darstellt. Man kann also Sprites so plazieren, daß sie unsichtbar bleiben, weil sie außerhalb des Bildschirms liegen. Auf dem Bild sind zwei Sprites gezeigt, von denen eins voll sichtbar (mit den Koordinaten 128, 120) und das andere unsichtbar ist (Koordinaten 360, 190). Auch teilweise sichtbare Sprites sind möglich.

Bild 5. In diesem Feld können sich Sprites aufhalten. Rot umrandet, der sichtbare Bildschirm.

Die X-Position eines Sprites wird ebenso wie die Y-Position in jeweils eine Speicherstelle gePOKEt. Diese findet man ab 53248, wobei 53248 die X-Position von Sprite 0, 53249 dessen Y-Position enthält, 53250 nimmt dann die X-Koordinate von Sprite 1, 53251 die Y-Koordinate auf und so weiter. Allgemein POKEt man die X-Koordinate für Sprite N in 53248 + 2*N und die Y-Koordinate in 53249 + 2*N.

Für die Y-Positionen ist das ohne Probleme. Bei der X-Koordinate tauchen Schwierigkeiten auf: Sobald eine X-Position größer als 255 benötigt wird, kann man sie nicht mehr ein-POKEn. In diesem Fall verfährt man so. Man bildet die Differenz X-256 = XI und POKEt XI ein. Außerdem muß man nun in einem weiteren Register in 53264 (das wieder für jedes Sprite ein Bit bereithält) das dazugehörige Bit auf 1 setzen.

Für diese Eigenheiten gibt es natürlich eine Menge sehr eleganter Programmier-Möglichkeiten, die alle das Manko haben, reichlich unverständlich zu sein. Begnügen wir uns also mit einfachen IF..THEN-Abfragen. Unser Programm lautet also weiter:

265 REM **** SPRITEPOSITIONEN ****
270 FOR N = 0TO1: PRINT"SPRITE"N;:INPUT"X,Y=";X(N),Y(N)
280 POKE 53249+2*N,Y(N)
290 IFX(N)>255THENPOKE53248+2*N,X(N)-256:POKE53264,PEEK(53264)OR(2↑N):GOTO305
300 POKE53248+2*N,X(N):POKE53264,PEEK(53264)AND(255-2↑N)
305 IF K=1 THEN RETURN
310 NEXT N

Dabei verlassen wir uns darauf, daß für X und Y nur sinnvolle Werte eingegeben werden. Wenn das aus irgendeinem Grund fraglich ist, müssen noch weitere IF…THEN-Abfragen dazukommen, die X und Y überprüfen vor Zeile 280. Die Zeile 305 ist vorsorglich für eine spätere Option eingebaut.

Schritt 9

Hier geht's um die Farbe unserer Sprites. Wie Sie sich denken können, müssen Multicolor-Sprites anders behandelt werden als einfarbige. Doch beiden gemeinsam ist zunächst folgendes:

Für jedes Sprite existiert ein Farbregister, das bei 53287 anfängt. Unser Sprite 0 hat in dieser Speicherstelle seinen Farbcode stehen, das Sprite 1 (das Multicolor-Sprite) in 53288 und so weiter. Allgemein steht die Farbe von Sprite N in Farbregister 53287+N.

Multicolorsprites bewahren in dieser Speicherstelle die Farbe auf, die zur Ziffernkombination 10 gehört. Die Kombination 01 und 11 werden für alle Multicolor-Sprites gemeinsam festgelegt. Dabei gehört der in 53285 stehende Farbcode zur Kombination 01, der in 53286 zur Kombination 11.

Außerdem muß für die Multicolor-Sprites noch der Mehrfarben-Modus eingeschaltet werden. Dazu existiert wieder ein Register (53276), in dem fürjede Spritenummer N ein Bit reserviert ist. Ist dieses Bit gleich 1, dann liegt das dazugehörige Sprite im Mehrfarben-Modus vor. Allgemein schaltet man für Sprite N also den Multicolor-Modus an durch: POKE 53276,PEEK(53276)OR(2↑N)

Das Rücksetzen geschieht durch Löschen des Bit N: POKE 53276,PEEK(53276)AND(255-2↑N)

Wir wählen für Sprite 0 die Farbe CYAN, für das Multicolor-Sprite die Zuordnungen:

Kombination 01 = Grün
11 = Gelb
10 = Rot
315 REM **** SPRITE-FARBEN ****
320 POKE53287,3:REM SPRITE 0 = CYAN
330 POKE53285,5:REM SPRITE 1 KENNUNG 01 = GRUEN
340 POKE53286,7:REM KENNUNG 11 = GELB
350 POKE53288,2:REM KENNUNG 10 = ROT
360 POKE53276,PEEK(53276)OR(2↑1):REM MULTICOLORMODUS EINGESCHALTET

Schritt 10

Wenn Sie bis hierher unser schrittweise entwickeltes Programm eingegeben und gestartet haben, sehen Sie nun auf Ihrem Bildschirm zwei nette Sprites. Im folgenden werden wir nun noch einige Besonderheiten der Sprite-Programmierung kennenlernen, die wir aber im Text nur noch mit den allgemeinen Befehlen bearbeiten. Ein beigefügtes Beispielprogramm, das bis hierher (außer den Zeilen bis 100) mit unserem bisher entwickelten identisch ist, enthält die konkreten Befehle. Um das weitere Vorgehen deutlich zu machen, sind dort zunächst in den Zeilen 370 bis 400 drei weitere Sprites (mit denselben Daten) eingerichtet worden.

Schritt 10 stellt uns vor die Frage, ob wir unser Sprite in X-Richtung doppelt so groß darstellen wollen. Zu diesem Zweck gibt es dann wieder ein Register (jedes Bit ein Sprite), das Register 53277. Die X-Vergrößerung ist eingeschaltet, wenn für Sprite N das Bit N auf 1 gesetzt wurde mittels: POKE53277,PEEK(53277)OR(2↑N)

Das Zurückschalten findet dann wieder statt mit …AND(255-2↑N). Im Programm wurde Sprite 2 derart vergrößert.

Schritt 11

Dasselbe wie eben kann auch in der Y-Richtung geschehen. Dazu dient uns das Register 53271. Anschalten dieser Y-Verdoppelung also durch: POKE53271,PEEK(53271)OR(2↑N) und Abschalten wie oben schon gezeigt mittels …AND(255-2↑N).

Im Programm ist Sprite 3 so vergrößert worden. Sprite 4 ist — auch das funktioniert — sowohl in X- als auch in Y-Richtung gedehnt worden.

Schritt 12

Hier geht's um die Vorfahrt. Wenn sich zwei Sprites überdecken, bleibt immer dasjenige »vorne«, das die niedrigere Nummer besitzt. Also verdeckt Sprite 0 alle anderen. Man hat darauf — außer durch geeignete Auswahl der Spritenummer ganz zu Beginn — keine Einflußmöglichkeit.

Anders verhält sich das bei der Vorfahrt von Sprites und Bildschirmzeichen. Die kann mittels Register 53275 geregelt werden. Wieder gehört zu jedem Sprite ein Bit. Ist dieses gleich 0, erscheint das Sprite vor den Bildschirmzeichen, ist es gleich 1, versteckt es sich dahinter. Im Beispielprogramm wurde in den Zeilen 450 und 460 geregelt, daß sich alle Autos (Sprites 0,2 und 4) hinter den Bildschirmzeichen aufhalten.

Schritt 13

Unser Computer paßt auch auf Zusammenstöße auf. Zwei Register (wieder Bit N für Sprite N) sind zuständig:

Kollisionen Sprite mit Sprite (53278), Kollisionen Sprite mit Bildschirmzeichen (53279). Für jedes Sprite, das in eine Kollision verwickelt wurde, wird das entsprechende Bit auf 1 gesetzt. Wenn also Sprite 0 mit Bildschirmzeichen zusammenstößt, findet man in Register 53279 den Wert 1. Die Berechnung des zu erwartenden Wertes kann mit dem Bild 4 geschehen, wobei für mehrere Sprites die Zahlen zu addieren sind. Im Beispielprogramm wird in den Zeilen 530 und 540 das Sprite/Sprite-Kollisionsregister 53278 auf Zusammenstöße von Sprite 0 mit Blumen (Sprite 1 und Sprite 3) abgefragt. Bei einer Kollision werden im Register 53278 die Werte 3 oder 9 erzeugt.

Das Auslesen dieser beiden Register ist ziemlich radikal: Nach dem PEEK-Kommando sind sie gelöscht. Deshalb sollte man die ausgelesenen Werte in einer Variablen speichern (Zeile 530:A). Außerdem kann man die Register durch PEEKen vor der ersten Kollisionsabfrage leeren, was wir in Zeile 460 machen.

Unser Beispielprogramm (Listing 1) ist so aufgebaut, daß wir mit den Cursortasten das Sprite 0 steuern können. Jedesmal, wenn dabei eine Blume überfahren wird, ändert sich die Autofarbe. Um das Programm kurz und übersichtlich zu gestalten, finden Sie keine Sicherung gegen das Heraussteuern aus dem zulässigen Sprite-Koordinaten-Bereich. Durch Eingabe von ← kann das Programm beendet werden.

Damit wissen Sie alles, was Sie zur Programmierung von Sprites brauchen. Probieren Sie mal ein wenig durch Veränderungen am Beispielprogramm herum. Falls Sie nun Blut geleckt haben, können Sie Ihr Wissen noch vertiefen durch die nachfolgend genannte Literatur:

(Heimo Ponnath/ah)
1 rem *********************************
2 rem *                               *
3 rem *  beispielprogramm zu sprites  *
4 rem *                               *
5 rem * heimo ponnath   hamburg  1985 *
6 rem *                               *
7 rem *********************************
8 rem
45 rem **** bildschirmfarben ****
50 poke53280,0:poke53281,0:poke646,14
95 rem **** einlesen sprite-daten ****
100 fori=704to766:readd:pokei,d:nexti
110 fori=832to894:readd:pokei,d:nexti
115 rem ****** datas von sprite 1 ******
120 data0,0,0,0,0,0,3,255,192,1,3,128
130 data1,51,128,1,115,128,1,59,192
140 data129,186,254,255,255,255,129,2,1
150 data129,2,1,253,254,127,222,0,157
160 data255,255,190,62,0,62,28,0,28
170 data0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
175 rem ****** datas von sprite 2 ******
180 data128,8,0,160,40,0,168,168,0
190 data40,168,0,42,170,0,10,234,160
200 data43,250,128,43,250,0,170,232,0
210 data170,168,0,165,168,0,129,90,0
220 data129,86,0,0,80,128,0,80,0
230 data0,16,20,0,16,85,0,17,84,0,21,0
240 data0,16,0,0,16,0
245 rem **** sprite-zeiger ****
250 poke2040,11:poke2041,13
255 rem **** einschalten ****
260 poke53269,peek(53269)or(2^0):poke53269,peek(53269)or(2^1)
265 rem **** sprite-positionen ****
270 forn=0to1:print"sprite"n;:input"x,y=";x(n),y(n)
280 poke53249+2*n,y(n)
290 ifx(n)>255thenpoke53248+2*n,x(n)-256:poke53264,peek(53264)or(2^n):goto305
300 poke53248+2*n,x(n):poke53264,peek(53264)and(255-2^n)
305 ifk=1thenreturn
310 nextn
315 rem **** sprite-farben ****
320 poke53287,3:rem sprite 0 =cyan
330 poke53285,5:rem sprite 1 kennung 01=gruen
340 poke53286,7:rem kennung 11=gelb
350 poke53288,2:rem kennung 10=rot
360 poke53276,peek(53276)or(2^1):rem multicolormodus eingeschaltet
365 rem *** sprite-groessen ***
367 rem +++++ 3 weitere sprites +++++
370 poke2042,11:poke2043,13:poke2044,11:rem spritezeiger auf vorhandene daten
380 poke53269,peek(53269)or(2^2):poke53269,peek(53269)or(2^3)
385 poke53269,peek(53269)or(2^4):rem anschalten
390 poke53248+2*2,100:poke53249+2*2,100:poke53248+2*3,80:poke53249+2*3,200
395 poke53248+2*4,150:poke53249+2*4,150:poke53291,4
400 poke53289,1:poke53290,6:poke53276,peek(53276)or(2^3):rem positionen+farben
405 rem +++++ diese vergroessern +++++
410 poke53277,peek(53277)or(2^2):rem sprite 2 in x-richtung verdoppeln
420 poke53271,peek(53271)or(2^3):rem sprite 3 in y-richtung verdoppeln
430 poke53271,peek(53271)or(2^4):poke53277,peek(53277)or(2^4):rem sprite 4 x+y
435 rem **** vorfahrt-regelung *****
440 poke53275,peek(53275)or(2^0):poke53275,peek(53275)or(2^2)
450 poke53275,peek(53275)or(2^4):rem alle autos hinter bildschirmzeichen
455 rem *** kollisionen vorbereiten ***
460 a=peek(53278):a=peek(53279)
465 rem *** steuern von sprite 0 ***
470 printchr$(147):n=0
480 geta$:ifa$=""then480
490 ifa$=chr$(17)thenpoke53249,peek(53249)+3:rem abwaerts
500 ifa$=chr$(145)thenpoke53249,peek(53249)-3:rem aufwaerts
510 ifa$=chr$(157)thenx(n)=x(n)-3:k=1:gosub290:k=0:rem links
520 ifa$=chr$(29)thenx(n)=x(n)+3:k=1:gosub290:k=0:rem rechts
525 rem *** sprite/sprite-kollision ***
530 a=peek(53278)
540 ifa=3ora=9thengosub600:rem kollision mit blumen ?
545 rem *** ende abfrageschleife ***
550 ifa$="_"thenend:rem programmende
560 goto480:rem erneute abfrage
565 rem *** kollisionsfolge ***
600 f=peek(53287)+1:iff>255thenf=0:rem farbcode sprite 0 erhoehen
610 poke53287,f:return:rem in farbregister von sprite 0
Listing 1. So können Sprites komplett programmiert werden.
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →