home *** CD-ROM | disk | FTP | other *** search
- Sprite-Editor in Turbo-Pascal
-
- Sprites, benutzerdefinierte Zeichen also, sind vielen sicherlich
- noch aus den Tagen der Heim-Computer bekannt. Auch mit modernen
- Programmiersprachen lassen sich diese Gebilde auf dem Bildschirm
- darstellen, welche Befehle dazu nötig sind und wie man sich die
- Arbeit, diese Sprites zu gestalten, wesentlich erleichtern können,
- soll Ihnen der folgende Beitrag am Beispiel von Turbo-Pascal 4.0
- aufzeigen. Verwendet wird dabei eine CGA-Graphik-Karte in der 320
- *200-Punkte-Auflösung (4 Farben), jedoch wurde versucht, das
- Programm so allgemein wie möglich zu halten, so daß es mit
- möglichst wenig Aufwand auch an andere Graphik-Karten bzw.
- Auflösungen angepasst werden kann.
-
- Turbo 4.0 stellt ja in der UNIT Graph eine beachtliche Menge an
- Graphik-Befehlen zur Verfügung. Die UNIT wurde leider nach
- Abschluss des Handbuches nochmals überarbeitet, so daß man sich vor
- dem Arbeiten hiermit auf jeden Fall das File "Graph.DOC", das
- wesentliche Ergänzungen und Erläuterungen enthält, ausdrucken
- lassen sollte. Etwas leichter tun sich sich hier die Besitzer des
- zusätzlichen Handbuches (Addendum) zu Turbo-C, V.1.5, in dem die im
- Prinzip gleichen Graphik-Befehle übersichtlich zusammengefasst
- sind. BGI, das Borland-Graphics-Interface, ist ja gleichermassen
- bei Turbo-C und Turbo-Pascal vorhanden, lediglich der Turbo-Basic-
- Compiler benutzt die zum GW-Basic kompatiblen Graphik-Befehle.
-
-
- Betrachten wir zunächst die Befehle, die uns die UNIT Graph für die
- Sprite-Gestaltung und -Bewegung zur Verfügung stellt :
-
- - mit GetImage (links, oben, rechts, unten, Puffer) wird ein
- rechteckiger Bildausschnitt in eine Puffervariable kopiert,
- - mit PutImage (x, y, Puffer, Operand) wird der Inhalt einer
- Puffervariablen in einen rechteckigen Bildausschnitt kopiert,
- wobei logische Verknüpfungen mit dem Inhalt dieses Ausschnitts
- über "operand" möglich sind.
- - mit ImageSize (links, oben, rechts, unten) wird die Grösse der
- Puffervariablen bestimmt.
-
- Viel ist das ja nun gerade nicht, und zu allem Überfluss ist die
- Puffervariable auch noch vom Typ POINTER, d.h. ein untypisierter
- Vertreter der berühmt-berüchtigten Zeiger. Ungünstig ist auch, daß
- jeder Sprite erst auf dem Bildschirm gezeichnet und "eingefangen"
- werden muss. Das könnte man zwar mit einem "Programm-Vorspann"
- lösen, in dem alle verwendeten Figuren dargestellt und so nebenbei
- mit GetImage erfasst werden, bei vielen, ev. noch aufwendigen
- Figuren kann das aber auf Dauer auch ganz schön nerven. Abgesehen
- davon, daß bei der Programmentwicklung jede Figur recht mühsam
- pixelweise am Bildschirm entworfen werden muß, muß das Programm
- alle Befehle zum Zeichnen der Figuren enthalten, was auch einiges
- an Speicherplatz kosten kann. Viel einfacher wäre doch, die
- fertigen Figuren aus einer Datei zu laden ! Nehmen wir also
- zunächst einmal die Puffervariable unter die Lupe und sehen, was
- sich damit alles anstellen läßt.
-
- Welche Daten enthält ein Sprite-Pointer ? Der Pointer selbst
- enthält bekanntlich nur eine Speicher-Adresse (er zeigt auf diese
- Speicherstelle) und ab dieser Adresse sind die maßgebenden Werte
- abgelegt. Nicht ganz einfach also, hier direkt den Pointer zu
- "knacken", mit WRITE können die Werte auch nicht ausgegeben werden,
- also überlisten wir den Zeiger und speichern ihn ganz einfach ab.
- Hierzu dient die folgende Function :
-
- LISTING 1 :
-
- (*-----------------------------------------------*)
- FUNCTION SaveGraphikScreen (Links, Oben, Rechts, Unten : INTEGER;
- DateiName : STRING) : INTEGER;
-
- VAR BildGroesse : INTEGER;
- BildPtr : POINTER;
- Hilf : INTEGER;
- Datei : FILE;
- (*------------------*)
- PROCEDURE ScreenToZeiger;
-
- BEGIN
- BildGroesse := ImageSize (Links, Oben, Rechts, Unten);
- (* Grösse der Sprites festlegen *)
- GetMem (BildPtr, BildGroesse);
- (* entsprechend dimensioniern *)
- GetImage (Links, Oben, Rechts, Unten, BildPtr^);
- (* vom Bildschirm ins RAM *)
- END;
- (*------------------*)
- BEGIN (* SaveGraphikScreen *)
- ScreenToZeiger;
- ASSIGN (Datei, DateiName);
- {$I-} REWRITE (Datei, 1); {$I+}
- (* untyp. Datei mit RECORD-Grösse "1" *)
- Hilf := IOResult; SaveGraphikScreen := Hilf;
- IF Hilf <> 0 THEN EXIT;
- BlockWrite (Datei, BildPtr^, BildGroesse);
- (* vom RAM auf Diskette *)
- CLOSE (Datei);
- END;
- (*-----------------------------------------------*)
-
-
- In diesem Programm ist bereits ein Teil der grundlegenden Routinen
- zur Sprite-Verwaltung enthalten. Das Ergebnis unserer Bemühungen
- kann mit einem Disketten-Editor (z.B. "Dump", PASCAL 8/87)
- betrachtet werden, ein Beispiel sehen wir in Bild 1. Die ersten 4
- Bytes enthalten die um jeweils 1 erniedrigten Grössen von Breite
- und Höhe des Bildschirmausschnitts, sie werden von der ImageSize-
- Prozedur automatisch reserviert und ermittelt. Im Turbo-Handbuch
- wird bei ImageSize auch erläutert, wie sich der erforderliche
- Speicherplatz ergibt :
-
- Bytezahl = (Breite * Höhe * Bits/Pixel) DIV 8
-
- Der Wert von Bits/Pixel gibt an, wieviel Bits zur Speicherung eines
- Pixels nötig sind und hängt von der jeweiligen Auflösung der
- Graphik- Karte ab. Bei der CGA-Karte in der niedrigen Auflösung hat
- Bits/Pixel den Wert 2, in einem Byte (= 8 bit) können also
- Informationen für 4 Pixels abgelegt werden. Ein wenig
- experimentieren mit einem kleinen Programm ergibt, daß die
- Farbnummer jedes Pixels als Faktor berücksichtigt wird und daß der
- Inhalt eines Bytes bei Farbe "1" die Werte (von links) 2 hoch 6
- (=64), 2 hoch 4 (=16), 2 hoch 2 (=4) und 2 hoch 0 (=1) betragen
- kann. Ist in einer Vierergruppe nur der Pixel ganz links gesetzt,
- so ist der Byte-Inhalt bei Farbe 1 gleich 64, bei Farbe 3 gleich
- 192. Wie sich leicht nachrechnen läßt, beträgt also der höchste
- Wert, der sich darstellen läßt, 255, und mehr ist ja bekanntlich
- auch nicht möglich mit einem Byte. Nicht ermitteln konnte ich
- allerdings, warum am Ende der Datei grundsätzlich immer noch 2
- Bytes zusätzlich angehängt werden, bei der Umwandlung der Daten in
- eine UNIT werden diese beide überzähligen Bytes dann auch ohne
- Schaden ausgeblendet. Zwar abgespeichert und als Klötzchen wieder
- dargestellt, jedoch nicht mehr als Sprite abgebildet wird eine
- Figur die nur eine Höhe von einem Pixel hat ! Und als letzte
- Einschränkung musste ich feststellen, daß der gesetzte ViewPort
- entgegen den Angaben in GRAPH.DOC von PutImage nicht berücksichtigt
- wird, d.h. ein Sprite wird auch über die von ViewPort gesetzten
- Grenzen hinaus gezeigt. Ein weiterer wichtiger Befehl für den
- Umgang mit Pointer-Variablen ist - zumindest bei unserem Editor -
- der "MOVE"-Befehl, mit
-
- MOVE (Sprite^, Block, Size)
-
- wird der von der Variablen "Sprite" angezeigte Speicherbereich in
- ein zuvor festgelegtes ARRAY OF BYTE transportiert, der
- Speicherinhalt, d.h. unsere Sprite-Daten können so auf üblichem
- Wege ausgewertet werden. (bitte nicht das "^"-Zeichen vergessen,
- mit "Sprite" alleine wird nur die Adresse angegeben, ab der die
- Daten im RAM abgelegt sind. Eine Verwechslung von Adresse und
- Inhalt garantiert Ihnen mit Sicherheit einen Warmstart Ihres
- Computers.)
-
- Mit dieser Möglichkeit, Sprite-Daten von Diskette zu laden, steht
- der Programmierung eines Sprite-Editors nichts mehr im Wege. Bevor
- wir einige der Hauptroutinen betrachten, seien die Möglichkeiten
- und Grenzen unseres Editors kurz aufgezeigt :
-
- - Pixelweises Zeichnen eines Sprites mit max. 24 Pixeln Breite und
- 16 Pixeln Höhe. Die jeweilige Länge und Breite kann im Editor
- festgelegt werden.
- - Für grössere Figuren können max. 9 Sprites zu einem grossen
- Sprite zusammengefasst werden. Der so zusammengefasste Sprite
- kann bis zu 72 Pixels breit und 48 Pixels hoch sein (immer
- ausgehend von der linken oberen Ecke).
- - Die Pixels im Editierfeld können in horizontaler und vertikaler
- Richtung gespiegelt werden, wobei die ursprüngliche Form gelöscht
- wird (es müsste statt "spiegeln" wohl besser "drehen" heissen.
- Will man spiegeln und die Ausgangsfigur dabei beibehalten, kann
- man die Ausgangsfigur einfach auf Diskette zwischenlagern und
- wieder dazuladen.
- - Der Sprite kann im späteren Hauptprogramm entweder aus einer
- (untypisierten) Datei eingelesen werden oder als UNIT aufgerufen
- werden. Max. 4 Farben stehen zur Verfügung (Wahl mit den Tasten
- 0..3); Hintergrundfarbe und Palette können im Hauptprogramm
- beliebig geändert werden, die beiden letztgenannten werden beim
- Abspeichern nicht berücksichtigt.
-
- Einige der wichtigsten Routinen sind im folgenden kurz erläutert,
- im Listing sind ebenfalls ausführliche Kommentare enthalten, so daß
- das Verständniss des Programmes wohl wenig Probleme bereiten
- dürfte.
- Betrachten wir zunächst die UNIT GraphV1, eine Zusammenstellung der
- allgemeingültigen Routinen, die auch in ihren eigenen Programmen
- Verwendung finden könnten. Die Routine zum Abspeichern und Laden
- des Sprites ist gleich so gehalten, daß auch ein kompletter
- Bildschirminhalt erfaßt werden kann. Sie könnten somit also mit
- einem Pascal-Programm auch den Bildhintergrund entwerfen, mit
- SaveGraphicScreen abspeichern und im späteren Hauptprogramm mit
- LoadGraphicScreen hinzuladen. Vielleicht ebenfalls ganz nützlich
- für andere Programme sind die Routinen, um den Bildschirminhalt
- nicht auf Diskette, sondern im RAM zu speichern (RamToScreen und
- ScreenToRam). Mit "Holstring" lassen sich im Graphikmodus Eingaben
- (mit einer vorgegebenen Länge) vornehmen. SchreibSpace schliesslich
- behebt den Mißstand, daß ein gesetzter Buchstabe zur Korrektur
- bislang in der Hintergrundfarbe überschrieben werden musste, das
- Space-Zeichen selbst ist natürlich auch als Sprite definiert.
-
- Die "Schalt-Zentrale" des Hauptprogrammes bildet die Prozedur
- "Bewegung". Hierin werden sämtliche Eingaben vorgenommen und
- ausgewertet. Die möglichen Eingaben sind auf dem Bildschirm in
- einem Fenster dargestellt und im Quelltext (hoffentlich
- ausreichend) kommentiert. In der Prozedur "MachGrossenSprite"
- befinden sich einige Routinen des übergeordneten Programmteils in
- leicht veränderter Form; hier könnte man ev. einiges
- zusammenfassen, aber ich glaube, so ist es übersichtlicher. Die
- Inhalte der beiden Eingabebildschirme bleiben übrigens bei einem
- Wechsel erhalten, so kann man problemlos ein kleines Bild editieren
- und gleich zu einem grossen Bild zusammenfassen.
-
- Der eigenen Kreativität sind nicht nur beim Entwerfen der Figuren
- keine Grenzen gesetzt, auch das Programm lässt sich noch in einigen
- Punkten ergänzen. Zwei Vorschläge :
-
- - Den bereits im Menue enthaltenen, aber nicht ausgearbeiteten
- Punkt "<D>irectory" mit dem in PASCAL 6/7 1988 enthaltenen und
- zur UNIT umgeschriebenen Programm "DatWahl" ausarbeiten.
- - Grosse Sprites pixelgenau zusammenfügen.
-
- Sicher fallen Ihnen noch einige weitere Punkte zur Verbesserung
- beim Studium des Listings ein. - Einstweilen viel Spaß beim Sprite-
- Zeichnen wünscht die Redaktion.
-
-
- Programm : Sprite-Editor
- Funktion : Toolbox zum Entwerfen und Darstellen von
- benutzerdefinierten Figuren (Sprites) für den
- CGA-320*200-Punkte-Modus
- Hardware : IBM-kompatibler Rechner mit CGA-Graphik
- Software : Turbo-Pascal 4.0
-
-
- (Gerd Kraus)