[WIP]
Moin, ich bastel grad an einer kleinen Spielengine - mal schauen ob es ein Projekt wird was ich auch mal fertigstelle
Inhaltsverzeichnis
- Grundsätzliche Definitionen (Anforderungen an die Engine und deren Bestandteile): weiter unten in Diesem Beitrag.
- System
- Sound
- Grafik
- usw.
Die Engine:
Die Anforderungen an die Engine sind wie folgt:
- für Adventures geeignet (also nicht unbedingt tauglich für Actionspiele)
- 640x480 Pixel in 8 Bit Farbtiefe
- Animationen
- PCM Sound per Creative ADPCM bis 11kHz (im Grunde genommen *.Voc Files - allerdings entferne ich vorher die Blockheader, keine Lust mich zur Laufzeit damit herumzuschlagen), bzw. Wav bis 22khz in 8 Bit - diese Formate können eben von allen Soundblasterkompatiblen abgespielt werden.
- Spielablaufsteuerung mittels kompilierten Skripten
- Maus- und Tastatursteuerung
- Savegames - am besten mit Aufwärtskompatibilität damit alte Savegames auch in neueren Spielversionen funktionieren (können)
Technisches:
Sound:
Kurz und knapp, Soundblaster 2 Kompatibilität ist angestrebt, d.h.
- Soundwiedergabe per DMA und Soundkarteninterrupt
- Creative ADPCM mit bis zu 11kHz in Mono, bzw. 8Bit PCM mit bis zu 22kHz in Mono
Grafik:
Wir haben 256 Farben bei 640x480. 256 Farben sind nicht viel, aber glücklicherweise ist der 8Bit Mode indexed - das heißt: Malen nach Zahlen!
Statt der Farbe wird eine Farbnummer gespeichert, diese wiederum zeigt auf eine Stelle in unserer Palette in der dann die tatsächliche Farbe zu finden ist - und diese Palette können wir relativ frei definieren, damit können wir die Palette so wählen dass sie nur Farben enthält die wir auch nutzen.
Also auch wenn nur 256 Farben zur Verfügung stehen, können wir wenigstens dafür sorgen dass es genau die Farben sind die wir auch nutzen.
Nachteil:
Diese 256 Farben gelten nicht für jedes einzelne Bild/jeden einzelnen Sprite, sondern für den gesamten Bildschirm. Wenn wir also mehrere Bilder gleichzeitig anzeigen müssen sich diese Bilder die 256 Farben teilen.
Noch eine kleine Anmerkung: 24/32 Bit Bitmaps verwenden eine Farbtiefe von 8 Bit pro Farbkanal, der Standard VGA DAC ist aber 6 Bittig, d.h. 2 Bit Pro Farbkanal können nicht dargestellt werden.
Ich muss also die Bitmaps gewissermaßen auf 18 Bit Farbtiefe reduzieren - das geht allerdings einfach mit einem shr 2.
Die Unterteilung des Farbraumes:
Sichtbare Bildelemente Ingame:
- User Interface
- Hintergrund
- bis zu 4 Charaktere
- 1 Portrait
Sichtbare Bildelemente Zwischenszene:
- User Interface
- Szene
- 1 Portrait
Farben:
016 User Interface
024 Portrait
024 Charakter
120 Hintergrund (256 - (User Interface (16)+4*Charakter(24)+Portrait(24)) )
216 Szene (256 - (User Interface (16)+Portrait(24))
Bei den Farben gibt es noch eine Besonderheit: die UI ist immer vorhanden und beinhaltet Transparenz - Bilder die Farben der UI nutzen bekommen Quasi mehr Farben.
Bleistift: in der UI (16 Farben) ist die Farbe Schwarz enthalten, in dem Hintergrundbild (120 Farben) ist ebenfalls die Farbe schwarz enthalten - d.h. der Hintergrund kann das Schwarz der UI nutzen statt ein eigenes schwarz zu definieren und kann daher 121 Farben nutzen - das wird bei der Bilddekodierung etwas mehr Aufwand, aber verbessert die Farbwiedergabe.
Bildgrößen:
Diese Engine entsteht weil ich ein Spiel portiere (ob ich es veröffentliche hängt davon ab, ob ich es fertigstelle und der eigentliche Entwickler es erlaubt) und daher muss ich mit vorhandenem Material arbeiten und die Engine ist letztlich genau für dieses Spiel angepassr, daher auch folgende Bildgrößen:
- Hintergrund/Szene 640x360
- Charakter 256x360
- Portrait 93x108
- GUI 640x120
Skriptengine/Compiler:
Das Compilierte Skript: (WIP)
Das hier ist noch stark in überarbeitung....
- Die darstellbaren Zeichen werden genullt, d.h. A=0,B=1 usw. - das macht den Interpreter einfacher
- Befehle sind Zeichen >=$80 - d.h. Zeichen ab Zeichen 128 aufwärts markieren Befehle - das macht letzlich 128 verschiedene Befehle, sollte reichen.
- Variablen/Objekte werden nicht mit Klarnamen gespeichert, sondern über eine 16Bit ID - das macht die Kompilierung der Skripte komplizierter (und ich brauche ab der ersten kompilierung eine Ressourcendatei um sicherzustellen dass immer dieselben IDs vergeben werden - sonst funktionieren die Savegames nicht Versionsübergreifend), aber den Interpreten deutlich einfacher
Befehle:
- IF (Ausdruck) Befehle ELSE Befehle ENDIF- Dieser Befehl muss gefolgt werden von einem Ausdrucksbefehl
- Ausdrucksbefehle:
-- Equal (Variable, Konstante)
-- Equal(Variable,Variable)
-- Equal(Operation,Konstante)
-- Equal(Operation,Variable)
-- Equal(Operation,Operation)
-- Dasselbe nochmal mit >,<,>=,<=
--- Der Befehlsumfang ließe sich mit der Verwendung von 4 reservierten Tempvariablen (pro Seite des Ausdrucks brauche ich 2 Tempvariablen) reduzieren auf Ausdruck(Variable, Konstante) + Ausdruck(Variable,Variable)
--- vermutlich werde ich den reduzierten Ansatz wählen - scheint einfacher, sinnvoller und belastet das Befehlsbudget weniger
- Operationsbefehle: (reduzierter Ansatz (Tempvariable))
-- shl (Variable,Konstante) (max 15)
-- shl (Variable,Variable) (max 15)
-- shr (Variable,Konstante) (max 15)
-- shr (Variable,Variable) (max 15)
-- not (Variable)
-- mul (Variable, Konstante)
-- mul (Variable, Variable)
-- div (Variable,Konstante)
-- div (Variable,Variable)
-- add (Variable,Konstante)
-- add(Variable,Variable)
-- sub (Variable,Konstante)
-- sub (Variable,Variable)
-- and (Variable,Konstante)
-- and (Variable,Variable)
-- or (Variable,Konstante)
-- or (Variable,Variable)
-- xor (Variable,Konstante)
-- xor (Variable,Variable)
Steuerbefehle
- Jump (LocationID) - Springt zu einer anderen Stelle im Skript
- Set (Variable, Operation)
- LoadPicture (Position, ID, PaletteID, Variant)
- LoadCharakter(ID, PaletteID, Variant, Preffered Slot) - ist dafür da weil es nur 4 Charakterslots gibt und es Situationen gibt in denen mehrere Charaktere auftauchen die gerne an derselben Position wären)
- LoadAnim (Position, ID, PaletteID, TimerDivider-1,Loop)
- Write (Position, Speed, String)
- PlaySound(Name/ID,Loop)
- StopSound
- LoadGame
- SaveGame
- Quit
- LoadClickableMap(Position, Handler) - wie machen wir das mit UI/Game? Sind 2 verschiedene Handler... - Daisychain?
Das User Interface
Für die Mausbedienung werde ich mich einer ClickAble-Map bedienen, vereinfacht gesagt ist dies eine Art Bild bei dem der Farbwert angibt was geklickt wurde.
Bei jedem Klick lasse ich mir vom Maustreiber die Cursorposition mitliefern, bestehend aus X und Y - damit hole ich mir einfach direkt von der Clickable Map den Rückgabewert und erspare mir jede Menge Vergleichsarbeit.
Rückgabewert:=Clickablemap[ (Cursorpos_Y*640)+Cursorpos_X ];
Ist eben deutlich schneller als:
If (Cursorpos_x >120) and (Cursorpos_x<160) and (Cursorpos_y >20) and (Cursorpos_y<90) then Aktion 1
Else If (Cursorpos_x >100) and (Cursorpos_x<130) and (Cursorpos_y >200) and (Cursorpos_y<210) then Aktion 2
Else If....
Ich werde jedoch die Auflösung der Clickable Map vierteln - damit hat diese nur noch 19200 bytes und passt somit in ein 64kb Segment und 4x4 Pixel sind als kleinstes klickbares Objekt auch angemessen.