Beiträge von M. J. im Thema „BASIC und Assembler mischen“

    aber ich glaube, mit etwas Übung (und Fragerei hier) würde ich das schon hinkriegen.

    Denke ich auch.

    Ich werde die Idee aber wohl [...] eher auf dem PC umsetzen.

    Das ist natürlich auch eine Möglichkeit, sich erst einmal klar zu werden über den Aufbau des Programms sowie die zugrundeliegende Spielmechanik. Viele C64-Programmierer testen neue Spiele zuerst am PC aus und portieren dann die fertigen Algorithmen auf den C64. Sofern Dein Rollenspiel nicht zu komplex ist, kann man ja die Möglichkeit im Hinterkopf behalten, davon später einen C64-Port zu erstellen.

    Eine Empfehlung möchte ich in dieser Hinsicht noch geben: Da Du nun nicht C64-Basic als Programmiersprache nehmen wirst, wäre es wahrscheinlich sinnvoll, wenn Du so viel Funktionalität wie möglich in Scriptdateien, d. h. Textdateien mit einem beliebigen Format, festhälst. Im Idealfall besteht am Ende das eigentliche Spiel hauptsächlich aus diesen Textdateien (z. B. für die Unterhaltungen mit NPCs, aber auch die Landkarten), die von einem kleinen Scriptinterpreter während des Spiels ausgewertet werden. Dies hat mindestens zwei Vorteile: 1.) Es erleichtert eine Portierung auf andere Rechner wie z. B. C64 oder CPC, da neben einer handvoll Grafikroutinen zunächst nur der Scriptinterpreter neu geschrieben werden muß. Um Speicherplatz zu sparen, kann man sich immer noch zusätzlich überlegen, die Scriptdaten zu kompilieren, d. h. Kommandos in Tokenbefehle umzuwandeln wie bei Basic oder Daten zu packen. 2.) Es erleichtert aber auch die Entwicklung des Spiels selbst, denn bei der Verwendung von Scripts lassen sich diese im laufenden Programm direkt gegen neue Versionen austauschen, so daß man das Spiel quasi während des Spielens unmittelbar weiterschreiben kann, ohne neuen Code kompilieren oder neustarten zu müssen.

    Viel Spaß und Erfolg beim Entwickeln!

    Tatsächlich geht es mir auch um Rollenspiele.

    Da Du DDD erwähnst, gehe ich davon aus, daß Du die Ultima-Darstellung meinst, bei der die Landschaftsanzeige aus 11x11 Kacheln besteht, wobei jede Kachel 2x2 Zeichen groß ist. Das Landschaftsscrollen erledigst Du am besten in Assembler. Ich meine mich zu erinnern, daß DDD für die Berechnung der Sichtbarkeit der Kacheln ebenfalls auf eine Assemblerroutine zurückgegriffen hat. Die Frage wäre, wie gut Deine Assemblerkenntnisse sind, um solch eine Scrollroutine zu schreiben. (Besonders schwierig ist es nicht.)

    Was die Speichereinteilung anbelangt, so würde ich Dir zu dem oben genannten Modell raten, d. h. die Landschaft wird ab $a000 abgelegt. Bei $cc00 befindet sich der neue Zeichenspeicher (Textram) und ab $e000 könnte man einen neuen Zeichensatz ablegen. (Ich vermute mal, Du möchtest wie bei DDD den Zeichensatzmodus verwenden trotz aller Einschränkungen und nicht wie Ultima die Hires-Bitmap-Darstellung.)

    Normalerweise wird bei solchen Rollenspielen die Karte beschränkt auf eine Größe von 32x32 oder 64x64, wobei ein Byte einer bestimmten Kachel (wie z. B. Mauer oder Wald) entspricht. Für eine Karte von 32x32 benötigt man folglich 32*32=1024 Bytes = 1 kb. Oder halt 64*64 = 4096 Bytes = 4 kb.

    Große Landschaften wie z. B. die Außenwelt würde man in verschiedene Karten zerstückeln. Die einzelnen Kartenteile werden dann automatisch nachgeladen, wenn die Spielfigur eine Karte verläßt. Hierbei muß rundherum ein Rand definiert werden von 5 Kacheln, die zwar angezeigt, aber nicht betreten werden können. Dieser Rand ist identisch mit den ersten 5 Kacheln der umliegenden Karten, d. h. an dieser Stelle überlappen sich die Karten. Wird die Spielfigur in die Randzone bewegt, wird die neue Karte geladen, die ihrerseits eine Randzone von 5 Kacheln hat, die der alten Karte entsprechen. (Ich hoffe, das war klar genug ausgedrückt... :schande:)

    Bei DDD wird zu jeder Stadt auch stets ein neues Basicprogramm geladen, weshalb die Tastenkommandos auch unterschiedlich sind. Das gesamte Spiel besteht also aus jeder Menge einzelner Basicprogramme. Da sollte man sich vorab genau überlegen, welche Programmteile in jedem Basicprogramm vorkommen, und ob man diese nicht vielleicht in Assembler schreibt und in eine Programmbibliothek packt, die dann bei $c000 liegt.

    Was die Ausgabe von Text auf dem Bildschirm betrifft, so kann man die Zeiger für den Textspeicher in der Zeropage etc umändern, so daß die Kernal-Zeichenausgabe auch für den verlegten Textspeicher bei $cc00 verwendet werden kann. Es fragt sich jedoch, ob nicht auch hier eine Zeichenausgabe in Assembler die bessere Wahl ist, da man hierbei gleichzeitig einen automatischen Wortumbruch einbauen kann, so daß man sich später beim Programmieren von Textnachrichten nicht mehr darum kümmern muß. Schreibt man die Ausgabe in Assembler, so bietet es sich an, einer Variablen in Basic, z. B. S zu Beginn des Programms den Wert der Sprungadresse in der Sprungliste für diese Routine zuzuweisen. Im Programm selbst braucht man dann nur noch zu schreiben SYS S"Text...", und die Assemblerroutine liest direkt aus dem Basic-Programm die Textnachricht heraus und schreibt sie auf den Bildschirm.

    Aber da ich weder am C64 beruflich programmiert habe und ich denke, dass auch bei vielen Indiegame-Entwicklern die Planung (anfangs) hinten an steht, ist mein Punkt schon valide.

    Davon möchte ich dringend abraten. Ein Rollenspiel ist schon ein großes Programm. Ohne ausreichende Planung vorab geht da gar nichts. Es gibt mehrere Sachen, die Du unbedingt geklärt haben mußt, bevor Du auch nur eine Zeile programmierst:

    - Was genau soll Dein Programm machen. Welche Tastendrücke/Menüs soll es geben? Was soll passieren?

    - Wie sieht die Anzeige aus? Wo steht was auf dem Bildschirm?

    - Wie sehen die Algorithmen aus für die Kämpfe? Welche Spielmechanik soll verwendet werden?

    - Welche Ausrüstungsgegenstände gibt es? Welche Charakterattribute? Was bewirken sie?

    - Welche weitere Elemente sollen verwendet werden? Nahrung wie bei Ultima? Tag- und Nachtzyklus? Eingeschränkte Sichtbarkeit der Landschaft?

    - Wie ist die Landschaft organisiert? Gibt es nur eine große Karte oder wird beim Betreten einer Stadt etc in die Karte gezoomt?

    - Welche Gegner gibt es? Wie werden diese gehandhabt? Wann erscheinen sie? Wie verhalten sie sich?

    - Wie funktioniert die Interaktion mit NPCs? Erscheinen bei Kontakt fertige Menüs, z. B. zum Kaufen von Gegenständen oder zur Auswahl von bestimmten Fragen, oder kann man wie bei Ultima Stichwörter eingeben, auf die die Charaktere reagieren?

    - Was passiert mit Gegenständen z. B. Schatztruhen oder Pferden, die vom Spieler nicht mitgenommen werden? Verbleiben diese am Ort? Falls ja, wie wird das gespeichert? Oder verschwinden sie nach einiger Zeit?

    Und noch viele weitere Grundfragen mehr. Und wie man sieht, sind dies nur technische Fragen und keine Fragen zum (Handlungs)Inhalt des Rollenspiels.

    Nebenbei: Routinen, die man in Assembler schreiben sollte (aus Gründen der Geschwindigkeit):

    - Kopieren der Karte in den Landschaftsausschnitt auf dem Bildschirm. Hierbei müssen die Kachelwerte aus der Karte in die 2x2-Kacheln umgewandelt werden.

    - Falls gewünscht: Eingeschränkte Sichtbarkeit

    - IRQ für Animation des Zeichensatzes (z. B. Wellen oder Feuer)

    - Scrollen des Textausgabefensters

    - Weitere häufig genutzte Routinen für die Ein- und Ausgabe wie z. B. Einlesen eines Strings oder Ausgabe eines Textes auf den Bildschirm.

    Wie gesagt, am besten packt man eine ganze Reihe von Routinen in die Assemblerbibliothek, damit sie a) schnell ausgeführt werden, so daß das Spiel nicht zu sehr lahmt (wie das bei DDD manchmal der Fall sein kann) und b) nur einmal programmiert werden müssen und immer wieder von verschiedenen Basic-Programmen aufgerufen werden können.

    Wird der BASIC-Speicher dadurch nicht extrem klein?

    Nein, denn der Basic-Speicher selbst ist ja eher ziemlich klein und läßt viel Ram des C64 ungenutzt.

    Da wäre zuerst das Ram von $c000..$cfff (49152-53247), auf das man auch von Basic aus direkt zugreifen kann, und das sich daher gut eignet, um hier einen Programmcode mit Sprungliste und/oder einen neuen Textschirm unterzubringen. Letzteres verwendet man gerne, wenn man a) einen eigenen Zeichensatz oder b) viele Sprites benutzen möchte.

    Diese werden dann in den Bereich $d000..$ffff gelegt unter den IO-Bereich und das Kernal-Rom. Zugriff darauf hat man dann lesend zwar nur noch per Assembler, da es sich aber meistens um konstante Daten handelt, ist dies nicht weiter schlimm.

    Neben dem Bereich bei $c000..$cfff gibt es aber auch noch den Speicher zwischen $a000 und $bfff (40960-49151), d. h. unter dem Basic-Rom. Von Basic aus lassen sich in dieses Ram zwar nur Werte hineinschreiben, aber ein Assemblerprogramm kann relativ leicht Werte auch daraus lesen. "Relativ leicht" bedeutet, daß für das Lesen lediglich das Basic-Rom weggeschaltet werden muß, wohingegen das Kernal-Rom aktiviert bleibt. Es ist folglich nicht notwendig, die Interrupts zu sperren. Diese zusammenhängenden 8 kb Ram eignen sich gleichermaßen gut für Programmcode als auch große Daten wie z. B. eine Map oder Wortlisten. So hatte ich beispielsweise früher mal ein Adventure programmiert, welches einen Parser in Assembler hatte, der bei $c000 lag, und das recht große Vokabular nach $a000 verlagerte. Diese Verteilung hat das Basic-Programm sehr entlastet, da Strings wie z. B. Wortlisten sehr viel Platz im Basic-Speicher verbrauchen, der dadurch frei bleiben kann für weitere Programmlogik.

    Mit einer Mischung aus Basic und Assembler läßt sich sehr viel erreichen. Für viele Spiele wie Rollenspiele, Adventures oder Strategiespiele ist diese Kombination auch ausreichend. Jedoch stellt sie eine große Gefahr dar: Man unterliegt schnell der Versuchung, immer mehr Routinen nach Assembler zu verlagern, bis man vor der Frage steht, ob man überhaupt noch Basic verwenden soll. ^^