Eigenes Videoformat

Diskussion zum Thema Programmierung unter DOS (Intel x86)
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Hallo!
Habe mir das Video mit dem neuen 40K NES Spiel angesehen, beeindruckend und inspirierend.
Trotzdem würde ich weiterhin eher auf effektive Kompression (der Grafik vor allem) im
"Hintergrund" bauen, immerhin habe ich ja auch fast die 15 fache Speicherkapazität zur Verfügung.


Zu ZVID:
Der bisherige Versuch, 8x8 Pixel-Block-weises Clipping zu implementieren gestaltete sich noch als
schwierig in meinen Routinen, aber es dürfte nicht unmöglich sein. Wenn ich jetzt keinen Denkfehler
mache muss ich den Bildschirmpuffer, um diese Clipping-Blöcke effektiv zu verstecken, links und
rechts und oben und unten um 7 Pixel erweitern. Das wären dann also 65528 div 334 = 196 Zeilen,
abzüglich der 14 Zeilen für Clipping oben und unten = 182 Zeilen, also ein Sichtbarer Bereich von
320x182 Pixeln.
Entweder es ist mir ein Denkfehler untergekommen, aber mein bisheriges Ausschliessen von Scrolling
kann ich revidieren:
Die Grafik eines Levels baut sich immer folgendermaßen auf:
- unbewegtes Hintergrundbild (weit entfernte Landschaft mit Himmel z.B.)
- Level-Blöcke
- Sprites
Da also sowieso immer alles komplett neu gezeichnet wird können sich die Level-Blöcke also
genausogut auch bewegen, und zwar in alle Himmelsrichtungen.
Das würde dann natürlich auf 286ern nicht mehr laufen, aber alles in allem programmiere ich ja für
gutbürgerliche 486er. Das Clipping wäre dann aber elementar wichtig.

Das was Du bzgl. des Textmodes für Grafik beschrieben hast wäre eine andere sehr interessante
Baustelle, aber ich gehe hier erstmal einen anderen Weg, weil auch schon für die Soundschnittstelle
am besten ein 486er benötigt wird und der ganze Code sowieso erst ab 32 Bit funktioniert.

Wenn ich daran denke, ein Jump&Run zu machen, denke ich durchaus an Kacheln. ZVID erlaubt mir
dann einfach nur, sehr viele verschiedene Kacheln zu machen. Aus Faulheit würde ich spontan gesehen
sogar Schrägen vermeiden, weil das ganze von der Physik oder was auch immer her komplizierter ist.
DOSferatu hat geschrieben: Von daher würde ich mir keine Sorgen darüber machen, daß Dinge wie ZIP "besser" packen.
Ich habe das nicht deutlich genug beschrieben. Wenn ich eine ungepackte Grafik nehme und diese
mit ZIP packe, wird sie auf eine bestimmte Größe gepackt. Nehme ich nun diese Grafik, packe sie
mit ZVID, und ich packe dieses ZVID nun nocheinmal mit ZIP, dann ist dieses ZIP kleiner als das ZIP,
das die ungepackte Grafik gepackt hat.
Offenbar kann ZIP die Daten besser packen, wenn sie vorher halbwegs intelligent mit ZVID vorgepackt
wurden, blockweise mit nur so viel Bit wie nötig, und mit per Bruteforce optimierter Meta-Palette.

Paletten:
Man kann übereinkommen, dass 16 bzw. 15 Farben (Transparenz) für alle Sprites ausreichend sind.
Und so wird das auch in den meisten Spielen gemacht. Das würde aber auch bedeuten, wenn man
noch genug Farben für anderes übrig haben will, dass man die Anzahl der Charaktere, wenigstens
für ein Level, beschränken muss, und dass es Redundanzen in der Palette geben kann. Daher mein
Wunsch nach einer allgemeinen Palette, aber da habe ich auch schon gemerkt dass man mit einer
3-3-2 oder soetwas vor allem bei den Graustufen ans Limit kommt.

EDIT: Das mit den Redundanzen und der damit verbundenen Einschränkung der Charaktere
muss natürlich nicht sein. Man kann einfach wieder Meta-Paletten für jedes Sprite definieren,
was ZVID sowieso macht, und einfach nur nach Bedarf nach und nach in der großen Palette
einzelne Farben für Sprites definieren, die dann wiederverwendbar sind.

Du kennst bestimmt Deluxe Paint. Die Palette davon ist eigentlich ganz brauchbar und ich habe sie
z.B. in Kotzman II einfach unverändert übernommen. Ich hatte kürzlich Besuch von zwei Freunden,
einer von denen zeichnet gerne, und vielleicht wollen wir gemeinsam ein Spiel machen. Na, was
heisst gemeinsam. Ich programmiere, mache Musik, und pixle nach der Vorlage der Zeichnungen
die Grafik. Habe bisher ein Sprite zum Test gepixelt (in Deluxe Paint mit der gegebenen Palette)
und der Zeichner fand es toll. Keine Ahnung was draus wird, ob wir weitermachen, das muss ich
erstmal herausfinden, damit ich nicht am Ende auf einem halbfertigen Spiel sitzenbleibe das
eigentlich nur fremden Ideen entsprungen ist.

Deine beschriebene Methode, den 8x8 Blöcken Paletten zuzuweisen ist im Prinzip die, die ich bei
ZVID mit heftigem Bruteforce realisiert habe. Dort werden Farben umsortiert, ganze Paletten
wieder entfernt wenn möglich... Ich weiss gar nicht mehr genau was ich da alles gemacht habe.
Du hast im Prinzip schon recht dass eine Reduktion auf 4 Bit ausreicht und gleichzeitig eine bessere
Performance bieten kann, aber ich wollte es eben wissen wie man so sagt.
Ich wollte Blöcke die nur 2 Farben haben mit 1 Bit speichern können oder eben andere, die ganz
"leer" bzw. einfarbig sind entsprechend nur mit Headerinformationen.
Bei groß ausschweifender Comicgrafik ohne Anti-Aliasing könnte sich ZVID bezahlt machen, da
dort sehr viel 1 Bit oder auch "Fläche" zum Einsatz käme.
Aber auch ansonsten wird es bei 15 Farben Sprites je nachdem wohl auch eher zu einer Datengröße
tendieren, die 2 Bit nahekommt.

Wie Du schon selbst sagst, wenn ich mich recht erinnere, strebst Du Spiele wie bei Super NES an.
Das ist gleichzeitig in etwa Amiga Standard. Fotorealismus war vielleicht eine fixe Idee.
Den kann man sich vielleicht für das Intro-Bild aufheben.
Aber was den Ton angeht, SNES und Amiga verwenden da gleichermaßen Trackermusik mit 8 Bit
Samples, da ist ZSM noch ein Eckchen bescheidener.
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von DOSferatu »

zatzen hat geschrieben:Hallo!
Habe mir das Video mit dem neuen 40K NES Spiel angesehen, beeindruckend und inspirierend.
Sehe ich genauso. Hier hätte es zwar z.B. zum "Packen" der Levels auch andere/weitere Möglichkeiten gegeben - Levels bestehen bekanntermaßen aus viel "freiem Raum" (irgendwo muß ja Bewegungsplatz für die Figuren sein) - da würde sogar eine Lauflängenkompression schon etwas bringen. Aber insgesamt schon ein schöner Ansatz. Und wo jemand mit sparsamem Ressourceneinsatz trotzdem noch Erstaunliches zuwege bringt - dem zolle ich wesentlich mehr Respekt, als jemandem, der zwar auch recht gute Dinge hinbekommt, dafür aber das über 100-fache der Leistung/Speicherplatz usw. erfordert, als eigentlich nötig wäre.
zatzen hat geschrieben:Trotzdem würde ich weiterhin eher auf effektive Kompression (der Grafik vor allem) im "Hintergrund" bauen, immerhin habe ich ja auch fast die 15 fache Speicherkapazität zur Verfügung.
Ja, wie schon mal erwähnt: Ich bin ein großer Freund von sog. "Trade-Off"s: Es gibt für mich immer so den "besten" oder "angenehmen" Mittelweg. Wenn man z.B. extrem effiziente Packalgorithmen hat und damit den zu packenden Inhalt auf 60% runterpackt, die Packroutinen aber selbst dann soviel Speicher brauchen, wie die "weggepackten" 40% und noch zusätzlich die Packroutinen auch noch die Performance minimieren - an solchen Punkten überlege ich dann immer, ob es sinnvoller wäre, weniger effizient zu packen, dafür kleinere Routinen zu haben und zusätzlich mehr Restperformance.

Der (DOS-)PC ist nunmal leider kein NES oder C64 oder ähnliches: Hier gibt es keine anderen Chips, die einem einen Großteil der Arbeit im Hintergrund abnehmen: Wir PC'ler haben keine Hardware-Sprites. Wir PC'ler haben keine Grafikmodi, die von sich aus gekachelte, scrollfähige Levels darstellen. Wir haben auch nicht diesen SNES-Soundchip (SPC700), der quasi programmierbar ist, also eine "zusätzliche CPU", die alleine für Sound zuständig ist und somit die eigentliche SNES-Haupt-CPU (MOS 65816) entlastet.

Was wir haben und was unsere Kisten könenn, ist klar, brauche ich nicht weiter aufzuzählen. Fakt ist, daß quasi die ganze Arbeit, die in Konsolen oder Semi-professionellen ("Spiel-")Computern von dedizierten Custom-Chips geleistet wird, hier durch unsere arme x86-CPU ganz alleine bewerkstelligt werden muß. Jedes Byte in irgendeinem Speicher (Heap, Grafik-RAM...), das irgendwie geändert werden muß, muß durch diese x86.

Deshalb denke ich da recht oft über so "Formate" und "Algorithmen" nach: Ich hatte z.B. auch schon Formate zur seriellen Datenübertragung entwickelt (lange bevor ich mich mit TCP/IP und UDP/IP etc. beschäftigt habe), die Daten sogar bitweise packen, mit durchnumerierten Bytes und Checksummen und allem - die entsprechenden Routinen waren riesig. Trotzdem war der ganze Kram am Ende fehleranfällig - denn wenn die Verbindung schlecht wurde, waren diese teil-nachgeforderten Daten ja auch schon wieder in Datenpaketen, die ebenfalls wieder fehlerhaft sein konnten. Timeouts/längere Unterbrechungen der Datenübertragungen brachten beide Seiten zum totalen Systemkollaps - das System hatte irgendwann nichts anderes mehr zu tun, als Daten vorzusortieren und zu versuchen, die Verbindung zu halten, sobald sich die ersten Bitfehler bei der Datenübertragung einstellten. (Hatte da z.B. auch Optionen, den Datenstrom eben auch auf 5-Bit, 6-Bit, 7-Bit und 8-Bit Übertragung zu ermöglichen, über entsprechende serielle Leitung.)

Das ist nur ein Beispiel davon, wie "sehr intelligent, aber zu kompliziert" am Ende genau das Gegenteil von dem bewirken kann, was es sollte. TCP/IP ist da z.B. viel einfacher: bytebasierte Daten, Paket- und Datenzähler. Und wenn Pakete verlorengehen, erkennbar daran, daß von der Gegenseite keine Pakete mit neuem Datenzähler mehr kommen, werden sie nach gewisser Zeit nochmal gesendet. UDP macht sich's noch einfacher: Wenn ein Paket verloren geht, ist es eben weg. Kontrolle der Pakete kann bei Bedarf ein übergeordnetes Protokoll regeln. Bedarf nicht immer vorhanden.

Ich hatte/habe nämlich auch schon Ideen, wie ich Sprites in 1-, 2-, 3-, 4-, 5-, 6-, 7-, und 8-Bit alle im selben Raum anordnen kann, wenn z.B. Sprites mit 32 Farben (5bit) da liegen, können an der gleichen Stelle noch Sprites mit 8 Farben (3bit) oder mehrere mit z.B. 1-Bit oder 2-Bit (2/4 Farben) liegen. Die Routinen maskieren/shiften das einfach aus. Auf die Art wären Pixel immer an ganzen byte-positionen...
Das wäre eine Erweiterung meiner Sprite-Routinen. Der Sprite-Speicher würde also 3-dimensional verwaltet werden: Zusätzlich zu der Fläche kämen übereinanderligende "Planes" und jedes Sprite würde eben angeben, wieviele Planes (also Bits für die Farbanzahl) es braucht und könnte entsprechend einsortiert werden (die Spritedaten würden sowieso irgendwo extern zusammengerechnet werden). Natürlich wären die Sprites dann selbst "ungepackt", nur würden sie trotzdem nur den Platz brauchen, den sie wirklich belegen UND wären einfacher einzulesen/darzustellen.
Momentan sind meine Sprites ja nicht so aufgeteilt, sondern jedes Sprite hat seinen Platz in der "Sprite-Fläche" (die intern 256 Pixel breit und beliebig hoch ist). Ein 4bit-Sprite ist im Speicher eben "schmal", weil es für zwei nebeneinanderliegende Pixel ein Byte benutzt. Die o.g, "planare" Methode wäre aber wohl noch interessanter: 1. könnten weiterhin auch 4bit-Sprites existieren (z.B. alle eben planar, die Hälfte benutzt das obere, die andere das untere Nybble), aber man könnte eben auch für größere Sprites (Spielfiguren) auch mehr Farben haben (z.B. 32) und die restlichen "darunterliegenden" Figuren könnten z.B. kleine Sprites sein, die man für Schüsse/Projektile/Raketen oder Explosionsfragmente oder Bonusgegenstände benutzt, die oft sowieso kaum mehr als 8 Farben haben.,,

Das sind so Ideen, die ich mir mache.

zatzen hat geschrieben:Zu ZVID: [...] also ein Sichtbarer Bereich von 320x182 Pixeln.
Entweder es ist mir ein Denkfehler untergekommen, aber mein bisheriges Ausschliessen von Scrolling kann ich revidieren:
Die Grafik eines Levels baut sich immer folgendermaßen auf:
- unbewegtes Hintergrundbild (weit entfernte Landschaft mit Himmel z.B.)
- Level-Blöcke
- Sprites
Da also sowieso immer alles komplett neu gezeichnet wird können sich die Level-Blöcke also genausogut auch bewegen, und zwar in alle Himmelsrichtungen.
Das würde dann natürlich auf 286ern nicht mehr laufen, aber alles in allem programmiere ich ja für gutbürgerliche 486er. Das Clipping wäre dann aber elementar wichtig.
Naja, ich selbst benutze im Spiel kein "Hintergrundbild", das wäre mir zuviel Geraffel. Ich benutze stattdessen mehrere mögliche Ebenen - und es werden Pixel nur einmalig gepixelt - indem ich vorher herausfinde, welchen Block/Pixel ich zu setzen habe (d.h. wenn vordere Ebene(n) transparente Pixel enthalten, werden die hinteren gezeichnet. Unglaublicherweise performt das trotzdem einigermaßen, was wohl auch daran liegt, daß ich die Pixel in einen "Stack" schreibe, der eigentlich eins der 32-Bit-Register ist, d.h. das benutze ich als Pixelstack (so geht dann auch die "Halbtransparenz").
Sprites klatsche ich aber einfach über das fertig gezeichnete Level drüber - das jetzt mit dem Level zu "interagieren" zu lassen, um festzustellen, wo Sprites "durchleuchten" und das Level dann durchscheinend zu pixeln, das ist der Mühe nicht wert, weil es wieder zu komplexe Routinen erfordern würde.
zatzen hat geschrieben:Das was Du bzgl. des Textmodes für Grafik beschrieben hast wäre eine andere sehr interessante Baustelle,
Ach, es war ja nur so eine Anmerkung, kein Vorschlag. Einfach eine Idee von mir.
zatzen hat geschrieben:aber ich gehe hier erstmal einen anderen Weg, weil auch schon für die Soundschnittstelle am besten ein 486er benötigt wird und der ganze Code sowieso erst ab 32 Bit funktioniert.
Ja, mein Zeug ist ja inzwischen auch alles auf 32bit ausgelegt. Somit zwar Mindestanforderung 386er - aber ich nehme an, daß für eine gescheite Funktion meines Zeugs wohl auch eher ein 486er vonnöten sein wird. - Ich programmiere hier ja auch auf meiner Monstermaschine (486er X5, 133MHz), das ist schon so High-End des 486ers.
zatzen hat geschrieben:Wenn ich daran denke, ein Jump&Run zu machen, denke ich durchaus an Kacheln. ZVID erlaubt mir dann einfach nur, sehr viele verschiedene Kacheln zu machen. Aus Faulheit würde ich spontan gesehen sogar Schrägen vermeiden, weil das ganze von der Physik oder was auch immer her komplizierter ist.
Ja, schon - aber für eine mögliche Interaktion zwischen Level und Figuren sollte man auch irgendwie auf das Level reagieren können. Dazu sollte die Figur auch irgendwie auf die Kachelnummern oder ähnliches reagieren können. Auf eine "Hintergrund-/Levelgrafik" pixelweise zu reagieren fände ich viel zu kompliziert für eine Spielsteuerung.

Meine (GameSys2) Unit erlaubt, direkt auf die Levelkacheln zu reagieren, aber mit höherer "Auflösung" als nur kachelweise. Jeder Kachel sind ein 16bit und ein 8bit-Wert zugeordnet (Tabellen). Der 16bit Wert legt gedacht eine 4x4-Bitmatrix über die Kachel. Je nachtem, ob das Bit 0 oder 1 ist, wird das untere oder obere Nybble als "Typ" (0-15) aus dem 8-Bit-Wert zurückgegeben. Somit kann man auf einfache Art auf das Level reagieren, weil die Kollisionsabfrage zwischen Figuren und Level diese "Sub-Quadrate" der Kacheln berücksichtigt... Naja - hatte ich wohl schon mal erklärt.

Die "Schrägen" aus meinem Testgame vor einiger Zeit werden intern auch nicht wirklich als Schrägen wahrgenomen, sondern als "Treppen". Daß die Figur sich in die Richtung dreht, passiert auch nur mit einer Tabelle in den Steuerdaten. Ich habe dort einfach festgelegt, welche Kachelnummer welchen Winkel hat und so wird der Figurensprite gedreht. Das war auch alles etwas "mit heißer Nadel gestrickt", weil es ja nur ein Demo zum Testen der Unit war/ist. In einem realen Spiel würde ich die Steuerung etwas verfeinern, damit das besser aussieht.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Von daher würde ich mir keine Sorgen darüber machen, daß Dinge wie ZIP "besser" packen.
Ich habe das nicht deutlich genug beschrieben. Wenn ich eine ungepackte Grafik nehme und diese mit ZIP packe, wird sie auf eine bestimmte Größe gepackt. Nehme ich nun diese Grafik, packe sie mit ZVID, und ich packe dieses ZVID nun nocheinmal mit ZIP, dann ist dieses ZIP kleiner als das ZIP, das die ungepackte Grafik gepackt hat.
Offenbar kann ZIP die Daten besser packen, wenn sie vorher halbwegs intelligent mit ZVID vorgepackt wurden, blockweise mit nur so viel Bit wie nötig, und mit per Bruteforce optimierter Meta-Palette.
Ja, OK. Aber: Wenn ZIP ein Bild so nehmen würde, wie es nach Packen mit ZVID aussieht - also mit reduzierter Palette usw. - ist dann ZVID immer noch voran?
zatzen hat geschrieben:Paletten: [...]
EDIT: Das mit den Redundanzen und der damit verbundenen Einschränkung der Charaktere muss natürlich nicht sein. Man kann einfach wieder Meta-Paletten für jedes Sprite definieren, was ZVID sowieso macht, und einfach nur nach Bedarf nach und nach in der großen Palette einzelne Farben für Sprites definieren, die dann wiederverwendbar sind.
Das sind ja auch die "Paletten", von denen ich rede/schreibe. Die wirkliche (in der Grafikkarte gesetzte) 256-Farb-Palette ist ja immer gleich und wird für alles (Level, Anzeige, Sprites usw.) benutzt, da gibt es keine speziellen Bereiche für bestimmte Sprites oder so,
Sprites (und bei palettenbasierten Kacheln auch diese) haben ja bei mir solche Meta-Paletten - also quasi Tabellen von Farbnummern. Ein 16-Farb-Sprite hat eine Referenz auf eine solche Tabelle von 16 Bytes. Und diese definiert, welche der 256 Farben für die 16 Farben des Sprites benutzt werden, diese müssen auch nicht aufeinanderfolgend sein.
zatzen hat geschrieben:Du kennst bestimmt Deluxe Paint.
Schonmal davon gehört. Irgendein Paint hab ich hier auch. Aber ich benutze zum Pixeln mein eigenes Malprogramm unter DOS.
zatzen hat geschrieben:Die Palette davon ist eigentlich ganz brauchbar und ich habe sie
z.B. in Kotzman II einfach unverändert übernommen.
Naja, ich baue generell immer meine eigenen Paletten. In so "bunten" Paletten sind mir oft zu wenig Hauttöne und keine vernünftigen Farbabstufungen enthalten.
Und das, was so Programme anbieten wie diese geditherten Farbverläufe sind mir eher ein Graus. Das mag ich nicht wirklich, weil es dann so "geometrisch" wirkt. Ich pixle da lieber manuell.
zatzen hat geschrieben:Ich hatte kürzlich Besuch von zwei Freunden, einer von denen zeichnet gerne, und vielleicht wollen wir gemeinsam ein Spiel machen. Na, was heisst gemeinsam. Ich programmiere, mache Musik, und pixle nach der Vorlage der Zeichnungen
die Grafik. Habe bisher ein Sprite zum Test gepixelt (in Deluxe Paint mit der gegebenen Palette) und der Zeichner fand es toll. Keine Ahnung was draus wird, ob wir weitermachen, das muss ich erstmal herausfinden, damit ich nicht am Ende auf einem halbfertigen Spiel sitzenbleibe das eigentlich nur fremden Ideen entsprungen ist.
Ja, ich kenne das Problem mit Leuten, die gerne bei einem Spiel mitmachen wollen, dann aber nie Zeit haben... usw. Habe das ja schon öfter mal erwähnt. Deshalb bin ich da auch nicht mehr so enthusiastisch, wenn jemand mir anbietet, mit mir zusammen etwas zu entwickeln. Bin da schon zu oft enttäuscht worden.

Zum Thema Grafik nochmal: Ich habe hier einen alten Handscanner (läuft unter DOS, 286er) inkl. Programm, der kann monochrom oder in Raster scannen.
UND: Ich habe einen Farbscanner (läuft unter Windows, an USB), der etwas größer als DIN A4 farbig scannen kann. Für gezeichnete Figuren und Hintergründe wäre der ideal. Ich überlege schon, ob ich den für Teile der Grafiken meines geplanten Jump'n'run benutzen werde. Die gescannten Grafiken werden dann natürlich noch an die Palette angepaßt und entsprechend nachberechnet und von Hand nachkorrigiert - trotzdem stelle ich mir vor, daß ich damit bessere Ergebnisse erziele als mit reinem Gepixel im Malprogramm. Mit einem Stift kann ich einfach besser zeichnen.
zatzen hat geschrieben:Wie Du schon selbst sagst, wenn ich mich recht erinnere, strebst Du Spiele wie bei Super NES an.
Naja, nur so in etwa von der Art der Grafik und Musik her - mein Zeug wird insgesamt wahrscheinlich technisch etwas schlechter als SNES werden (vor allem der Sound).
zatzen hat geschrieben:Das ist gleichzeitig in etwa Amiga Standard. Fotorealismus war vielleicht eine fixe Idee. Den kann man sich vielleicht für das Intro-Bild aufheben.
Aber was den Ton angeht, SNES und Amiga verwenden da gleichermaßen Trackermusik mit 8 Bit Samples, da ist ZSM noch ein Eckchen bescheidener.
Naja, der Soundchip im SNES hat z.B. 8 Stimmen, gibt 16bit Sound mit 32kHz aus. Und: Er ist ISM wahrscheinlich ähnlicher als man denkt, weil er auch "programmiert" wird - er wird wirklich in einer Art Maschinencode programmiert. Er hat einen Speicher von 64 kByte insgesamt für alle Samples zusammen, die in einer Art ähnlich ADPCM vorliegen (um Speicher zu sparen).

Wenn in meinen Spielen überhaupt Samples (in Musik oder Effekten) eingesetzt werden sollten, sollten sie alle zusammen auch kaum mehr als 64kByte beanspruchen, finde ich. (Die Samples werden in 4bit gespeichert, d.h. es sind eigentlich 131072 Sampleframes zu je 4bit insgesamt (pro Sample sind noch je 7 Nybbles für den Header abzuziehen).

Ja, ich weiß: Wenn man so ein Spiel total orchestrial untermalen will, reichen dem versierten Musiker diese Einschränkungen nicht aus - da soll am liebsten der komplette Heap voller Samples sein und ob das restliche Spiel überhaupt Platz für Level/Grafik/Sprites hat, geht den Musiker nichts an... - sowas gibt's aber bei mir nicht. Und außerdem bin ich nicht der Meinung, daß so Spielmusik immer gleich 8stimmig sein muß. (Auf SNES sind es insgesamt 8 Stimmen - die müssen sich Musik und Effekte teilen. Auf Amiga sind es insgesamt sogar nur 4 Stimmen.)

Aber, wie ja bekannt ist, wird auch bei Grafik und anderem gespart: Sprites in begrenzter Farbanzahl, Kacheln je nach Modus auch. Figurensteuerung in einer "Maschinencode-artigen" Steuersprache usw. - und wenn alles andere Speicher spart, muß es die Musik eben auch.

Und ja: Ich habe knapp 615 kByte in meiner normalen Konfiguration frei - meine maximale ist 624 kByte - aber: Ich will kein Spiel bauen, das dann viele Leute nicht zum Laufen kriegen - weil nicht jeder so eine Konfiguration hat oder hinbekommt.

Und ja, ich weiß auch, daß man in DOSbox viel Speicher haben kann. Aber ein DOS-Spiel, das nur im Emulator läuft, aber nicht auf der richtigen Hardware, ist einfach nur lame - das würde ich gar nicht veröffentlichen wollen - wäre mir peinlich.

Meine gesetzte Obergrenze sind so 580 bis 600 kByte - darüber soll es eigentlich nicht gehen, damit auch jeder, der einen DOS-Rechner hat, auf recht einfache Art (ohne Konfigurations-Marathon, Startdisketten o.ä.) das Spiel starten kann. Ich finde, im Jahre 2019 muß man so etwas wirklich keinem mehr zumuten. Und daß man ein gutes Spiel auch mit deutlich weniger Speicherverbrauch hinbekommen kann, haben schon viele Leute bewiesen.

OK, genug erst einmal für heute.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Ich denke auch gerne an die genügsamen Spiele zurück, die wir damals auf dem 286er hatten.
Teilweise Spiele wie Alley Cat, nur eine EXE oder COM, insgesamt < 64 KB. Soetwas hatte auch
seinen Zauber.
Viele EGA oder CGA Spiele der 80er waren genügsam, was Rechenleistung angeht, aber auch
was Speicher angeht, niemand schien das Bedürfnis zu haben, die auch damals schon
vorhandenen (<) 640 KB auszureizen.

Es wäre interessant gewesen, die Möglichkeiten eines 286ers mit Pascal und Assembler zu
erforschen, aber damals machte ich erst meine bescheidenen ersten Schritte mit QBASIC,
und heute locken die bösen 32 Bit.

Trade-Off:
Ich habe die tatsächliche kompilierte Größe der ZVID Routinen nicht im Sinn, aber eine
Demo die ich mal gemacht habe und die ZSM und ZVID vereint ist gerade einmal 17 KB groß,
d.h. die EXE. Von daher denke ich, lohnt sich ZVID sehr bald, zumindest was den Speicher
angeht. Die Performance kann beeinträchtigt sein, aber auch je nachdem sogar verbessert
oder wenigstens auf gleichem Level angesiedelt sein, als würde man teils redundante/aufgeblähte
Daten einfach 1:1 kopieren.
16 Farben Sprites sind natürlich irgendwie das vernünftigste, zumindest was den Code-Aufwand angeht,
und bei Dir bleibt dann noch Platz für Rotation und Skalierung.
Aber ich freue mich einfach wenn beispielsweise drei Comic-ähnliche Frames mit den Maßen 64x64 Pixel
auf 1400 Byte gerafft werden, volle Bilder, kein Delta. Mit normalem 4 Bit wären es 6144 Byte.
Ich hätte irgendwie dann immer ein schlechtes Gefühl wenn ich bei 4 Bit die möglichen >7 Farben nicht
ausnutzen würde, und darüber hinaus hätte ich auch ein unwohles Gefühl mit einfarbigen Flächen oder
auch transparenten Bereichen. Oder auch mit Redundanzen. Wenn ich eine Figur mal sprechen lassen will
dass sich nur der Kopf bewegt brauche ich das nicht per Hand auseinanderdröseln, sondern kann einfach
ein komplettes Sprite speichern und darauf vertrauen, dass nur der Kopf neue Daten erzeugt.
Also ich denke, zumindest wenn man ein Spiel Comic-artig aufziehen wollte, würde sich ZVID ziemlich
rentieren. Ich muss einfach mal sehen wie es sich auf Dauer in der Praxis schlägt.


Die Idee mit dem mehrdimensionalen Sprite-Speicher finde ich sehr interessant. Sehr elegant und einfach
zu handhaben sogar, selbst bei "krummen" Bitzahlen wenn man 3 und 5 Bit zusammen unterbringt.
Es besteht eben nur immer eine Abhängigkeit der Sprites voneinander. Vielleicht ist diese weniger
schwerwiegend wenn man die Sprites wie bei eines NES handhabt und einfach Felder von 8x8 Pixel Grafik-
blöcken hat. Es muss eben nur irgendwo aufgehen, sonst bleibt ungenutzter Speicherplatz frei. Oder sehe ich das falsch?

Mir stellt sich die Frage, da ich ja Scrolling durch einfaches Neuzeichnen der Level-Blöcke erzielen will,
ob es dann nicht schneller geht eine vollständige Hintergrundgrafik schnell reinzuklatschen als meinetwegen
100 mal einen Hintergrundblock mittels ZVID Routinen zu zeichnen. Also vielleicht lieber eine offene Landschaft
mit nur etwa 30% scrollendem Inhalt, Plattformen etc. als Szenario, als ein Kerker bei dem wirklich alles mitscrollt.
Aber wenn es mir gelingt, ZVID um Block-Clipping zu erweitern, werde ich einfach mal einen Performance-Test
machen und auch hier anbieten.
Ich denke noch ein-ebig sozusagen. Bzw. Sperrbereiche oder Ebenen auf denen man laufen kann, Hintergrund, Vordergrund. Vordergrund könnte z.B. ein Zaun sein oder ein Tunnel mit Fenstern, einfach nur wegen der Optik,
bzw. beim Tunnel noch definieren dass man da nicht raus kann. Naja ich hab 24 Jahre kein richtiges Spiel mehr
programmiert, ich muss mal sehen wie das wird.

Pixelweise habe ich noch bei Kotzman II überprüft. Einfach mit GET(x, y) ob schwarz oder nicht.
ber jetzt habe ich ein 8x8 Pixel Raster vor. Ich denke auf Schrägen kann ich verzichten, ich fand die in Spielen
sowieso immer nervig. Ich muss "einfach mal anfangen". Besser als Kotzman wird's schon werden.
DOSferatu hat geschrieben: Ja, OK. Aber: Wenn ZIP ein Bild so nehmen würde, wie es nach Packen mit ZVID aussieht - also mit reduzierter Palette usw. - ist dann ZVID immer noch voran?
Nach dem Packen mit ZVID sieht ein Bild genauso aus wie vorher, verlustfrei und CRC treu.
Falls ich Dich dahingehend falsch verstanden habe, die andere Antwort:
ZVID packt ein Bild knapp schlechter als GIF.
ZVID Daten bestehen aus den ganzen 8x8 Blöcken mit jeweiligem Header von ein zwei oder drei
Bytes, und optimierten, meist mehrfach verwendeten Meta-Paletten auf die in den Blöcken
referenziert wird. Durch dieses "Vor aufräumen" packt ZIP das ganze anschliessend besser als
wenn es die Daten roh bekommen hätte.
Somit also die Antwort:
Ein nicht mit ZIP gepacktes ZVID ist gegenüber einem ZIP gepacktem ZVID größer.
Aber eine nicht mit ZVID gepackte Grafik die mit ZIP gepackt ist, ist gegenüber
einer mit ZVID gepackten UND danach mit ZIP gepackten Grafik größer.


Musik/Samples:
Ich könnte mit ZSM eigentlich auch wunderbar NES-ähnliche Musik machen.
Dreieck, Rechteck und ein bisschen Rauschen sind ja als Samples kein Problem und belegen auch nicht viel.
Kann ich ja mal probieren und beim baldigen ZSM Release untermogeln, nen bekannten Titel der nicht
allzu schwer ist...
Mir sind auch einige MODs untergekommen, Konvertierungen, und die Patterns rasen mit 50 Zeilen pro Sekunde
durch, was das ein wenig aufbläht, aber so mit 2-3 KB Patternspeicher hält sich das noch in Grenzen.
Naja, man kann da aber auch etwas gezähmter rangehen, einziger Wermutstropfen ist dass meine Schleifen-
Routinen etwas schlechter performen als die ohne Schleife, weil ich bei den Schleifenroutinen bei jedem
Durchlauf prüfen muss ob das Sample schon durch ist, bei der Routine ohne Schleife kann ich vorher kurz
berechnen wieviel Durchläufe es sein müssen.
In der Praxis habe ich aber noch nicht bemerkt dass sich Schleifen-Samples negativ auswirken.
Übrigens sind die Kanäle bei ZSM eher Symbolisch. Theoretisch könnte die Engine auch 100 Samples gleichzeitig
spielen, dass schafft dann nur der Rechner nicht mehr. Bei den Musikmodulen dienen die Kanäle vielmehr als
Information, wann ein Sample wieder gestoppt werden soll, nämlich wenn auf dem gleichen Kanal das nächste
oder ein "Note Off" kommt. Für Soundeffekte kann man aber noch Samples oben drauf laden und beliebig abspielen,
als Begrenzung sollte ich da aber auch besser ein vielleicht 4 Kanäle System einbauen bei dem immer bei einem
neuen Event auf dem Kanal gespielt wird der schon die Längste Zeit keinen neuen Abspielbefehl mehr bekommen hat.
Diese Anzahl der Kanäle könnte man Zwecks Performanceoptimierung im Setup des Spiels ja auch noch ändern.

Ich habe auch noch in Erinnerung dass ich glaube ich sogar zu manchen Zeiten nur 560K frei hatte, und eigentlich
liefen alle Spiele. Ich habe auch nicht vor wirklich mehr als 500 KB in ein Spiel zu packen.
Ein bisschen (schmerzliche) Erfahrung habe ich diesbezüglich.
Quickbasic kannte keinen Heap (oder ich war zu dumm auf ihn zuzugreifen) und ich musste Kotzman II auf
mehrere EXE Dateien verteilen, je eine für jedes Level, habe ich dann irgendwie umbenannt und mit
Kommandozeilenpasswörtern geschützt.

Priorität wird bei mir auch die Grafik haben, und dann sehe ich
was ich mir Musikmäßig noch leisten kann.
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von DOSferatu »

zatzen hat geschrieben:Ich denke auch gerne an die genügsamen Spiele zurück, die wir damals auf dem 286er hatten.
Teilweise Spiele wie Alley Cat, nur eine EXE oder COM, insgesamt < 64 KB. Soetwas hatte auch seinen Zauber.
Viele EGA oder CGA Spiele der 80er waren genügsam, was Rechenleistung angeht, aber auch was Speicher angeht, niemand schien das Bedürfnis zu haben, die auch damals schon vorhandenen (<) 640 KB auszureizen.
Das ist ein großer Irrtum, dem viele Leute heutzutage unterliegen.
Die ersten IBM-PCs wurden mit 64kB, 128kB, 256kB, teurer auch mal 512kB ausgeliefert. Dieses "volle untere MB" (mit den 640kB nutzbaren) war damals nicht selbstverständlich.
(Die Grafikkarte/BIOS-ROM sind davon natürlich ausgeklammert, das ist Extra-RAM [auf der Grafikkarte] und Extra-ROM [BIOS].)
Und: Es gab Zeiten, da war sogar mehr als diese unteren 640kB nutzbar: Die Hercules-Monochrom bzw. CGA-Grafikkarten blenden ihren Speicher erst bei $B000:$0000 ein und damals war der Zeichensatz fest im Grafikkarten-ROM und konnte nicht geändert werden.
zatzen hat geschrieben:Es wäre interessant gewesen, die Möglichkeiten eines 286ers mit Pascal und Assembler zu erforschen, aber damals machte ich erst meine bescheidenen ersten Schritte mit QBASIC, und heute locken die bösen 32 Bit.
(Ja, wie gesagt: Mal in YouTube nach "The 8-Bit Guy" und "Planet X3" suchen...)
Und, viel mehr als die 32-Bit-Erweiterung der Register (obwohl die auch schon toll ist) interessieren mich die ab 386er verfügbaren zusätzlichen Segmentregister (FS, GS). Nicht ständig diese zeitraubenden Segmentwechsel durchführen zu müssen, wenn man verschiedene Dinge macht, ist schon praktisch.
zatzen hat geschrieben:Trade-Off:
Ich habe die tatsächliche kompilierte Größe der ZVID Routinen nicht im Sinn, aber eine Demo die ich mal gemacht habe und die ZSM und ZVID vereint ist gerade einmal 17 KB groß, d.h. die EXE.
Tja, alleine die Subroutine (100% Assembler) von GameSys2 (GS2) (für die Figurensteuerung/Kollisionsabfragen usw.) ist bei mir schon etwas über 20kB groß. Und dann kommt ja noch der eigentliche "Code" dazu, den GS2, was ja quasi wie eine "virtuelle CPU" arbeitet, noch abarbeiten kann. OK, der kann dann relativ klein sein, weil man da viel zusammenfassen kann und der Code ebenfalls "Assembler-artig" ist.

Aber natürlich muß man GameSys2 in meinen Programmen nicht unbedingt benutzen. Aber es stellt eben eine automatische Verwaltung von Figurenspeicher und Kollisionsabfragen mit meiner "Kollisionsmatrix" zur Verfügung - das bräuchte man dann nicht mehr selbst bauen. Die 20kB ergeben sich dadurch, daß ich bei GameSys2 aus Performancegründen etwas Speicher verschwendet habe. (Immerhin ist es ja eine "Maschine auf einer Maschine". Ich hätte mit viel mehr Sprüngen, CALLs und ähnlichem etwas Speicher sparen können - aber wenn schon die unterste "VM" lahm ist, ist alles darüber noch lahmer. Und erstaunlicherweise (siehe mein TestGame) performt es ziemlich gut.)

Das ist es, was ich mit Trade-Off so meine: Mitunter lasse ich mich dazu hinreißen, größere vorberechnete Tabellen (statt Berechnungen) zu benutzen, um z.B. in Schleifen ein paar Zyklen zu sparen. Denn es spart ja dann [Zyklen * Schleifendurchläufe]. Da wird eben Speicher verschwendet, dafür aber Performance gewonnen.

Und dabei muß man aber immer das "Gesamtbild" im Auge behalten - arten Look-Up-Tables, unrolled Loops oder ähnliches so weit aus, daß man da beim zweistelligen kB-Bereich landet, braucht man schon wirklich einen überzeugend hohen Performance-Gewinn auf der anderen Seite. Und das Gleiche ist es umgekehrt: Wenn man durch kleine, aber komplexe Packroutinen Daten in einem komplizierten Format packt, kann man extrem viel Speicher sparen. Nur sollte man auf der anderen Seite überlegen, wie oft man Zugriff auf diese Daten benötigt. Ein "Live-Zugriff" (d.h. z.B. 50x oder 70x pro Sekunde für weiches Scrolling) ist dann mitunter nicht mehr gegeben.
Andererseits kann man aber so (durch effizientes Packen) viel mehr Grafik/Sounds (Samples) oder viel größere Levels im kleinen RAM halten und damit beeindruckendere Spielwelten und Klangerlebnisse erzeugen.

Da gibt es meiner Meinung nach keinen "besten Weg"; Man hat zwei Enden, an denen man "schrauben" kann: 1. CPU-Leistung, die irgendwann an eine Grenze stößt (z.B. wenn alles so langsam wird, daß es nicht mehr spielbar ist) und 2. Speicher, der irgendwann ebenfalls an eine Grenze stößt - wo nichts mehr frei ist, kann nichts gespeichert werden. Alles, worauf die CPU oder die Peripherie (z.B. Soundkarte) Zugriff haben muß, muß im RAM liegen - die CPU kann nicht auf die Festplatte/Diskette zugreifen.

Leider beobachtet man heutzutage, auch bedingt durch "hardwaremäßige Verfügbarkeit" einen totalen Verfall der Programmiererdisziplin (wobei von "Verfall" nur sekundär die Rede sein kann, weil die "jüngere Generation" es gar nicht mehr anders lernt z.B. im Studium) - nämlich: Verschwendung von Rechenzeit UND Verschwendung von Speicher. Also beides in die Negativ-Richtung. Solche Leute kommen schnell zum Programmier-Ergebnis, das stimmt. Aber man könnte ihnen kein "kleines System" geben, auf dem sie etwas erzeugen sollten. Wenn die vorgefertigten "Frameworks", mit denen sie arbeiten, dafür nicht genug Platz oder CPU-Leistung vorfinden, können sie gar nicht arbeiten.

Die Variante "Performant UND speichersparend" kommt relativ selten vor - weil meist das Eine das andere bedingt bzw. ausschließt. Hier ist man stark von der umgebenden Hardware abhängig - wenn diese keine Möglichkeit dafür bietet, kann man nur "das bestmögliche versuchen".

Es gibt ja auch noch einen dritten Parameter des "Trade-Off"s - nämlich die Entwicklungsdauer. Sowohl speichersparend als auch performant zu arbeiten, erfordert beides ein gewisses Vorab-Wissen und vor allen Dingen: Viel Zeit. D.h. je effizienter (egal ob Speicher oder Leistung) man programmiert, umso länger ist man beschäftigt und umso mehr Fachwissen wird benötigt. Und das mag auch der Grund sein, wieso "speicherfressend und leistungshungrig" für Programme heutzutage trotzdem oft akzeptabel ist: Zu lange Entwicklungsdauer gibt der Konkurrenz die Möglichkeit, früher als man selbst mit dem neuen Produkt auf dem Markt zu sein. - ...

Allerdings sollten solche Dinge im Hobby-Bereich natürlich keine Rolle spielen. Es ist zwar schade, wenn man mit seiner Software erst später herauskommt als erwartet - aber es gibt ja keine Aktionäre, denen Dividenden abhanden kommen und keine Chefs, die sich deshalb keine neue Villa oder Motorjacht kaufen können. Programmierung im Hobby-Bereich, ohne materielle Vergütung - da ist die einzige Konkurrenz (wenn überhaupt) andere Hobby-Entwickler - und hier ist man sich eher nicht "spinnefeind". wie es große Firmen sind, sondern sogar bereit, sich gegenseitig zu unterstützen - weil es um die Sache an sich, d.h. das Endprodukt geht, und nicht, wie bei Firmen, nur darum, wieviel man daran verdienen kann.
zatzen hat geschrieben:Von daher denke ich, lohnt sich ZVID sehr bald, zumindest was den Speicher angeht. Die Performance kann beeinträchtigt sein, aber auch je nachdem sogar verbessert oder wenigstens auf gleichem Level angesiedelt sein, als würde man teils redundante/aufgeblähte Daten einfach 1:1 kopieren.
Ja, wie bekannt ist, sind meine Grafikdaten und Levels redundanter als sie sein müßten - mit dieser Redundanz erkaufe ich mir die Möglichkeit, z.B. meine Sprites skalieren, spiegeln, drehen und pixelabhängig transparent zu machen, bzw. auf jede Stelle des Levels "wahlfrei" zugreifen zu können (d.h. ständig "genau zu wissen, wo man ist").

Wie auch z.B. in Xpyderz gesehen (ja, Entschuldigung, daß ich das so oft erwähne, aber weil es mein bisher größtes - wenn auch nicht perfektes und zudem unfertiges - Spielprojekt ist, dient es mir oft als Referenz)... also, wie auch in Xpyderz gesehen, gibt es mir die Möglichkeit, ständiger Kollisionsabfrage sowohl mit dem Level als auch den Figuren und das sogar außerhalb des im Bildschirm sichtbaren Bereichs (weil für Xpyderz u.a. mal Multiplayer angedacht war).

Weil Sprites nicht (bzw. selten) rechteckig sind, d.h. einiges an transparentem Bereich vorliegt, könnte man sie viel speichersparender packen - der LZW-Algorithmus (ja, aus GIF), ist bekanntermaßen extrem effizient, wenn man erst einmal seine Funktion begriffen hat auch leicht verständlich und wäre für Sprites geradezu ideal: Er packt jegliche gleiche Folge zusammen, er ist bitbasiert, so daß man auch bei einer krummen Anzahl Farben noch effizient packen könnte, ohne Platz zu verschwenden, usw....

ABER: Mit diesem Algorithmus könnte man Sprites zwar, wenn man entsprechend intelligent programmiert, um 90°, 180°, 270° drehen, sowie spiegeln und mit noch viel mehr Aufwand eventuell auch noch skalieren - aber spätestens, wenn man auch noch in nicht-rechtwinklige Winkel drehen wollte, müßte man hier anderswo zum Entpacken puffern - das Gleiche gilt für eventuelles Clipping. Und außerdem wäre die Performance nicht mehr gegeben - die Anzahl Sprites müßte man dann ebenfalls reduzieren.

Und da wären wir wieder beim Trade-Off...

Der "8-Bit Guy" (siehe oben) hat sich für sein Spiel (die C64-Version Planet X2) sogar eine "Memory Map" gemacht, d.h. genau festgelegt, was wo im Speicher liegen soll und wieviel Platz z.B. für Grafik, Sound und Code vorgesehen sind. Und wo mehr Code benötigt wird, kann z.B. weniger Grafik sein... wenn die 64kByte voll sind, sind sie voll. Er hat gesagt, daß er, als er mit dem Spiel fertig war und so viel eingebaut hat, wie es möglich war, am Ende noch 12 Bytes im Speicher frei hatte - SO effizient hat der entwickelt!
zatzen hat geschrieben:16 Farben Sprites sind natürlich irgendwie das vernünftigste, zumindest was den Code-Aufwand angeht, und bei Dir bleibt dann noch Platz für Rotation und Skalierung.
Naja, auf die 16 Farben kam ich damals, weil es das plausibelste schien - mit "krummen" Bit-Anzahlen (keine 2er Potenz) wollte ich damals nicht hantieren. Und 256 Farben pro Sprite sind bei mir ja auch möglich - aber so viele werden ja nie gebraucht: Kein Sprite benutzt ALLE Farben der vorhandenen Palette - wie soll das aussehen?

Hier war meine Grundidee die gleiche, die es jetzt immer noch ist:
DIe Sprites werden nicht einzeln gespeichert, sondern quasi in eine große Grafik wie eine "Collage" eingeklebt. Diese Grafik (das ist nur so "gedacht"), also quasi eine 8bit-Bitmap im Speicher.... ist 256 Pixel breit und kann beliebig hoch sein. Sie verschwendet etwas Platz, bringt aber Performance, wenn man geschickt ist:
Sprites beginnen immer an einer durch 16 teilbaren X-Position (Spalte) in dieser Bitmap und einer beliebigen Y-Position (Zeile). D.h. sie haben einen Offset vom Anfang dieser Bitmap. Dieser wird in ein Segmentregister geladen. Und weil die Bitmap 256 Pixel breit ist, ist, egal, wo das Sprite darin liegt, immer der zeilenweise Abstand zweier untereinanderliegener Pixel genau 256 Pixel/Bytes.

Wieso ist das nützlich? Naja, wenn man z.B. anfangs das Segment ES auf den Start der Bitmap PLUS dem Sprite-Offset setzt, kann man mit ES:[BX] alle Pixel des Sprites auslesen - BX ist ja praktischerweise teilbar - so steht in BL immer die Sprite-Spalte und in BH die Sprite-Zeile (daher diese 256er-Breite der Bitmap). Und so weiß man ständig, welchen Pixel man trifft.

Das Drehen ist zwar etwas "tricky" - aber es funktioniert mit Sinustabellen und quasi einer ähnlichen Technik des "Aufaddierens" wie ich sie in ISM bei Samples/Wellen oder Portamentogleiten benutze, quasi die "Nachkomma"-Methode. Außerdem drehe ich (ebenfalls mit Sinustabelle) vorher die 4 gedachten Ecken des Sprite-Rechtecks um den Sprite-Hotspot und arbeite dann wirklich nur in diesem (gedrehten) Rechteck. Es ist quasi wie eine Erweiterung einer Füllroutine für ein konvexes Polygon (in dem Fall Rechteck) - und ich arbeite dabei senkrecht - was nur etwas mit den Besonderheiten des Mode-X zu tun hat. Weil BX trotzdem jederzeit weiß, auf welche Stelle in der Bitmap (und damit welchen Pixel) es zeigt, ist das möglich.

Das Ganze funktionierte prima für 8-Bit-Sprites. Nun merkt man aber schnell, daß Sprites normalerweise nicht besonders groß sind und auch nie wirklich so viele Farben brauchen. Daher habe ich es nachträglich auf 4-Bit erweitert und zwar eigentlich in einer zwar funktionierenden, aber nicht idealen Methode: Jetzt hat ein Byte 2 Pixel, was die Berechnung etwas komplizierter macht, weil man ja für die Sprite-Zeilen jetzt zwischen "geraden" und "ungeraden" Pixeln unterscheiden muß. Aber dafür kann die Bitmap gleichzeitig 4-Bit und 8-Bit Sprites beherbergen. Die 4-Bit-Sprites sehen dabei quasi "schmaler" aus, wenn man die Bitmap darstellen würde.

Die bessere Methode wäre gewesen, die Bitmap in mögliche 2 Layer aufzuteilen, in denen entweder 8-Bit Sprites liegen würden (brauchen beide Layer gleichzeitig) oder 4-Bit Sprites (jeweils nur in einem Layer) - bei der Darstellung wird der andere Layer, wie auch bereits in der o.g. Methode, jeweils mit AND 15 oder SHR 4 ausmaskiert. Die Layer-Methode würde auch erlauben, andere Bit-Anzahlen (und damit max. Anzahl Farben) zu ermöglichen. Sprites mit mehr als 4 Bit würden immer im "unteren Layer" landen, damit mit "AND .." ausmaskierbar. Sprites mit 1 bis 4 Bit könnten unten oder oben landen und würden für oben mit SHR 4 bis SHR 7 "ausmaskiert" und unten mit AND 15. Das wäre möglich, weil ich für Paletten ebenfalls ein Segment habe und da wird auch nur Segmentweise gearbeitet, d.h. ein 3bit-Sprite oder weniger hat trotzdem eine 16er-Palette und die Farben wären dann mehrmals da (bei 3bit also doppelt), so daß kein nacheinanderfolgendes AND und SHR sein müßte, sondern nur (wie auch bei 4bit) entweder ein AND ("unten") oder ein SHR (für alles was im oberen Nybble liegt).
Somit könnten. Lediglich Kombinationen, wo im "unteren" Bereich schon nur 3bit- oder weniger liegen, müßte man noch gesondert behandeln - wenn "oben" dann auch nur 3-Bit oder weniger liegen, d.h. mehr als zwei Sprites "gestapelt" wären...

Aber all dies kann mit cleverer Vorberechnung abgedeckt werden. Für das EInpassen von 4-Bit und 8-Bit Sprites (auch mit der Option, sie um 90° zu drehen, wenn sie dann besser passen) in diese Bitmap habe ich bereits ein Tool vorliegen, das auch damals bei Xpyderz benutzt wurde. Es paßt die Sprites entsprechend ein, berechnet die Offsets und generiert selbständig "Paletten" (also Meta-Paletten/Farblisten). So werden die Sprite-Images da eingepaßt um möglichst wenig Platz zu verschwenden...

Egal... So funktioniert's derzeit. Das mit den Layern und der Möglichkeit, 1-, 2-, 3-, 5-, 6-, 7-Bit Sprites zu haben (und 4-Bit auch mit Layern statt mit "Doppelpixeln"), ist nur so eine Idee von mir. Müßte man mal überlegen und umsetzen. Aber, weil mein Zeug so modular aufgebaut ist und z.B. GameSys2 oder SLOT nicht wissen müssen, WIE die Sprites da hingepixelt werden, kann man die derzeitigen Routinen später immer noch jederzeit durch "coolere"/effizientere Routinen ersetzen - ohne am restlichen Programm etwas ändern zu müssen.
zatzen hat geschrieben:Aber ich freue mich einfach wenn beispielsweise drei Comic-ähnliche Frames mit den Maßen 64x64 Pixel auf 1400 Byte gerafft werden, volle Bilder, kein Delta. Mit normalem 4 Bit wären es 6144 Byte.
Klar. Das freut natürlich jeden gescheiten Programmierer, wenn er 4744 Bytes sparen kann. (Obwohl der Korinthenkacker sagen würde, daß 64x64 = 4096 sind und mit 4-Bit wären es demzufolge 2048, somit wären es 2048-1400 = 648 Byte Ersparnis - was trotzdem nicht schlecht ist, es wäre ca. ein Drittel.)

Wie gesagt: Trade-Off. Wenn ich z.B. einen "Film" machen würde, der von festen, vorberechneten Abläufen ausgeht, da würde ich viel mehr packen. Bei einem interaktiven Programm (Spiel), wo ich freibewegliche Objekte (Sprites) haben will, will ich, daß diese so flexibel wie möglich einsetzbar sind und opfere dafür öfter auch mal Bytes, die eigentlich packbar wären - spare CPU-Leistung UND erhöhe die Einsatzmöglichkeiten.

Aber, hier ist jeder Trade-Off möglich und auch angebracht: Wenn man z.B. ein Spiel baut, bei dem sowieso kaum mehr als 20 Sprites gleichzeitig auf dem Bildschirm sind (und das sind bei Arcade die meisten, gerade in den Auflösungen, mit denen unsereins arbeitet), dafür aber viele mögliche Sprites bzw. viele mögliche Animationsphasen der Sprites, dafür aber z.B. Drehbarkeit oder Skalierbarkeit gar nicht benötigt wird/eingesetzt werden soll, ist eine effiziente Packmethode hier vielleicht die wesentlich bessere Wahl als z.B. die von mir oben beschriebene generalisierte "Sprite-Bitmap".
zatzen hat geschrieben:Ich hätte irgendwie dann immer ein schlechtes Gefühl wenn ich bei 4 Bit die möglichen >7 Farben nicht ausnutzen würde, und darüber hinaus hätte ich auch ein unwohles Gefühl mit einfarbigen Flächen oder auch transparenten Bereichen.
Ja, das stimmt. Schön ist so etwas nicht. Aber ich versuche immer, einfarbige Flächen zu vermeiden. Das ist nicht so mein Grafikstil. Selbst bei comicartigen Sprites (siehe "Day of the Tentacle") sind die Flächen eher klein, weil noch mindestens eine "dunklere" dabei ist, die so ein bißchen Räumlichkeit "hart" andeutet, so daß jede farbige Fläche schon mindestens 2 Farben hat. Bei so geringer Auflösung sieht "Fläche, mit Linie in gleicher, aber dunklerer Farbe umrandet" besser aus als schwarze Ränder (wie bei Comicfiguren). Schwarzgeränderte Comicfiguren würde ich frühestens ab 640er Auflösung einsetzen, bei 320 wirken diese schwarzen "Trauerränder" immer etwas klobig.
zatzen hat geschrieben:Oder auch mit Redundanzen. Wenn ich eine Figur mal sprechen lassen will dass sich nur der Kopf bewegt brauche ich das nicht per Hand auseinanderdröseln, sondern kann einfach ein komplettes Sprite speichern und darauf vertrauen, dass nur der Kopf neue Daten erzeugt.
Ja, aber dafür gibt es eine andere, viel einfachere Möglichkeit.
Man zerlegt Figuren einfach in Teilfiguren (d.h. setzt sie aus mehreren Sprite-Images zusammen) und ändert nur das Image, das sich bewegen soll. GameSys2 hat diese Methode bereits "serienmäßig" vorgesehen: Man kann Sprites "verlinken" und dabei auch festlegen, welche Parameter von der Ursprungsfigur (oder anderer in der "Figurenkette") übernommen werden sollen und welche nicht. Nehmen wir an, X und Y (die Koordinaten) sind auf "übernehmen von Grundfigur" gesetzt. Dann enthalten die Koordinaten der "gelinkten" Figur keine "echten" Koordinaten mehr, sondern vorzeichenbehaftete Offsets zur Grundfigur. Das wird dann bei der Darstellung automatisch einberechnet und braucht nicht vom User gesondert "programmiert" werden. Dann hat man einen bewegenden Kopf (oder Mund+Unterkiefer oder wasweißich) und braucht nur für diesen die Images ändern.

Die freie Zuordnung von Meta-Paletten erlaubt auch noch andere Dinge: in meinem "TestGame" (TGAME.EXE) wo es diese 8 verschiedenen Männchen gibt, ist das alles derselbe Sprite (und nur in einer Richtung, andere wird gespiegelt) mit 12 Animationsphasen zum Laufen und 3 zum Hinknien. Dazu gibt es 8 Farbpaletten (also "Meta-Paletten") und schon hat man 8 unterschiedliche Leute, die sich in Kleidung, Schuhe, Socken, Hautfarbe, Haarfarbe, Augenfarbe, ... unterscheiden. Durch rekombinierbare Teil-Sprites (andere Köpfe mit anderen Frisuren o.ä.) könnte man das Ganze noch weitertreiben und hätte mit wenigen Mitteln und ganz normalen Sprites eine Vielzahl verschiedener Leute, die praktisch nur durch eine Liste von "Nummern" gebildet werden könnten, auch per Zufall. Wär doch ulkig: ein Spiel wie Lemmings, aber es gibt Jungs und Mädels und alle sehen anders aus...
zatzen hat geschrieben:Also ich denke, zumindest wenn man ein Spiel Comic-artig aufziehen wollte, würde sich ZVID ziemlich rentieren. Ich muss einfach mal sehen wie es sich auf Dauer in der Praxis schlägt.
Ja, Dein Ansatz unterscheidet sich hier sehr maßgeblich von meinem. In diese Richtung habe ich - was Spiele angeht - noch nie entwickelt. Daher kann ich da auch kaum mit Erfahrungen dienen.

Anm.: Ich habe auch ein ziemlich gepacktes Format, das Transparenzen quasi schon "ausschließt" (gar kein Test, ob pixeln oder nicht, es wird einfach übersprungen), lauflängen-packt und gleiche Spalten nur 1x speichert und außerdem bitweise funktioniert. Dieses kommt z.B. zum Einsatz für das große "XPYDERZ" Logo, das in meinem "DBM" Format gepackt ist und irgendwas bei 11kB braucht. Das Format ist schon älter, da würde ich sicher heute noch etwas mehr rausholen - aber mir ging es u.a. auch darum, daß es speziell im Mode-X gut funktioniert und auch Clipping kann. (In Mode-X, daher wird von oben nach unten gespeichert. Hochkant ist bei Mode-X immer eine gute Idee.)
zatzen hat geschrieben:Die Idee mit dem mehrdimensionalen Sprite-Speicher finde ich sehr interessant. Sehr elegant und einfach zu handhaben sogar, selbst bei "krummen" Bitzahlen wenn man 3 und 5 Bit zusammen unterbringt.
Ja, finde ich auch - vielleicht setze ich das mal IRGENDWANN um. Momentan reicht mir auch mein derzeitiger Ansatz, weil ich auch endlich mal wieder ein Spiel machen will.
zatzen hat geschrieben:Es besteht eben nur immer eine Abhängigkeit der Sprites voneinander.
Inwiefern? Die "Layer" würden total unabhängig voneinander behandelt werden. Es wäre nicht nötig, daß "unter" einem Sprite wieder genau eins von gleicher Größe liegt. Wie gesagt: Es ist eine große, 256 Pixel breite Fläche, in der jedes Sprite für sich ansprechbar ist. Es gibt quasi keine einzelnen Sprites mit Headern oder so. Die Header existieren außerhalb, in einem gesonderten Array, das Offset, Breite, Höhe, HotspotX, HotspotY und Flags enthält - eventuell auch die Standard-(Meta)Paletten-Nr. Aber diese (Meta)paletten will ich sowieso frei zuweisbar haben (siehe oben).
zatzen hat geschrieben:Vielleicht ist diese weniger schwerwiegend wenn man die Sprites wie bei eines NES handhabt und einfach Felder von 8x8 Pixel Grafik-blöcken hat. Es muss eben nur irgendwo aufgehen, sonst bleibt ungenutzter Speicherplatz frei. Oder sehe ich das falsch?
Das mit den 8x8 Blöcken beim NES oder den 24x21 Blöcken bei C64 ist so gesehen eigentlich Mist - es liegt daran, wie es hardwaremäßig gelöst ist. Beim NES sind die Sprites quasi eine Erweiterung der Darstellung der Levels und des Zeichensatzes und waren mit so 8x8 Matrix am leichtesten technisch zu implementieren. Beim C64 verhält es sich ähnlich: Hier hat man nach dem bestmöglichen Verhältnis gesucht, das bei einer /8 Pixel teilbaren Breite noch eine passende Höhe gibt, ohne zuviel Speicher zu verbrauchen. Der VIC hat nur immer direkten Zugriff auf 16kByte gleichzeitig (außer der FarbRAM, der ist extra verdrahtet). Mit 256 Sprite-Images per 16kByte wären also pro Sprite 64 Bytes möglich. Da entschied man sich für 24x21 Pixel, was 63 Bytes braucht (das 64. Byte ist unbenutzt). Hätte man 16 Pixel Breite genommen, wären es 16x32 Pixel-Sprites gewesen (damit es noch 256 sind), bei 32 Pixel Breite 32x16 Pixel. Die 24er Methode macht es einigermaßen quadratisch - somit ist die größtmögliche Basis abgedeckt. Für eine größere Figur kann man ja mehrere übereinander darstellen.
Aber: Alles, was schmaler ist als ein Vielfaches von 24 oder niedriger als ein Vielfaches von 21 verschwendet Speicher und Rechenzeit des VIC: Auch die "Null-Bytes" werden eingelesen, bremsen pro Sprite-Zeile 3 Zyklen der CPU... (das nur so detailliert, weil ich mich mit C64 so gut auskenne).

Beim NES ist es aber das gleiche Problem: Alles, was keine Vielfachen von 8 groß ist (Breite oder Höhe) verschwendet Platz und Rechenpower, weil die generalisierten Grafikchips eben das alles darstellen, auch "leere" (volltransparente) Bytes werden technisch genauso verarbeitet wie der Rest und brauchen Speicher.

Mein Ansatz ist da auch nicht viel besser, weil ich auf volle 16er-Breiten gehe (aber die Höhen sind bei mir pixelgenau). Außerdem kann ich ein Image auch 90° gedreht ablegen und ein entsprechendes Flag (siehe oben) setzen, damit die Darstellungsroutine das weiß (die ist sowieso für drehbar ausgelegt und dreht dann einfach 90° "weniger"). Somit speichere ich das Image so, daß möglichst wenig Platz verschwendet wird. Andererseits müßte ich nur einen Befehl ändern und die Offsets erweitern, somit würde ich nicht bei ES:[BX] sondern bei ES:[BX+ofs] abfragen, ofs (0-15) würde ich vorher setzen. Nur braucht [BX+ofs] mehr Zyklen (hängt von der CPU ab) als nur [BX].
Aber mit der Methode wäre ich nicht mehr auf ganze 16er Startadressen angewiesen. Die Breite ist natürlich trotzdem 1-255, d.h. da liest er nicht mehr als er muß - nur der Startpunkt (die "linke obere Ecke") muß (derzeit) in der Bitmap an X-Position 0, 16, 32, 48... usw. liegen, damit ich mit einem "ganzen Segment" arbeiten kann.

Und weil ich keine 8x8-Matrix benutze für Sprites, ergibt sich auch kein Clipping-Problem, wie es manchmal bei NES oder Sega Master System zu sehen ist, wenn die Leute etwas schlampig programmiert haben.

Der C64 hat übrigens für Scrolling die Option, das Bild oben und unten um je 4 Pixel und links/rechts um 7 bzw 9 Pixel zu verkleinern (d.h. da is der Rahmen dann breiter).
Das dient dazu, das Clipping für das Level (aus Textzeichen ODER Grafikmode, der sehr ähnlich wie der Textmode aufgebaut ist) zu "verstecken", weil er auch keine "halben Zeichen" darstellen kann. Die C64-Sprites sind davon aber unabhängig, die Clippen problemlos.
zatzen hat geschrieben:Mir stellt sich die Frage, da ich ja Scrolling durch einfaches Neuzeichnen der Level-Blöcke erzielen will, ob es dann nicht schneller geht eine vollständige Hintergrundgrafik schnell reinzuklatschen als meinetwegen 100 mal einen Hintergrundblock mittels ZVID Routinen zu zeichnen.
Naja. Das "Schöne" an Grafikspeicher ist ja, daß er nicht kreuz und quer angeordnet ist, sondern einer gewissen Regelmäßigkeit unterliegt. Im MCGA (Mode $13, 320x200x256) Mode z.B. ist der Pixel links vom anderen um 1 Byte davor, rechts davon um 1 Byte dahinter. darunterliegende Pixel um 320er Abstand usw.

D.h. nur ein totaler Vollhonk (oder ein Skripter. der nichts anderes hat oder dem Hardwarezugriffe nicht gegeben sind) würde hier irgendeine generalisierte Pixelroutine benutzen, die jedesmal (aus einer Tabelle oder - schlimmer noch! - per Multiplikation!) die Pixelposition ausrechnet und den Pixel da dreinsetzt, wenn der nächste sowieso direkt dahinter/darunter ist, bzw der vorige direkt davor/darüber.

Und so verhält es sich auch mit einem Level: Selbstverständlich kann man ja, während man das "pixelt" die regelmäßige/rechteckige Anordnung des Speichers ausnutzen und Blöcke entsprechend pixeln. Wenn man z.B. im MCGA arbeitet und kein Scrolling will (oder meinetwegen mit nem 64kB Puffer und mit Scrolling) könnte man sogar die Blöcke ebenfalls in einer "Grafik" im Speicher liegen haben, die die gleichen Abmessungen hat wie der Puffer ODER der MGCA und könnte mit dem GLEICHEN Indexregister (BX, SI oder DI) sowohl den Block auslesen als auch schreiben und nur nach entsprechender Anzahl Zeilen jeweils das Segment für den Block auf eine andere/neue Stelle setzen...

Bei mir ist es natürlich wieder so, daß ich das Level spaltenweise direkt in den Grafikspeicher setze (Mode-X, natürlich mit mehreren "Seiten" möglich, dadurch Double/Triple/Quadruple-Buffer möglich). Und zwar, bei 16er Blöcken eben mit dem BX-Register lese und mit SI oder DI (müßt ich nachsehen) schreibe. In jeder Zeile addiere ich die Breite/4 zu DI (wie gesagt: Von oben nach unten) und 16 zu BL. wenn bei der Addition zu BL das Carry kommt (Überlauf), bin ich aus dem 16x16er Block raus, dann kommt nach BH die neue Blocknummer. Das mache ich 4x, weil ich von allem immer nur die 4. Spalte setze. Dann brauche ich nämlich beim Mode-X nur 4x die Plane ändern und nicht z.B. 320x (für jede Spalte).

Auch da könnt ich heute sicher noch mehr optimieren, aber so geht's auch erstmal. Schade ist, daß diese Methode mit 256farb-Blocks arbeitet (also 64kB Blockspeicher, wenn man 256 verschiedene Blocks/Kacheln will), aber da gibts auch schon andere Routinen. Die 32x32er Block Routine (eine Abwandlung davon arbeitet in Xpyderz) benutzt z.B. Paletten für die Blocks und die Blocks sind 16-farbig.

Die Arcade01-Unit, die das darstellt, kann Blocks übrigens wie Sprites darstellen, d.h. man setzt mit einem Flag, ob die Image-Nummer ein Block oder ein Sprite sein soll und wenn Block, werden die Daten aus dem Block-Speicher geholt - als Sprites dargestellte Blocks haben dann auch alle Möglichkeiten, die es für Sprites gibt (drehen, skalieren, dithern ...)
zatzen hat geschrieben:Also vielleicht lieber eine offene Landschaft mit nur etwa 30% scrollendem Inhalt, Plattformen etc. als Szenario, als ein Kerker bei dem wirklich alles mitscrollt.
Auch bei einer feststehenden Landschaft wäre es möglich, hier von der Landschaft nur die Pixel zu holen, die man wirklich braucht und ansonsten die Block-Pixel. Ist natürlich zu testen, was mehr performt: REP STOSD der Landschaft, dann die Blocks überall drüber, wo kein "Nullblock" ist oder eben das "0-Pixel" des Blocks testen und wenn ja, Landschaftspixel setzen (dadurch würden keine Landschaftspixel gesetzt, die dann nachträglich durch Blockpixel ersetzt würden).

Aber diese zweite Methode geht wohl nur, wenn man komplett andere Routinen (statt ZVID) benutzen würde - weiß ich nicht genau, kann natürlich sein, daß die Transparenz-Option von ZVID das hergibt.

Bei mir ist es eben so, daß ich Levels (also alles hinter den Sprites, das gekachelte Level) komplett anders behandle als die Sprites - weil ja ein Levelausschnitt ein rechteckiges Gebilde ist, das von generalisierten Level- und Blockdaten abhängt, die nicht irgendwie noch anderweitig bearbeitet werden müssen (wie es bei drehbaren/transparenzfähigen usw Sprites mit variablen Breiten/Höhen wäre) und somit durch viel kompaktere, schnellere, generalisiertere Routinen darstellbar wäre.

Weil meine Spriteroutine ja auch Blocks als Sprites darstellen kann, wäre es also quasi möglich, ein Hintergrundbild oder was auch immer zu setzen und meine Blocks, wenn sie kein volltransparenter Block sind, als "Sprites" mit Winkel 0 und Skalierung 100% an die Stellen im Bild setze, die ich brauche... (Das entspräche technisch dem, was Du oben beschreibst, nur daß meine Blocks nicht so gepackt vorliegen.) Aber so irre wäre ich nicht, daß ich die für Sprites gedachten Routinen, die eigentlich andere Stärken haben, dafür nehme, wenn ich stattdessen auch direkt für flächige Levels ausgelegte Routinen habe, die dementsprechend da mehr performen.
zatzen hat geschrieben:Aber wenn es mir gelingt, ZVID um Block-Clipping zu erweitern, werde ich einfach mal einen Performance-Test machen und auch hier anbieten.
Wäre interessant zu sehen.
zatzen hat geschrieben:Ich denke noch ein-ebig sozusagen. Bzw. Sperrbereiche oder Ebenen auf denen man laufen kann, Hintergrund, Vordergrund.
Naja, das mit den mehreren Ebenen ist nur ein Gimmick, damit es schicker aussieht, so langsamer scrollende "weite" Hintergründe, um dem Ganzen etwas "Raum" zu geben, wie z.B. bei Mario (und vielen anderen Spielen) auf SNES. Das SNES hat 5 "Ebenen": 4 Unabhängig scrollbare "Hintergründe" und 1 Sprites. Kommt natürlich auf den Modus an. Gibt auch noch den "Mode 7" mit drehbarem Level. So einen Modus wollt ich auch noch mal bei mir einbauen. Wäre darstellungsmäßig kein Problem - die Steuerung müßte dann nur "umgedacht" werden, damit sie sich intern weiterhin auf das "ungedrehte" Level bezieht, sonst wäre es zu kompliziert. Und die Sprites müßten dann entsprechend ebenfalls "versetzt" dargestellt werden. Aber da Darstellung und Steuerung bei mir getrennt sind, wäre das möglich.

Wer sehen will, was ich meine: SNES Super Probotector, Levels 2 und 5.
zatzen hat geschrieben:Vordergrund könnte z.B. ein Zaun sein oder ein Tunnel mit Fenstern, einfach nur wegen der Optik, bzw. beim Tunnel noch definieren dass man da nicht raus kann. Naja ich hab 24 Jahre kein richtiges Spiel mehr programmiert, ich muss mal sehen wie das wird.

Pixelweise habe ich noch bei Kotzman II überprüft. Einfach mit GET(x, y) ob schwarz oder nicht. ber jetzt habe ich ein 8x8 Pixel Raster vor. Ich denke auf Schrägen kann ich verzichten, ich fand die in Spielen sowieso immer nervig. Ich muss "einfach mal anfangen". Besser als Kotzman wird's schon werden.
Naja, Farben/Pixel testen ist natürlich auch eine feine Methode - funktioniert aber nur, wenn man bestimmte Farben (oder nur 1 Farbe) für Hintergrund setzt. Wenn das Level sowieso nur aus Blöcken/Patterns/Kacheln besteht, reicht es auch z.B. die Figurenkoordinaten SHR 4 (für 16x16er Blocks) SHR 5 (für 32x32er Blocks) usw. zu nehmen, damit im das Level an der Position den Block abzufragen und man weiß, was es ist. Bei der Abfrage muß man natürlich den Offset zur Figurenposition vorher zu den Koordinaten addieren/subtrahieren.

Meine Sprites werden z.B. immer so dargestellt, daß der "Hotspot" (im Image) da landet, wo die Koordinaten sind. Der Hotspot ist dabei praktischerweise irgendwo in der Mitte - aber was hier praktisch ist, kann man auch selbst entscheiden. Bei einer Bewegung bietet GameSys2 von sich aus die Möglichkeit, X- und Y- Reichweite anzugeben und zu prüfen, ob Bewegung dorthin möglich (bzw. einen Kollisionstyp auszugeben) und nur wenn möglich, sich zu bewegen. Statt X- und Y- Reichweite (vorzeichenbehaftet) kann man auch einen Winkel und einen Abstand angeben, dann wird es "vektorisiert" bewegt (wie bei Xpyderz).

Und, wie bereits letztens erwähnt: Ein "Block" kann zwei 4-Bit-Typen haben und 16 (4x4) quadratische Subbereiche, denen jeweils einer der Typen zugeordnet ist. Somit kann die Kollisionsabfrage 4x genauer sein als nur "blockgenau". Bei 16x16er Blocks 4x4 Pixel, bei 32x32er Blocks 8x8 Pixel... (bei 8x8er Blocks 2x2 Pixel...)

Theoretisch wäre auch eine pixelgenaue Abfrage möglich - dann müßte man pro Block definieren, welche "Farben" welche Reaktion verursachen - UND - GameSys2 müßte ein Zugriff auf die Blockgrafikdaten erhalten. Das würde das Ganze aber etwas unflexibler machen, weil man dann wieder abhängig davon wäre, wie diese Daten intern gespeichert sind, was anders sein kann als die Darstellung auf dem Bildschirm. Deshalb reicht mir diese 4x4-"Kollisionsauflösung" auch erst einmal.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ja, OK. Aber: Wenn ZIP ein Bild so nehmen würde, wie es nach Packen mit ZVID aussieht - also mit reduzierter Palette usw. - ist dann ZVID immer noch voran?
Nach dem Packen mit ZVID sieht ein Bild genauso aus wie vorher, verlustfrei und CRC treu. [...]
Somit also die Antwort:
Ein nicht mit ZIP gepacktes ZVID ist gegenüber einem ZIP gepacktem ZVID größer. Aber eine nicht mit ZVID gepackte Grafik die mit ZIP gepackt ist, ist gegenüber einer mit ZVID gepackten UND danach mit ZIP gepackten Grafik größer.
Ja, macht natürlich auch Sinn. ZVID ist speziell zum effizienten Packen von Grafik ausgelegt, während ZIP einfach generalisierte Daten packt. Je mehr man einen Pack-Algorithmus an die mit großer Wahrscheinlichkeit zu erwartenden Daten anpaßt, umso effizienter wird er packen.

Beispiel: Die Grafik eines Fraktals ist allgemein schlecht zu packen, wenn man von den reinen Daten ausgeht. Wenn man aber stattdessen einfach die Fraktal-Formel angibt, ist das Verhältnis von dargestellter zu gepackter Menge quasi z.B. 100000:1 (je nach Auflösung).

Beispiel: Ein Bild, das sich immer wieder wiederholende Pixel mit Farbe 0,1,2,3,4 hat, ist als PCX quasi überhaupt nicht zu packen. GIF dagegen würde das extrem klein packen können - eine "Bildungsformel" noch kleiner.
zatzen hat geschrieben:Musik/Samples:
Ich könnte mit ZSM eigentlich auch wunderbar NES-ähnliche Musik machen.
Dreieck, Rechteck und ein bisschen Rauschen sind ja als Samples kein Problem und belegen auch nicht viel. Kann ich ja mal probieren und beim baldigen ZSM Release untermogeln, nen bekannten Titel der nicht allzu schwer ist...
Ja, da habe ich schon die entsprechenden Sachen gesehen. Ich schreibe in den zugehörigen Threads etwas dazu.
zatzen hat geschrieben:Mir sind auch einige MODs untergekommen, Konvertierungen, und die Patterns rasen mit 50 Zeilen pro Sekunde durch, was das ein wenig aufbläht, aber so mit 2-3 KB Patternspeicher hält sich das noch in Grenzen.
Ja, ich nehme an, daß alles, was MOD selbst nicht per (Sub-)Befehl abbilden kann, quasi "außenrum manuell" gemacht werden muß.
So einen C64-mäßigen Arpeggio habe ich in ISM z.B. nicht drin, auch prISM und AtavISM sehen das nicht von sich aus vor. Weil ISM aber "programmierbar" ist, also Stack, "Variablen" und Schleifen anbietet, ist es möglich, das entsprechend nachzubilden.
zatzen hat geschrieben:Naja, man kann da aber auch etwas gezähmter rangehen, einziger Wermutstropfen ist dass meine Schleifen-Routinen etwas schlechter performen als die ohne Schleife, weil ich bei den Schleifenroutinen bei jedem Durchlauf prüfen muss ob das Sample schon durch ist, bei der Routine ohne Schleife kann ich vorher kurz berechnen wieviel Durchläufe es sein müssen.
Achso, Der Unterschied zwischen geloopten Samples und welchen ohne Wiederholung.
Naja, ich prüfe das ja generell, weil bei mir auch berücksichtigt ist, daß ein Sample auch zu Ende sein kann, bevor der nächste Ton angeschlagen wird (dann geht ISM in dieser Stimme bis dahin auf Stille).
zatzen hat geschrieben:Übrigens sind die Kanäle bei ZSM eher Symbolisch. Theoretisch könnte die Engine auch 100 Samples gleichzeitig spielen, dass schafft dann nur der Rechner nicht mehr. Bei den Musikmodulen dienen die Kanäle vielmehr als
Information, wann ein Sample wieder gestoppt werden soll, nämlich wenn auf dem gleichen Kanal das nächste oder ein "Note Off" kommt. Für Soundeffekte kann man aber noch Samples oben drauf laden und beliebig abspielen,
Klar, weil das Ganze ja von MOD abstammt, das zeilenweise gestaltet ist und was pro Spalte passiert, kann viel oder wenig sein.
ISM dagegen ist spaltenweise gestaltet, also genau umgekehrt: Die Stimmen kennen sich nicht, sondern funktionieren unabhängig voneinander. D.h. hier sind die Spalten definiert und dafür keine festen "Zeilen" (der nächste Ton kommt, wenn der Timer des letzten Tons abgelaufen ist).
zatzen hat geschrieben:als Begrenzung sollte ich da aber auch besser ein vielleicht 4 Kanäle System einbauen bei dem immer bei einem neuen Event auf dem Kanal gespielt wird der schon die Längste Zeit keinen neuen Abspielbefehl mehr bekommen hat.
Naja, das kommt immer drauf an. Manche Abspielbefehle dienen auch dazu, ein "Soundbett" (lange, sich langsam ändernde Begleitstimme) zu erzeugen und die dürften auch seltener als andere Töne neu angeschlagen werden.
Bei Soundeffekten will ich da noch überlegen, eine 2-bit-"Kategorisierung" für Sounds zu haben, also a) welcher Effekt auf derselben Stimme gespielt wird (und den letzten gleichen unterbricht) und b) ob ein Effekt durch andere unterbrechbar ist oder nicht.

Idee dahinter: Wenn ein Dauerfeuer-Dings rattert, kann der nächste Ratterton ruhig auf dem gleichen Kanal/Stimme kommen und den anderen abbrechen. Wenn ein Ton kommt, der nicht so ist ODER nicht die gleiche Nummer wie der "Dauerfeuer-Ton" hat, sucht er sich eine neue Stimme. Dabei darf er nur Stimmen nehmen, die keine WICHTIGEN (nicht unterbrechbaren) Sounds haben. Findet er keine freie Stimme (oder nur noch welche mit "WICHTIGEN" Sounds, wird er gar nicht abgespielt.
Ausnahme: Ist der neue Sound ein wichtiger Sound, darf er den "unwichtigsten" unterbrechen, wenn sonst nichts mehr frei ist.
usw...
Das ist mir schon vor ein paar Jahren eingefallen, um das Problem der begrenzt vorhandenen Soundkanäle zu lösen. Aber dafür entwickle ich noch eine genauere Definition. Ich will, daß das irgendwo "außerhalb des Spiels" durch den Entwickler definierbar wird, damit er nicht während der Entwicklung der Steuerung darüber entscheiden und Abfragen treffen muß, ob er den Sound jetzt aktivieren darf oder nicht.
zatzen hat geschrieben:Diese Anzahl der Kanäle könnte man Zwecks Performanceoptimierung im Setup des Spiels ja auch noch ändern.
Ja, das wäre für Soundeffekte eine gute Idee, das gibt es auch bei vielen Spielen (z.B. DOOM) für Effekte. Für Musiken könnt ich's weniger gebrauchen, da wüßt ich nicht, wie ich bei Musik mit fehlenden Kanälen umgehen soll. Beim C64 haben die das teilweise recht elegant gelöst (Katakis, R-Type). Erstaunlich, wie die mit nur 3 Stimmen sowohl Musik als auch SFX gleichzeitig machen, ohne daß es doof klingt.
zatzen hat geschrieben:Ich habe auch noch in Erinnerung dass ich glaube ich sogar zu manchen Zeiten nur 560K frei hatte, und eigentlich liefen alle Spiele. Ich habe auch nicht vor wirklich mehr als 500 KB in ein Spiel zu packen.
Naja, ich entwickle da erst einmal und sehe dann, was ich zum Schluß so erreichen kann. Dann muß man vielleicht etwas weglassen, wenn man etwas anderes wichtiger findet. Vielleicht schaffe ich's auch, daß es von vornherein weniger als 500kB braucht.

Xpyderz (ja, schon wieder) braucht z,B, über 600kB in der "Vollversion", die auch Multiplayer UND den Leveleditor gleich mit enthält - aber für die "offiziell erhältliche" (siehe meine Website) habe ich Multiplayer (was sowieso noch nicht funktioniert) und Leveleditor auskommentiert und es braucht weniger als 500 kB (irgendwas bei 480-490kB). Den Leveleditor könnte ich aber eigentlich auch mal anschalten - könnte mir vorstellen, daß viele Leute gerne Levels dafür bauen würden. Und Xpyderz kann viel mehr als es in den automatisch erzeugten Levels zeigt: z.B. Blöcke, die man mit dem Panzer verschieben kann (auch Boni und Teleporter UNTER solchen Blöcken) oder Bereiche definieren, zu denen die "freien" Teleporter nicht hinteleportieren. Oder Geschütztürme, die erst blockieren und wenn sie vernichtet sind, den Weg freigeben... Oder Geheimwege, die aber wie Mauern aussehen... Und natürlich kann man selber die Boni und deren Respawn-Geschwindigkeit usw festlegen... naja... was auch immer.
(Erstaunlich, was mit so ein bißchen Pascal und Assembler machbar ist...)
zatzen hat geschrieben:Ein bisschen (schmerzliche) Erfahrung habe ich diesbezüglich.
Quickbasic kannte keinen Heap (oder ich war zu dumm auf ihn zuzugreifen) und ich musste Kotzman II auf mehrere EXE Dateien verteilen, je eine für jedes Level, habe ich dann irgendwie umbenannt und mit Kommandozeilenpasswörtern geschützt.
Ja, da könntest ja inzwischen einen Remake machen... Fand das mit dem "Level laden müssen" Menü da auch irgendwie dusselig. Und die komischen Steuertasten... (Naja, dafür hab ich ja einen "Hack" geschrieben, den man vorher lädt und der dann beliebige Tasten zuläßt.)
zatzen hat geschrieben:Priorität wird bei mir auch die Grafik haben, und dann sehe ich
was ich mir Musikmäßig noch leisten kann.
Naja, Prioritäten kann man ja setzen, wie man will - es kommt ja darauf an, was einem wichtig ist. Einzige 4 Grenzen sind ja, wie oben bereits angedeutet:
1. Was schafft die CPU (und die restliche Peripherie)?
2. Wieviel Speicher ist da?
3. Wieviel Zeit hat man/will man investieren?
4. (neu) Welche Fähigkeiten hat der Entwickler?
Innerhalb dessen kann man sich ja -quasi- "frei bewegen".
4. kann man z.B. immer wieder erweitern, indem man lernt. 3. liegt auch beim Entwickler selbst, so langer er nicht für eine Firma nach Vorgaben arbeiten muß. An 1. und 2. kann man am wenisten ändern. Da hat man nur die Chance, dem User eine Mindestanforderung mitzuteilen. Je geringer diese ist, umso mehr User haben die Chance, das Programm (Spiel) zu benutzen. Aber gleichzeitig nimmt man sich mit geringen Systemanforderungen auch manche Möglichkeiten, die mit "dickeren" Maschinen machbar wären.

Andererseits: Siehe "The 8-Bit Guy": sein "Planet X3" hat Mindestanforderung IBM-PC XT 4,77 MHz, Grafik: Herc-Mono, CGA oder Tandy. Für ruckelfreies VGA: mind. 286er, läuft aber auch auf XT.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Sorry dass es solange gedauert hat. Und ich schreibe hier nicht ganz so viel wie Du,
habe es aber interessiert alles gelesen.

DOSferatu hat geschrieben: Das ist ein großer Irrtum, dem viele Leute heutzutage unterliegen.
Die ersten IBM-PCs wurden mit 64kB, 128kB, 256kB, teurer auch mal 512kB ausgeliefert. Dieses "volle untere MB" (mit den 640kB nutzbaren) war damals nicht selbstverständlich.
Okay, so erklären sich wohl auch die frühen Sierra Adventures, wo die Hintergründe nicht aus Bitmaps bestanden
sondern gezeichnet wurden. Vielleicht kennst Du den Editor dazu, ziemlich interessant, es werden nur "Zeichenbefehle"
gespeichert.

Irgendwann in den 90ern wurden dann 640 KB aber wohl zum Standard.

Ich würde Deine Units nutzen wollen, wenn es mir darum ginge, möglichst schnell an ein wirklich gutes Ergebnis zu
kommen. Aber solange ich nicht im Team arbeite möchte ich alles bis auf die kleinste Ecke selber machen. Bis auf
kleine Dinge wie XMS Kopierroutinen, die ich bisher nur eher unverstanden irgendwoher übernommen habe.
Als Team allerdings könnte man sich austauschen mit den Units. Man könnte ISM verwenden oder ZSM, oder Deine
Grafikroutinen, die mehr können als mein ZVID, welches aber vielleicht bei einer größeren Menge gut packbarer Grafiken
wiederum einen Vorteil hätte.

Stichwort XMS: Der Gedanke diesen zu benutzen stellt bei mir in Frage ob das mit den Delta-gepackten-Samples in ZSM
dann überhaupt noch einen Sinn macht. Würde ich Samples aus dem XMS beziehen, könnte ich ziemlich aus dem Vollen
schöpfen, ohne irgendeine Speichergrenze im Auge behalten zu müssen. Entscheidend bei der Frage wäre für mich aber
auch, was ist schneller: Im Speicher liegende Delta-Samples decodieren oder (für 8 Kanal Module) 8x Pro Zeile einen
Sample-Fetzen aus dem XMS Kopieren? Naja ganz wohl ist mir dabei nicht.
Aber für ein Spiel mit Sprachausgabe würde ich XMS nutzen wollen, und dann passend zur ZSM Engine dort auch Delta-
Samples auslesen, aber dann einfach in Pufferlängen. Abgesehen davon, dass eine simple Decodierung über eine Tabelle
nötig ist, ergibt sich gegenüber ungepackten Samples nur dann ein erhöhter Datendurchsatz, wenn die Abspielfrequenz
des Samples größer ist als die Mischfrequenz, und das ist selten, zumindest bei 44.1 kHz.
DOSferatu hat geschrieben:wenn die 64kByte voll sind, sind sie voll.
Ich bin mir nicht sicher ob ich das richtig verstehe, aber nur zur Sicherheit mal die Frage bzgl. des Programmcodes:
Ich habe hier ein Programm von mir, das ist kompiliert knapp 68 KB groß.
Ist der Code auf 64 KB beschränkt, oder wird einfach schonmal CS verändert?

Wenn man ein Spiel so sorgfältigt entwickelt und von vorne bis hinten ersteinmal alles durchplant kann man natürlich so
entwickeln wie der "8-Bit Guy". Aber solche Formate wie ZSM oder ZVID lassen soetwas gar nicht zu, weil die Größe der
gepackten Daten eher unvorhersehbar von den Rohdaten abhängt. Daher würde ich in meinem Fall so entwickeln, dass
ich die letztliche Größe eher abschätze um dann bei Pi*Daumen 500 KB zu landen, plusminus 100KB, abhängig von der
Kompression, so in etwa. Letztlich würde ich schätzen, dass ich so mehr Daten in das Spiel bekomme als unkomprimiert.

Über Kollisionsabfragen habe ich mir bisher noch gar keine großen Gedanken gemacht und ZVID einfach nur als
Möglichkeit betrachtet, Grafiken anzuzeigen. Früher habe ich einfach die Nähe der Objekte ausgewertet.
Es ist vielleicht für ein Spiel der falsche Weg, aber ich habe da modular gedacht im Sinne von verschiedenen Formaten
die klar definiert sind und die man multimedial kombinieren kann.

Wie gesagt lässt XMS eigentlich alle Pack-Bemühungen lächerlich aussehen, vor allem dann, wenn man sowieso Rechner
wie 486er oder gar Pentium voraussetzt, die mitunter reichlich XMS oder EMS bereitstellen. Naja trotzdem macht es mir
Spaß, etwas Denkarbeit in Echtzeit-entpackbare Formate zu stecken. Die Patternkompression bei ZSM stellt da überhaupt
kein Übel dar, weil sie überhaupt nicht zeitkritisch ist aufgrund des niedrigen Datendurchsatzes von durchschnittlich
vielleicht 10 Byte pro Sekunde, eingelesen durch vielleicht 30 Aufrufe einer kleinen Assembler-beliebige-Bitbreite-Lese-
Routine in dieser Zeit. Die Delta-Samples haben einen Einfluss, aber eine Halbierung der Daten kann wertvoll sein.
Was ZVID angeht ist es so ausgelegt, dass "idiotensicher" alles optmimal gepackt werden soll, ohne dass man sich als
Grafiker Gedanken beim Pixeln machen muss, dort also frei nach Schnauze arbeiten kann.

Ich finde Deine Art und Weise, Sprites "einzulagern" sehr klug, und es wird sicherlich sehr gut performen. Ich bin wohl
schon zu sehr verseucht, seriell zu denken im Sinne von Multimedia, was schon mit Formaten wie FLI(C) anfing. Oder ich
habe einfach noch nicht hardwarenah genug gedacht, wie man Sprite-Daten am einfachsten addressieren kann. ZVID ist
dahingehend eher eine Katastrophe, weil jedes einzelne Sprite als Frame verstanden wird, einen eigenen Platz auf dem
Heap reserviert bekommt und über Pointer addressiert werden muss und für jeden 8x8 Block eine Routine gestartet wird.
Überdies läuft noch eine Routine aussen herum, die für soetwas wie Lauflängenkomprimierung gleicher
aufeinanderfolgender Blöcke zuständig ist.
DOSferatu hat geschrieben:Klar. Das freut natürlich jeden gescheiten Programmierer, wenn er 4744 Bytes sparen kann. (Obwohl der Korinthenkacker sagen würde, daß 64x64 = 4096 sind und mit 4-Bit wären es demzufolge 2048, somit wären es 2048-1400 = 648 Byte Ersparnis - was trotzdem nicht schlecht ist, es wäre ca. ein Drittel.)
Ich muss hier einfach mal aufklären um welches Beispiel es sich handelt. Es sind tatsächlich drei Bilder und nicht eines,
ich habe sie einfach mal nebeneinandergeklatscht.
alien.png
alien.png (930 Bytes) 12735 mal betrachtet
Tatsächlich sind die Dimensionen 64x72, ich hatte nur vertikal die 8 letzten Pixel weggelassen weil da kaum Information
drin waren. 1. Bild Augen zu, 2. Bild Augen offen, 3. Bild grinsen und winken.
Zugegebenermaßen sind die Bilder ziemlich dünn besiedelt und gleichen sich einander stark. Aber ZVID wird dem gerecht
und packt hier ordentlich, die ZVD-Datei ist genau 1407 Byte groß. Die png-Datei die ich hier im Post verwende ist 930
Byte groß wenn ich die mögliche Farbanzahl vor dem Speichern auf 16 Farben setze. ZVID packt also im Vergleich dazu
immerhin 66% so gut. Bei 256 Farben-Einstellung ist das abgespeicherte png-Bild 1720 Byte groß und ZVID liegt sogar vorn.
Klar, allein die Palette von 768 Byte muss ja irgendwo hin. Korinthenkackerische Anmerkung: Die ZIP der 1407 Byte
großen ZVD Datei ist 866 Byte groß, also kleiner als das 930 Byte png.
Aber, keine Frage, das ist/war ein Extrembeispiel mit nur 3 Farben und viel einfarbiger/transparenter(gelb) Fläche.
Hätte man auch einfach als 2 Bit abspeichern können, aber ZVID komprimiert es im Vergleich dazu nochmal auf weniger
als hätte man es mit 1 Bit abgespeichert. Ähnlich ist es bei anderen Sprites - ich hatte in einer Demo "fotorealistische"
Sprites die ich auf 63 Farben beschränkt habe, ZVID bringt sie auf unter den Speicherplatzbedarf von 16-Farben-Sprites.
Also kann man sagen: Mehr Farben bei weniger Speicherplatz. Auf Kosten von etwas Performance versteht sich.

Ich überlege die letzten Tage an einem "ZVID2", denke darüber nach ob man die Blöcke auf 4x4 verkleinert. Vorteil wäre,
dass generell maximal 16 verschiedene Farben in den Blöcken wären, auch generell weniger Farben unterhalb als bei 8x8,
und dass man mehr einfarbige Flächen "trifft". Nachteil wäre, dass die Headerdaten jedes Blocks im Verhältnis zu den
Daten schwerer wiegen würden.
Eine Headergröße von 1 Byte wäre anzustreben. Und dann könnte man noch die Maximalgröße einer ZVID(2)-Datei auf
65528 Byte begrenzen, so dass man für einen "Packen" Sprites nur einen einzigen Pointer braucht.
Aber ist alles nur Gedankenspielerei ersteinmal.

Trade-Off: Da ich (auch angesichts des heutigen Angebots an Spielen, auch der historischen) gar nicht als einziges Ziel
habe, ein eigenes Spiel, egal wie, zu programmieren, habe ich einfach Spaß daran, so "nebenher" solche Packalgorithmen
zu basteln, und ja, ich tendiere eher zu "langsamen" Spielen als zu spielen wo die Performance bis auf letzte bisschen
ausgereizt wird. Da solltest Du mich ja mittlerweile kennen, da für mich auch noch nicht der Tag gekommen ist, da ich
die Framerate von der Leistung des Computers abhängig mache, sondern sie fest definiere wie bei einem Video. Das
macht für mich momentan einiges einfacher, wie auch Animationen.

Teilfiguren: Ja, das habe ich mir in einem andere Zusammenhang auch schon einmal überlegt. Für den Fall dass man sich
seine Spielfigur beliebig aus verschiedenen Teilen (Gesicht, Körper, Kleidung...) konfigurieren kann. Oder denkbar(!)
wäre auch, dass man die Grafiken gliedmaßenweise gewissermaßen hochauflösend vorzeichnet, dann in alle möglichen
Stellungen zurechtdreht und sich so die Figuren baut. Auch an soetwas wie ein verborgenes Skelett hatte ich mal
gedacht, auf dem man die Körper aufbaut.
DOSferatu hat geschrieben:Die Arcade01-Unit, die das darstellt, kann Blocks übrigens wie Sprites darstellen, d.h. man setzt mit einem Flag, ob die Image-Nummer ein Block oder ein Sprite sein soll und wenn Block, werden die Daten aus dem Block-Speicher geholt - als Sprites dargestellte Blocks haben dann auch alle Möglichkeiten, die es für Sprites gibt (drehen, skalieren, dithern ...)
Deine Sprite/Block Routinen sind wohlüberlegt und für ein Spiel optimiert. Ich habe mir bisher solche Gedanken noch
nicht gemacht und nur überlegt, wie ich Grafik möglichst effizient speichere, und ich habe sie bisher nur wie Daten
betrachtet, die man einfach irgendwie auf den Bildschirm bringt. Und naja, mich hat wohl bisher weniger eine
höchstmögliche Performance gereizt als vielmehr möglichst kompakte Daten. Das liegt aber auch an meiner Vorstellung
von Spielen. Ich möchte wie gesagt keinen Weltraum-Shooter mit 70 fps machen, sondern ein eher gemütliches Spiel wo
25 fps oder weniger genügen sollten - ob Scrolling damit noch erträglich ist wäre abzuwarten. Ich denke da manchmal an
"Captain Comic". Kleiner Bildschirmausschnitt, 10 fps, Scrolling in 8 Pixel Einheiten. Davon gibt es auch eine Nintendo NES
Version mit dem entsprechenden flüssigen Scrolling, 50/60 fps und richtig schöner Grafik. Da könnte man sich schon
fragen, wozu sich überhaupt noch auf einer 486er Plattform bemühen, wenn ein 80er Jahre 8 Bit System soetwas schon
besser stemmen konnte als ich es in absehbarer Zeit hinkriegen würde.
Aber letztlich geht es ja auch um den Spaß den man als Hobbyprogrammierer (so würde ich mich bezeichnen, Dir will
ich das gar nicht unterstellen) hat, und in gewissen Dingen zeigen sich auch bei einem Amateur die Vorzüge eines
moderneren 32 Bit Systems gegenüber einer antiken Spielekonsole.
DOSferatu hat geschrieben:Wenn das Level sowieso nur aus Blöcken/Patterns/Kacheln besteht, reicht es auch z.B. die Figurenkoordinaten SHR 4 (für 16x16er Blocks) SHR 5 (für 32x32er Blocks) usw. zu nehmen, damit im das Level an der Position den Block abzufragen und man weiß, was es ist
Genauso hatte ich mir das gedacht. Aber eher so, dass ich im "Hintergrund" ein Datenfeld habe von 1000 (40x25)
Elementen, somit also für einen kompletten Screen mit 8x8 Pixel großen Blöcken.

Ich merke, dass Du schon sehr viel mehr Arbeit in wirkliche Spiele-Routinen investiert hast. Bzw. habe ich noch gar keine
Spiele-Routinen geschrieben sondern nur ZSM und ZVID. Ich wollte ursprünglich ja mal eine generalisierte Game-Engine
angefangen haben, der man nur kompilierte Spiel-Daten vorlegen muss. Du hast mich zu der Zeit überzeugt, dass soetwas
weniger gut wäre, da eine solche Engine je nachdem zu viel Code enthält, der gar nicht vom jeweiligen Spiel genutzt
würde. Ich hatte es allerdings damals eher so eingeschätzt, dass die Engine selbst gar nicht so unglaublich aufgeblasen
sein würde, weil sie die meisten Daten aus den Spiele-"Modulen" beziehen würde. Vielleicht sollte ich noch einmal
darüber nachdenken.
DOSferatu hat geschrieben:Naja, ich prüfe das ja generell, weil bei mir auch berücksichtigt ist, daß ein Sample auch zu Ende sein kann, bevor der nächste Ton angeschlagen wird (dann geht ISM in dieser Stimme bis dahin auf Stille).
Ich muss bei den ZSM-Routinen die Pufferlänge und die Zeilenlänge/-dauer berücksichtigen. Die Einberechnung eines
Samples wird also durch die Zeilen und die Puffergrenzen unterbrochen. Innerhalb dieser Grenzen berechne ich bei nicht-
geloopten Samples die tatsächlich einzurechnende Länge, d.h. wenn das Sample innerhalb der gesteckten Grenzen endet
wird das vorher erkannt und der Zähler für die Routine entsprechend gesetzt.
Bei einem geloopten Sample ist das aber anders, wenn dieses innerhalb der einzurechnenden Länge sein Ende erreicht
muss es wieder von vorne beginnen, und zwar der Abspielgeschwindigkeit entsprechend um eine Vor- und
Nachkommastelle weiter. Vielleicht etwas schlecht erklärt. Jedenfalls kostet das wohl etwas Performance, weil ich nach
jedem "Tick" den ich einrechne eine Überprüfung mache, ob das Sample schon wieder "durch" ist. Ich werde bei
Gelegenheit mal in den Code gucken, und es, wenn es noch nicht so ist, es so umschreiben, dass nur gesprungen wird,
wenn das Sample das Ende erreicht hat.
JMP ist langsam, nicht CMP, richtig?
DOSferatu hat geschrieben:Bei Soundeffekten will ich da noch überlegen, eine 2-bit-"Kategorisierung" für Sounds zu haben, also a) welcher Effekt auf derselben Stimme gespielt wird (und den letzten gleichen unterbricht) und b) ob ein Effekt durch andere unterbrechbar ist oder nicht.
Das ist eine gute Idee, Du kannst Dir denken, dass ich gerne Sounduntermalung haben werde, aber irgendwo gibt es eben
auch Performancegrenzen, und damit kann man das gut strukturieren.
DOSferatu hat geschrieben:Erstaunlich, wie die mit nur 3 Stimmen sowohl Musik als auch SFX gleichzeitig machen, ohne daß es doof klingt.
Ähnlich beim NES. Da haben die Soundeffekte höhere Priorität als die Musik, das hört man teilweise.
DOSferatu hat geschrieben:Ja, da könntest ja inzwischen einen Remake machen... Fand das mit dem "Level laden müssen" Menü da auch irgendwie dusselig. Und die komischen Steuertasten... (Naja, dafür hab ich ja einen "Hack" geschrieben, den man vorher lädt und der dann beliebige Tasten zuläßt.)
Ein Remake, um ZSM und transparente Grafik reinzubauen wäre ganz nett, aber das Spiel an sich finde ich mittlerweile
langweilig. Das mit dem Level-Laden-müssen ist die Konsequenz von meiner ungeplanten und spontanen
Herangehensweise damals. Dieses komische Ding wo Kotzman reingebeamt wurde hatte ich einfach mal so gepixelt als
mir langweilig war und wusste nicht so recht was ich damit anfangen sollte. Dann machte ich damit einen Animationstest,
dass man es in so einem 32x32 Pixel-Block-Level umherbewegen konnte. Und das wurde dann so ein improvisiertes Ding
mit Türen und Schlüsseln. Und dann hat mich irgendwie die Faszination gepackt und ich wollte ein komplexeres Spiel
draus machen. Hätte ich mal besser ganz wegnehmen sollen dieses "Probierlevel". Aber ich habe das nicht objektiv genug
gesehen.
Ist Dein Steuerungs-Hack ein TSR?
Die komischen Tasten rühren übrigens noch von einer alten Empfehlung des Buches "Spiele Programmieren mit QBasic"
her, da diese Tasten über die Tastaturstatusbytes abfragbar sind, ohne dass man sich mit Typematic rumschlagen muss.
DOSferatu hat geschrieben:Naja, Prioritäten kann man ja setzen, wie man will - es kommt ja darauf an, was einem wichtig ist. Einzige 4 Grenzen sind ja, wie oben bereits angedeutet:
1. Was schafft die CPU (und die restliche Peripherie)?
2. Wieviel Speicher ist da?
3. Wieviel Zeit hat man/will man investieren?
4. (neu) Welche Fähigkeiten hat der Entwickler?
1.: Ich gestehe mir ein dass ich nur ein mittelmäßiger Programmierer bin
und setze eine 486er CPU voraus und Peripherie entsprechender Zeit und
erstelle Programme, die auf diesem System so gut performen wie jene
von Spitzenprogrammierern auf 386er-Systemen
2.: Auch wenn im Protected Mode oder per XMS/EMS Megabytes an Speicher
zugänglich wären motiviert es mich bei 640K zu bleiben, da mehr verfügbarer
Speicher meine Bemühungen unsinnig macht, Daten zu komprimieren.
3.: Da es nur eines meiner Hobbys ist sehe ich mich keinem Zeitdruck
ausgesetzt. Ob ich in einem oder fünf Jahren ein Spiel rausbringe ist
für mich nicht so wichtig. Klar wäre es schöner, wenn früher.
4.: Nicht gerade der typische hochbegabte Informatiker, aber motiviert.
Musiker/Sound, Pixelgrafik geht auch, aber auch nur mit Motivation.
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von DOSferatu »

zatzen hat geschrieben:Sorry dass es solange gedauert hat. Und ich schreibe hier nicht ganz so viel wie Du, habe es aber interessiert alles gelesen.
Es besteht ja keine Verpflichtung, "viel" zu schreiben - und die Textmenge ist nicht automatisch äquivalent zur Qualität. Ich habe nur das Problem, mich schlecht kurz fassen zu können - was eigentlich ein schlechter Schreibstil ist.
zatzen hat geschrieben:Okay, so erklären sich wohl auch die frühen Sierra Adventures, wo die Hintergründe nicht aus Bitmaps bestanden sondern gezeichnet wurden. Vielleicht kennst Du den Editor dazu, ziemlich interessant, es werden nur "Zeichenbefehle" gespeichert.
Ja, das ist mir bewußt. Habe zwar noch keins dieser Sierra-Adventures gespielt (nur mal eins angespielt). Im Gegensatz zu den Lucas-Adventures konnte die Spielfigur bei dem Sierra-Ding an jeder Ecke sterben - immer nur Trial-and-Error und wieder neuladen... das frustrierte mich irgendwann.
zatzen hat geschrieben:Irgendwann in den 90ern wurden dann 640 KB aber wohl zum Standard.
Naja, irgendwann hat man den vollen Speicher (und mehr) eingebaut. Die "obere Grenze" bei 640 kB für den "normalen Heap" ergibt sich ja aus dem ab dort eingeblendeten Grafikkartenspeicher.
zatzen hat geschrieben:Ich würde Deine Units nutzen wollen, wenn es mir darum ginge, möglichst schnell an ein wirklich gutes Ergebnis zu kommen.
Naja, solange man sie nicht ändert, könnte man meine Units nutzen. Allerdings müßte man sich da natürlich teilweise auf meine selbstentwickelten Formate und Ideen einlassen, weil manches nur damit funktioniert. Außerdem sind viele meiner Units so gebaut, daß sie zusammen mit anderen meiner Units funktionieren, bzw. darauf aufbauen.
Warum keine Änderung? Nun ja - wenn man die Units ändern will, sollte zumindest meine ID oder ähnliches daraus entfernt werden, weil ich mich bei meinen eigenen Units darauf verlasse (und verlassen können will), daß sie so funktionieren, wie gedacht.
Die Dokumentation zu meinen Units ist auch (leider) nicht überall gleichermaßen vorhanden oder gleichermaßen gut.
zatzen hat geschrieben:Aber solange ich nicht im Team arbeite möchte ich alles bis auf die kleinste Ecke selber machen. Bis auf kleine Dinge wie XMS Kopierroutinen, die ich bisher nur eher unverstanden irgendwoher übernommen habe.
Ja, das verstehe ich. So geht es mir auch. Erstens ist man da relativ sicher davor, daß einem irgendwann jemand damit käme, man hätte seinen Code "geklaut" usw... Und zweitens lernt man dabei natürlich viel mehr. Kleiner Nachteil ist natürlich immer, daß die Softwareentwicklung dadurch relativ lange dauert.
XMS zu benutzen, ist an sich nicht so schwer. Ich habe das auch aus irgendwelchen Beispielen/Dokumentationen gelernt (und mir dann natürlich eine eigene XMS-Unit gebaut).
zatzen hat geschrieben:Als Team allerdings könnte man sich austauschen mit den Units. Man könnte ISM verwenden oder ZSM, oder Deine Grafikroutinen, die mehr können als mein ZVID, welches aber vielleicht bei einer größeren Menge gut packbarer Grafiken wiederum einen Vorteil hätte.
Ja, prinzipiell ist so etwas immer möglich. Allerdings bin ich kein professioneller Programmierer - das was unter manchen Leuten als "saubere Programmierung" gilt, ist bei mir nur sehr unterirdisch ausgeprägt. Mir geht es um Performance, Speichersparen oder beides und solange ich selbst meinen Code verstehe, reicht es mir. Das Problem ist, daß Leute, die völlig unvorbereitet auf meine "Textblöcke" aus Code treffen, davon etwas abgeschreckt sein könnten.

Außerdem muß natürlich berücksichtigt werden, daß meine Programme (und Units) über einen Zeitraum von über 20 Jahren entstanden sind und sich Kenntnisse und Programmierstil da auch teilweise verändert haben. Hier sollte man also nicht so etwas "feines" erwarten wie man es vielleicht von Softwarefirmen gewöhnt ist.

Meine Procedure/Function-Header sind natürlich normal nutzbar, die Parameterübergabe ist (meistens) auch recht normal. So lange man das Zeug also nur "benutzen" will, sollte es keine Probleme geben. Nur - wie oben erwähnt: Manches funktioniert nur, wenn die Daten entsprechend vorliegen. Meine Sprite- und Level-Darstellungsroutinen beispielsweise funktionieren nur für Mode-X (verschiedene Auflösungen, auch u.a. 320x200, aber eben auch z.B. 360x240 oder 256x256 oder ähnliche). (In diesem bekannten, als "Mode-X" bezeichneten VGA-Grafikkarten-Tweak sind die Grafikdaten / Pixel etwas anders angeordnet als z.B. im MCGA, also Mode 13h).
zatzen hat geschrieben:Stichwort XMS: Der Gedanke diesen zu benutzen stellt bei mir in Frage ob das mit den Delta-gepackten-Samples in ZSM dann überhaupt noch einen Sinn macht.
Effiziente Programmierung macht meines Erachtens immer Sinn.
zatzen hat geschrieben:Würde ich Samples aus dem XMS beziehen, könnte ich ziemlich aus dem Vollen
schöpfen, ohne irgendeine Speichergrenze im Auge behalten zu müssen.
XMS ist nicht "irgendein unendlicher Speicher, der einfach da ist". Es ist trotzdem nur so viel Speicher da, wie jemand in den PC eingebaut hat. "Aus dem Vollen schöpfen" ist nie so meine Devise beim Programmieren gewesen.
zatzen hat geschrieben:Entscheidend bei der Frage wäre für mich aber auch, was ist schneller: Im Speicher liegende Delta-Samples decodieren oder (für 8 Kanal Module) 8x Pro Zeile einen
Sample-Fetzen aus dem XMS Kopieren? Naja ganz wohl ist mir dabei nicht.
Sollte es auch nicht sein. Auf XMS kann ja nicht direkt zugegriffen werden - es muß immer in den Heap kopiert werden, damit Zugriff (vom Real-Mode aus) erfolgen kann. Also muß auch im Heap (also <640k) Speicher frei sein. Wie schnell die XMS-Kopiererei am Ende ist, hängt von der Zugriffsgeschwindigkeit des Speichers, der CPU und auch vom Chipsatz des Motherboards ab - das ist schwer kalkulierbar. Bei meiner Kiste ist das zwar z.B. recht schnell - ich habe aber auch, was 486er angeht, ein absolutes Spitzenmodell hier, das kann man kaum als durchschnittliche 486er-Hardware bezeichnen.
zatzen hat geschrieben:Aber für ein Spiel mit Sprachausgabe würde ich XMS nutzen wollen, und dann passend zur ZSM Engine dort auch Delta-Samples auslesen, aber dann einfach in Pufferlängen.
Ich frage mich immer wieder, was so wichtig an dieser verflixten Sprachausgabe ist.
Selbst die Lucas-Adventures wurden zuerst ohne Sprachausgabe entwickelt und beim fertigen Spiel hat man dann noch später eine Sprachausgabe dazugepflanzt - meist auf einer CD-Version, wo dann ein mehrere 100 MB großes Sprachfile mit dabei war.

Meine Meinung dazu: Sprachausgabe ist ein netter Nebeneffekt - sollte aber nicht das erste sein, worauf es ankommt. Also ein Spiel "um die Sprachausgabe herum" programmieren, das halte ich z.B. für eine schlechte Idee. Desweiteren kommt hinzu: Sprachausgabe erfordert Sprach-EINGABE. Das heißt: Entweder man spricht alles selbst (dann klingt jede "Figur" gleich). Und nicht jeder hat eine gute Stimme oder kann Dinge so ausdrucksstark wie gemeint wiedergeben. (Nicht umsonst gibt es dafür eine Ausbildung.) ODER man spricht nicht alles selbst - dann braucht man genügend Leute, die mitmachen. Und natürlich braucht man Zeit und ein gescheites Equipment zur Aufnahme UND muß organisieren, daß die entsprechenden Leute da dann alle hinkommen usw. usf.

Sprachausgabe wäre so ziemlich das Allerletzte, was ich einem Spiel noch hinzufügen würde, wenn alles andere fertig ist und 100% funktioniert.
zatzen hat geschrieben:Abgesehen davon, dass eine simple Decodierung über eine Tabelle nötig ist, ergibt sich gegenüber ungepackten Samples nur dann ein erhöhter Datendurchsatz, wenn die Abspielfrequenz des Samples größer ist als die Mischfrequenz, und das ist selten, zumindest bei 44.1 kHz.
Ich werde in meinen Spielen wahrscheinlich eher so bei 11 kHz bleiben - höchstens 22 kHz und auch mono/8bit. Die Soundqualität von ISM rechtfertigt einfach keine Ausgabe in CD-Qualität und Spiele wie ich sie plane, werden weder grafisch noch soundtechnisch irgendwie "realistisch" sein.
zatzen hat geschrieben:
DOSferatu hat geschrieben:wenn die 64kByte voll sind, sind sie voll.
Ich bin mir nicht sicher ob ich das richtig verstehe, aber nur zur Sicherheit mal die Frage bzgl. des Programmcodes:
Ich habe hier ein Programm von mir, das ist kompiliert knapp 68 KB groß.
Ist der Code auf 64 KB beschränkt, oder wird einfach schonmal CS verändert?
Kann man nicht genau sagen. Eine EXE enthält neben dem reinen Programmcode auch noch einen Header, der auch schon mal etwas größer sein kann. Es hängt auch vom Compiler ab bzw. von den Compilereinstellungen. In Turbo-Pascal ist es so, daß zwar das "Grund"-Codesegment nur 64kByte groß werden kann, jedoch jede Unit für sich es das auch wieder kann, so daß man auch (durch "Auslagern" in Units, bzw. indem man gleich so modular programmiert, daß Dinge, die in Units gehören sollten, auch dort sind) auch Programme bauen kann, die z.B. wirklich 620 kByte Code enthalten. Natürlich wird in solchen Fällen das Codesegment gewechselt - d.h. immer dann, wenn ein FAR CALL (oder FAR JMP) erfolgt.

Und ja, FAR CALLs / FAR JMPs sind natürlich ein klein bißchen weniger performant als entsprechende NEAR CALLs / NEAR JMPs - aber letztere beschränken Code eben auf die bewußten 64kByte (ggf. sogar weniger). Beispiel: Ein Programm mit knapp 64kB Größe hat am Ende einen Sprung, der an den Anfang springt. Ist das ein NEAR JMP? Nein. Wieso? Ein NEAR JMP hat eine 16-Bit (also 64kByte) Reichweite - aber: Er ist vorzeichnenbehaftet. D.h. der gleiche Opcode springt sowohl vorwärts als auch rückwärts, der 16Bit-Parameter reicht von -32768 bis +32767, RELATIV zur Stelle, wo sich der Opcode befindet (genauer gesagt, ausgehend von der Position direkt nach dem kompletten Befehl).

Also, falls gleich wieder das Geheule losgeht, von wegen: "Oh, wenn das ein bißchen weniger performt, kann man es gar nicht benutzen": Ja, kann man so sehen wollen. Dann kann man aber eben auch nur kleine Programme schreiben - im Realmode sind die Segmente nunmal nicht größer.

Ich für meinen Teil habe den geringfügigen Performanceverlust akzeptiert, der z.B. durch FAR-CALLs der in Units stehenden Procedures/Functions entsteht und gleiche das aus, indem ich z.B. Performancekiller wie innere Schleifen möglichst performant code und indem ich einen Trade-Off zwischen Speichereinsparung und durch komplexen Code möglichem Performanceverlust zu finden versuche. Ich bin da auch nicht perfekt - und mitunter empfinde ich auch nicht JEDEN "Hack" als wirklich lohnend. Als Einzelkämpfer will man auch mal mit Dingen fertig werden und verschwendet auch schon mal ein paar Bytes oder Zyklen. Trotzdem behalte ich alle solche Codestellen immer noch so im Hinterkopf, weiß also, wo man noch drehen könnte, falls Speicherknappheit oder Performanceverlust zickt. Also: Auch wenn man mal "schlechten Code" schreibt, sollte man sich (meiner Meinung nach) dessen bewußt bleiben bzw. sich sicher sein, daß man das absichtlich macht, z.B. aus Gründen der Entwicklungsdauer oder Faulheit oder weil der Speicher-/Performance-Gewinn eventuellen Mehraufwand nicht rechtfertigt.
zatzen hat geschrieben:Wenn man ein Spiel so sorgfältigt entwickelt und von vorne bis hinten ersteinmal alles durchplant kann man natürlich so entwickeln wie der "8-Bit Guy".
Beim C64 bleibt einem keine andere Möglichkeit. Erstens wird mit allem außer Assembler da nichts Vernünftiges draus, die Kiste hat nunmal nur ca. 1 MHz Takt, bei im Schnitt 3-4 Takten pro Befehl. Zweitens hat man eben nur diese 64kByte und entweder man lädt andauernd nach (aber auch eine Diskette ist irgendwann voll!) oder man hält viel im Speicher. Also speziell bei "Planet X2" hat der 8-Bit Guy da wirklich alles so ausgenutzt wie er wollte (und auf Nachladen während des Spiels wollte er komplett verzichten).

Und bei der PC-Version wollte er eben auch a) möglichst viel machen und b) war das Zielsystem ein XT mit ältestmöglicher Hardware. Wenn man da nicht diszipliniert entwickelt, kann man's auch gleich ganz seinlassen.

Naja, aber ich plane bei großen Projekten auch viel durch. Auch beim PC, der wesentlich mehr Speicher als ein C64 hat, stößt man irgendwann an Grenzen. Und daß ich meine Units aufeinander abstimme, bzw. daß ich jetzt so einen "allgemeinen Rahmen" dafür baue, der alles verbindet, ist auch ein Ergebnis dieser "Planung".

Bei Xpyderz habe ich nichts geplant, sondern einfach "drauflos" programmiert und entwickelt. Klar geht das auch - nur hatte ich hinterher unheimlich viel unnötige Mehrarbeit: Mußte Zeug in Units auslagern, habe Workarounds für meinen eigenen Code geschrieben, teilweise unsauber hingeschlunzte Softwareschnittstellen zwischen verschiedenen Procedures gemacht, weil die Teile in unterschiedlichen Units oder teils im Hauptprogramm waren... Habe sogar teilweise Code, der das gleiche/ähnliches macht, doppelt da drin. Irgendwann hat zwar alles funktioniert, aber das Ding ist codemäßig ein Desaster. Den Code möchte ich nicht mehr anfassen. Sollte ich jemals ein Xpyderz 2 machen wollen, würde ich alles komplett neu machen (bzw. mit meinem neuen Rahmen und den Units).

Und so weit will ich es nie wieder kommen lassen, daß ich ein Projekt/Spiel habe, bei dem am Ende so das Chaos regiert, daß nicht mal ich als Entwickler noch etwas damit zu tun haben will. Da könnt ich ja auch gleich bei Microsoft anfangen...
zatzen hat geschrieben:Aber solche Formate wie ZSM oder ZVID lassen soetwas gar nicht zu, weil die Größe der gepackten Daten eher unvorhersehbar von den Rohdaten abhängt. Daher würde ich in meinem Fall so entwickeln, dass ich die letztliche Größe eher abschätze um dann bei Pi*Daumen 500 KB zu landen, plusminus 100KB, abhängig von der Kompression, so in etwa. Letztlich würde ich schätzen, dass ich so mehr Daten in das Spiel bekomme als unkomprimiert.
Naja, das ist natürlich klar.

Ich weiß auch nicht, wieviel Platz am Ende meine ganzen Levels/Sprites/Musiken/Steuerdaten brauchen werden - das bemerkt man erst während der Entwicklungszeit. Da kann man ja immer wieder am Speicher "drehen" und dem Einen oder Anderen mehr oder weniger zuweisen. Es ist nur so, daß alles zusammen nicht mehr Speicher (und Rechenpower) brauchen darf, wie vorhanden ist - sonst funktioniert's nicht. Will man mehr (realistische) Samples im Heap halten, muß man an anderer Stelle sparen (Grafik/Code). Will man mehr Grafikelemente (fotorealistische Hintergründe) haben, muß man dann wieder beim Sound sparen. Will man viel Grafik UND viel Sound, bleibt weniger Platz für Code (der ja denselben RAM belegt wie alles andere) und man hat dann gute Grafik und guten Sound aber ein total primitives Programm/Spiel, weil mit wenig Code nicht viel machbar ist... usw. Und dabei wären wir wieder beim Trade-Off.

Wenn man schon vorher Dinge entwickelt (Musiken, Levels, Grafikdaten, Sprites), sieht man ja, wie groß die einzelnen Daten / Files werden und kann einschätzen, wie diese zusammenpassen. Ein Beispiel: Ein Spiel mit mehreren Levels. Die Levels sind nicht alle gleichzeitig im Speicher, sondern werden nachgeladen. Neues Level hat auch neue Musik und teilweise neue Figuren. Man kann Speicher natürlich dynamisch reservieren. Aber trotzdem muß Level+Grafik/Spritedaten+Musik zusammen immer noch in den noch freien RAM passen. Da kann man entweder generalisiert Speicher für Level, Speicher für Grafikdaten, Speicher für Sprites, Speicher für Musikdaten, Speicher für Samples in festen Größen festlegen (also statisch statt dynamisch) und jede der Einzeldaten darf nur diese Größe oder kleiner sein - ODER - man macht das Ganze dynamisch, muß dann aber aufpassen, daß, wenn ein Bereich mehr Speicher braucht, dieser dann woanders fehlt.

Man hat natürlich einen Vorteil, wenn alles so "einfach" ist, daß selbst wenn alle Daten da sind, immer noch viel Speicher frei bleibt - dann nutzt man den Speicher eben nicht voll aus - das muß man ja auch nicht, keiner zwingt einen, die vollen 500 oder 600 kB auszureizen. Aber natürlich kann man mit mehr Daten auch mehr Möglichkeiten haben (d.h. umso bunter/klangvoller/interessanter kann ein Spiel werden).
zatzen hat geschrieben:Über Kollisionsabfragen habe ich mir bisher noch gar keine großen Gedanken gemacht und ZVID einfach nur als Möglichkeit betrachtet, Grafiken anzuzeigen. Früher habe ich einfach die Nähe der Objekte ausgewertet.
Ich werte da auch nur Nähe aus. Pixelweise Kollisionsabfrage (zumindest softwaremäßig) halte ich für Schwachsinn. Erstens steuert kein Spieler pixelgenau. Zweitens wäre das ein Speicher-und/oder Performancekiller sondergleichen.
zatzen hat geschrieben:Es ist vielleicht für ein Spiel der falsche Weg, aber ich habe da modular gedacht im Sinne von verschiedenen Formaten die klar definiert sind und die man multimedial kombinieren kann.
Naja, es ist immer so: Je mehr auf einen bestimmten Zweck angepaßt, umso unflexibler sind Routinen - dafür sind sie für den Zweck umso idealer/ressourcenschonender (Speicher/Power).
Meine (Kachel-)Level-/ und Spriteroutinen sind eben nur für Spiele geeignet - für anderen multimedialen Einsatz würde ich da etwas anderes benutzen. Andererseits denke ich bei meiner Spieleentwicklung sowieso nicht "multimedial" - auf einem durchschnittlichen DOS-Rechner finde ich persönlich Performancegewinn und Speichersparen am Wichtigsten, weil ein schlecht steuerbares (Framerate) oder zu primitives Spiel (Speicher) natürlich weniger Spaß macht. Sollte ich jemals planen, in meinen Spielen "Filmsequenzen" zu verwenden, die über das reine Animieren von Sprites vor fixen Hintergründen hinausgehen, würde ich dafür wohl ein extra Format entwickeln. Ich weiß - diese zweckgebundenen Routinen zu bauen (statt universelle) kostet Zeit und Mühe, aber ich will nur Spiele machen, auf die ich auch selbst Lust hätte, sie zu spielen.
zatzen hat geschrieben:Wie gesagt lässt XMS eigentlich alle Pack-Bemühungen lächerlich aussehen, vor allem dann, wenn man sowieso Rechner wie 486er oder gar Pentium voraussetzt, die mitunter reichlich XMS oder EMS bereitstellen.
Wie ich oben schon erwähnte: Erstens ist XMS (im RealMode) nur indirekt erreichbar, zweitens weiß man nicht, ob und wieviel Speicher jemand in den Rechner eingebaut hat. Je mehr man "aus dem Vollen schöpft", umso mehr Mindestanforderungen muß man dem Enduser zumuten. Und wenn die Mindestanforderungen am Ende das Ergebnis nicht rechtfertigen sollten, enttäuscht man die Spieler.

zatzen hat geschrieben:Naja trotzdem macht es mir Spaß, etwas Denkarbeit in Echtzeit-entpackbare Formate zu stecken. Die Patternkompression bei ZSM stellt da überhaupt kein Übel dar, weil sie überhaupt nicht zeitkritisch ist aufgrund des niedrigen Datendurchsatzes von durchschnittlich
vielleicht 10 Byte pro Sekunde, eingelesen durch vielleicht 30 Aufrufe einer kleinen Assembler-beliebige-Bitbreite-Lese-Routine in dieser Zeit.
Naja, wie sehr es performt, kann man erst bei Benutzung feststellen. Ich habe auch schon Routinen gebaut, die "klein und schnell" wirkten, aber wegen vieler Sprünge oder weil die "kleine" Routine eben aus verschachtelten Schleifen bestand, trotzdem nicht performte. "Pro Sekunde" darf man auch gar nicht denken im Spielebereich (meiner Meinung nach), sondern eher "pro 20tel" oder "pro 50tel" Sekunde, also "pro Frame". Ein interaktives Spiel mit 1 FPS würde wohl kaum jemand spielen wollen. Und bei 20 FPS sind 30 Aufrufe pro Sekunde eben 600 Aufrufe pro Frame und 10 Byte pro Sekunde eben 200 Byte pro Frame - für EIN Pattern... - ja, sobald etwas interaktiv wird, also Performance in Frames geht, fange ich selbst an, in Mikrosekunden zu denken.
zatzen hat geschrieben:Die Delta-Samples haben einen Einfluss, aber eine Halbierung der Daten kann wertvoll sein.
Eine Halbierung der Daten (also das Speicherverbrauchs) ist definitiv wertvoll. Habe ich ja damals auch bei Xpyderz erlebt mit den Grafikdaten.
zatzen hat geschrieben:Was ZVID angeht ist es so ausgelegt, dass "idiotensicher" alles optmimal gepackt werden soll, ohne dass man sich als Grafiker Gedanken beim Pixeln machen muss, dort also frei nach Schnauze arbeiten kann.
So sollte es auch sein. Natürlich kann man einem Grafiker gewisse Vorgaben geben (z.B. Größe der Figur, maximale Anzahl Farben), weil man ja sowohl für das Spiel als auch für die benutzten Routinen mit gewissen Einschränkungen arbeitet/arbeiten will/muß. Andererseits sollte einem guten Spiel am Ende nicht an jeder Ecke anzusehen sein, daß man da und dort immer wieder gespart hat - im Gegenteil: Ein richtig gutes Spiel (oder auch sonstiges Programm) versetzt die Leute in Erstaunen, wo mit wenig Leistung/Speicher viel erreicht wird. (Als die Leute damals Turrican auf dem C64 gesehen haben, fiel denen die Kinnlade runter, weil sie nicht fassen konnten, wie man das auf einem C64 hinbekommen hat.)

Das Gegenteil ist weniger schön: Wenn man auf einem Rechner mit mehreren GHz CPU mit mehreren Kernen, Speicher im GB-Bereich und Festplatten im TB-Bereich Programme oder Betriebssysteme hat, die trotz dieser Systemparameter es nicht schaffen, ruckelfrei zu performen oder irgend etwas in Echtzeit auszuführen. Diese Art Programmierer möchte ich niemals werden.
zatzen hat geschrieben:Ich finde Deine Art und Weise, Sprites "einzulagern" sehr klug, und es wird sicherlich sehr gut performen.
Naja. diese Idee kam mir mal irgendwann. Gerade beim Coden in Assembler, relativ maschinennah, fange ich oft an, "um die Ecke" zu denken und mache ungewöhnliche Sachen, auf die man als reiner Hochsprachenprogrammierer nicht käme. Ich denke da nur an meine "Doppelzähler" in 32bit-Registern oder diese "verdrehten" Zähler, mit dem "Nachkomma" in den oberen Bits. Alleine, was man alles mit geschicktem Einsatz der Flags und Binärarithmetik anstellen kann... das sind alles Dinge, auf die systemferne Entwickler nie kommen könnten.
zatzen hat geschrieben:Ich bin wohl schon zu sehr verseucht, seriell zu denken im Sinne von Multimedia, was schon mit Formaten wie FLI(C) anfing. Oder ich habe einfach noch nicht hardwarenah genug gedacht, wie man Sprite-Daten am einfachsten addressieren kann.
Ja, wenn man schon so lange mit den x86-Registern und Opcodes herummurkst, kommen einem immer wieder neue kreative Einfälle. Bei Hochsprachen "nimmt man, was man kriegt" - bei Assembler bestimmt man selbst, was zu kriegen ist (im Rahmen der Hardware natürlich). Ohne mathematisches Verständnis und Binärarithmetik kommt man eben nicht drauf, wie man eine Subtraktion durch eine Addition ersetzen kann (weil man z.B. das Carry oder Zero genau andersrum braucht) oder wie man manche "Multiplikationen"/"Divisionen" mit ein paar Shift-Befehlen schneller und einfacher hinbekommt. Der dumme Programmierer rechnet A*2, der schlauere rechnet A+A und der schlaueste schiebt A um ein Bit nach links... - alles hat das gleiche Ergebnis, aber nicht alles ist gleich schnell. (Anm.: In manchen Fällen ist in x86 Assembler aber "ADD Register,Register" übrigens schneller als "SHL Register,1" - und ADD ändert auch mehr Flags, was manchmal nützlich ist.)
zatzen hat geschrieben:ZVID ist dahingehend eher eine Katastrophe, weil jedes einzelne Sprite als Frame verstanden wird, einen eigenen Platz auf dem Heap reserviert bekommt und über Pointer addressiert werden muss und für jeden 8x8 Block eine Routine gestartet wird.
Naja, daß einzelne Sprites als Frame verstanden werden, so hatte ich das früher auch mal. Kann auch gut sein, spart evtl. mehr Speicher als meine "Plane" Methode, nur bei "krummen" Breiten eben nicht so schön zu rechnen oder zu drehen. Andererseits verschwenden 8x8 Blocks bei Sprites evtl. noch mehr Speicher als meine "Plane", es sei denn, die Sprites haben immer /8 teilbare Breiten/Höhen.

Ja, die Einzelaufteilung in diese Blöcke kann natürlich die Performance killen, weil eigentlich "zusammenhängende" Daten einzeln bearbeitet werden und man sich dadurch die durch die "Zusammenhänglichkeit" entstehenden Vorteile verschenkt. z.B. braucht man bei einem kompakten/soliden Sprite die Endposition auf dem Bildschirm nur einmalig berechnen. Ich hoffe, die wird bei den Blöcken nicht für jeden Block einzeln neuberechnet, sondern hoffentlich ausgehend von den Positionen der vorigen Blöcke?
zatzen hat geschrieben:Überdies läuft noch eine Routine aussen herum, die für soetwas wie Lauflängenkomprimierung gleicher aufeinanderfolgender Blöcke zuständig ist.
OK, das ist bei größeren Grafiken natürlich nützlich - bei Sprites wird das quasi so gut wie nie gebraucht werden, außer es sind wirklich merkwürdig aussehende Sprites.
zatzen hat geschrieben:Ich muss hier einfach mal aufklären um welches Beispiel es sich handelt. Es sind tatsächlich drei Bilder und nicht eines, ich habe sie einfach mal nebeneinandergeklatscht.
alien.png
Tatsächlich sind die Dimensionen 64x72, ich hatte nur vertikal die 8 letzten Pixel weggelassen weil da kaum Information drin waren. 1. Bild Augen zu, 2. Bild Augen offen, 3. Bild grinsen und winken.

Zugegebenermaßen sind die Bilder ziemlich dünn besiedelt und gleichen sich einander stark. Aber ZVID wird dem gerecht und packt hier ordentlich, die ZVD-Datei ist genau 1407 Byte groß. Die png-Datei die ich hier im Post verwende ist 930 Byte groß wenn ich die mögliche Farbanzahl vor dem Speichern auf 16 Farben setze.
Hinweis: PNG kann auch 4-farbig und 2-farbig. 4-farbig hätte hier also ausgereicht.
zatzen hat geschrieben:ZVID packt also im Vergleich dazu immerhin 66% so gut. Bei 256 Farben-Einstellung ist das abgespeicherte png-Bild 1720 Byte groß und ZVID liegt sogar vorn. Klar, allein die Palette von 768 Byte muss ja irgendwo hin. Korinthenkackerische Anmerkung: Die ZIP der 1407 Byte großen ZVD Datei ist 866 Byte groß, also kleiner als das 930 Byte png.
Wie gesagt: Mal PNG in 4-farbig versuchen.
zatzen hat geschrieben:Aber, keine Frage, das ist/war ein Extrembeispiel mit nur 3 Farben und viel einfarbiger/transparenter(gelb) Fläche. Hätte man auch einfach als 2 Bit abspeichern können, aber ZVID komprimiert es im Vergleich dazu nochmal auf weniger als hätte man es mit 1 Bit abgespeichert.
Wenn das nicht 1 Bild wäre, sondern 3 "hintereinanderliegende" Bilder eines animierten Sprites, dann versuch es mal als 3-phasiges Bild mit GIF zu packen, das ja auch delta-packt, sowie auch bitbasiert (also auch z.B. 2-bit ). Ich habe es nicht getestet - aber ich nehme an, Du wirst erstaunt sein...
zatzen hat geschrieben:Ähnlich ist es bei anderen Sprites - ich hatte in einer Demo "fotorealistische" Sprites die ich auf 63 Farben beschränkt habe, ZVID bringt sie auf unter den Speicherplatzbedarf von 16-Farben-Sprites.
Also kann man sagen: Mehr Farben bei weniger Speicherplatz. Auf Kosten von etwas Performance versteht sich.
Klar. Und wie gut etwas packt, hängt natürlich in jedem Fall vom Ausgangsmaterial ab. Es gibt Dinge, die in einem Format total gut packbar sind und in einem anderen quasi gar nicht. Es gibt sogar die Möglichkeit, daß ein 8-Bit BMP (also quasi "ungepackt"), das man in PCX wandelt, dieses in PCX mehr Speicher braucht als das BMP, wegen der Besonderheit, wie PCX die Lauflängencodierung macht - also "worst case".
zatzen hat geschrieben:Ich überlege die letzten Tage an einem "ZVID2", denke darüber nach ob man die Blöcke auf 4x4 verkleinert. Vorteil wäre, dass generell maximal 16 verschiedene Farben in den Blöcken wären, auch generell weniger Farben unterhalb als bei 8x8, und dass man mehr einfarbige Flächen "trifft". Nachteil wäre, dass die Headerdaten jedes Blocks im Verhältnis zu den
Daten schwerer wiegen würden.
Das denke ich auch. Je kleiner der "Payload" wird, umso ungünstiger wird das Verhältnis von Header zu Payload. Ich persönlich kann mir nicht vorstellen, daß es mit 4x4 besser packt, rein von meiner Intuition her. Müßte man wohl ausprobieren. Und hängt dann wohl auch wieder von den zu packenden Daten ab.
zatzen hat geschrieben:Trade-Off: Da ich (auch angesichts des heutigen Angebots an Spielen, auch der historischen) gar nicht als einziges Ziel habe, ein eigenes Spiel, egal wie, zu programmieren, habe ich einfach Spaß daran, so "nebenher" solche Packalgorithmen zu basteln, und ja, ich tendiere eher zu "langsamen" Spielen als zu spielen wo die Performance bis auf letzte bisschen ausgereizt wird.
Naja, ich bin ja inzwischen auch eher der "Engine-Coder" als ein richtiger Spiele-Entwickler. So viel "Hintergrund-Zeug", wie ich in letzter Zeit gebaut habe (und demgegenüber wie wenig Spiele)... Ob ein Spiel langsam oder schnell ist, hat ja erst einmal nichts mit Performance zu tun. Die Performance innerhalb des Programms bestimmt eher, ob die Steuerung flüssig ist oder die Grafik flackert oder der Sound stottert. Auch ein 50 fps-Spiel kann einen langsamen Spielablauf haben. Wenn aber die Steuerung hakelt, weil die CPU ständig mit anderen Sachen ausgelastet ist und ein Spiel nur 5 fps hat, so daß nichts so wirkt, als würde es wirklich auf die Eingabe des Spielers reagieren, dann sorgt das über kurz oder lang für Frust. Ein Spieler, der nicht aufgrund eigenen Unvermögens (weil er zu schlecht spielt), sondern aufgrund von Mängeln im Programm "verliert", nicht weiterkommt, usw. ist erfahrungsgemäß von einem Spiel weniger begeistert und fühlt sich ständig "betrogen".
zatzen hat geschrieben:Da solltest Du mich ja mittlerweile kennen, da für mich auch noch nicht der Tag gekommen ist, da ich die Framerate von der Leistung des Computers abhängig mache, sondern sie fest definiere wie bei einem Video. Das macht für mich momentan einiges einfacher, wie auch Animationen.
Ja, das Thema hatten wir ja schon. So lange ein spielbares Spiel herauskommt, das auch noch Spaß macht oder herausfordernd ist oder ähnliche Eigenschaften hat, die den Spieler bei der Stange halten und begeistern können, sind solche technischen Eigenheiten nebensächlich. Natürlich ist es trotzdem nicht schlecht, wenn's dann auch funktioniert. Feststehende Frameraten können, wie bekannt ist, dazu führen, daß die Mindestanforderung recht hoch angesetzt werden muß, um in jeder denkbaren Spielsituation die feste Framerate aufrechtzuerhalten.
zatzen hat geschrieben:Teilfiguren: Ja, das habe ich mir in einem andere Zusammenhang auch schon einmal überlegt. Für den Fall dass man sich seine Spielfigur beliebig aus verschiedenen Teilen (Gesicht, Körper, Kleidung...) konfigurieren kann. Oder denkbar(!) wäre auch, dass man die Grafiken gliedmaßenweise gewissermaßen hochauflösend vorzeichnet, dann in alle möglichen
Stellungen zurechtdreht und sich so die Figuren baut. Auch an so etwas wie ein verborgenes Skelett hatte ich mal gedacht, auf dem man die Körper aufbaut.
Ja, die Idee hatte ich auch schon mal. Genauso, wie ich schon die Idee hatte, meinen selbstgebauten 3D-Objekt-Editor etwas zu erweitern, Figuren als 3D zu designen (natürlich mit gedachtem "Skelett" - man nennt sowas Rigging) und dann die Figur in alle Richtungen/Haltungen drehen und als normale 2D-Grafiken speichern (also als vorgerenderte Sprites). Klingt erst einmal so, als hätte man dann weniger Aufwand, weil man ja nicht die ganzen Einzelframes pixeln muß...

ABER... Dafür muß man die Figur eben als 3D-Modell entwickeln, inklusive Texturen und evtl. Belichtung. UND man muß das Programm dafür bauen. Und wenn man zu wenige Vektoren nimmt, wirken die Figuren nur klotzig und tot. Vielleicht mache ich IRGENDWANN mal so etwas - aber momentan will ich erst einmal den ganzen Engine-/Unit-Kram wieder mal zu etwas Spielbarem zusammenbauen.
zatzen hat geschrieben:Deine Sprite/Block Routinen sind wohlüberlegt und für ein Spiel optimiert. Ich habe mir bisher solche Gedanken noch nicht gemacht und nur überlegt, wie ich Grafik möglichst effizient speichere, und ich habe sie bisher nur wie Daten betrachtet, die man einfach irgendwie auf den Bildschirm bringt.
Naja, ich habe das Zeug von Anfang an direkt für 2D-"Arcade""-Spiele ausgelegt, so wie ich sie vom NES/SNES oder Sega Master System o.ä. kenne.
zatzen hat geschrieben:Und naja, mich hat wohl bisher weniger eine höchstmögliche Performance gereizt als vielmehr möglichst kompakte Daten.
Naja, ZVID ist ja auch u.a. für Videos ausgelegt - so etwas haben meine Sprite-/Level Units gar nicht.
zatzen hat geschrieben:Das liegt aber auch an meiner Vorstellung von Spielen. Ich möchte wie gesagt keinen Weltraum-Shooter mit 70 fps machen, sondern ein eher gemütliches Spiel wo 25 fps oder weniger genügen sollten - ob Scrolling damit noch erträglich ist wäre abzuwarten.
Auch 25 fps können "viel" sein. 25 fps bedeuten (mind.) 1600000 Pixel pro Sekunde zu generieren aus Sprite-/Level-/Blockdaten und diese anzuzeigen. Das sollte der Rechner/das Programm schaffen - und noch genügend Rechenzeit zum Generieren von Sounddaten und Steuern der Figuren übriglassen.
zatzen hat geschrieben:Ich denke da manchmal an "Captain Comic". Kleiner Bildschirmausschnitt, 10 fps, Scrolling in 8 Pixel Einheiten. Davon gibt es auch eine Nintendo NES Version mit dem entsprechenden flüssigen Scrolling, 50/60 fps und richtig schöner Grafik.
Ich hab mir letztens auf Youtube einen Walkthrough von "Captain Comic" (PC-Version) angesehen. Wollte mal sehen, wie das aussieht, wo Du es so oft erwähnst.
Ja, schon nett. 16-farbig. Ich frage mich, welcher Grafikmodus benutzt wurde.
Aber ein PC (vor allem ein 486er) könnte auch mehr schaffen als das.
zatzen hat geschrieben:Da könnte man sich schon fragen, wozu sich überhaupt noch auf einer 486er Plattform bemühen, wenn ein 80er Jahre 8 Bit System soetwas schon besser stemmen konnte als ich es in absehbarer Zeit hinkriegen würde.
Hierbei muß berücksichtigt werden, daß das "80er Jahre 8 Bit System" einen Grafikchip enthält, der kachelnbasierte Levels, Scrolling und und Sprites hardwaremäßig von sich aus darstellen kann - während wir (DOS, VGA) all das softwaremäßig machen müssen.
Und ... naja, was ist schon "absehbare Zeit"? Ich murks schon ein halbes Jahrhundert auf PCs herum (und davor 10 Jahre auf anderen Computern/Chips usw.) und das, was ich bisher (an Spielen) zustande gebracht habe ist kaum etwas, das heutzutage jemand längere Zeit spielen würde - egal, wie "retro" er ist...
zatzen hat geschrieben:Aber letztlich geht es ja auch um den Spaß den man als Hobbyprogrammierer (so würde ich mich bezeichnen, Dir will ich das gar nicht unterstellen)
Nur interessehalber: Und was würdest Du mir unterstellen wollen?
zatzen hat geschrieben:hat, und in gewissen Dingen zeigen sich auch bei einem Amateur die Vorzüge eines moderneren 32 Bit Systems gegenüber einer antiken Spielekonsole.
Naja... "antik"... Ich selbst nutze "die Vorzüge eines moderneren 32 Bit Systems", was Programmierung angeht, nicht im Mindesten aus. Ich programmiere krudes mittelgutes Zeug in RealMode mit etwas 32-Bit darin. Das "moderne 32 Bit System", auf dem ich programmiere, ist auf dem technischen Stand von vor über 25 Jahren...
zatzen hat geschrieben:
DOSferatu hat geschrieben:Wenn das Level sowieso nur aus Blöcken/Patterns/Kacheln besteht, reicht es auch z.B. die Figurenkoordinaten SHR 4 (für 16x16er Blocks) SHR 5 (für 32x32er Blocks) usw. zu nehmen, damit im das Level an der Position den Block abzufragen und man weiß, was es ist
Genauso hatte ich mir das gedacht. Aber eher so, dass ich im "Hintergrund" ein Datenfeld habe von 1000 (40x25) Elementen, somit also für einen kompletten Screen mit 8x8 Pixel großen Blöcken.
Naja, dadurch, daß bei mir die Blöcke sowieso gleich im Level angeordnet sind, brauche ich nur die Blöcke so ausgeben, wie sie im Level liegen. Das Level ist "ungepackt", ja. Viel Leerraum dazwischen. Aber es ist im Prinzip ja das gleiche wie das von Dir angegebene Datenfeld - nur daß ich es gleich für beides - zur Anzeige UND "Kollisions-"Abfrage - benutze. Und diese "Levelmatrix" (die angibt, wo die Blöcke/Kacheln sind) würde ich auch nicht packen - verschwendet zwar etwas Speicher - aber: In einem eventuell gepackten Level würde ich ja wahnsinnig werden, wenn ich feststellen wollte, wo sich die Figuren gerade befinden, ob sie irgendwo draufstehen/anstoßen usw.
zatzen hat geschrieben:Ich merke, dass Du schon sehr viel mehr Arbeit in wirkliche Spiele-Routinen investiert hast. Bzw. habe ich noch gar keine Spiele-Routinen geschrieben sondern nur ZSM und ZVID.
Naja, mein Ziel war von Anfang an immer die Spieleprogrammierung und die Anzeigen und Steuerung usw. habe ich gleich speziell für (2D-) Spiele ausgelegt. Seit ich so Dinge wie "Mario Bros," und "Giana Sisters" und "Alex Kidd" bzw. Dinge wie "R-Type" und "Katakis" gesehen habe, denke ich: "Sowas muß ich auch mal mal machen". Das geplante Jump'n'Run (wenn es denn je gemacht wird), wird aber teils umfangreichere Features enthalten als die erwähnten 8-Bit-Spiele. Wie schon erwähnt: Ideen über Ideen... - Und inzwischen auch endlich genügend Code zusammen, um den Kram umzusetzen...
zatzen hat geschrieben:Ich wollte ursprünglich ja mal eine generalisierte Game-Engine angefangen haben, der man nur kompilierte Spiel-Daten vorlegen muss. Du hast mich zu der Zeit überzeugt, dass soetwas weniger gut wäre, da eine solche Engine je nachdem zu viel Code enthält, der gar nicht vom jeweiligen Spiel genutzt würde.
Naja, mein GameSys2 (die Steuersubroutine) enthält auch 128 Befehle, die wahrscheinlich nie ALLE gebraucht werden und die Spriteroutinen enthalten umfangreiche Features, die sicher auch nicht in JEDEM Spiel gebraucht werden und trotzdem baue ich die ein.
Was ich meinte, war eher: Wenn man z.B. eine generalisierte EXE (quasi wie eine VM) macht und immer nur mit einem Spieldatenfile dazu startet (wie bei einem Konsolen-Emulator), enthält die EXE viel, was eventuell IRGENDEINS der Spiele gebrauchen kann, ein anderes aber nicht. Deshalb werden meine Spiele trotzdem ein Compilat bleiben, indem ich z.B. festlege, welche Units in der EXE enthalten sein werden bzw. welche Befehle zur Auswahl stehen. D.h. an "Auswahl-Enden", wo das Spiel sowieso "nie hinkommt" (d.h. die Spielsteuerdaten nicht brauchen), werden die Sachen auskommentiert. (Wofür gibt's schließlich Compilerschalter?)
Ich plane auch, vielleicht ein kleines simples Frontend, das den "Template-"Sourcecode entsprechend modifiziert, indem man die Werte und benötigten Dinge auswählen kann. Mal sehen.

Für GameSys2 (GS2) habe ich, wie bereits erwähnt, auch mal dran gedacht, für die "Assembler-artige" GS2-Sprache noch einen "Compiler" drüberzusetzen, der z.B. in einem einfachen BASIC-Dialekt funktioniert, damit das Bauen der Steuersubroutinen für die Figuren einfacher wird. Aber solange ich selbst der Einzige bin, der den Kram benutzt, ist das erstmal hintangestellt, das hat keine Priorität.

Du siehst: Selbst für meine Tools habe ich Ideen über Ideen. Wenn ich nur mal nicht so müde und kraftlos wäre...
zatzen hat geschrieben: Ich hatte es allerdings damals eher so eingeschätzt, dass die Engine selbst gar nicht so unglaublich aufgeblasen sein würde, weil sie die meisten Daten aus den Spiele-"Modulen" beziehen würde. Vielleicht sollte ich noch einmal darüber nachdenken.
Ja, so "Spiel-Modul" artige Files sind ja auch mein Plan, daher heißt dieser "Container" ja auch SLOT. Aber diese werden direkt mit dem Container verbunden, als Overlay. D.h. die EXE ist dann trotzdem jedesmal anders, je nachdem, was das Spiel ("Modul") benötigt und kann auch zusätzlich durch handprogrammierte Dinge erweitert werden.

Im Netz geistern viele so "Game-Maker" gemachte Spiele umher, die alle mit dem gleichen "Game-Maker" gemacht sind und denen man das auch ansieht. Sie sind umfangreich und mit viel Liebe gemacht - aber trotzdem hat man oft das Gefühl, immer das gleiche Spiel, nur mit anderer Grafik/Sound zu spielen. Das würde ich bei meinen Spielen gern vermeiden.
zatzen hat geschrieben:
DOSferatu hat geschrieben:[...] bevor der nächste Ton angeschlagen wird (dann geht ISM in dieser Stimme bis dahin auf Stille).
Ich muss bei den ZSM-Routinen die Pufferlänge und die Zeilenlänge/-dauer berücksichtigen. Die Einberechnung eines Samples wird also durch die Zeilen und die Puffergrenzen unterbrochen. Innerhalb dieser Grenzen berechne ich bei nicht-geloopten Samples die tatsächlich einzurechnende Länge, d.h. wenn das Sample innerhalb der gesteckten Grenzen endet wird das vorher erkannt und der Zähler für die Routine entsprechend gesetzt.
Bei einem geloopten Sample ist das aber anders, wenn dieses innerhalb der einzurechnenden Länge sein Ende erreicht muss es wieder von vorne beginnen, und zwar der Abspielgeschwindigkeit entsprechend um eine Vor- und Nachkommastelle weiter. Vielleicht etwas schlecht erklärt. Jedenfalls kostet das wohl etwas Performance, weil ich nach jedem "Tick" den ich einrechne eine Überprüfung mache, ob das Sample schon wieder "durch" ist. Ich werde bei
Gelegenheit mal in den Code gucken, und es, wenn es noch nicht so ist, es so umschreiben, dass nur gesprungen wird, wenn das Sample das Ende erreicht hat.
Ach, ist ausreichend gut erklärt - ich verstehe schon, was Du meinst. Ich selbst habe da gar nicht unterschieden, ob das Sample-Ende sich innerhalb des aktuellen Frames befinden wird oder nicht. (Wäre natürlich performanter gewesen, wenn man eine "generelle" Subroutine haben würde und eine, die nur benutzt wird, wenn das Sampleende abgefragt werden muß, da hast Du Recht.)
Bei mir wird das immer abgefragt. Aber ich arbeite ja sowieso im ganzen ISM dauernd mit so "Doppelzählern" (die gleichzeitig addieren und subtrahieren im gleichen Register), weiß gar nicht mehr genau, wie ich es in den Subroutinen der Samples gelöst habe.
Aber ISM enthält viele einzelne Subroutinen:
Attack, Decay, Sustain und Release (ADSR), diese jeweils kombiniert mit Wave (W) oder Noise (N) und mit Geradlinig (F) oder Portamento (P).
Also gibt es, alleine für die NICHT-Sample-Bearbeitung diese 16 Kombinationen, für jede eine Subroutine:
AWF, DWF, SWF, RWF, ANF, DNF, SNF, RNF, AWP, DWP, SWP, RWP, ANP, DNP, SNP, RNP.
In Wirklichkeit gibt es noch 4 mehr, weil es zwei Arten von Release gibt (Hard- und Softrelease).

Für Samples gibt es 2 Subroutinen, eine für Geradlinig und eine für Release. Außerdem wird zwischen Even/Odd unterschieden, d.h. ob das Sample im unteren oder oberen Nybble im Speicher liegt, das wird aber jeweils vor der Schleife mit modifiziertem Code gelöst. (D.h. da wird an die Stelle ein AND AL,$F0 oder ein SHL AL,4 geschrieben (das Sample/die Welle ist immer "oben", die Lautstärke/Hüllkurve "unten" im Byte. ein XLAT macht dann die "Multiplikation"... Naja, würde jetzt zu weit führen... ziemliches Gemurks. Wundere mich, daß es überhaupt hörbare Musik produziert...
zatzen hat geschrieben:JMP ist langsam, nicht CMP, richtig?
Ach naja, was ist schnell/langsam? Das hängt immer vom gesamten Programm ab. Ohne Sprünge könnte man auch kaum gescheite Programme schreiben, irgendwann gibt es immer mal Entscheidungen.

JMP oder jeder andere Sprung oder Aufruf löscht den sog. Prefetch-Queue der CPU (PQ-Flush). Die CPU liest ja immer schon etwas mehr ein, damit sie "schonmal weitermachen kann" (und bei neueren CPUs auch die Befehle "vertauschen", wenn es Vorteile bringt)... Wenn aber gesprungen wird, ist ja der darauffolgende Befehl nicht mehr der linear eingelesene, sondern es geht woanders weiter. D.h. der Vorteil des "Vorherlesens" geht in diesem speziellen Fall verloren, weil die CPU an der neuen Position erst einmal überhaupt wieder etwas einlesen muß, bevor sie weitermachen kann.

Wenn man allerdings, nur um einen Sprung zu vermeiden, komplizierte Vorberechnungen anstellt, hat man am Ende nicht wirklich Zyklen eingespart. Natürlich gibt es immer auch das Mittel der "unrolled Loops", wie Du selbst ja auch schon erkannt hast. Nur sind diese natürlich auch wieder ein Trade-Off zwischen Speicherverbrauch und Geschwindigkeit. Maximale Geschwindigkeit erreicht man, wenn es quasi gar keine Schleife gibt und alles linear ausgeführt wird. Allerdings dann eben auch mit viel Code.

Also sollte die Aussage "Sprünge löschen den Prefetch-Queue (PQ) der CPU und verbrauchen daher mehr Takte" nicht gleich zu totaler Panik führen, überhaupt keine Sprünge mehr anzuwenden, sondern lediglich ein Hinweis sein, bedacht zu programmieren und "Spaghetticode" mit sinnlosem "Hin-/Her- und Drüberspringen" zu vermeiden, wo es möglich ist.

Ein bedingter Sprung löscht z.B. nur dann den PQ, wenn auch wirklich gesprungen wird (d.h. die Bedingung zutrifft). Manchmal können Dinge wie zwei Sprünge trotzdem mehr Performance bringen als ein Sprung. Ein Beispiel: In einer Schleife, die 100-mal durchlaufen wird, wird, muß immer geprüft werden, ob eine bestimmte Bedingung zutrifft und dann etwas alternatives ausgeführt werden. Diese Bedingung trifft aber statistisch gesehen selten auf (z.B. nur 0-3 mal in den 100-mal), muß aber trotzdem berücksichtigt werden. Dann kann ein bedingter Sprung aus der Schleife heraus und ein Rücksprung in die Schleife hinein sinnvoller sein als ein "Überspringen" innerhalb der Schleife bei nichterfüllter Bedingung. Denn das Überspringen sind in diesem Beispiel über 97-100 ausgeführte Sprünge innerhalb der Schleife (jedesmal mit PQ-Flush), die Raus-/Rein-Springen-Methode dagegen würde 0-6 zusätzliche Sprünge verursachen.

Soviel übrigens zum Thema "heutige Compiler optimieren besser als es jeder "von Hand" Programmierer könnte: Der Compiler kann ja gar nicht wissen, welche der Bedingungen statistisch häufiger auftritt, weil zur Compilierungszeit im Source gar nicht ersichtlich ist, welche Art Daten die Schleife erwartet (was der Programmierer natürlich weiß). Und ja, ich weiß, daß es schon CPUs gibt, die Code umsortieren, um bei Sprüngen je nach Häufigkeit das Prefetching "umzukehren". Darauf kann/sollte man sich aber nicht verlassen - und ob es funktioniert, hängt auch von der Komplexität des Programms ab.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Bei Soundeffekten will ich da noch überlegen, eine 2-bit-"Kategorisierung" für Sounds zu haben, also a) welcher Effekt auf derselben Stimme gespielt wird (und den letzten gleichen unterbricht) und b) ob ein Effekt durch andere unterbrechbar ist oder nicht.
Das ist eine gute Idee, Du kannst Dir denken, dass ich gerne Sounduntermalung haben werde, aber irgendwo gibt es eben auch Performancegrenzen, und damit kann man das gut strukturieren.
Naja, bei mir ist es ja so, daß ich nur eine begrenzte Anzahl Stimmen zur Verfügung habe (und auch nicht unbedingt die max. 16 möglichen Stimmen einsetzen will) und gerade z.B. bei so Dauerfeuer macht es nichts, wenn der nächste Schußeffekt den vorigen unterbricht oder bei einer Explosion oder ähnlich lautem Geräusch werden viele leise andere Geräusche sowieso egal. Und wenn ich aber extern den Sounds solche Flags zuweise, die den Einsatz über eine externe Entscheidungsroutine koordinieren, braucht der Entwickler der Figurensteuerung, der auch per "Aktionsbefehl" einen Soundeffekt auslöst, nicht zu überlegen, ob/wo diese abgespielt wird. So wird vermieden, daß man während der Steuerung der Figuren (inkl. Projektile usw.) überlegen muß, welche Stimme man benutzen will.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Erstaunlich, wie die mit nur 3 Stimmen sowohl Musik als auch SFX gleichzeitig machen, ohne daß es doof klingt.
Ähnlich beim NES. Da haben die Soundeffekte höhere Priorität als die Musik, das hört man teilweise.
Ja, gerade wenn wenig Stimmen zur Verfügung stehen, merkt man so etwas. Aber was Hülsbeck in den C64-Versionen von Katakis und R-Type angestellt hat, ist wirklich bemerkenswert.
zatzen hat geschrieben:[...Kotzman...]Und dann hat mich irgendwie die Faszination gepackt und ich wollte ein komplexeres Spiel draus machen.
So ging es mir mit Xpyderz. Das war anfangs auch eher ein "Experiment".
zatzen hat geschrieben:Hätte ich mal besser ganz wegnehmen sollen dieses "Probierlevel". Aber ich habe das nicht objektiv genug gesehen. Ist Dein Steuerungs-Hack ein TSR?
Nein. Es ist eine kleine EXE, die dann von sich aus Kotzman aufruft, aber in den Keyboard-Interrupt einhakt.
zatzen hat geschrieben:Die komischen Tasten rühren übrigens noch von einer alten Empfehlung des Buches "Spiele Programmieren mit QBasic" her, da diese Tasten über die Tastaturstatusbytes abfragbar sind, ohne dass man sich mit Typematic rumschlagen muss.
Aufgrund der Tasten, die Du für das Spiel gewählt hast, hatte ich mir genau das gedacht. Deshalb war es für mich auch so einfach, da diesen "Hack" drumherum zu stricken.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Einzige 4 Grenzen sind ja, wie oben bereits angedeutet:
1. Was schafft die CPU (und die restliche Peripherie)?
2. Wieviel Speicher ist da?
3. Wieviel Zeit hat man/will man investieren?
4. (neu) Welche Fähigkeiten hat der Entwickler?
1.: Ich gestehe mir ein dass ich nur ein mittelmäßiger Programmierer bin und setze eine 486er CPU voraus und Peripherie entsprechender Zeit und erstelle Programme, die auf diesem System so gut performen wie jene von Spitzenprogrammierern auf 386er-Systemen
Ich kann schlecht kategorisieren, in welche Sparte Programmierer ich gehöre. Ich bin nie wirklich zufrieden mit dem, was ich programmiert habe. Meist relativiere ich meine anfängliche Begeisterung über ein fertiggestelltes Stück Software schon kurze Zeit später und denke schon wieder daran, wie ich es anders/besser hätte machen können. Inzwischen versuche ich mir da eine fatalistische Einstellung anzueignen im Sinne von "irgendwann muß es auch mal als 'fertig' definiert werden", damit ich mal weiterkomme. Außerdem können so Programmteile, wenn sie erst einmal so funktionieren, daß man sie irgendwo "einbauen kann", auch erst einmal benutzt und später immer noch erweitert/optimiert/ausgetauscht werden.
zatzen hat geschrieben:2.: Auch wenn im Protected Mode oder per XMS/EMS Megabytes an Speicher zugänglich wären motiviert es mich bei 640K zu bleiben, da mehr verfügbarer Speicher meine Bemühungen unsinnig macht, Daten zu komprimieren.
Ja, das ist (meines Erachtens) lobenswert. Speicher zu "verschwenden" nur "weil er da ist", ist meiner Meinung nach keine gute Einstellung. Und der eigene Computer sollte nie "das Maß der Dinge" sein, wenn man beabsichtigt, Software zu erstellen, die auch von anderen Leuten benutzt werden soll.
zatzen hat geschrieben:3.: Da es nur eines meiner Hobbys ist sehe ich mich keinem Zeitdruck ausgesetzt. Ob ich in einem oder fünf Jahren ein Spiel rausbringe ist für mich nicht so wichtig. Klar wäre es schöner, wenn früher.
Ja, ich hatte es nur erwähnt als eines der Kriterien bei der Softwareentwicklung. Zeit- und Kostenfragen stellen sich in der privaten Softwareentwicklung selbstverständlich kaum.
zatzen hat geschrieben:4.: Nicht gerade der typische hochbegabte Informatiker, aber motiviert. Musiker/Sound, Pixelgrafik geht auch, aber auch nur mit Motivation.
[/quote]
Naja, der 4. Punkt ist ja nur der Vollständigkeit halber erwähnt, weil er eine Selbstverständlichkeit ist. Was das Programm am Ende kann, ist, selbst bei unbegrenzt vorhandener Hardware und Zeit, schließlich auch noch abhängig von den Fähigkeiten des Entwicklers.

Naja, das soll's erstmal dazu gewesen sein. Deinen anderen Beitrag im anderen Thread beantworte ich demnächst.
BluesmanBGM
Norton Commander
Beiträge: 133
Registriert: Fr 10. Aug 2018, 10:46

Re: Eigenes Videoformat

Beitrag von BluesmanBGM »

Zur Diskussion kann ich leider nichts beitragen (programmiertechnisch bin ich eine Niete, abgesehen von etwas Basic), aber ich habe mir zumindest die letzten Seiten im Thread gerade mal aufmerksam durchgelesen.

Danke an DOSferatu und zatzen für diese sehr detaillierte und überaus interessante Lektüre. So würde ich mir mehr Diskussionen zu sogenannten "Retro"-Themen wünschen - respektvoll, auch gegenüber der originalen Hardware, ins Detail gehend, gut verständlich ausformuliert, und vor allem informativ für Beteiligte und Mitleser.

Leider ist es ja auch in Retroforen mittlerweile üblich geworden, Gespräche nur noch mit einer Handvoll Sätzen zu führen bzw. überall Links zur "leichteren Verdaulichkeit" einzubauen, anstatt mit Worten zu erklären. Guckst du hier wikipedia. Guckst du hier youtube etc etc. Da freut es mich immer, lange Gespräche zu finden, die das Medium Webforum noch in klassischer Weise benutzen.

Wollte ich nur mal gesagt haben :-).

Chris
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Danke, BluesmanBGM, es ist immer schön zu sehen, wenn auch andere Forenmitglieder interessiert mitlesen.
DOSferatu hat geschrieben:[...]konnte die Spielfigur bei dem Sierra-Ding an jeder Ecke sterben - immer nur Trial-and-Error und wieder neuladen... das frustrierte mich irgendwann.
Kann man nicht so gut vergleichen, aber ähnliches frustriert mich eben auch an klassischen Konsolenspielen dass es sich
letztlich anfühlt, als würde man durch ein Minenfeld laufen. Also einfach für meinen Geschmack etwas zu stressig.

Nur eigenen Code nutzen: Ein Vorteil ist da auch noch, dass man genau weiss was da alles drin ist, und man ist sicher,
keinen überflüssigen Ballast ins Programm einzubauen.
DOSferatu hat geschrieben:Ich frage mich immer wieder, was so wichtig an dieser verflixten Sprachausgabe ist.
Nicht wichtig, aber eben:
DOSferatu hat geschrieben:Sprachausgabe ist ein netter Nebeneffekt
Nicht mehr und nicht weniger.
Ich male mir eben nur so ein Spiel schonmal in Gedanken aus, und je nachdem kann es unterhaltend und bereichernd
sein, so wie gute Grafik oder eine gute Levelstruktur. Für den ein oder anderen also motivierend sein, das Spiel zu
spielen. Zumal war ich wenigstens früher schwer beeindruckt, als sich zur Adlib Musik Digi-Sounds gesellten, und
letztlich sogar Sprache.
Es müssen ja auch nicht unbedingt ganze Sätze sein, irgendwelche Schreie oder kurze Äußerungen kann man ja im
Rahmen von Sound-FX auch auf dem Heap halten.
DOSferatu hat geschrieben:Ich werde in meinen Spielen wahrscheinlich eher so bei 11 kHz bleiben - höchstens 22 kHz und auch mono/8bit.
Mehr als 8 Bit macht für mich für Spiele auch keinen Sinn, man sollte nur ordentlich aussteuern damit man die 8 Bit auch
halbwegs nutzt. 11, 16 oder 22 kHz wären mir im Grunde auch genug, aber erst bei 44 kHz klingt es wirklich sauber, weil
bei ZSM da am wenigsten Aliasing auftritt. 33075 Hz wäre noch eine Option die ich einbauen könnte. Alle vielfachen
von 11025 Hz sind möglich.

FAR-Calls: Ich denke wenn die Routinen die in einer Unit stehen für sich eine gewisse Rechenzeit beanspruchen
relativiert sich das. Also mal in blaue geraten, wenn ein FAR-Call 20 Takte braucht, die Routine in der Unit aber 500,
dann ist das nicht so dramatisch.
DOSferatu hat geschrieben:[...]ODER - man macht das Ganze dynamisch, muß dann aber aufpassen, daß, wenn ein Bereich mehr Speicher braucht, dieser dann woanders fehlt.
Ich habe bisher immer dynamisch gedacht, da meine Laderoutinen nur so viel Speicher reservieren wie auch wirklich
für das konkrete ZSM oder ZVID benötigt werden. Gerade für ZVID kann man eigentlich nur dynamisch an die Sache
rangehen, weil ja ein ganzer Haufen ZVD Files für ein Spiel geladen werden muss. Trotzdem würde ich einfach zusehen,
dass ich die Daten deutlich kleiner halte als die 640K, das macht dann auch weniger Arbeit beim erstellen der Grafiken,
mein Problem war nur die Fragmentierung des Heap, aber da muss man eben getmem und freemem so einsetzen dass
man die Reihenfolge beachtet. Wenn das denn funktioniert.
DOSferatu hat geschrieben:bei 20 FPS sind 30 Aufrufe pro Sekunde eben 600 Aufrufe pro Frame und 10 Byte pro Sekunde eben 200 Byte pro Frame
Nein, das Pattern läuft ja nicht auf einmal 20 zum Quadrat (also 400) mal so schnell, sondern die Aufrufe und der
Datendurchsatz pro Sekunde muss im Gegenteil auch noch durch die Anzahl der Frames pro Sekunde geteilt werden.
Also sind es pro Frame nur 1,5 Aufrufe und 0,5 Byte. Es kommt natürlich auf das jeweilige ZSM an, bei 50 Zeilen
pro Sekunde sind es dann auch mehr Routinenaufrufe und je nach Dichte auch deutlich mehr Daten.
DOSferatu hat geschrieben:Ich hoffe, die wird bei den Blöcken nicht für jeden Block einzeln neuberechnet, sondern hoffentlich ausgehend von den Positionen der vorigen Blöcke?
Bei ZVID geschieht das soweit ich mich erinnere (habe länger nichts an dem Code gemacht) durch simple Offset-
Subtraktion, ergibt sich dann u.a. aus der Breite des Puffers in den man schreibt, d.h. es werden so viele Bytes
subtrahiert dass man von der Unterkante des zuletzt geschrieben Blocks bis zur linken Oberkante des Blocks
daneben kommt.
DOSferatu hat geschrieben:Wie gesagt: Mal PNG in 4-farbig versuchen.
Ich kann mit meinem Bildbearbeitungsprogramm nicht auf 4 Farben reduzieren, der nächstliegende Schritt
sind 2 Farben. Dabei verliert das Bild natürlich Farben, die PNG ist dann 710 Byte groß.
Also, ich denke, PNG ist selber so schlau dass es die vorhandenen Farben mit so wenig Bits wie nötig
arrangiert oder wie auch immer, unabhängig davon, wieviel Farben grundsätzlich erlaubt sind bzw.
wie lang die Palette ist.
DOSferatu hat geschrieben:Wenn das nicht 1 Bild wäre, sondern 3 "hintereinanderliegende" Bilder eines animierten Sprites, dann versuch es mal als 3-phasiges Bild mit GIF zu packen, das ja auch delta-packt, sowie auch bitbasiert (also auch z.B. 2-bit ). Ich habe es nicht getestet - aber ich nehme an, Du wirst erstaunt sein...
Erstaunt nicht wirklich, ich habe ZVID natürlich von Anfang an mit anderen amtlichen Formaten verglichen und GIF
war schon bei Einzelbildern besser, und ich glaube Dir, dass GIF bei Animationen noch mehr reinhaut. Die Idee von ZVID
ist aber einfach, dass es ohne Huffman funktionieren soll, denn ich verstehe mich nicht auf rekursive Prozeduren, und
es ist fraglich wie soetwas in einem Spiel performen würde, wenn man sagen wir 30 Huffman Routinen in einem Frame
laufen lassen will, das ganze würde ja auch den Stack beeinflussen - klar, es müsste und kann ja nur eine nach der
anderen laufen, aber wie gesagt, Huffman ist ein Buch mit sieben Siegeln für mich. Sollte das ganze schneller als ZVID
sein und effektiver und auch noch implementierbar ohne Probleme, dann wäre ZVID hinfällig und ich sollte mir
stattdessen lieber mal ne Huffman Routine klauen...
DOSferatu hat geschrieben:[...]in PCX mehr Speicher braucht als das BMP, wegen der Besonderheit, wie PCX die Lauflängencodierung macht
Ja, ich erinnere mich da an sowas dass PCX abwechselnde Pixel total aufbläht. Weil es einfach alles packen will,
auch wenn es nur ein einziger Pixel ist, oder so ähnlich war es. Mit Verlaub aber auch eine sehr dumme Eigenschaft
des Formates.
DOSferatu hat geschrieben:Ich persönlich kann mir nicht vorstellen, daß es mit 4x4 besser packt, rein von meiner Intuition her. Müßte man wohl ausprobieren.
Genau, es wäre wert, das auszuprobieren. Immerhin wäre eine durchschnittliche Bitanzahl von 2 bis 3 pro Block
realistisch. Man muss nur schaffen, die Header auf 1 Byte zu halten. Das würde die Palettenanzahl auf 256 beschränken.
Ein einfarbiger Block wäre also 1 Byte groß, bei 1 Bit Farbtiefe wäre er 3 Byte groß. Maximale Blockgröße 9 Byte.
Und dann eben noch der Palettenschwanz dran.
Dieser könnte so aussehen:
Bis zu 256 WORDS: 12 Bit: Offset in den Paletten-"String",
4 Bit: Anzahl Farben (0[transp.], 1, 2, 4, 8, 16, 2 mit tr., 4 mit tr., 8 mit tr. 16 mit tr.)
Palettenstring: Bytes möglichst kompakt optimiert, aus denen mit den Offsets die jeweiligen Farben gelesen
werden können.
Die Differenzierung mit Transparenz ist sinnvoll, weil sonst der Paletten-String
total durch die Transparenzfarbe durchsetzt wäre, was ihn total aufblähen würde.
Obwohl das von der Betrachtungsweise abhängt... "Transparent" ist ja auch nur eine Farbe.
Würde die Sache evtl. vereinfachen, naja, da muss mehr drüber nachgedacht werden als
jetzt mal eben in 10 Minuten.

Aber wie gesagt müsste man mal in Erwägung ziehen, wie geeignet ein amtlicher Huffman Algorithmus wäre.
Aller Begeisterung für ZVID zum Trotz.
DOSferatu hat geschrieben:Ich hab mir letztens auf Youtube einen Walkthrough von "Captain Comic" (PC-Version) angesehen. Wollte mal sehen, wie das aussieht, wo Du es so oft erwähnst.
Ja, schon nett. 16-farbig. Ich frage mich, welcher Grafikmodus benutzt wurde.
Aber ein PC (vor allem ein 486er) könnte auch mehr schaffen als das.
Ja, das ist ein Spiel aus dem Jahr 1988, ich habe es damals auf einem 286er gespielt. Wird wohl EGA sein.
Klar kann ein 486er mehr, aber man könnte ja ein ähnliches Spiel machen und das ganze einfach nur "aufgemotzt".
20 fps wären im Vergleich schon eine Wohltat, mehr Farben wären schön, und Digisound/Musik...
So ähnlich wurde es ja auf dem NES umgesetzt, es glänzt da mit hoher Framerate, allemal ein wenig
schönere Grafik und Sound könnte man noch machen auf einem 486er mit VGA und Soundblaster.
DOSferatu hat geschrieben:Und was würdest Du mir unterstellen wollen?
Unterstellen will ich Dir nichts, ich will Dir aber nicht den Stempel "Hobbyprogrammierer"
aufdrücken und damit sagen dass Du keine gute Software machen würdest.
DOSferatu hat geschrieben:Ja, das ist (meines Erachtens) lobenswert. Speicher zu "verschwenden" nur "weil er da ist", ist meiner Meinung nach keine gute Einstellung.
Es gab eine Zeit so um 1998 rum, da hatte ich gerade ein bisschen Assembler gelernt und konnte auf XMS zugreifen,
und programmierte auf meinem Pentium nicht ganz ernstgemeinte Spielchen (wie TS&Besen z.B.) und hatte dann eher
so eine "Windows" Manier.
Da entstand auch meine "Background Sprite & Sound Engine".
Unkomprimierte riesige Bitmaps, unkomprimierte lange 22 kHz mono Musikstücke...
Das hatte alles nichts mit der Kunst zu tun, ein überlegtes und geplantes Spiel zu machen,
es war nur mal eine Art Befreiungsakt von den Limitationen von Qbasic.
Aber wenn ich jetzt etwas anfange wird es wohlüberlegter und wohl innerhalb 640K bleiben.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Ich habe mir mal ein kleines Programm geschrieben das eine 8x8 Pixel Rasterung mit einer 4x4 vergleicht.

Als Material habe ich diesen Screenshot aus dem Adventure Sam&Max genommen:
Bild

Ich finde, es ist eine gute Mischung aus Comic-Grafik und Farbverläufen mit Anti-Aliasing,
d.h. es existieren letztlich sowohl Blöcke mit vielen Farben, wie aber auch Blöcke die nur einfarbig sind.
Es sind effektiv 126 Farben.

Bei einer 8x8 Rasterung zählt mein Programm rohe 27584 benötigte Byte.
Bei 4x4 sind es erwartungsgemäß weniger, nämlich 18602 Byte, was aber dennoch beachtlich ist.

Die Häufigkeitsverteilung:
8x8:
0 Bit / einfarbig: 2
1 Bit: 14
2 Bit: 134
3 Bit: 333
4 Bit: 418
5 Bit: 99
6 Bit: -

4x4:
0 Bit / einfarbig: 183
1 Bit: 534
2 Bit: 1361
3 Bit: 1643
4 Bit: 279

Man kann hier also gut sehen, wie bei 4x4 die einfarbigen Bereiche besser erfasst werden (das
ist natürlich vom Bild abhängig, aber hier tritt der Effekt deutlich auf) und dass bei 4x4 1-3 Bit
deutlich häufiger vorkommen, selbst wenn man den Flächenfaktor 4 herausrechnet. Zudem
ist die Häufigkeit von 4 Bit bei 4x4 schon wieder deutlich rückläufig, während sie bei 8x8 dort
noch ansteigt.

Nun muss man zu den Rohdaten natürlich noch die Headerdaten pro Block dazurechnen.
Wir haben bei 8x8 Pixel-Blöcken für 320x200 1000 Blöcke, bei 4x4 sind es 4000 Blöcke.
Schafft man es also, die Header auf ein Byte zu beschränken, dann haben wir bei 4x4 18602 + 4000 = 22602 Byte.
Diese Zwischensumme klingt attraktiv, da die .PNG die ich hier verlinkt habe auch immerhin 22781 Byte groß ist.
Die effektiven Bilddaten der PNG kann man wieder auf rund 22000 Byte zusammstreichen da der Rest Paletten-
daten sind. Aber immerhin käme man schonmal in die Nähe eines so effizient gepackten Grafikformates.
Natürlich muss noch der "Palettenschwanz" ran. Dieser müsste aber nach meinem Verständnis auch
kürzer sein als beim "alten" ZVID.
Und wenn wir noch einmal vergleichen:
18602 Byte + 4000 Byte Headerdaten = 22602 Byte.
Selbst wenn wir bei den 8x8 Rohdaten nur 1000 Byte addieren würden, somit also beim alten ZVID auch
nur 1-Byte-Header hätten (was nicht so ist), dann kämen wir trotzdem noch auf 28584 Byte + Palette,
die erwartungsgemäß größer ausfallen wird als bei 4x4.

Also:
Am Beispiel überprüft und befunden, dass 4x4 mindestens 22602 / 28584 --> 79 %,
also noch einmal um 21% kleiner packen könnte als 8x8.
Selbst wenn man 2 Byte pro Header bräuchte läge man in diesem Beispiel bei
4x4 Blöcken noch bei 18602 + 8000 = 26602 Byte und damit immer noch unterhalb 8x8, selbst wenn man
dieses mit nur 1 Byte pro Header einrechnet.

Weitere Vorteile von 4x4 wären übersichtlichere Palettendaten, da die Spannbreite dort nicht bis 64 sondern
nur bis 16 geht, und kleinere Blöcke erfordern bei einer Implementierung von blockweisem Clipping weniger
Maskierung.

Das war exemplarisch für diesen Screenshot. Ob es für Grafik mit mehr Farbverläufen doch eher
zugunsten von 8x8 geht werde ich noch prüfen.
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von DOSferatu »

zatzen hat geschrieben:Danke, BluesmanBGM, es ist immer schön zu sehen, wenn auch andere Forenmitglieder interessiert mitlesen.
Ja, @BluesmanBGM: Hätte gar nicht gedacht, daß noch jemand außer zatzen und mir diese langen Texte hier mitliest...

@zatzen: Bitte um Entschuldigung, daß ich so lange gebraucht habe, um endlich mal zu antworten. Wenn ich vom Job nach Hause komme, bin ich oft so müde. Und am Wochenende mache ich oft einige andere Dinge. (Derzeit will ich noch einen letzten Befehl/"Opcode" in GameSys2 einbauen - habe dafür einen anderen entfernt, der mit einem weiteren "zu ähnlich" war, um dessen Vorhandensein zu rechtfertigen. Allerdings habe ich an GameSys2 schon eine Weile nichts "komplizierteres" mehr gemacht - muß mich da erst wieder reinfinden...)
zatzen hat geschrieben:Nur eigenen Code nutzen: Ein Vorteil ist da auch noch, dass man genau weiss was da alles drin ist, und man ist sicher, keinen überflüssigen Ballast ins Programm einzubauen.
Naja, mit eigenem Code lernt man natürlich mehr - man hat dann einiges "schon mal gesehen" und kann es auch an anderer Stelle wieder anwenden. Wenn man nicht weiß, wie etwas funktioniert, sondern es immer nur "benutzt", kommt man zwar auch an's Ziel, ist aber auch beschränkt darauf, was einem von anderer Seite angeboten wird.

Und ja: Bei vielem, was so in totaler "Featuritis" ausartet, schleppt man Code mit, den man nicht braucht - und im schlimmsten Fall wird auch noch "generalisiertes" Zeug an jeder Stelle ausgeführt, selbst wenn man die entsprechende Subfunktion nie benutzt. Das hängt natürlich auch immer davon ab, wie elegant die "Bibliothek" usw. gebaut ist - aber wenn man nicht weiß "was drin ist", muß man sich eben blindlings darauf verlassen.

Ich will damit keinesfalls ausdrücken, daß ich generell gegen die Verwendung fertiger (Fremd-)Routinen bin oder daß ich jedem davon abraten würde. Man hat natürlich einen größeren Software-Output, wenn man "sich auch mal helfen läßt". Nur sollten einem die obengenannten Dinge eben auch immer bewußt bleiben.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ich frage mich immer wieder, was so wichtig an dieser verflixten Sprachausgabe ist.
Nicht wichtig, aber eben:
DOSferatu hat geschrieben:Sprachausgabe ist ein netter Nebeneffekt
Nicht mehr und nicht weniger.
Ja, und an dieser Stelle wollte ich vor allem das "NEBEN-" in "Nebeneffekt" betont wissen.
Steuerung = Unverzichtbare Hauptsache, ohne die es kein Spiel ist.
Grafik = Für Spiel mit Spielfeldern/Spielfiguren unverzichtbare Hauptsache.
Sound = Teilweise verzichtbar, weil man viele Spiele auch ohne Sound spielen könnte.
Sprachausgabe = Nebensache, die Spiele zwar netter/interessanter macht, aber nicht benötigt wird, um spielen zu können.

Das meine ich. Und solange die Dinge, die wirklich unbedingt nötig sind, damit es ein spielbares Spiel wird, nicht vorhanden sind, ist es (meines Erachtens) wenig sinnvoll, zu überlegen, wo und wie man die Zusatzfeatures unterbringt. Das ist so, als würde man ein Haus bauen und als allererstes stellt man die Tapete hin und baut dann die Wände drumherum...
zatzen hat geschrieben:Ich male mir eben nur so ein Spiel schonmal in Gedanken aus, und je nachdem kann es unterhaltend und bereichernd sein, so wie gute Grafik oder eine gute Levelstruktur. Für den ein oder anderen also motivierend sein, das Spiel zu spielen. Zumal war ich wenigstens früher schwer beeindruckt, als sich zur Adlib Musik Digi-Sounds gesellten, und letztlich sogar Sprache.
Ja, ich kenne diesen Effekt. So ging es mir früher auch. Ich habe in einem Spiel bestimmte Dinge gesehen/gehört/erlebt und gedacht: "Das ist cool, so etwas will ich auch machen." Aber ein Spiel "um einen bestimmten Effekt herum" zu bauen, nur weil der unbedingt drin sein soll - das geht meiner Erfahrung nach in die Hose. Man verliert sich in Kleinigkeiten und hat am Ende ein kleines Testprogramm, in dem der besagte Effekt gut funktioniert und für das ganze "Spiel drumherum" hat man keinen Platz, keine Zeit, keine Lust mehr.

Es klingt vielleicht hart - aber: Wenn man wirklich ein SPIEL bauen will, bei dem viele Dinge A, B, C, D, E, ..., X, Y, Z gebraucht werden (wobei A=das wichtigste und Z=das unwichtigste), müssen/sollen zwar alle Dinge enthalten sein, aber es wird nichts, wenn man bei Z anfängt und denkt: "D, C, B, A wird schon noch - ist ja egal, womit ich anfange". Von der Logik her stimmt das zwar - aber von der menschlichen Erfahrung her nicht.
zatzen hat geschrieben:Es müssen ja auch nicht unbedingt ganze Sätze sein, irgendwelche Schreie oder kurze Äußerungen kann man ja im Rahmen von Sound-FX auch auf dem Heap halten.
"Sprachausgabe" in dieser Form würde ich technisch nicht anders behandeln als alle anderen Soundeffekte - da macht es technisch keinen Unterschied, ob das Sample eine Explosion oder ein "Hallo!" enthält.
zatzen hat geschrieben:Mehr als 8 Bit macht für mich für Spiele auch keinen Sinn, man sollte nur ordentlich aussteuern damit man die 8 Bit auch halbwegs nutzt. 11, 16 oder 22 kHz wären mir im Grunde auch genug, aber erst bei 44 kHz klingt es wirklich sauber, weil bei ZSM da am wenigsten Aliasing auftritt. 33075 Hz wäre noch eine Option die ich einbauen könnte. Alle vielfachen von 11025 Hz sind möglich.
Ja, ich werde bei meinen Spielen vieles einstellbar machen - und bei Spielen für PCs ist das meiner Meinung nach auch generell eine gute Idee: Jeder hat ein anderes Motherboard/CPU/Grafikkarte/Soundkarte/Speicher eingebaut - alle mit unterschiedlicher Geschwindigkeit/Platz/Datendurchsatz. Beim PC kann man - im Gegensatz zu Homecomputern oder Spielkonsolen - nicht von einer monolithischen Maschine ausgehen. Und damit die Leute auch auf anderen Kisten ein gescheites (oder überhaupt vorhandenes) Spielerlebnis haben können, sind so einstellbare Dinge (wie eben z.B. die Rate beim Sound) schon kein schlechter Ansatz.
zatzen hat geschrieben:FAR-Calls: Ich denke wenn die Routinen die in einer Unit stehen für sich eine gewisse Rechenzeit beanspruchen relativiert sich das. Also mal in blaue geraten, wenn ein FAR-Call 20 Takte braucht, die Routine in der Unit aber 500, dann ist das nicht so dramatisch.
Naja, dann hat der CALL 4% (bzw. wenn man ihn mitzählt, also insgesamt 520 Takte, 3,8...%) Ich denke mal, daß Routinen, die nur 500 Takte haben, nur welche sind, die keinerlei Schleifen enthalten. Jede Schleife ist ja wie ein Multiplikator. Man sieht einer kleinen Routine oft nicht an, wieviele Takte sie hat - aber da reicht eine kleine Schleife, die vielleicht 100x durchlaufen wird, um schon bei über 1000 Takten zu landen...
Aber, ja, wie ich bereits sagte: Über den "Zeitverlust" von FAR-Calls mache ich mir nicht mehr allzu viele Sorgen. Man sollte eben innerhalb seiner "Engines" eher NEAR-CALLs (wenn überhaupt CALLs) benutzen, dann ist der einmalige Aufruf der Engine von "außerhalb" nicht mehr das Problem.

Oder, anders ausgedrückt: Es nützt nichts, außerhalb, in der Hauptschleife des Programms, überall Performance zu sparen, wenn der "innere Kern", also die Engines, nicht genug performen. Das ist so, als würde jemand sagen: "Mein Sourcecode in Javascript ist total performant!" - klar... um einen extralahmen Skript-Interpreter herum "performanten" Skriptcode zu schreiben, macht ja auch total Sinn...
zatzen hat geschrieben:Ich habe bisher immer dynamisch gedacht, da meine Laderoutinen nur so viel Speicher reservieren wie auch wirklich für das konkrete ZSM oder ZVID benötigt werden.
Alles andere würde auch wenig Sinn ergeben.
Ich bin froh, mir über GetMem/FreeMem und eventuelle Speicherlücken/Speicherfragmentierung keine Gedanken mehr machen zu müssen, da ich ja bekanntermaßen eine entsprechende Unit habe, die mir diese Bürde abnimmt. Solange alles zusammen in den Heap passen würde, paßt es bei mir auch.
zatzen hat geschrieben:Nein, das Pattern läuft ja nicht auf einmal 20 zum Quadrat (also 400) mal so schnell, ...
Stimmt, da hatte ich mich irgendwo verlesen.
zatzen hat geschrieben:
DOSferatu hat geschrieben:...GIF...
Huffman ist ein Buch mit sieben Siegeln für mich. Sollte das ganze schneller als ZVID sein und effektiver und auch noch implementierbar ohne Probleme, dann wäre ZVID hinfällig und ich sollte mir stattdessen lieber mal ne Huffman Routine klauen...
Mir ging es in dem Beispiel nur darum, daß man, wenn es nur um "Packen" geht, man immer weiter abgehen kann... ABER:

Ich selbst halte Huffman und auch ähnliche Packmethoden zwar für sehr sinnvoll, wenn es darum geht, Bilder anzuzeigen/zu archivieren/bandbreitenschonend zu übertragen, usw..., aber für ein SPIEL würde ich da immer Grenzen ziehen und das Ganze relativieren. Ein Format, das zur gepackten Speicherung von BILDERN entwickelt wurde, ist nicht unbedingt gleichermaßen dazu geeignet, um Sprites und Spielsituationen zu speichern - dafür ist es zu ggf. zu unflexibel.

Anders ausgedrückt: Wenn man Levelhintergründe/Sprites u.ä. sehr effizient packt, durch die Speicherersparnis aber auch erhebliche Performanceeinbußen hat - und zusätzlich, weil man in gepackten Daten nicht "wahlfrei lesen" kann (also live auf jede Stelle zugreifen, ohne den Rest entpacken zu müssen) noch andere Metadaten braucht, damit die Engine, die für Verhalten/Erkennen der FIguren zueinander und zum Level zuständig ist, damit arbeiten kann... so daß diese zusätzlichen Metadaten fast so groß werden wie der durchs "Power-Packen" gesparte Speicher... - dann hat man nichts gewonnen, außer:
- eine komplizierte Packroutine, die zusätzlich Speicher belegt und Rechenzeit verbraucht
- genausoviele Daten wie vorher, nur mit schwererem Zugriff, bzw. nur durch die (ebenfalls Speicher belegenden) Zusatzdaten

Damit will ich die generellen Bemühungen um effiziente Anordnung von Daten im Speicher nicht schlechtreden usw. - es ist nur mein Denkansatz dazu, der zu den Dingen geführt hat, mit denen ich momentan arbeite.

Ich sag's mal so: Als ich bei C64 von BASIC auf Assembler umgestiegen bin - vom C64 auf PC umgestiegen bin - von 286er auf 486er umgestiegen bin - und bei PC mich von Pascal zu Pascal+ASM erweitert habe... - habe ich jedes Mal gedacht: "Woah! Der Wahnsinn! Was für eine Geschwindigkeit! Da gibt es wohl nichts, was das je herunterbremsen könnte!" - und habe ausnahmslos jedes Mal falsch gelegen!

Woran liegt das?
Mit den erweiterten Möglichkeiten/Fähigkeiten steigen auch die Ansprüche, die man an sich selbst hat. Wenn "etwas geht", will man es auch machen. Dann baut man eine Routine, testet sie, und sie funktioniert und sie performt. Dann baut man eine andere Routine für etwas anderes, testet sie und sie performt auch. Und eine dritte, vierte, fünfte Routine...

Und jetzt kommt der Knackpunkt:
So'n 486er (DOS) hat fatalerweise aber keine Anzeige dafür, wie ausgelastet das System durch die einzelnen Routinen/Engines ist!
(Wieviel Speicher sie brauchen, ist ja eher leicht festzustellen.)
Also: Wer sagt denn, daß etwas, das im Einzelnen performt, auch im Zusammenspiel mit 4 anderen (vielleicht einzeln ebenfalls performanten) Sachen immer noch performen würde?

Ich bin da jetzt einfach mal realistisch (auch wenn es vielleicht stellenweise nicht nett klingt) :
* Grafik und Sprites per komplexer Packroutine - spart Speicher, braucht aber Leistung. Wieviel Speicher gespart wird, ist nicht abschätzbar.

* Steuerung benötigt zusätzliche Daten für Hintergrund - ob Scrolling gewünscht oder nicht, diese zusätzlichen Daten sind so groß wie das Level in "Blocks".

* Will man ohne Scrolling arbeiten und außerdem nicht pro Frame den ganzen Hintergrund neu entpacken/zeichnen (um Performance zu sparen), benötigt jedes Sprite (nein, jede FIGUR! also zwei gleich aussehende Figuren brauchen trotzdem BEIDE...) einen zusätzlichen Puffer in Größe des Sprites, um das zu sichern, was das Sprite verdeckt. Könnte man zwar auch "packen", aber a) ist gepackte Größe nicht abschätzbar, b) dazu brauchts zusätzlich Performance. Um das (also den Puffer pro Figur) zu vermeiden, könnte man zwar auch auf die Level-/Hintergrunddaten zugreifen und sich die Daten von dort holen, aber die sind ja ebenfalls komplex gepackt ohne wahlfreien Zugriff.

* Sound per Soundroutine, soll aber 44,1 kHz sein, damit es "sauber klingt" - sagen wir mal 8 Stimmen (4 Musik, 4 Effekte) - sind im "Peak" 44100*8 = mindestens 352800 Operationen pro Sekunde, bei 10 FPS also 35280 (langsamer Grafikaufbau+Steuerung, aber wenigstens Sound klingt sauber)...

* Spiel mit fester, durch Soundkarten-IRQ getriggerter Framerate. Bedeutet: Alles Obengenannte muß innerhalb eines Frames stattgefunden haben. Ein einzelnes Sprite zu viel, eine Explosion zu groß, ein Soundeffekt zu komplex - oder der PC einen Tick zu langsam - schon beginnt der Sound zu stottern, die Steuerung zu hakeln, schlimmstenfalls Absturz durch Stack Overflow (bzw. Underrun), weil eine Subroutine öfter aufgerufen wird als das System es schafft...

Aber: Auch wenn sich der Computer aufgehängt hat, ist die stehende Hintergrundgrafik immer noch ein sehr schönes Vollbild und der Soundpuffer wird in Dauerschleife - aber dafür mit voller Rate sauber abgespielt...

Ja, Worst Case Szenario, ich weiß...
Es ist nur so: Ich habe schon so einiges Zeug programmiert und nach der ganzen Zeit kann ich dazu sagen: Es ist NIE "genug Speicher da" es ist auch NIE "genug Systemleistung da" - zumindest nicht auf den KIsten, die ich zum Programmieren benutze und bei meinen Fähigkeiten.

"Wird schon reichen" und "Aus dem Vollen schöpfen" verbiete ich mir selbst, überhaupt zu denken. Xpyderz - ja, das Spiel OHNE Sound, mit Grafikroutinen in fast 100% ASM - hat eine Performance-Anzeige in Prozent (ausgehend von 50FPS = 100%). Auf meinem 486er X5, 133 MHz (also abgesehen vom 166er dem besten, was an 486er zu haben ist), mit meiner PCI-Grafikkarte (16 MB/sek. Datendurchsatz) läuft es am Anfang mit 80% (=40 FPS), wenn etwas mehr auf dem Schirm los ist, mit 60%-70% (also 30 FPS bzw. 35 FPS).

Also: Auf einem 486er Spitzenmodell, ohne wirklich gepackte (nur 8 zu 4 bit) Grafikdaten, ohne Sounderzeugung, komme ich im normalen Spielgeschehen auf etwas über 30 FPS. Der MCGA - und damit auch 320er Mode-X - hat eine Bildrate von 70 Hz. Ohne Ruckeln geht da also nichts ab. Die 50 Hz sind nur das Spiel selbst, d.h. 50 "Schritte" pro Sekunde macht jede Figur.

Wenn man von vornherein nur 10 "Schritte" pro Sekunde einplant, müssen die Figuren dafür größere Schritte machen (sonst wäre es ein schneckenlahmes Spiel). Größere Schritte bedeutet aber auch: Ungenauere Schritte.

Ich weiß, ich bin ein Schwarzmaler usw. - aber habe auch schon einiges erlebt. Beispiel (mal wieder) Xpyderz: Jemand (weiß nicht mehr wer, steht irgendwo im Forum, glaub in meinem Xpyderz-Thread) hat das Ding auf 386er getestet und er teilte mit: Selbst wenn er es auf LowRes stellt (Hintergrund wird nur 160x200 gepixelt, mit dem Chained-Trick des Mode-X) ist es kaum sinnvoll spielbar. Will damit nur sagen: Ich frage mich, wie "spielbar" es wäre, wenn ich noch zusätzlich Sounderzeugung einbauen würde...

OK, soweit erstmal dazu.

Mein "SLOT" ist inzwischen fast soweit, daß ich darin ein Spiel bauen könnte - wahrscheinlich werde ich zuerst wieder ein "Demo-Spiel" damit machen, um das Zusammenwirken aller Komponenten zu testen. Vielleicht stelle ich das auch online - mal sehen. Es gibt noch einige Sachen an SLOT, die mir noch fehlen und die ich gleich einbauen und nicht schon wieder "workarounden" will. "Fast soweit" heißt also NICHT: In zwei Wochen oder sowas.
zatzen hat geschrieben:
DOSferatu hat geschrieben:[...]in PCX mehr Speicher braucht als das BMP, wegen der Besonderheit, wie PCX die Lauflängencodierung macht
Ja, ich erinnere mich da an sowas dass PCX abwechselnde Pixel total aufbläht. Weil es einfach alles packen will, auch wenn es nur ein einziger Pixel ist, oder so ähnlich war es. Mit Verlaub aber auch eine sehr dumme Eigenschaft des Formates.
Nein, ganz so ist es nicht. PCX benutzt zum Packen alle Werte 193 bus 255 ($C1 bis $FF), indem es mit den unteren 6 Bit angibt, wieviele gleiche Bytes folgen und dann das Byte. Weil Bytes (Farben/Werte) aber auch selbst 192 (ja, 192!) bis 255 sein können, müssen diese ebenfalls gepackt werden. Wenn also auch ein einzelner Wert 235 auftaucht, muß er mit 193, 235 dargestellt werden. PCX prüft zum Packen nur die beiden oberen Bits, was auch der Grund ist, wieso $C0 nie zum Packen benutzt wird, trotzdem aber selbst "gepackt" werden muß. DAS ist wirklich dumm.

Bei 8bit-Bildern mag das ja noch gehen: Man sortiert einfach entweder die häufigen Farben nach vorn bzw. bemüht sich darum, daß Farben, die in langen gleichen Folgen auftreten, hinten sind (weil es da keinen Unterschied macht und sie sowieso gepackt werden, nur eben nicht als "einzelne").

Bei 24bit-Bildern ist das Ganze aber wirklich blöder, weil natürlich RGB-Werte über 191 (die helleren) vergleichsweise schon häufig auftreten und dann immer "gepackt" werden müssen, auch als Einzelbytes.

PCX ist eben nur praktisch, weil es a) ein relativ primitives (weil altes) Format ist und b), was es gegenüber BMP von Vorteil macht: Es speichert das Bild von oben nach unten - so wie es auch in der Grafikkarte und überall sonst liegen würde. BMP hat zwar auch eine Option, die Zeilen von oben nach unten zu speichern (wenn man die Höhe als negativen Wert angibt), aber das wird nicht von allen Programmen unterstützt und spätestens beim Speichern wird es wieder mit umgekehrten Zeilen gespeichert.

Klar - BMP ist ja auch von Microsoft, die müssen immer irgendeine hirnverbrannte Extrawurst haben. Nehme an, die Idee dahinter ist, daß Punkt 0;0 unten links ist, wie beim Koordinatensystem. Aber es liest/konvertiert sich eben dadurch schlechter. Oft liest man Daten nur zeilenweise ein, um Speicher zu sparen oder gleich beim Lesen zu konvertieren. Und "Linksrum-Lesen" verkompliziert es, "Prefetch" zu lesen (also nicht Byte-für-Byte, sondern pufferweise, was schneller ist). Diese blöde Idee mit dem "von unten nach oben speichern" würde ich am liebsten Bill Gates jeden Tag um die Ohren schlagen...
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ich persönlich kann mir nicht vorstellen, daß es mit 4x4 besser packt, rein von meiner Intuition her. Müßte man wohl ausprobieren.
Genau, es wäre wert, das auszuprobieren. Immerhin wäre eine durchschnittliche Bitanzahl von 2 bis 3 pro Block realistisch. Man muss nur schaffen, die Header auf 1 Byte zu halten. Das würde die Palettenanzahl auf 256 beschränken.
Naja, ist der Header denn nur die Palette? Er muß doch auch noch enthalten, wieviele Bit usw oder ist das in der Palette selbst vermerkt?
zatzen hat geschrieben:4-bit...
Ja, klingt sehr plausibel alles. Vom Packen her sollte es gut funktionieren.
zatzen hat geschrieben:Aber wie gesagt müsste man mal in Erwägung ziehen, wie geeignet ein amtlicher Huffman Algorithmus wäre. Aller Begeisterung für ZVID zum Trotz.
Ja. wie bereits oben erwähnt: Huffman packt zwar ziemlich gut (hängt natürlich auch von den Daten ab), aber ob es für Grafikdaten eines Spiels geeignet wäre, müßte noch überlegt werden.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ich hab mir letztens auf Youtube einen Walkthrough von "Captain Comic" [...] Aber ein PC (vor allem ein 486er) könnte auch mehr schaffen als das.
Ja, das ist ein Spiel aus dem Jahr 1988, ich habe es damals auf einem 286er gespielt. Wird wohl EGA sein. Klar kann ein 486er mehr, aber man könnte ja ein ähnliches Spiel machen und das ganze einfach nur "aufgemotzt". 20 fps wären im Vergleich schon eine Wohltat, mehr Farben wären schön, und Digisound/Musik... So ähnlich wurde es ja auf dem NES umgesetzt, es glänzt da mit hoher Framerate, allemal ein wenig schönere Grafik und Sound könnte man noch machen auf einem 486er mit VGA und Soundblaster.
Ja, wie auch oben erwähnt: Wenn man die Sachen einzeln für sich hat (Grafik, Sound, Steuerung) sieht es immer so aus, als würde alles total flutschen und quasi überhaupt keine Zeit brauchen, um ausgeführt zu werden. Erst wenn das alles gleichzeitig in einer großen "Haupt-Spiel-Schleife" gemeinsam funktionieren muß und jedes dem anderen Performance klaut, merkt man erst, daß so'n 486er auch kein "magischer unbegrenzt Würfel" ist (Tesserakt...).
zatzen hat geschrieben:
DOSferatu hat geschrieben:Und was würdest Du mir unterstellen wollen?
Unterstellen will ich Dir nichts, ich will Dir aber nicht den Stempel "Hobbyprogrammierer"
aufdrücken und damit sagen dass Du keine gute Software machen würdest.
Naja...
1. Ich BIN ein Hobbyprogrammierer. Ich programmiere als Hobby. Und in letzter Zeit leider oft zu wenig. Also bin ich noch weniger als ein Hobbyprogrammierer.
2. Ich denke nicht, daß, ein Hobbyprogrammierer zu sein, automatisch bedeuten muß, keine gute Software zu machen. Viele Hobbyprogrammierer (nicht ich) beweisen das Gegenteil - sie übertreffen sogar teilweise professionelle Software in vieler Hinsicht.
zatzen hat geschrieben:
DOSferatu hat geschrieben:Ja, das ist (meines Erachtens) lobenswert. Speicher zu "verschwenden" nur "weil er da ist", ist meiner Meinung nach keine gute Einstellung.
Es gab eine Zeit so um 1998 rum, da hatte ich gerade ein bisschen Assembler gelernt und konnte auf XMS zugreifen, und programmierte auf meinem Pentium nicht ganz ernstgemeinte Spielchen (wie TS&Besen z.B.) und hatte dann eher so eine "Windows" Manier. Da entstand auch meine "Background Sprite & Sound Engine". Unkomprimierte riesige Bitmaps, unkomprimierte lange 22 kHz mono Musikstücke... Das hatte alles nichts mit der Kunst zu tun, ein überlegtes und geplantes Spiel zu machen, es war nur mal eine Art Befreiungsakt von den Limitationen von Qbasic.
Aber wenn ich jetzt etwas anfange wird es wohlüberlegter und wohl innerhalb 640K bleiben.
Ja, so Experimente haben wir wohl alle mal gemacht: Mal herauszufinden, "was möglich ist" und damit herumzuspielen. Bei Spieleprogrammierung (man will vielleicht, daß außer einem selbst auch noch jemand anders das spielt) gehe ich aber nicht von der eigenen Maschine aus. Hat jemand weniger XMS eingebaut als ich selbst - zack! - schon kann er das Spiel GAR NICHT spielen, bloß weil ich XMS vielleicht nur für einen unwichtigen Nebenaspekt eingesetzt habe oder zu faul war, eine Alternative einzubauen... Das fände ich schade.

Andererseits verhindert auch meine inzwischen fast standardmäßige Nutzung von 386er-Code (32bit-Register, 386er-Befehle, erweiterte Segmentregister FS/GS) vielen 286er-Fans (auch hier im Forum), mein Zeug zu benutzen. Eigentlich auch schade. Eigentlich wollte ich auch mal ein "Minimal-Spiel" machen, was auch auf 286ern mit CGA noch läuft... aber ich komme kaum mit meinen regulären Projekten weiter, weil ich selten in der Verfassung bin, überhaupt mal über längere Zeit anständig zu programmieren und viel zu schaffen.

Um z.B. diesen letzten Befehl (siehe ganz oben im Beitrag) im GameSys2 einzubauen, muß ich mich wohl wieder erst mal eine Weile hinsetzen, um das Ganze zu durchdenken. Ich meine: Ich weiß, was er machen soll und es ist technisch auch kein Problem, das zu machen. Aber es soll schnell und klein (und natürlich in Assembler) sein und da denkt man ganz anders (Sprünge vermeiden, wo Tabelle und wo Berechnung, Segmentwechsel vermeiden, usw.)
zatzen hat geschrieben:Ich habe mir mal ein kleines Programm geschrieben das eine 8x8 Pixel Rasterung mit einer 4x4 vergleicht.

Als Material habe ich diesen Screenshot aus dem Adventure Sam&Max genommen:
Bild
Ja, das kenne ich. Ich mag ja die Lucas-Arts-Adventures. Da versucht er, mit dem Magneten an der Golfzange, den Stimmungsring aus dem größten Wollknäuel der Welt rauszufischen.
(Erstaunlich, daß ich mir sowas merken kann. Namen von realen Personen vergesse ich dauernd. Gehirne haben ja einen Filter zwischen "wichtig" und "unwichtig". Und die Filtereinstellungen sind wohl auf die jeweilige Persönlichkeit/Interessen angepaßt. Was sagt das wohl über meine Person aus?)
zatzen hat geschrieben:Ich finde, es ist eine gute Mischung aus Comic-Grafik und Farbverläufen mit Anti-Aliasing, d.h. es existieren letztlich sowohl Blöcke mit vielen Farben, wie aber auch Blöcke die nur einfarbig sind. Es sind effektiv 126 Farben.
Naja, bei der Packmethode, die Du benutzt, sind Comic-Grafik, Farbverläufe und Anti-Aliasing alle gleichwertig, weil Du ja nach reinen Pixelfarben packst (also nur Farbe gleich oder ungleich) und keine Farbskalierung wie es z.B. in JPG vorkommt, vornimmst.
zatzen hat geschrieben:Bei einer 8x8 Rasterung zählt mein Programm rohe 27584 benötigte Byte.
Bei 4x4 sind es erwartungsgemäß weniger, nämlich 18602 Byte, was aber dennoch beachtlich ist.
[...]
Aber immerhin käme man schonmal in die Nähe eines so effizient gepackten Grafikformates.
Natürlich muss noch der "Palettenschwanz" ran. Dieser müsste aber nach meinem Verständnis auch
kürzer sein als beim "alten" ZVID.
[...]
Und wenn wir noch einmal vergleichen:
18602 Byte + 4000 Byte Headerdaten = 22602 Byte.
Selbst wenn wir bei den 8x8 Rohdaten nur 1000 Byte addieren würden, somit also beim alten ZVID auch nur 1-Byte-Header hätten (was nicht so ist), dann kämen wir trotzdem noch auf 28584 Byte + Palette, die erwartungsgemäß größer ausfallen wird als bei 4x4.
[...]
Das war exemplarisch für diesen Screenshot. Ob es für Grafik mit mehr Farbverläufen doch eher
zugunsten von 8x8 geht werde ich noch prüfen.
Naja, wie ja bereits erwähnt, hängt es IMMER von den Ausgangsdaten ab, wie effizient man packen kann. Deine Vorgehensweise verspricht natürlich schöne Grafiken - d.h. das Level kann aussehen wie ein Bild, ohne die Limitierungen, die sich durch die Kachel-Variante ergeben. Allerdings sollten pro 4x4 oder 8x8 (oder 16x16) Bereich irgendwo noch Meta-Daten sein, damit die Figuren mit dem Level interagieren können und es nicht nur reine "Dekoration" ist. Also, z.B. auf welche Stellen man draufspringen kann, ob es Bereiche gibt, die der Spielfigur bei Berührung Energie (oder Leben) kosten usw. Bonusgegenstände könnte man auch als Sprites realisieren - Bonus im Level integriert erfordert aber weitere Interaktion: Wenn der Bonus weggenommen wurde, muß an diese Stelle etwas anderes "übermalt" werden usw.

Für Sprites bin ich mir nicht sicher, ob die gleiche Methode, die Du für Bilder benutzt, ebenfalls so viel Ersparnis bringt. In Sprites gibt es viel weniger "sich wiederholende" Teile und ob da die komplizierte Aufteilung in Einzelquadrate am Ende wirklich die Speicherersparnis (und komplizierteren Routinen) rechtfertigt, müßte man sehen - ich bin da etwas skeptisch.

Zum Thema Sprites: Hat Deine Routine Möglichkeiten, die Daten gespiegelt zu entpacken? Viele Figuren sehen von der linken Seite genau so aus wie von der rechten, nur gespiegelt. Da wäre es ja Verschwendung, bei all der komplizierten Packerei, den doppelten Speicher für 2 verschiedene (nur gespiegelte) Images zu benutzen - denn dann nützt das Packen ja kaum noch etwas.

Ich sage mal so: Wenn man - aus verschiedenen Gründen - sowieso nur ein Spiel mit 10 FPS machen möchte mit festem Hintergrund (ohne Scrollen) und vielleicht max. 8-16 Sprites gleichzeitig im Bild, bei denen noch nicht einmal alle verschieden sind, könnte man am Ende vielleicht sogar besser kommen mit total ungepackten Sprites.

Gut, das soll mein (schon längst fälliger) Senf dazu gewesen sein. Zum anderen Thread (über ZSM usw.) antworte ich andermal.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Eigener Code:
Vor allem, wenn es mir um die Sache an sich geht, also der Weg eher das Ziel ist, dann möchte ich möglichst alles selbst
machen. Bis auf irgendwelche kleinen Dinge, die nur ein paar Zeilen Code sind, aber in die ich mich tagelang einlesen
müsste, um sie wirklich zu verstehen.

Dein Speichermanager wäre aber dennoch einmal interessant, allerdings habe ich bisher noch keine Not danach, was
auch daran liegt dass ich bisher kein großartig komplexes Programm geschrieben habe. Aber auch den Speichermanager
würde ich verstehen wollen und müssen, damit ich damit richtig umgehen kann.

Sprachausgabe:
Es ist absolut klar und schlüssig, dass soetwas für ein Spiel generell eine ziemliche Nebensache ist. Etwas anders kann
das aber durchaus bei Adventures aussehen. Da kann die Sprachausgabe zu einem reelleren Erlebnis beitragen. Zudem
hatte ich da mit Freunden schon einen riesen Spaß, diese Sprachausgabe zu manipulieren und die Charaktere
mitunter völligen Blödsinn oder übertrieben albern reden zu lassen - sicher nicht jedermanns Humor, aber immerhin
mancher eins.

Letztendlich wäre für mich bei einem eigenen Spiel aber auch soetwas wie Commander Keen 1 aufwendig genug,
kleine Sprites, alles eher simpel, das hat auch seinen Charme. Ich würde es mit dem Sound nicht übertreiben, aber
mehr als PC Speaker piepsen würde ich schon wollen. Wobei CK1 in den Levels keine Hintergründe hat, das würde ich
schon wollen. Gleichzeitig sind mir die Fortsetzungen grafisch zu komplex als dass ich das ganz alleine stemmen könnte,
also vielleicht eher wieder in Richtung Kotzman, nur mit etwas interessanteren Animationen und eben Hintergründen.

Auch Adlib finde ich im Nachhinein betrachtet gar nicht mal so übel, vor allem wäre es reizvoll wenn man die
Pattern/Musikdaten ähnlich komprimiert wie bei ZSM und vielleicht noch etwas effizienter und intelligenter, aber
letztlich fühle ich mich mit Samples als Klangerzeuger doch freier.

Wie gesagt war mein letztes ernsthaftes und vollendetes Spiel Kotzman II, und das ist jetzt 24 Jahre her. Programmiert
habe ich in der Zwischenzeit nur Versuche, kleine Demos oder auf tiefgehenderer Ebene Tools.
Eine gut Überlegte Herangehensweise an ein Spiel wäre für mich nach wie vor eher neu. Aber ich würde natürlich ein
Spiel, in das ich Arbeit und Mühe stecke, relativ ernst aufziehen und eben nicht mit der Tapete anfangen um die ich
die Wände baue, aber vielleicht bin ich ja dann doch lustig genug um ausgefallene Tapeten zu verwenden.
Furzman war schon keine schlechte Idee eigentlich, aber generell bemerke ich bei mir eine gewisse Prokrastination,
weil ich mich derzeit noch so sehr auf Dinge wie Grafikkompression konzentriere, die für ein kleines Spiel eigentlich
gar nicht nötig sind. Aber sowas macht mir eben Spaß.

Metadaten für den flexiblen Zugriff auf gepackte Sprites:
Darin habe ich bisher kein Problem gesehen, da ich auch bei Kotzman II einfach nur die Grafik in den
Bildschirmspeicher "geklatscht" habe, ohne dass weitere Zugriffe auf die Quelle nötig waren.
Also denke ich, dass die Quelle auch beliebig gepackt sein kann.

Schätzung von Speicher/Leistung:
Vorab: Ich ziele nicht darauf ab, das bestmögliche an Leistung aus dem System herauszuholen. Dazu reizt mich derzeit
die Idee der Datenkompression zu sehr. Evtl. könnte ich aber ein Spiel machen das ohne Datenkompression nicht
unterhalb 640K unterzubringen wäre, bloß mit etwas Einschränkung an Framerate bzw. Geschwindigkeit.

Ich sehe die Vorteile der Grafikkompression für mich als bedeutsamer an, als den Verlust der Performance. Beispiel: Ich
würde eher kleine/wenige Sprites auf dem Screen verwenden, aber mit vielen Animationphasen. Denkbar jedenfalls.
Ob ich überhaupt künstlerisch genug bin so viel zu pixeln sei mal dahingestellt. Aber man könnte auch große,
comicartige Sprites mit viel gleichfarbigen Flächen verwenden, die dann im Schnitt fast so schnell performen wie
unkomprimiert, da sehr viel "stosd" und wenig "movsb". Falls das überhaupt einen Unterschied macht in der
Geschwindigkeit.
Du hast meine ZSM-Moonwalker Demo gesehen, da habe ich einiges an Sprites aufgefahren und
es funktionierte so in Dosbox mit 20000 Cycles, während XPYDERZ damit sehr zäh läuft.
Vielleicht ist diese Demo einfach noch zu simpel was die Berechnung abgesehen von der
Grafikdarstellung angeht. 100% sicher kann ich Dir das mit XPYDERZ als Benchmark Feedback
gerade nicht sagen, ich habe gerade nur Zugriff auf meinen Laptop, der die 20000 Cycles
nicht stabil emuliert. Wir können später noch einmal darauf zurückkommen.
EDIT: Wie dumm von mir. Du hast bestimmt selber einen fähigen Rechner, der 20000 Dosbox
Cycles hinbekommt.
Mir fällt da nur gerade ein: Du hast mir mal XLAT empfohlen. Ich wollte das in der ZSM Engine
verwenden, habe dann aber mal im ASM Guide nachgelesen und es hatte deutlich mehr
Takte als ein normaler Zugriff auf eine Tabelle. Ich weiss nicht wieviel Du XLAT benutzt,
aber vielleicht verlangsamt das manches von Dir.
Okay, Demo nochmal angesehen, das ist natürlich kein Vergleich mit dem was grafisch bei
XPYDERZ passiert. Aber viel mehr brauche ich in einem Spiel nicht, und ob noch mehr geht
muss ich dann einfach sehen.

Levelhintergrunddaten: Da plane ich derzeit nach wie vor ein 8x8 Pixel Raster, 320x200 wären dann 1000 Byte,
also könnte ich bis zu 65 Bildschirme für ein Level anlegen.

Ich glaube auch, man sollte nicht das Spektakel machen mit merken, was von den Sprites überschrieben wurde und
dann nur das ersetzen. Naja müsste man sehen, ich könnte in meinem 8x8 Raster ein Bit als Flag setzen, Notfalls
auch Words benutzen statt Bytes, um mehr Flags und Werte zu haben. Aber spontan habe ich da bis zuletzt eher
"klassisch" gedacht - einfach alles neu zeichnen, notfalls zugunsten der Performance und des Speichers
den aktiven Screen verkleinern.

Ich merke gerade, Speicher verschwenden, und dann Performance kompromittieren um komprimierte Daten
zu verwenden... Du hast schon recht, aber naja, ich mach das nur zum Spaß. Ich muss selber meinen Holzweg gehen...

Beim Sound stimme ich Dir mehr oder weniger zu.
Mit 44100 kann es natürlich zum Worst Case kommen, und das sollten vielleicht dann nur Leute mit nem Pentium so
nutzen. Allerdings habe ich in ZSMPLAY bei der Rate und ner Framerate von ca. 20 fps überhaupt keine Probleme, und
die Dosbox läuft auf 20000 Cycles, was einem eher langsamen 486 entspricht.
Die Sache ist, unabhängig von der Grafikframerate, bleibt die Sound-Rate pro Sekunde immer gleich.
Wenn man Dein Beispiel auf 20 fps umrechnet, dann sind es in einem Frame nur noch 17640 Byte Sounddaten,
gegenüber (ja auch Worst Case, wobei kopieren in den Bildpuffer nicht mal mit drin ist) 64000 Byte Bilddaten.
Also, da kann man sich schon überlegen Ob 27,6% Speichertransferleistung bzw. Rechenleistung für den Sound
gegenüber der Grafik nicht noch akzeptabel sind. Real Case Szenario wäre eher 22 kHz, und dann wären es eben
nur noch 13,8%, oder 8820 Byte per Frame. Das wäre für mich realistisch, so würde ich das ansiedeln, irgendwo
um 20 fps herum.
Nochmal EDIT: Kann man wiederum schlecht abschätzen, da bei ZSM die Sample-Datenrate an sich auch
unabhängig von der Mix-Samplerate ist. Wie in einem vergangenen Thread beschrieben, ergibt eine
Verdopplung der Mix-Samplerate nicht eine Verdopplung der Rechenbeansprung.

Spiel mit fester, durch Soundkarten-IRQ getriggerter Framerate:
Ja, das ist natürlich irgendwie blöd. Aber es übersteigt wie oft schon gesagt derzeit meine Programmierfähigkeiten,
soetwas dynamisch aufzuziehen. Ich kann es nur so handhaben, dass Headroom verschwendet wird, indem ich
leistungsmäßig kleine Brötchen backe, so dass das bescheidene Spiel was ich dann mache eben einen 486er braucht,
auch wenn man das ganze mit mehr Programmieraufwand auch auf nem 386er hinkriegen würde.
Ist alles nicht wirklich das, was Du und auch ich unter effizienter Programmierung verstehen.
Ich kann es auf dem Gebiet der Spieleprogrammierung einfach noch nicht, aber vielleicht performen
meine Sachen doch etwas schneller als welche die jemand in QBASIC ohne ASM schreibt.

Größere Schritte der Figuren: Ich würde intern feinere Schritte rechnen (ohne Sprites dabei anzuzeigen) und dann
eben nur jeden fünften Schritt oder so anzeigen. Ist mein Gedanke, hab ich nie praktisch probiert. Dann hätte ich bei
20 angezeigten fps intern 100 fps.
Dann müsste ich die Steuerung entweder intern auch so schnell tickern oder sie bleibt so ungenau, bei den internen
feineren Schritten würde es mir um Kollisionen gehen, vor allem gegenüber Level-Blöcken.
Der Gedanke von mir war aber eigentlich auch, nicht mit 100 Hz zu tickern, sondern einfach zwischen jedem
Bild 5 (Objektpositions-)Berechnungen zu machen, damit sich die Elemente effektiv nur in 1 Pixel Schritten
bewegen. Letztlich werden wohl auch 3 reichen.


Okay, nun zu "ZVID2", ich stelle mir das bislang so vor:

Der Header eines jeden Blocks enthält einen Offset in eine Tabelle, in der dann ein Word steht mit Anzahl
der Bits (0-4, 0 für einfarbig) und Offset (12 oder 13 Bit breit) in den Palettenstring. Das könnte knapp
werden (max. 256 Möglichkeiten) für bildschirmfüllende Bilder, aber für Sprites müsste es wunderbar funktionieren.
Soweit alles gut und schön, das Problem ist der Palettenstring bzw. Farbnummernstring.
Der soll ja möglichst kurz sein. Man kann erstmal hingehen und alle Farben der Blöcke in Arrays sammeln und
aufsteigend sortieren, dabei auch die Anzahl merken. Dann klatscht man das alles in den String, der dann erstmal
recht groß werden kann, im Extremfall (aber dann wäre auch keine Kompression möglich - EDIT: doch, die 4x4
Blöcke sind ja alle maximal 4 Bit) so groß wie die Bilddaten selber. Jetzt geht man hin und durchsucht für jeden Block diesen String wo die entsprechenden Farben gefunden werden und markiert diese Bereiche. Danach löscht man aus dem
String alle nicht markierten Bereiche. Evtl. rutschen dadurch manche Farbwerte so zusammen dass bei einem nächsten
Suchdurchlauf weniger String-Werte den kompletten Farbgehalt abdecken. Es überfordert mich gerade das alles
verständlicher auszudrücken.
Jedenfalls geht es letztlich darum, mit einem möglichst kleinen Farbstring allen Farbblöcken ihre Werte zu geben.
Eine Sache die man dabei ausnutzen kann ist, dass z.B. ein 5 Farben-Block mit 3 Bit funktioniert und er sich einer
8 Byte breiten Spanne aus dem Farbstring bedienen kann. D.h. seine 5 Farben müssen nicht alle in den ersten 5 Werten
zu finden sein sondern es genügt wenn sie innerhalb 8 Werten vorkommen. Und die Farben müssen nur vorkommen,
sie können beliebig umsortiert und im 4x4 Bitfeld angepasst werden.
So ähnlich habe ich das bei ZVID(1) realisiert. Verwendete Farben im String markiert, Reste gelöscht, wieder gesucht
usw. bis sich keine Reste mehr ergaben. Und das dauert, besonders wenn man an die hundert Frames mit je 1000
Blöcken hat.
ZVID(1) gefällt mir aber so irgendwie nicht mehr richtig, ich würde gerne ZVID2 machen speziell für Sprites, und die
Gesamtgröße einer Datei auf 65528 Byte beschränken, so dass man sich eine ZVID Datei, in der sich dann ein Sprite-Set
einer Figur befinden kann, mit einem Schlag in einen durch nur einen Pointer auf dem Heap reservierten Platz laden
kann. Die neuen Routinen sollen dann auch etwas weniger ausschweifend werden, und nochmal neu "from scratch"
geschrieben werden, inkl. blockweisem Clipping, was bei ZVID2 evtl. schon deshalb leichter wäre, weil ich keine
Block-Lauflängenkompression einplane.

Warum sich ZVID(2) gerade bei Sprites lohnt:
Die sind meistens ja in der Farbgebung von Frame zu Frame sehr ähnlich bis gleich. Wenn man letztlich vielleicht 20
Animationsphasen hat, fällt selbst ein etwas größerer Palettenstring kaum noch ins Gewicht.

video = lat. "ich sehe"
Bei ZVID hatte ich von Anfang an einfach nur an ein Format zur Darstellung
von Grafik gedacht, nicht wirklich auf Videos spezialisiert. Okay, oder nach
kurzer Zeit. Ich kann nicht leugnen dass der Thread hier "eigenes Videoformat" heisst...
DOSferatu hat geschrieben:Für Sprites bin ich mir nicht sicher, ob die gleiche Methode, die Du für Bilder benutzt, ebenfalls so viel Ersparnis bringt. In Sprites gibt es viel weniger "sich wiederholende" Teile und ob da die komplizierte Aufteilung in Einzelquadrate am Ende wirklich die Speicherersparnis (und komplizierteren Routinen) rechtfertigt, müßte man sehen - ich bin da etwas skeptisch.
Natürlich habe ich bei Sprites keine endlosen einfarbigen Flächen die vor allem beim Delta-Verfahren bei
Bildschirmfüllenden ZVID-Animationen entstanden sind. Aber dennoch müsste man bei durchschnittlichen
Sprites von einer Reduktion auf 2-3 Bit im Schnitt ausgehen können. Delta könnte man bei ZVID2 immer
noch anwenden, im Spiel dann aber nur in Bereichen außerhalb des "Action Screens". Bei ZVID2 habe ich
bisher keine Redundanten Blöcke eingeplant, der einfacheren Implementierung halber. Aber die Erfahrung
mit ZVID(1) hat gezeigt, dass diese auch nicht so oft auftauchen.

Spiegeln ist kein Problem, man teilt der Anzeigeroutine das einfach mit. Da es 4x4 Blöcke sind habe ich
vorab aber schon daran gedacht, eine Zeile direkt über ein 32 Bit Register in den Speicher zu hauen,
das müsste man beim Spiegeln dann anders machen, entweder erst verdrehen (ror ax,8; ror eax,16; ror ax,8),
oder doch Byte für Byte mit std.
Blöcke mit Transparenz müssten sowieso Byte für Byte gehandhabt werden.
DOSferatu hat geschrieben:Ich sage mal so: Wenn man - aus verschiedenen Gründen - sowieso nur ein Spiel mit 10 FPS machen möchte mit festem Hintergrund (ohne Scrollen) und vielleicht max. 8-16 Sprites gleichzeitig im Bild, bei denen noch nicht einmal alle verschieden sind, könnte man am Ende vielleicht sogar besser kommen mit total ungepackten Sprites.
Natürlich... Ich bin bei Kotzman II sogar komplett mit dem Datensegment hingekommen mit klotzigen ungepackten
Arrays. Aber auch nur auf Biegen und Brechen. Aber naja, es wäre ja langweilig nicht zu packen, aus meiner Sicht.
Selbst wenn ich am Ende nur 8 KB Grafikdaten habe find ich das dann irgendwie toll, besser als wenn der gleiche
Kram 30 KB belegen würde und Luft drin hätte. Auch schön das Gefühl, dass man alles von Strichmännchen bis hin
zu "fotorealistisch" kreiren kann und immer nur so viel Speicher wie nötig belegt wird. Mehrere gleiche Byte
hintereinander bzw. ein ganzes Byte wo auch zwei Bit reichen würden empfinde ich als Verschwendung, und
auch wenn es irgendwie sinnlos ist da genug Speicher da ist und es extra Performance kostet, ist es in meinem Rahmen
des Programmierens für mich ein Ansporn. Ich habe ja keinen Auftrag für eine Firma zu erledigen sondern kann ganz
alleine bestimmen wie meine Software funktioniert, und diese Spielerei mit der Datenkompression will ich mir nicht
nehmen lassen, es macht einfach Spaß.

Ich bin wohl irgendwie nicht so der "Performer", vielleicht auch wegen des Speichermangels in Qbasic ein
gebranntes Kind was das alles angeht, keine Ahnung, oder einfach nur ein "sich Dateiformate ausdenken"-Idiot.


DOSferatu hat geschrieben:Naja, wie ja bereits erwähnt, hängt es IMMER von den Ausgangsdaten ab, wie effizient man packen kann. Deine Vorgehensweise verspricht natürlich schöne Grafiken - d.h. das Level kann aussehen wie ein Bild, ohne die Limitierungen, die sich durch die Kachel-Variante ergeben. Allerdings sollten pro 4x4 oder 8x8 (oder 16x16) Bereich irgendwo noch Meta-Daten sein, damit die Figuren mit dem Level interagieren können und es nicht nur reine "Dekoration" ist.
Genau das hab ich ja vor. Neben den Grafikdaten die ich anzeige wird es wie oben beschrieben ein 8x8 Raster geben
mit Informationen ob man da draufgehen kann oder sonstwas. Und eigentlich strebe ich doch eher eine Kachel-Variante
an, aber das muss ich mal sehen. Letztlich würden mit ZVID größere Grafikteile schneller eingerechnet werden als wenn
man die Routine zig mal kachelweise aufruft.

Der magische Würfel 486er:
Ich hab immerhin meine Programmierkarriere auf einem 286er begonnen. Klar, Du auch auf dem C64. Aber auf dem 286er
war das PUT von Basic noch so langsam, selbst bei kleinen Sprites... Das waren noch die Zeiten in denen man
monolithisch programmiert hat in dem Sinne, dass sehr viele Spiele auf einem Rechner > 286 zu schnell liefen.
Und dann hatte ich einen 486er, da konnte man gar nicht so schnell gucken... Auch in Dos, die Auflistung der Dateien.
286er: Blubb, blubb, blubb... (Zeile für Zeile) 486er: KLATSCH!
Wird natürlich nicht alles nur am Prozessor allein gelegen haben sondern vielleicht auch an der Grafikkarte -
aber für mich war das eher so, naja ich wurde 4 Jahre durch den 286er "gedeckelt" und an die Grenzen
des 486ers bin ich selber durch eigene Programme gar nicht gestoßen, allemal mein WAV Renderer
für die DMFs lief noch sehr langsam auf dem 486, aber mit Spiele programmieren habe ich dann erst wieder
weitere drei Jahre eher weniger als mehr angefangen, als ich einen Pentium hatte, und den hab ich auch
nicht ausreizen können, dann kam nach vier Jahren ein schneller Windows-Rechner, nach 5 Jahren noch einer usw...
Ich habe mir für DOSbox aktuell die 20000 Cycles festgelegt und bis jetzt bin ich da selbst mit den extremsten
ZSMs noch nicht an die Grenzen gestoßen.

Ich habe wohl noch nicht richtig gelernt, wie man ein Spiel auf korrekte Weise "time't", bei Kotzman II
hab ich es so gemacht wie ich es jetzt etwas anders auch vor habe, nämlich indem ich in die Hauptschleife
ein SOUND 0, .1 oder ähnliches eingefügt habe. Oder war es doch eine Verzögerungsschleife? Jedenfalls
statisch und wie ich meine schlimmer als warten auf einen Interrupt, weil sich ja die Grafikrechenzeit
und die Warteschleife addiert haben, was besonders bei langsameren Rechnern und höherem Grafik-
aufkommen bemerkbar gemacht hat. Von daher wäre Interrupt-Timing für mich erstmal die nächst
bessere Erfahrung...
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Kleines Update bzgl. ZV2:

Ich wollte schon ohne Daten zu haben mit der ASM-Decodier-Routine anfangen,
um das Format nach den Routinen auszurichten und nicht umgekehrt, habe dann
aber beschlossen, ersteinmal ein Programm zu schreiben, welches klärt, ob das mit
den auf 256 beschränkten individuellen Meta-Paletten überhaupt hinkommen würde.
Grundsätzlich hat man ja für ein 320x200 Bild ersteinmal 80x50 4x4 Blöcke, also 4000
Farbinhalte. Man kann dann sehen, welche davon redundant sind und sich ineinander
integrieren lassen. Man kann aber nur diejenigen kombinieren, die jeweils die gleiche
Bitbreite und damit "Spannweite" haben. Also 1, 2, 4, 8 oder 16.
Das liegt daran, weil ich in den 256 möglichen Farbtabellen Bitbreite und Offset
in den Farbstring jeweils kombiniere und nicht unabhängig voneinander halte.

Kurz gesagt haben sich bei obigem Bild mit der Greifhand 1801 individuelle Paletten
ergeben, was ein wenig ernüchternd ist, da sich ja die Anzahl nur etwas mehr als
halbiert hat.
Bei ZVID(1) war das alles kein Problem, dort habe ich die Bitbreite UND den Palettenoffset
direkt im Header deklariert, aber diesmal soll der Header pro Block ja nur ein Byte groß sein.

Wie auch immer, man kann daraus schliessen dass sich ZVID2 nur für Sprites eignen
wird - oder nur für extrem spärlich mit unterschiedlichen Farben besetzte Vollbilder.

Bei Sprites dürften dann Größen bis 64x64 Pixel "safe" sein, da sie genau 256 Blöcke
belegen. Natürlich geht auch schmaler und dafür höher, hauptsache es bleibt bei
ca. 256 Blöcken.
Das sind natürlich nur durchschnittliche Grenzwerte, man kann an die Grenzen stoßen
wenn zu viele verschiedene Farben in den Sprites vorkommen oder die Sprites
in einem Set farblich zu stark variieren. Letzlich dürfte man aber bei durchschnittlich
gearteten Sprites bis zu einer Größe von 128 Blöcken (z.B. 32x64) auf der absolut sicheren Seite sein.

Erklärung des Begriffs "Set": ZVID2 soll, ähnlich wie ZVID(1), mehrere "Frames" enthalten,
die sich dann eine Metapalette und eine Zuordnungstabelle (in die die Header-Bytes weisen)
teilen. Dadurch verringert sich im Verhältnis der Speicher-Overhead für die Kompression.

Die Zuordnungstabelle besteht ja aus Words und ist maximal 512 Byte groß, was in jedem
Fall vertretbar sein sollte - vor allem ist sie nur bei entsprechend viel Grafikdaten so groß.
Ein ungelöstes Problem ist nach wie vor der Farbstring, vorab werde ich zu Testzwecken
einfach alle Paletten aneinanderreihen, was dann etwa ein drittel bis halb so viel Daten
ergeben wird, wie ein unkomprimiertes Sprite im Set. Den Farbstring zu optimieren ist eine
Sache für sich - wer da eine zündende Idee hat möge sich bitte melden, ich werde mich aber
auch nochmal in den ZVID(1) Code einlesen, wie ich das da halbwegs gut gelöst habe.

Vielleicht nenne ich das Format am Ende lieber ZSPR, da es ja wirklich nur für Sprites gedacht ist.

Eine solche Datei soll möglichst nur mit einem Pointer auf dem Heap adressiert werden.
ZVID(1) erforderte etliche Pointer und Variablen für eine Datei.
Die ASM-Decodier-Routine wollte ich möglichst komplett am Stück schreiben, ohne weitere Routinen
die ich per CALLs aufrufen würde, lieber alles mit Sprungtabellen.
Auf die der Routine übergebenen Parameter kann man auch ohne DS zugreifen, ich könnte
dieses Register also umbiegen, fragt sich nur ob ich nicht doch noch ein paar Speichervariablen brauche.
Das wird noch eine Herausforderung, vor allem mit dem Clipping, wahrscheinlich schreibe ich das erstmal alles in Pascal.

Decodiervorgang:
1. Header-Byte lesen
2. An entsprechender Position aus der Zuweisungstabelle die Bitbreite und den Palettenstring-Offset auslesen
3. Blockdaten entsprechend Bitbreite interpretieren und jeweilige Farbe an Position des Palettenstring-Offsets
+ jeweiligem Blockdatum lesen, Pixel in Zieldatenfeld schreiben

Tja, alles im Grunde nicht nötig, man müsste überhaupt erstmal genug Material pixeln um
unkomprimiert 300K an Sprites zu füllen - aber es macht Spaß, eine kleine Etüde...
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Ich habe heute mal provisorisch den ersten Decoder in Freepascal geschrieben und dabei
noch den Encoder debuggt.
Ich finde die Kompressionsrate erfreulich und das decodieren gestaltet sich so auch ersteinmal einfach.
Hier nochmal an einem Beispiel:
64x64.png
64x64.png (1.83 KiB) 12185 mal betrachtet
Als PNG 1874 Bytes.
Die ZV2 Datei ist 2637 Bytes groß.
Klingt so erstmal nach nichts besonderem, allerdings muss man bedenken, dass bei einem
Set von Grafiken, also mehrere Sprites in einer ZV2, die Farbtabelle und der Farbstring relativ
zu den Blockdaten schrumpfen.
Auseinanderdividiert sind die Blockdaten inkl. Headerdaten nur 1462 Bytes groß, angenommen man hat
dann 16 Stück dieser Sprites mit je 1500 Bytes -> 24000 Bytes, und wenn die restlichen Daten nur unwesentlich
anwachsen, sagen wir auf 2 KB, dann hat man, in diesem Fall von 16x16 Blöcken d.h. 64x64 Pixel,
16 * 64 * 64 Bytes = 65536 auf 26000 Bytes reduziert, also auf knapp 40%.
Tja, zugegeben, an diesem Beispiel zeigt sich eher dass sich hier eine simple Reduktion auf
4 Bit eher auszahlen würde. Allerdings wäre man auf 16 Farben eingeschränkt - dieses Beispiel
hier hat 60 Farben.
Ich müsste noch ein Beispiel anbringen von wirklich typischen Sprites die man als 4 Bit speichern
*könnte*, wie sich ZV2 da schlägt.

Ok... geschrieben, getan:
guyb.png
guyb.png (445 Bytes) 12185 mal betrachtet
(das ist der VGA-Guybrush mit konkret 12 Farben, nicht EGA wo vielleicht nur 8 verwendet werden)

PNG (mit nur 16 Farben eingestellt): 445 Bytes
ZV2 (komplett mit Tabelle und Farbstring): 344 Bytes

Diesmal hätte also klar ZV2 Vorteile, selbst schon bei nur einem Sprite.
Die reinen Blockdaten sind 240 Bytes groß, das Sprite ist 6x11 Blöcke also 66 * 16 Bytes = 1056 Bytes groß,
und rein so gesehen ergibt sich eine Kompression auf 23%.

Zum Thema GIF: Habe es auch mal als GIF gespeichert, 16 Farben, ergibt 343 Bytes, also nur um 1 Byte
kleiner, und wie gesagt sind als Sprite-Set noch deutlich kleinere Durchschnittswerte (siehe 240 Bytes
reine Blockdaten) zu erwarten.
Die Farbstrings können noch geringfügig optimiert werden, sagen wir um ca. 10% verkleinert, allzuviel
kann man da nicht herumschieben, sonst bräuchte man wieder mehr Tabelleneinträge (diese enthalten
Offsets in den Farbstring zusammen mit der Bitbreite).
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von DOSferatu »

zatzen hat geschrieben:Dein Speichermanager wäre aber dennoch einmal interessant, allerdings habe ich bisher noch keine Not danach, was auch daran liegt dass ich bisher kein großartig komplexes Programm geschrieben habe. Aber auch den Speichermanager würde ich verstehen wollen und müssen, damit ich damit richtig umgehen kann.
Naja, so kompliziert ist der nicht. Anfangs hatte ich ihn bytegenau gemacht - was man auch machen kann. Dann habe ich aber zusätzlich einen Typ eingeführt, der nur aus einem Word besteht (um an ganzen Segmentgrenzen zu beginnen) und habe den so umgebaut, daß ALLE Pointer an Segmentgrenzen landen (also immer Offset=0). Das verschwendet manchmal ein paar Bytes, aber macht das Ganze einfacher für den "Segment-Only"-Typ. Trotzdem wird die Größe in Bytes zurückgegeben, wenn man danach fragt. Vor jedem reservierten Bereich ist ein Header, der die Adresse der Variable (Pointer, Word, o.ä.) enthält und die Größe und Typ usw. Das ist wichtig, weil, wenn sich die Speichergrößen ändern, die Bereiche verschoben werden und dann die Pointer, die auf die Bereiche zeigen, mit geändert werden müssen. Das ist eigentlich das ganze "Geheimnis" dahinter. Wie man so etwas dann in Code umsetzt, ist dann kein Problem mehr.
zatzen hat geschrieben:Sprachausgabe:
Es ist absolut klar und schlüssig, dass soetwas für ein Spiel generell eine ziemliche Nebensache ist. Etwas anders kann das aber durchaus bei Adventures aussehen. Da kann die Sprachausgabe zu einem reelleren Erlebnis beitragen. Zudem hatte ich da mit Freunden schon einen riesen Spaß, diese Sprachausgabe zu manipulieren und die Charaktere mitunter völligen Blödsinn oder übertrieben albern reden zu lassen - sicher nicht jedermanns Humor, aber immerhin mancher eins.
Mag ja alles richtig sein - hat nur nichts mit meiner Aussage zu tun: Das Spiel würde auch ohne Sprachausgabe funktionieren. (Es wäre nicht so lustig, etc. pp. ...) Aber es gibt eben Sachen, ohne die das Spiel NICHT funktionieren würde und bevor diese nicht funktionieren/eingebaut sind, kann man sich nicht über die "Zusatzfeatures" hermachen. Wenn die zwar funktionieren, aber für die unbedingt zur Funktion notwendigen Sachen kein Speicher/Rechenzeit/usw. mehr da ist, bekommt man das Programm/Spiel nicht fertig. Deshalb ist es so wichtig, erst sicherzustellen, daß das Grundgerüst, aus dem das Spiel besteht, lauffähig zu bekommen, bevor man überlegt, wieviel Speicher/Rechenzeit usw. man für die "Süßigkeiten" einsetzen will.
zatzen hat geschrieben:Wie gesagt war mein letztes ernsthaftes und vollendetes Spiel Kotzman II, und das ist jetzt 24 Jahre her. Programmiert habe ich in der Zwischenzeit nur Versuche, kleine Demos oder auf tiefgehenderer Ebene Tools.
So geht's mir ja auch. Mein letztes großes Spielprojekt (von den kleinen Spielchen mal abgesehen, die ich nicht ernstnehme) war dieses Xpyderz, und das ist nicht einmal fertig geworden.
zatzen hat geschrieben:Eine gut Überlegte Herangehensweise an ein Spiel wäre für mich nach wie vor eher neu. Aber ich würde natürlich ein Spiel, in das ich Arbeit und Mühe stecke, relativ ernst aufziehen und eben nicht mit der Tapete anfangen um die ich die Wände baue,
Ja, das meine ich: Wenn man sich so viel Mühe macht, wäre es schade, wenn man da ungeplant herummurkst und dann hat man ein halbfertiges Chaos da, in das man vielleicht 3 Jahre Mühe und Arbeit reingesteckt hat und das dann keiner spielen kann.

Weil ich da schon so Erfahrungen gemacht habe, bin ich ja jetzt so "vorsichtig" geworden und plane vorher (leider sehr viel), um so etwas zu vermeiden. Wenn man schon, wenn man 10% des Spiels fertig hat, damit anfangen muß, sein selbstprogrammiertes Zeug (Units, Subroutinen) zu "workarounden", weil sie nicht so funktionieren, wie man sich's gedacht hat oder brauchen würde, dann ufert das immer mehr aus und man verzettelt sich ständig nur in Nebensächlichkeiten, das Zeug zum Funktionieren "hinzupatchen" und kommt vor lauter Gemurks gar nicht mehr dazu, die Sachen zu machen, die man eigentlich im Spiel haben wollte.
zatzen hat geschrieben:Furzman war schon keine schlechte Idee eigentlich, aber generell bemerke ich bei mir eine gewisse Prokrastination, weil ich mich derzeit noch so sehr auf Dinge wie Grafikkompression konzentriere, die für ein kleines Spiel eigentlich gar nicht nötig sind. Aber sowas macht mir eben Spaß.
Ja, ich prokrastiniere hier ja auch schon seit Monaten/Jahren. Immer wieder gibt es irgendwas, das mir noch nicht so gefällt. Erst letztens habe ich noch diesen einen Befehl in GameSys2 eingebaut usw. Immer ist irgendwas. Am liebsten würde ich meine ganzen Sprite- und Level-Routinen (die sind 15 Jahre alt!) schon wieder neu schreiben, bin in Assembler ja auch etwas besser geworden... Aber irgendwann will ich den ganzen gemachten Kram auch mal irgendwo einsetzen und deshalb zwinge ich mich dazu, diese Routinen nicht anzufassen. Kann man später immer noch machen, NACHDEM man mal wieder ein Spiel gemacht hat - ich baue das Zeug ja immer modularer auf, so daß man auch später noch Routinen gegen bessere austauschen könnte.
zatzen hat geschrieben:Metadaten für den flexiblen Zugriff auf gepackte Sprites:
Darin habe ich bisher kein Problem gesehen, da ich auch bei Kotzman II einfach nur die Grafik in den Bildschirmspeicher "geklatscht" habe, ohne dass weitere Zugriffe auf die Quelle nötig waren.
Also denke ich, dass die Quelle auch beliebig gepackt sein kann.
Naja - für Xpyderz (Draufsicht) brauchte ich ja z.B. die Drehbarkeit der Sprites und das aus "block-gepackten" Daten zu extrahieren/darzustellen... SO irre bin ich dann doch nicht. Ich komme später noch darauf zurück.
zatzen hat geschrieben:Schätzung von Speicher/Leistung:
Vorab: Ich ziele nicht darauf ab, das bestmögliche an Leistung aus dem System herauszuholen. Dazu reizt mich derzeit die Idee der Datenkompression zu sehr. [...]
Ich sehe die Vorteile der Grafikkompression für mich als bedeutsamer an, als den Verlust der Performance. Beispiel: Ich würde eher kleine/wenige Sprites auf dem Screen verwenden, aber mit vielen Animationphasen. Denkbar jedenfalls.
Ja, die muß auch erstmal jemand pixeln. Sieht schön aus, so viele Animationsphasen - wenn man's kann und die Zeit/Muße hat.
Andererseits - mal kurz überlegen: Viele Animationsphasen sind ja dazu da, flüssige Bewegungen darzustellen. Wenn man viele Animationsphasen hat, aber wegen Performanceproblemen gar nicht die Framerate erzeugen kann, um diese ruckelfrei darzustellen, sondern deshalb Phasen auslassen muß, wird das Ganze wie eine Schlange, die sich selbst in den Schwanz beißt...
zatzen hat geschrieben:Ob ich überhaupt künstlerisch genug bin so viel zu pixeln sei mal dahingestellt.
Ich bin es schonmal nicht. Ich bin froh, wenn ich überhaupt mal ansehbares Zeug hinkriege. Die "Grafik" von Xpyderz und auch meinen sonstigen Spielen ist ja eher primitiv.
zatzen hat geschrieben:Du hast meine ZSM-Moonwalker Demo gesehen, da habe ich einiges an Sprites aufgefahren und es funktionierte so in Dosbox mit 20000 Cycles, während XPYDERZ damit sehr zäh läuft.
Stimmt. Dazu siehe weiter unten.
zatzen hat geschrieben:Vielleicht ist diese Demo einfach noch zu simpel was die Berechnung abgesehen von der Grafikdarstellung angeht. 100% sicher kann ich Dir das mit XPYDERZ als Benchmark Feedback gerade nicht sagen, ich habe gerade nur Zugriff auf meinen Laptop, der die 20000 Cycles nicht stabil emuliert. Wir können später noch einmal darauf zurückkommen.
EDIT: Wie dumm von mir. Du hast bestimmt selber einen fähigen Rechner, der 20000 Dosbox
Cycles hinbekommt.
Hab ich - und mit 20000 Cycles ist es... zwar noch spielbar, aber eben schon, wie Du sagtest, ziemlich zäh.
zatzen hat geschrieben:Mir fällt da nur gerade ein: Du hast mir mal XLAT empfohlen. Ich wollte das in der ZSM Engine verwenden, habe dann aber mal im ASM Guide nachgelesen und es hatte deutlich mehr Takte als ein normaler Zugriff auf eine Tabelle. Ich weiss nicht wieviel Du XLAT benutzt,
aber vielleicht verlangsamt das manches von Dir.
Naja, der Einsatz/Sinn eines Befehls besteht nicht nur in den Zyklen, aus denen er selbst besteht, sondern auch daraus, was man "drumherum" anstellen muß, um sich auf die Benutzung des Befehls vorzubereiten.
Beispiel: Die STOSx, MOVSx-Befehle brauchen immer mehrere (festgelegte) Register, zusammen mit REP noch mehr. D.h. ihr Nutzen ergibt sich erst ab einer bestimmten Datenmenge und wenn man diese auch ohne "Zwischenprüfungen" ausnutzen kann.
Der XLAT-Befehl braucht ein paar Zyklen, das ist richtig. Dafür ist es der einzige Befehl, bei dem man mit einem BYTE-Register indirekt indizieren kann (und es auf sich selbst zurückspeichern) und zwar, ohne das Basisregister dafür zu verändern.
XLAT ersetzt also quasi eine Befehlsfolge wie (beispielsweise) diese:

Code: Alles auswählen

push BX
add BL,AL
adc BH,0
mov AL,[BX]
pop BX
Und wenn ich EINEN Befehl habe, der das alles macht, spare ich vielleicht DOCH Zyklen.
Natürlich sollte man nicht alles "um einen XLAT herum" programmieren. "Empfohlen" habe ich XLAT nicht, lediglich erwähnt, daß ich ihn in ISM benutze. Natürlich benutze ich ihn nur da, wo es sinnvoll ist.

Daß Xpyderz so langsam ist, liegt NICHT an XLAT - in Xpyderz wird kein XLAT benutzt. Es gibt mehrere Gründe für die Langsamkeit von Xpyderz:

1. Jedes Sprite wird mit der gleichen Routine dargestellt, die Fähigkeiten wie indirekte Paletten, 4bit-Pixel, Drehbarkeit und Skalierbarkeit enthält.

2. Weil Xpyderz ursprünglich mal Multiplayer werden sollte, sind die Levels größer als der Bildausschnitt - UND ALLE "Figuren" (ja auch die kleinen Schrapnelle von Explosionen sind Figuren und alle Schüsse usw.) werden gesteuert von in Pascal geschriebenen Routinen (da gabs noch kein GameSys2, noch nichtmal GameSys1). Diese Steuerung enthält auch Kollisionsabfragen für Figur-Figur-Kollisionen und Figur-Level-Kollisionen. Da gibt es zwar auch schon meine Kollisionsmatrix (da auch noch in Pascal), aber diese reduziert nur die Kollisionsabfragen auf ein erträgliches Maß (ohne diese wären es Fakultät(Anzahl_Figuren) Abfragen (also Anzahl! ). Zum Vergleich: Ein Taschenrechner, der auf 100 Stellen genau rechnet, steigt bei 70! aus, 69! ist die letzte, die er noch anzeigt, also eine 100-stellige Zahl. So viele (theoretische) Abfragen wären es bei 69 Figuren OHNE meine Kollisionsmatrix.

3. Es sind aber nicht 69 Figuren, sondern schon nach kurzer Zeit (1-2 Minuten) sind schon mehrere hundert freibewegliche Objekte unterwegs mit jeweils eigener Steuerung, die auf andere Objekte und das Level reagieren müssen.

Dafür, daß die Steuerung (inkl. Kollisionen) komplett in Pascal ist und die Sprite-Routinen etwas mehr können als bloßes "Hinkopieren", finde ich die erreichte Performance selbst heute noch einigermaßen OK.

Nochmal zu XLAT: In ISM verwende ich ja BX dann als Tabellenstart (XLAT geht ja nur mit BX/AL). Eigentlich sind es aber mehrere Tabellen. Ich brauche dann nur BH erhöhen/vermindern, um auf weitere 256er-Tabellen zu referenzieren, ohne anderen Code zu ändern usw. Also: Nicht bei einem einzelnen Befehl wie XLAT nur auf die Taktzyklen gucken, die dieser eine Befehl braucht, ohne zu sehen, was ringsherum passiert.
zatzen hat geschrieben:Levelhintergrunddaten: Da plane ich derzeit nach wie vor ein 8x8 Pixel Raster, 320x200 wären dann 1000 Byte, also könnte ich bis zu 65 Bildschirme für ein Level anlegen.
Wie ich auch im anderen Thread erwähnte: Um den Speicher für's Level mache ich mir weniger Sorgen. 65 Bildschirme für 'n Level klingt gut - aber so ein großes Level muß man auch erstmal bauen! Meist wird es eher weniger sein.
zatzen hat geschrieben:Ich glaube auch, man sollte nicht das Spektakel machen mit merken, was von den Sprites überschrieben wurde und dann nur das ersetzen. Naja müsste man sehen, ich könnte in meinem 8x8 Raster ein Bit als Flag setzen, Notfalls auch Words benutzen statt Bytes, um mehr Flags und Werte zu haben. Aber spontan habe ich da bis zuletzt eher "klassisch" gedacht - einfach alles neu zeichnen, notfalls zugunsten der Performance und des Speichers den aktiven Screen verkleinern.
Ja, es diente ja nur der Veranschaulichung. Die immer wieder Zeichnen/Löschen-Methode ist auch nicht so meins. Sobald sich Sprites überdecken sollen, fangen die Probleme an - und wenn sie sich NICHT überdecken dürfen, weil's die Routine nicht hergibt, muß man die ganze Zeit bei der Steuerung darauf achten, daß das nicht passiert - was nerven würde. Außerdem bräuchte man für jede Figur einen spritegroßen (nicht packbaren) Puffer - weil man ja vorher nicht wissen kann, was da grad im Hintergrund wäre... Und man müßte beim Setzen/Löschen in umgekehrter Reihenfolge vorgehen, sobald es Überdeckung gibt - was ab einer gewissen Anzahl zu flackernden Sprites führen würde... Alles Dinge, denen ich gern aus dem Weg gehe.
zatzen hat geschrieben:Ich merke gerade, Speicher verschwenden, und dann Performance kompromittieren um komprimierte Daten zu verwenden... Du hast schon recht, aber naja, ich mach das nur zum Spaß. Ich muss selber meinen Holzweg gehen...
Naja, Machbarkeitsstudien können schon nützlich sein. Wenn es funktioniert, ist es egal, wie es gemacht ist oder ob jemand die Methode gut findet oder was auch immer. Das ist sowieso mein Credo: Bewiesen ist, was funktioniert. Wenn irgendwer eine theoretisch tolle Idee hat, die in der Umsetzung so aber nicht machbar ist oder nicht so funktioniert wie gedacht, kann die Idee noch so toll sein - sie ist in der Praxis unbrauchbar. Wenn dann jemand mit einer viel simpleren Idee kommt, die am Ende aber funktioniert, dann stehts 1:0 für diese Idee. So seh ich das.
zatzen hat geschrieben:Beim Sound stimme ich Dir mehr oder weniger zu. Mit 44100 kann es natürlich zum Worst Case kommen, und das sollten vielleicht dann nur Leute mit nem Pentium so
nutzen. Allerdings habe ich in ZSMPLAY bei der Rate und ner Framerate von ca. 20 fps überhaupt keine Probleme, und die Dosbox läuft auf 20000 Cycles, was einem eher langsamen 486 entspricht.
Wie gesagt: War nur ein Gedankenexperiment, das verdeutlichen sollte, daß Dinge, die im Einzelnen prima funktionieren, das nicht unbedingt immer noch tun, wenn sie alle "gleichzeitig" laufen sollen. Und ja, deshalb wäre es vielleicht keine schlechte Idee, gewisse Parameter (wie auch z.B. die Soundframerate) einstellbar zu machen.
zatzen hat geschrieben:Die Sache ist, unabhängig von der Grafikframerate, bleibt die Sound-Rate pro Sekunde immer gleich.
Naja, mit 44100 Hz müssen aber doppelt so viele Daten pro Sekunde berechnet werden als mit 22050 Hz - das sollte doch klar sein, oder? Also auch doppelt so viele pro Frame.
zatzen hat geschrieben:Wenn man Dein Beispiel auf 20 fps umrechnet, dann sind es in einem Frame nur noch 17640 Byte Sounddaten, gegenüber (ja auch Worst Case, wobei kopieren in den Bildpuffer nicht mal mit drin ist) 64000 Byte Bilddaten.
Es geht ja nicht um "pro Frame" - auch wenn das komisch klingt.
Man muß hier von der allgemeinen Systemleistung ausgehen. Und das bedeutet einfach: Wieviele Grafikdaten und Sounddaten kann man gleichzeitig berechnen, damit diese "rechtzeitig" zur Verfügung stehen - und das im Durchschnitt über einen längeren Zeitraum. Außerdem machen sehr kurze Soundpuffer das Ganze langsamer - denn wie man ja weiß, ist bei jedem Aufruf so einer Generator-Routine noch ein gewisser Overhead an "Vorbereitungscode" dabei vor der eigentlichen Generatorschleife, so daß häufigere Aufrufe für kürzere Puffer den Anteil Overhead erhöhen. (Zu) kurze Puffer haben auch den kleinen Nachteil, daß man mehr Aufwand hat, darauf zu achten, daß der Puffer nicht "leerläuft", bevor man dazu kommt, den neuen zu generieren. Wenn Grafik-/Steuerroutinen zu lange brauchen, fängt es an zu stottern... PC-Spiele-Programmierung ist schon ein Kreuz...
zatzen hat geschrieben:Nochmal EDIT: Kann man wiederum schlecht abschätzen, da bei ZSM die Sample-Datenrate an sich auch unabhängig von der Mix-Samplerate ist. Wie in einem vergangenen Thread beschrieben, ergibt eine Verdopplung der Mix-Samplerate nicht eine Verdopplung der Rechenbeansprung.
Ja, und was viel entscheidender ist: Eine Halbierung der Mix-Samplerate ergibt eben NICHT eine Halbierung der Rechenzeit! Das ist es, was ich im vorigen Absatz ausdrücken wollte.
zatzen hat geschrieben:Spiel mit fester, durch Soundkarten-IRQ getriggerter Framerate:
Ja, das ist natürlich irgendwie blöd. Aber es übersteigt wie oft schon gesagt derzeit meine Programmierfähigkeiten, soetwas dynamisch aufzuziehen. Ich kann es nur so handhaben, dass Headroom
Max? ... <scnr>
zatzen hat geschrieben:verschwendet wird, indem ich leistungsmäßig kleine Brötchen backe, so dass das bescheidene Spiel was ich dann mache eben einen 486er braucht,[...]
Ja, solange es überhaupt ein spielbares Spiel wird, wäre es ja schon mal gut. Auf 486er laufen ist immer noch besser als gar kein Spiel.
Und - mein Zeug braucht wahrscheinlich auch 486er. Hab das ja auch schon erwähnt: Auf 386er performt mein Zeug auch eher bescheiden.
zatzen hat geschrieben:Größere Schritte der Figuren: Ich würde intern feinere Schritte rechnen (ohne Sprites dabei anzuzeigen) und dann eben nur jeden fünften Schritt oder so anzeigen. Ist mein Gedanke, hab ich nie praktisch probiert. Dann hätte ich bei 20 angezeigten fps intern 100 fps.
Dann müsste ich die Steuerung entweder intern auch so schnell tickern oder sie bleibt so ungenau, bei den internen feineren Schritten würde es mir um Kollisionen gehen, vor allem gegenüber Level-Blöcken.
Der Gedanke von mir war aber eigentlich auch, nicht mit 100 Hz zu tickern, sondern einfach zwischen jedem Bild 5 (Objektpositions-)Berechnungen zu machen, damit sich die Elemente effektiv nur in 1 Pixel Schritten bewegen. Letztlich werden wohl auch 3 reichen.
Es scheint, daß Du langsam zu begreifen anfängst, wie das bei mir (und mit GameSys2) funktioniert. Dann kann ich ja vielleicht doch noch mal versuchen, es zu erklären:

Es gibt eine - ungetimete! - Spielschleife, die die ganze Zeit (vereinfacht) das macht:
* Schleifenstart
* Steuerung berechnen, X-mal (und X Nullsetzen)
* Grafik berechnen+Anzeigen (ausgehend von der "Situation" in den Steuerdaten)
* Sound berechnen (abgespielt wird im IRQ/DMA)
* [eventuell auf Vertical Retrace warten]
* Zurück zum Schleifenstart

Wo ist das Timing?
In der Schleife: nirgends!
Denn: Nebenher läuft ein Timer-Interrupt, der macht folgendes in jedem Tick:
* X um 1 erhöhen.
* Steuerdaten des Spielers abfragen und in einen Ringpuffer werfen

Jedesmal also, wenn die Spielschleife an die Stelle kommt, wo die Steuerung berechnet wird, fragt sie X ab und berechnet dann X-mal (idealerweise ist X<=1) die Bewegung der Figuren. Aber NICHT für jede Figur gleich X "Schritte": Sondern für jede Figur EINEN Schritt. Und das ganze für alle Figuren. Und dann wiederholen. Solange, bis das alles X-mal passiert ist.

Dann hat sich eine "End-Situation" in den Figurendaten (Koordinaten usw) ergeben und die Grafik wird daraus berechnet. Die "Zwischenschritte" davor werden für die Grafik ignoriert - so wird damit umgegangen, wenn keine volle Framerate erreicht werden kann, weil der Rest in der Schleife (Grafikberechnung usw) zu lange braucht.

Dann wird z.B. geprüft, ob neue Sounddaten gebraucht werden, falls ja, wird der Generator mit Füllen eines neuen Puffers beauftragt.

Das Warten auf Retrace kann man abschaltbar machen - das kann eingeschaltet werden, falls X<=1 wird, d.h. volle Framerate erreicht wird - dann kann man eben flüssig scrollen/bewegen und den Bildrefresh des Monitors drauf abstimmen. Wenn man dafür sowieso zu langsam ist, würde das Warten auf den nächsten Retrace nur weiter verlangsamen, daher abschaltbar. (Xpyderz hat da Möglichkeiten AN/AUS/AUTO. Bei AUTO prüft das Spiel live/dynamisch die erreichte Leistung und schaltet automatisch V-Retrace-Scan an/aus.)

Daß auch die Abfrage der Spielereingabe im Timer-Tick erfolgt hat den Zweck, daß immer genauso viele Steuer-Daten des Spielers zur Verfügung stehen, wie gebraucht werden UND daß der Spieler unabhängig von der Framerate (die je nach Spielsituation variabel ist) trotzdem immer gleichschnell steuern kann und ALLE seine Steuerungen sich auch auf das Spiel auswirken (also keine Tastendrücke usw. "ausgelassen"/"nicht erkannt" werden, falls das Spiel mal langsamer macht).

(Die Steuerdaten werden nicht wirklich im Timer-Tick GENERIERT, sondern nur eingespeichert. Vor-Generiert werden sie im Keyboard-Interrupt: Da wird immer, wenn eine Taste gedrückt ist, das Feld (Bit, Byte) der Taste auf 1 gesetzt und wenn eine Taste losgelassen wird, wird wieder 0-gesetzt. Im Timer-Tick werden nur die gerade gedrückten Tasten gegen die "gesetzten" 8, 16, 32.... Tastennummern getestet und die Bits in einer Bitmatrix dazu gesetzt.)

Das Ganze funktioniert deshalb, weil das menschliche Gehirn Einzelbilder mit Objekten an unterschiedlichen, aufeinanderfolgenden Positionen eigenständig zu einer Bewegung umrechnet - auch wenn diese mal weniger Bilder hat (sonst würde z.B. Fernsehen und Film nicht funktionieren). D.h. für den Spieler sieht auch eine ruckelige Bewegung noch wie eine Bewegung aus und sein Steuerverhalten bezieht sich auf die ganze Bewegung - interessanterweise auch auf die Szenen, die bei geringer Performance gar nicht grafisch dargestellt werden (die interpoliert das Gehirn). Deshalb meine Wahl der Steuerungs-Abfrage im Interrupt. D.h. ABGEFRAGT wird die Steuerung im Ticker, aber BENUTZT außerhalb des Tickers, in der Steuersubroutine, die das dann aus dem Ringpuffer holt.

Natürlich ist es klar, daß es für jede Framerate eine "nicht mehr spielbare" Untergrenze gibt, ab wo etwas nicht mehr als Bewegung wahrgenommen wird und ab wo zu viele fehlende Zwischenbilder die Steuerung des Spiels unmöglich machen. Außerdem kann so ein Ringpuffer auch mal "überlaufen", wenn nichts mehr abgeholt wird. Wenn der Ringpuffer für 1 Sekunde Daten reicht, ist das mehr als ausreichend. Warum? Naja, ich denke, mit 1 FPS wird keiner mehr spielen... Wenn ein Steuer-Bitfeld 32bit wäre, wären es bei einem 50-Schritte-Pro-Sekunde-Spiel 201 Bytes, die der Ringpuffer haben müßte. (Es werden aber wohl eher 8 oder 16bit sein.)

Das mit den Bits hatte ich schonmal erklärt: Es ist quasi wie bei den Pins eines alten Digitalpads/Digitaljoysticks gedacht: Jede für das Spiel relevante Taste aktiviert eins der Bits - die anderen Tasten machen gar nichts mit dem Steuer-Bitfeld. Auf diese Art hat man zwei Vorteile: 1.) Durch diese Indirektheit kann man dem Spieler die Option geben, selbst seine Tastenbelegung zu wählen. 2.) Das Spiel braucht nicht "wissen", welche Tasten oder Steuergeräte der Spieler nutzt, man kann das so programmieren, daß man sich immer darauf verlassen kann, daß das jeweilige Bit im Bitfeld genau das macht, was seiner Bitposition entspricht, z.B. Bits 0 bis 5: Hoch/Runter/Links/Rechts/Sprung/Feuer.

Mein "Tastenfeld" reicht für 255 Tasten - so viele gibt es in Wirklichkeit nicht. In die freiwerdenden Stellen habe ich quasi "Pseudo-Tasten" eingefügt, die abhängig von einem/zwei Joysticks am Joystickport ($201) digital umgerechnet und gesetzt/gelöscht werden. So kann man auf einfache Art auch einen Joystick benutzen und die gleichen Steuerroutinen wie für Tastatureingaben benutzen. Entsprechende Mausabfrage folgt vielleicht auch noch.
zatzen hat geschrieben:Okay, nun zu "ZVID2", ich stelle mir das bislang so vor:[...]
Palettenstring.[...]
Meine Idee dazu: Die Blocks scannen und als erstes die Blocks mit den meisten unterschiedlichen Farben nehmen. Grund: Die braucht man sowieso alle "auf einem Fleck", damit es funktioniert. Und die mit den wenigeren Farben, die aber teilweise noch auf die anderen passen, kann man dann dazwischen einsortieren.
zatzen hat geschrieben:Warum sich ZVID(2) gerade bei Sprites lohnt:
Die sind meistens ja in der Farbgebung von Frame zu Frame sehr ähnlich bis gleich. Wenn man letztlich vielleicht 20 Animationsphasen hat, fällt selbst ein etwas größerer Palettenstring kaum noch ins Gewicht.
Naja, warum ICH meine Sprites UND meine Paletten NICHT (bzw. Sprites höchstens 8zu4bit) packe, liegt daran, daß ich mir meine Features erhalten will:
Bei den Sprites: Drehbarkeit, Spiegelbarkeit, Skalierbarkeit, Ditherbarkeit
Bei den Paletten: Ich kann einem Spriteimage mehrere Paletten geben, d.h. mit dem gleichen Image verschiedene (verschiedenfarbige) Dinge darstellen. So wird es z.B. mit den kleinen Spinnen bei Xpyderz gemacht. Die haben je 4 Bewegungsphasen, es gibt aber 9 verschiedene kleine Spinnen - trotzdem brauche ich nicht 36, sondern nur 8 Images. Die schwarze ist "besonders", weil leicht andere Zeichnung mit Kreuz usw. (4 Images). Die "bunten" haben nur verschiedene Paletten, aber das gleiche Image (also nur 4 Images insgesamt für die anderen 8 Spinnen.)

Ich habe ein (selbstgebautes) Programm hier, das auch für Xpyderz genutzt wird und die Images selbst nach Größe in die "Imagematrix" einpaßt (256 Pixel breit, erwähnte ich mal), wenn nicht paßt oder quer weniger Platz als längs braucht, dann quer. Die erkennt auch gleiche Images, die nur andere Farben haben (nimmt dann den gleichen Offset, speichert sie also nur 1x ab) und erkennt auch für unterschiedliche Images, ob Paletten wiederverwendet werden können. Für meine Zwecke finde ich das mehr als ausreichend.
zatzen hat geschrieben:Spiegeln ist kein Problem, man teilt der Anzeigeroutine das einfach mit. Da es 4x4 Blöcke sind habe ich vorab aber schon daran gedacht, eine Zeile direkt über ein 32 Bit Register in den Speicher zu hauen, das müsste man beim Spiegeln dann anders machen, entweder erst verdrehen (ror ax,8; ror eax,16; ror ax,8), oder doch Byte für Byte mit std.
Blöcke mit Transparenz müssten sowieso Byte für Byte gehandhabt werden.
Naja, ich arbeite sowieso Byte für Byte - schon weil allgemein immer von "gedreht" ausgegangen wird (auch wenn's 0° sind).

Und - so traurig das jetzt vielleicht klingt - auch Du wirst wohl Byte für Byte arbeiten müssen. Wieso? Weil Du das DWord (EAX) ja nicht irgendwo auslesen und reinballern kannst, sondern weil die Daten (Bits 4,3,2,1...) im 4x4-Block ja nur Referenzen auf Farbnummern im Farbpalettenstring sind. Wenn Du dann die einzelnen Bytes von EAX (auch die "oberen beiden", an die man so schlecht "einzeln drankommt" mit den Farben beladen willst, nur um am Ende EVENTUELL (falls kein "Loch" drin ist) ein STOSD machen zu können - da bezweifle ich ernsthaft, ob das wirklich noch einen Geschwindigkeitsvorteil bringen wird.
zatzen hat geschrieben:Selbst wenn ich am Ende nur 8 KB Grafikdaten habe find ich das dann irgendwie toll, besser als wenn der gleiche Kram 30 KB belegen würde und Luft drin hätte. Auch schön das Gefühl, dass man alles von Strichmännchen bis hin zu "fotorealistisch" kreiren kann und immer nur so viel Speicher wie nötig belegt wird.
Ja, stimmt natürlich. Daher hatte ich ja die 4-Bit-Idee.
Aber, wie ich ja erwähnte, versuche ich da immer den Spagat zu schaffen zwischen Performance und Speicher. Am meisten Speicher sparen könnte man, wenn man jede Grafik aus einer beliebig komplexen Formel berechnen läßt. Für die Performance wäre das dann gleichzeitig der Worst Case. Umgekehrt könnte man am meisten performen, wenn man jedes Sprite ungepackt in ein 256x256-Feld klatscht ohne den überstehenden Rest zu berücksichtigen. Nur wäre dann der Speicher bald voll. Und meiner Meinung nach ist ein Mittelweg da am besten. Man schafft es nicht, soviel Speicher freizumachen (bzw. es macht keinen Sinn für ein simples DOS-2D-Game) und andererseits kommt man auch leicht an die Grenzen der Performance, wenn es interaktiv werden soll. Aber, wo man welche Grenze zieht, muß natürlich jeder selbst wissen.

Wie ich ja schon erwähnte, bin ich kein Freund davon im "laufenden Prozeß" vom RealMode aus auf XMS zuzugreifen, weil 1.) die Performance-Einschränkungen da unvorhersehbar sind (zu sehr von der Gerätekonfiguration abhängig) und 2.) man nicht weiß, ob und wieviel XMS überhaupt eingebaut ist. Deshalb wird das für mich immer nur für Editoren oder als Zusatzfeature gelten, aber nie für reguläre Funktionen in einem Spiel.
zatzen hat geschrieben:Mehrere gleiche Byte hintereinander bzw. ein ganzes Byte wo auch zwei Bit reichen würden empfinde ich als Verschwendung, und auch wenn es irgendwie sinnlos ist da genug Speicher da ist und es extra Performance kostet, ist es in meinem Rahmen des Programmierens für mich ein Ansporn. Ich habe ja keinen Auftrag für eine Firma zu erledigen sondern kann ganz
alleine bestimmen wie meine Software funktioniert, und diese Spielerei mit der Datenkompression will ich mir nicht nehmen lassen, es macht einfach Spaß.
Ja, ich kenne das. Habe früher auch viele Formate erfunden und manche davon waren auch recht komplex. (In meinem Fall ging es z.B. um serielle Übertragungsprotokolle.) Irgendwann ist mir dann aufgefallen, daß ich quasi gar nichts mehr gespart habe - weil die Routinen, die die Daten am Ende gepackt und verpackt haben, dermaßen viel eigenen Speicher brauchten und durch ihre Komplexität immer anfälliger für unbehebbare Fehler wurden, daß ich dann eingesehen habe, daß die ganze Mühe vom Ergebnis her eher "nach hinten losgegangen" ist und ich damit kaum etwas erreicht habe. Irgendwann war alles nur noch "um diese Subfunktion herum" angeordnet und alles hatte sich der Bereitstellung dieser Subfunktion unterzuordnen - obwohl es eben nur der Multiplayer-Datenlink war. Das hat mir dann nicht mehr gefallen.

Inzwischen ist mir auch klargeworden, daß ein klein wenig "Verschwendung" zwei Vorteile haben kann:
1.) Mitunter kann so ein unvollständig gepacktes Zeug wesentlich besser performen UND da die Leseroutinen viel einfacher sind, spart man zwar weniger an den Daten, spart dafür aber Speicher UND Performance bei den Routinen selbst.
2.) Ungenutzte Bytes oder Bits (die man standardmäßig nullsetzt) können nützlich sein, um das Zeug später abwärtskompatibel erweitern zu können, ohne neue Formatnamen/-Erweiterungen für jede neue Version erfinden zu müssen und so, daß neue Routinen neue und alte Versionen lesen können, ohne extreme Verzweigungen haben zu müssen.

Natürlich alles nur wieder meine persönliche Ansicht dazu.
Ich habe schon öfter mal bereut, manches von meinem alten Zeug "zu dicht komprimiert" zu haben, so daß kein Platz für Erweiterungen mehr da war und habe mich andererseits jedes Mal gefreut, wenn ein altes Format noch Dinge "offen gelassen" hatte, so daß ich es noch weiter verwenden und erweitern konnte.
zatzen hat geschrieben:Ich bin wohl irgendwie nicht so der "Performer", vielleicht auch wegen des Speichermangels in Qbasic ein gebranntes Kind was das alles angeht, keine Ahnung, oder einfach nur ein "sich Dateiformate ausdenken"-Idiot.
Naja, die ganzen Formate und Protokolle, die ich mir schon ausgedacht habe, zähle ich schon gar nicht mehr. Irgendwann, Jahre später, merkt man dann, was davon sich bewährt hat (was man immer noch, bzw. immer wieder verwendet) und anderes, was mal eine "Schnapsidee" war, die gut aussah, aber in der Praxis nicht wirklich verwendbar war (entweder zu groß und sperrig, zu kompliziert einzubauen oder auch zu nichts im Mindesten kompatibel außer zu sich selbst). Wenn ich heutzutage ein Format benutze, will ich NICHT mehr (wie leider früher manchmal!) alles "um das Format", bzw. "um diese Routine herum" bauen müssen. Da finde ich es inzwischen besser, Zeug so zu machen, daß man es "nur noch einzubauen" braucht und es quasi alles, was es braucht, selbst macht, bzw. "von selbst mitbringt". Nur das schafft es (zumindest für mich), daß ich meine Zeit dann auch wieder auf etwas anderes, "neues" aufwenden kann - wenn ich mich um einmal "fertiges" Zeug wirklich "nicht mehr kümmern" muß, weil es quasi wie ein Baustein nur noch "mit anderen Bausteinen" zu einer funktionierenden Einheit zusammengebaut werden muss.
zatzen hat geschrieben:Letztlich würden mit ZVID größere Grafikteile schneller eingerechnet werden als wenn man die Routine zig mal kachelweise aufruft.
Meine Levelroutinen arbeiten nicht kachelweise. Es ist zwar gekachelt angeordnet, aber die Grafik wird spaltenweise generiert und entsprechend Mode-X dann "4-Spalten-weise", so daß die "Plane" so selten wie möglich gewechselt werden muß.
zatzen hat geschrieben:Der magische Würfel 486er:
Ich hab immerhin meine Programmierkarriere auf einem 286er begonnen. Klar, Du auch auf dem C64.
Angefangen habe ich Mitte der 80er auf einem KC85/1 (in einer außerschulischen "Computer-AG"). Da hatte ich noch keinen eigenen Computer. Das habe ich 3 Jahre lang gemacht, DANN erst habe ich einen eigenen (teuren) KC85/4 bekommen. (Falls Du Dich fragst: Das sind Modelle aus der DDR.) und erst in der Wendezeit kam dann der C64. Zu der Zeit hatte ich schon knapp 5 Jahre (allerdings ausschließlich in BASIC) programmiert.
zatzen hat geschrieben:Aber auf dem 286er war das PUT von Basic noch so langsam, selbst bei kleinen Sprites... Das waren noch die Zeiten in denen man monolithisch programmiert hat in dem Sinne, dass sehr viele Spiele auf einem Rechner > 286 zu schnell liefen.
Naja, daß für "flächige Dinge" (wie Sprites, Rechtecke/Polygone) der fortwährende Zugriff auf eine generalisierte Pixelroutine (die jedes Mal aus Koordinaten die Pixelposition ausrechnet und für den Grafikmode entsprechend die Daten setzt für den Pixel) eine Scheißidee (weil langsam) war, habe ich schon recht schnell herausgefunden. Sprites aus einzelnen Pixel(X,Y,C) oder PUT (wasauchimmer) zusammenzusetzen - so verrückt war ich nie.
zatzen hat geschrieben:Und dann hatte ich einen 486er, [...] und an die Grenzen des 486ers bin ich selber durch eigene Programme gar nicht gestoßen, [...] als ich einen Pentium hatte, und den hab ich auch nicht ausreizen können, dann kam nach vier Jahren ein schneller Windows-Rechner, nach 5 Jahren noch einer usw...
Naja, wie ja bereits mehrmals erwähnt: Um zu merken, daß es wirklich funktioniert, reicht es nicht, zu sehen, ob die Steuerung alleine oder die Grafik alleine oder der Sound alleine funktionieren. Erst, wenn ALLES ZUSAMMEN arbeitet und immer noch funktioniert, DANN wird's ein brauchbares Spiel. Und der PC (im Gegensatz zu Konsolen, C64 u.ä.) hat nunmal keine dedizierten Spezialchips für Sound Effekte, grafische Sprites/Fonts usw. - da muß das alles fast 100%ig und "quasi-gleichzeitig" durch die CPU bewerkstelligt werden.

Daß ich JETZT so einen "Aufwand" betreibe, einen einigermaßen gescheiten "Rahmen" für Spiele zu schaffen, liegt daran, daß zum Schluß nicht bei jeder neuen Spielidee wieder von Null angefangen werden soll, im Sinne von: Wo krieg ich den ganzen Speicher her? Wie baue ich das Zeug so zusammen, daß es funktioniert ohne abzuschmieren oder rumzuruckeln? Also quasi soll dieser ganze Kram schon so "getestet und für gut befunden" sein, daß man ein Spiel drauf entwickeln kann OHNE jedes Mal komplett neue Routinen bauen zu müssen, die eigentlich das gleiche machen wie die des letzten Spiels. Viele Dinge, die ich so mache oder bei einem Spiel mache/machen würde, würde ich immer wieder auf die gleiche Art und Weise machen - da macht's schon Sinn, wenn das Code-Zeug auch mal (und wenn's nur teilweise ist) eine problemlose Wiederverwendbarkeit hat.
zatzen hat geschrieben:Ich habe mir für DOSbox aktuell die 20000 Cycles festgelegt und bis jetzt bin ich da selbst mit den extremsten ZSMs noch nicht an die Grenzen gestoßen.
Naja, wie ich auch schon erwähnte: DOSBox ist ein wundervolles Tool und - Respekt! an die Leute, die das gemacht haben. Aber für reale Performance-Tests ist es trotzdem nur mäßig geeignet.
zatzen hat geschrieben:Ich habe wohl noch nicht richtig gelernt, wie man ein Spiel auf korrekte Weise "time't", bei Kotzman II hab ich es so gemacht wie ich es jetzt etwas anders auch vor habe, nämlich indem ich in die Hauptschleife ein SOUND 0, .1 oder ähnliches eingefügt habe. Oder war es doch eine Verzögerungsschleife? Jedenfalls statisch und wie ich meine schlimmer als warten auf einen Interrupt, weil sich ja die Grafikrechenzeit und die Warteschleife addiert haben, was besonders bei langsameren Rechnern und höherem Grafik- aufkommen bemerkbar gemacht hat.
Ja, das entspräche dem (bei sowieso zu langsamer Maschine sinnlosem) Warten auf den Vertical Retrace (Rasterstrahlrücklauf, bzw. Bildaufbauende) - siehe oben.
zatzen hat geschrieben:Von daher wäre Interrupt-Timing für mich erstmal die nächst bessere Erfahrung...
Ja, wie Du es machen willst/würdest - keine Ahnung. ICH fahre mit der Kombination "ungetimete Schleife" mit "Ticker für Steuersignale/Schrittzähler" bisher ganz gut. Grund ist, daß das Verhalten eines Spiels nie "monolithisch" ist: Mal ist mehr, mal weniger los. Mit so "Warte-States (Sound 0,x usw...) kann man ja immer nur auf den Worst Case timen und verschenkt so leider die ganze Zeit, wo das Spiel auch flüssiger laufen könnte.

Meiner Erfahrung nach scheinen sehr viele (spätere) Coder, vor allem im 3D-Bereich, das so wie ich zu machen: Die FPS-Raten der Spiele sind variabel und hängen davon ab, wie aufwendig gerade berechnet werden muß. So gibt es eben auch mal "Engstellen", wo die Performance einbricht (wenn man das Maschinen-Limit erreicht hat bei mittelschnellen Kisten), aber dann ist es eben nur DANN mal kurz lahm - und nicht die ganze Zeit. Meiner Meinung nach immer noch die bessere Option.

Anders - und allgemeiner - ausgedrückt: Die internen Berechnungen sind mal aufwendiger, mal weniger aufwendig, was die FPS variabel werden läßt und trotzdem wirkt das Spiel von außen her trotzdem so, als würde zumindest der "Spielfortschritt" bzw. das "Spielgeschehen" immer durchschnittlich "gleich schnell" ablaufen.

Diese Idee hatte ich damals auch bei Xpyderz - und der Grund dafür war eigentlich ein komplett anderer, aber ich bin froh darüber, daß ich dadurch angeregt war zu meiner Idee. Grund war, daß - wie erwähnt - das Ding mal Multiplayer werden sollte. Und bei Multiplayer - auf mehreren Kisten gleichzeitig - MUß das Spiel ja überall "gleich schnell" ablaufen, weil es sonst unsynchron wird! Das war der ursprüngliche Knackpunkt, den ich damals bewältigen wollte, weil ich wußte, daß Multiplayer nicht funktionieren kann, wenn die (durchschnittliche) Spielgeschwindigkeit auf jeder Kiste unterschiedlich ist. Kleine Abweichungen dabei sind hinzunehmen - für leichte Asynchronität wurden schließlich Ringpuffer erfunden, die das ausgleichen. Aber im Allgemeinen muß die Geschwindigkeit eingehalten werden.

Und, auch wenn man kein Multiplayer-Spiel macht, finde ich, daß eine vorhandene Grundgeschwindigkeit Teil des Spielerlebnisses ist. Wie schnell sich die eigene Figur oder die anderen Figuren pro Sekunde bewegen und damit das Spielgeschehen/-erlebnis beeinflussen, sollte der Spielemacher festlegen - NICHT die Leistung der ausführenden Maschine.
zatzen hat geschrieben:Kleines Update bzgl. ZV2: [...]
Kurz gesagt haben sich bei obigem Bild mit der Greifhand 1801 individuelle Paletten ergeben, was ein wenig ernüchternd ist, da sich ja die Anzahl nur etwas mehr als halbiert hat.
Bei ZVID(1) war das alles kein Problem, dort habe ich die Bitbreite UND den Palettenoffset direkt im Header deklariert, aber diesmal soll der Header pro Block ja nur ein Byte groß sein.

Wie auch immer, man kann daraus schliessen dass sich ZVID2 nur für Sprites eignen wird - oder nur für extrem spärlich mit unterschiedlichen Farben besetzte Vollbilder.
Naja, im Gegensatz zu Deiner Vorgehensweise habe ich von Anfang an für Sprites, (Level-) Hintergründe, große Logos usw. immer andere, jeweils auf den jeweiligen Zweck zugeschnittene Routinen gebaut. Meine Sprite-Routinen wären für Bilder/Level denkbar ungeeignet, meine Level-Routinen wären für Sprites nicht zu gebrauchen. Aber das, was sie können/können sollen, können sie dafür ziemlich gut.

Generalisierte Routinen benutze ich eher gern dort, wo NICHT absolute Echtzeit gebraucht wird: Z.B. in Editoren, die viel Platz für Daten lassen sollen und daher Speicher sparen - dort können so generalisierte "Eine kann alles"-Routinen gut Speicher sparen - und müssen ja sowieso keine "Voll-Frame-Rate" erreichen. Die sind dann langsamer, aber universeller einsetzbar und man braucht nur eine einfache Routine statt 3 oder 4 komplexe.
zatzen hat geschrieben:Erklärung des Begriffs "Set": ZVID2 soll, ähnlich wie ZVID(1), mehrere "Frames" enthalten, die sich dann eine Metapalette und eine Zuordnungstabelle (in die die Header-Bytes weisen) teilen. Dadurch verringert sich im Verhältnis der Speicher-Overhead für die Kompression.
Ja, wie ich oben erwähnte, gehe ich hier von anderen Voraussetzungen aus. Sprite-Images stehen immer für sich allein - aber können durchaus eine gleiche (gemeinsam genutzte) Palette belegen, das erkennt das Programm, das die Spritedaten zusammenstellt, automatisch. Das erlaubt es auch, der gleichen Figur (deren gesamte Phasen die gleiche Palette nutzen) einmalig eine komlett neue Palette zuzuweisen, um eine bewegliche Figur in komplett anderen Farben haben zu können. Mein Beispiel-TGAME.EXE nutzt diesen Effekt beispielsweise aus: 8 unterschiedliche "Männchen" - jedes mit anderer Haut-/Augen-/Haarfarbe und anderen Klamotten. Und obwohl es immer das gleiche Sprite-Image (bzw. Images für die Animation) ist, wirkt es so, als hätte man mehr... - Es ist wirklich erstaunlich, was man durch simples Umfärben erreichen kann.
zatzen hat geschrieben:Den Farbstring zu optimieren ist eine Sache für sich - wer da eine zündende Idee hat möge sich bitte melden, ich werde mich aber auch nochmal in den ZVID(1) Code einlesen, wie ich das da halbwegs gut gelöst habe.
Ja, wie ich ganz oben erwähnt habe: Zuerst mit den vielfarbigen Blocks anzufangen scheint mir hier die beste Idee zu sein, die in absehbarer Zeit gute Ergebnisse bringen sollte. Klar, das "Überlappen" von Paletten (an den Enden) könnte noch etwas mehr 'rausholen, da könnte man noch "herumsortieren" um noch geringfügig Platz zu sparen. Wenn man aber "nur" 256 Farben hat, werden realistisch gesehen kaum je mehr als 16-32 Farben gleichzeitig im gleichen Spriteimage verwendet werden, weil nun mal kein gescheit aussehendes Sprite so ALLE Farben des vorhandenen Farbspektrums gleichzeitig benutzen wird.
zatzen hat geschrieben:Vielleicht nenne ich das Format am Ende lieber ZSPR, da es ja wirklich nur für Sprites gedacht ist.
Ja, Hauptsache Zxxx, oder...?

Ja, vieles von meinem Zeug ist auch mit Ixx benannt. (I für Imperial Games) Meist nutze ich eher nur 3 Buchstaben/Zeichen - so kann man es auch gleich als Filenamen-Erweiterung verwenden...
Mir ist nur aufgefallen, daß ich für mein Logo-Bitmap Format (senkrecht gespeicherte/gepackte Bitmaps) das BM für Bitmap nicht mit I kombinieren konnte (oder wollte), weil ich mein Format weder IBM (International Business Machines) noch BMI (Body-Mass-Index) nennen wollte - so heißen sie nun DBM (Downwards BitMap)...
zatzen hat geschrieben:Eine solche Datei soll möglichst nur mit einem Pointer auf dem Heap adressiert werden. ZVID(1) erforderte etliche Pointer und Variablen für eine Datei.
Ja, seit einigen Jahren neige ich auch eher zu so kompakten Formen/Formaten, die möglichst einfach in Assembler auszulesen/darstellbar sind.
zatzen hat geschrieben:Das wird noch eine Herausforderung, vor allem mit dem Clipping, wahrscheinlich schreibe ich das erstmal alles in Pascal.
Das mache ich oft so - habe ich früher noch öfter gemacht. In Pascal ist Zeug leichter zu debuggen, so daß man erst einmal die grundlegende Funktionalität sicherstellen kann, bis es genau das macht, was es soll. Es dann in Assembler umzusetzen ist dann eher eine Übung, das unter Ausnutzung der (im Gegensatz zu Hochsprachen vorhandenen) Möglichkeiten der Register und Flags den ganzen Kram klein und performant zu kriegen,
zatzen hat geschrieben:Decodiervorgang:
1. Header-Byte lesen
2. An entsprechender Position aus der Zuweisungstabelle die Bitbreite und den Palettenstring-Offset auslesen
3. Blockdaten entsprechend Bitbreite interpretieren und jeweilige Farbe an Position des Palettenstring-Offsets
+ jeweiligem Blockdatum lesen, Pixel in Zieldatenfeld schreiben
Klar, genau so habe ich das auch die ganze Zeit verstanden.
Du sagtest ja, um zu sparen, soll jeder Block nur ein Byte als Header haben. Allerdings verweist dieses Byte auf ein Word - und wenn man Pech hat, braucht jeder Block sein eigenes Word, so daß man quasi in Wirklichkeit insgesamt 3 Byte Header hat, nur daß 2 davon "woanders stehen".
Und, wie Du schon sagtest, kann es passieren, daß man nicht mehr "safe" ist, weil man evtl. mehr Verweise braucht, als es Bytes gibt.
zatzen hat geschrieben:Tja, alles im Grunde nicht nötig, man müsste überhaupt erstmal genug Material pixeln um unkomprimiert 300K an Sprites zu füllen - aber es macht Spaß, eine kleine Etüde...
"Etüde" - der Musiker wieder...
Aber ja, es kann durchaus lehrreich sein, sich mit solchen Dingen zu befassen. Ob man sie später einsetzt oder nicht, kann man ja immer noch entscheiden. Aber es kann nicht schlecht sein, bei aufkommender Drohung von Speichermangel schon vorbereitet zu sein und einen "Plan B" in der Tasche zu haben, um dann Daten schrittweise zu packen. Meine Xpyderz-Sprites, die sowieso recht klein und auch nicht gerade "kakelbunt" sind, auf 16 Farben zu "reduzieren", hat mir nicht besonders wehgetan (ursprünglich hatte das Spiel INSGESAMT nur 16 Farben) - mit der 256er Palette haben die Figuren usw. jetzt IMMER NOCH je 16 Farben, nur eben 16 aus beliebigen 256. (Eigentlich 15 aus 255, Keyfarbe und so.) Die Figuren sehen für meine Ansprüche an das Spiel immer noch gut aus - und ich konnte den Speicherbedarf für die Sprites auf einen Schlag auf fast genau die Hälfte reduzieren - wo die Sprites vorher 8bit waren und kaum jemals an 16 Farben pro Sprite je herangekommen waren - eher so 8 bis 12.
zatzen hat geschrieben:Ich habe heute mal provisorisch den ersten Decoder in Freepascal geschrieben und dabei noch den Encoder debuggt.
Ich finde die Kompressionsrate erfreulich und das decodieren gestaltet sich so auch ersteinmal einfach.
Hier nochmal an einem Beispiel:
64x64.png
Als PNG 1874 Bytes.
Die ZV2 Datei ist 2637 Bytes groß.
Klingt so erstmal nach nichts besonderem, allerdings muss man bedenken, dass bei einem Set von Grafiken, also mehrere Sprites in einer ZV2, die Farbtabelle und der Farbstring relativ zu den Blockdaten schrumpfen.
Ja, kenne ich - ist bei meinen auch so.

Und, daß natürlich ein blockbasiertes Format Grafiken (mit begrenzter Farbanzahl) gut packt, liegt daran, daß in Flächen Farben in geometrischer Nähe zueinander viel häufiger ähnlich oder gleich sind als Farben, die zeilenweise gespeichert werden. Da sind ein paar nebeneinanderliegende Farben zwar ähnlich, aber die Zeilen selbst haben quasi nichts miteinander zu tun. Für so Flächen könnte man noch so eine "Zähler"-Option einbauen, daß die häufigste Farbe nicht angegeben werden muß, sondern immer wenn ein "Zähler" Byte/Datum auftritt, soviele Pixel von der "häufigsten" Farbe des Blocks gepixel werden... - ABER... das würde nur Sinn machen bei 16x16er oder VIELLEICHT noch 8x8er Blocks. Bei 4x4er würde der Speicherbedarf dafür meiner Schätzung nach höher sein als der Platzgewinn (also würde kaum wirklich Packen).
zatzen hat geschrieben:Auseinanderdividiert sind die Blockdaten inkl. Headerdaten nur 1462 Bytes groß, angenommen man hat dann 16 Stück dieser Sprites mit je 1500 Bytes -> 24000 Bytes, und wenn die restlichen Daten nur unwesentlich anwachsen, sagen wir auf 2 KB, dann hat man, in diesem Fall von 16x16 Blöcken d.h. 64x64 Pixel, 16 * 64 * 64 Bytes = 65536 auf 26000 Bytes reduziert, also auf knapp 40%.
Naja gut, von so "angenommen"-Daten gehe ich nie aus, um irgendwelche Statistiken zu "verschönern" oder wasauchimmer. Realistisch ist es für mich, wenn ich es wirklich SO in einem Spiel verwenden würde.

Wenn ich z.B. Schüsse habe, die rund und 5x5 Pixel groß wären, bräuchte man 4 (oder 3, weil unten rechts wegen der Rundung nichts gebraucht) solcher 4x4er Blocks dafür. In MEINEM Format wäre es kaum besser, weil die immer mit ganzen 16er Positionen gespeichert werden, somit also 80 Bytes (5 Zeilen, 16 Bytes/Zeile). Wenn 16-farbig, wäre es nicht weniger, weil die Pixeldopplung nach rechts (und nicht nach unten) geht und wenn man etwas Rundes speichert, braucht es quer und längs die gleiche Anzahl Pixel und auch als 4-Bit wären es noch 80 Bytes. Nun ist das aber ein Sonderfall und nicht etwas, das andauernd auftritt. Außerdem könnte ich diesen Schuß dann in allen Farbkombinationen speichern, mittels zusätzlicher Paletten.

Und ja. Da packe ich WESENTLICH weniger effizient als Du! Mir geht es ja unter anderem auch darum, alle Sprite-Features verwenden zu können und auch auf einfache Weise "vom Spiel aus" auf die Daten zugreifen zu können, um z.B. einer Figur eine andere Palette verpassen zu können oder die Figur gespiegelt, skaliert, gedreht anzeigen zu können ohne zusätzliche Image-Daten zu brauchen. Nur weiß ich natürlich, daß z.B. die "gedreht"-Option in Spielen mit seitlicher Ansicht gar nicht so oft gebraucht werden würde (als es z.B. in der Xpyderz-mäßigen Draufsicht der Fall ist) - und trotzdem wird diese Option quasi "mitgeschleppt". Da würde eine einfachere, spiegelfähige, aber NICHT drehfähige Spriteroutine sicher SO EINIGES an Performance bringen.

Hier sieht man wieder den erwähnten Nachteil generalisierter Routinen: Sie können "alles" - machen "alles" aber leider oft auch, wenn es gar nicht gebraucht wird...
zatzen hat geschrieben:Tja, zugegeben, an diesem Beispiel zeigt sich eher dass sich hier eine simple Reduktion auf 4 Bit eher auszahlen würde. Allerdings wäre man auf 16 Farben eingeschränkt - dieses Beispiel hier hat 60 Farben.
Ich müsste noch ein Beispiel anbringen von wirklich typischen Sprites die man als 4 Bit speichern *könnte*, wie sich ZV2 da schlägt.
Naja, dieser Bildausschnitt ist nur (meiner groben Meinung nach) kaum ein Beispiel für einen typischen Sprite. 60 Farben - d.h. fast ein Viertel der vorhandenen 256er Palette - wird meiner bescheidenen Meinung nach kaum je ein einzelnes Spriteimage belegen.
zatzen hat geschrieben:Ok... geschrieben, getan:
guyb.png
(das ist der VGA-Guybrush mit konkret 12 Farben, nicht EGA wo vielleicht nur 8 verwendet werden)

PNG (mit nur 16 Farben eingestellt): 445 Bytes
ZV2 (komplett mit Tabelle und Farbstring): 344 Bytes

Diesmal hätte also klar ZV2 Vorteile, selbst schon bei nur einem Sprite.
Ja, wie erwähnt: Hier zeigt sich der Vorteil blockbasierter Speicherung zum "Zusammenfassen" von Farben, weil diese in geometrischer, flächiger X/Y-Nähe zueinander viel häufiger "passend" auftreten als z.B. zeilenweise. Ich weiß nur nicht auswendig, ob PNG nicht auch blockweise packt. JPG tut es bekanntlich definitiv - aber JPG ist ja kein realistischer Vergleich, weil es (teilweise stark) verlustbehaftet arbeitet und daher meiner Meinung nach für Sprites in Größenordnungen, wie wir "320er" sie benutzen, sowieso ungeeignet wäre. Je gröber die Auflösung, umso bescheidener sieht JPG aus...
zatzen hat geschrieben:Die reinen Blockdaten sind 240 Bytes groß, das Sprite ist 6x11 Blöcke also 66 * 16 Bytes = 1056 Bytes groß, und rein so gesehen ergibt sich eine Kompression auf 23%.
Naja, das ist jetzt EIN Beispiel-Sprite.
Man müßte da mal so ganze Sprite-Sammlungen haben. (Gibts übrigens im Internet. Es scheint viele Leute zu geben, die Sprites in allen Animationsphasen aus alten 2D-Spielen herausrippen und in großen Bitmapgrafiken zusammenfassen.) Und die gleichen Routinen auf quasi so mehrere VERSCHIEDENE Sammlungen anwenden und DANN aus ALLEM eine durchschnittliche Kompressionsrate errechnen, das wäre dann realistischer. Allerdings sind viele dieser Sprite-Sammlungen auch schon eher auf 16bit- oder 24bit-Grafik eingestellt, so daß hier nach Farbnummer packen schon nicht mehr möglich/sinnvoll ist.

Ich muß aber auch dazu sagen, daß oft mit viel größerer Farbtiefe gearbeitet wird als überhaupt nötig. Ein 16x16-Sprite kann z.B. schon rein mathematisch nie mehr als insgesamt 256 Farben haben, in der Realität hat es meist kaum ein Sechstel davon - und trotzdem wird dann Schwachsinn betrieben wie solche Sprites in 24bit (oder 32bit) anzulegen. Habe da schon mal ein Spiel von einem Typen gespielt, das da kaum auf meinen einen Rechner lauffähig zu kriegen war, weil dauernd "Speichermangel" bzw. Windows mit Swappen gar nicht mehr hinterherkam. Tja, wenn man eigentlich Kachel-/Arcadegame-mäßige Hintergründe als scrollende 24bit-Vollgrafik speichert und Sprites, egal wie groß/klein ebenfalls als 24bit und zwar IMMER 200x200 Pixel (bei kleineren war dann eben ringsherum riesiger Leerraum)... Dann funktioniert es zwar, frißt aber Speicher UND Performance und weil die Sprites auch noch GEPIXELT waren, brauchten sie eigentlich niemals 24bit, sondern waren kaum 16-farbig... (aus 16777216 Farben!) ... Wenn man so etwas sieht und sich dann "die Leute nicht erklären können, wieso das ruckelt oder auf manchen Computern mit Grund Speichermangel nicht starten will"... - da fällt einem auch echt GAR NICHTS mehr ein, was man da noch sagen soll... - Klar, Respekt, daß überhaupt noch Leute selbst Spiele machen... - Aber wenn man selber codet, tut einem sowas schon weh.
zatzen hat geschrieben:Zum Thema GIF: Habe es auch mal als GIF gespeichert, 16 Farben, ergibt 343 Bytes, also nur um 1 Byte kleiner, und wie gesagt sind als Sprite-Set noch deutlich kleinere Durchschnittswerte (siehe 240 Bytes reine Blockdaten) zu erwarten.
Naja, fairerweise muß man sagen: "GIF87a" bzw "GIF89a" sind 6 Bytes. Die "Sequenzblock-Header" und "Subsequenzblock-Header" brauchen insgesamt auch nochmal bei der Gesamtgröße schätzungsweise so 10 Bytes... - Und dann kommt noch dazu: Wenn man GIF "interlaced" speichert, wird es evtl. etwas schlechter packen als "nicht interlaced" (progressiv).

Und ja, wie schon erwähnt: Hier zeigt sich der Vorteil blockweisen Packens (geometrische Nähe gleicher/ähnlicher Pixel). Andererseits wird vielleicht GIF evtl. wieder besser abschneiden, wenn man ein Bild nimmt, dessen Abmessungen keine Vielfachen von 4 sind, weil blockbasierte Speicherung bekanntlich immer dann "zum Problem wird", wenn man angeschnittene Blocks trotzdem als volle Blocks speichern muß.
zatzen hat geschrieben:Die Farbstrings können noch geringfügig optimiert werden, sagen wir um ca. 10% verkleinert, allzuviel kann man da nicht herumschieben, sonst bräuchte man wieder mehr Tabelleneinträge (diese enthalten Offsets in den Farbstring zusammen mit der Bitbreite).
Ja, man könnte hier auch STUNDENLANG herumrechnen lassen, um den optimalsten Palettenstring zu finden, indem quasi "alles mit allem" kombiniert wird... aber da denke ich, für die 10-20 Bytes, die man da realistisch gesehen am Ende gewinnen wird, wäre es den Aufwand kaum wert. Das macht vielleicht auf einem C64 Sinn, wo man wirklich alles (Code, Grafik, Level, Sound) in die 64kByte quetschen muß... - Natürlich ist es schön, zu wissen, daß man das absolute Optimum an Spritedaten gewonnen hat. Allerdings... naja. Jedes Mal, wenn man vielleicht mal andere Sprites benutzen will, müßte man das neu berechnen, es wäre also jedes Mal ein Daten-"Unikat", von dem man nie vorher wüßte, wie groß es am Ende werden wird und wie viel Speicher man dafür zu reservieren, d.h. freizuhalten hat.

Gut, dann habe ich also auch zu DIESEM Thread endlich meinen schon länger geplanten Senf abgelassen. (Ja, hier in der Gegend wird bekannter Senf hergestellt...)
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Eigenes Videoformat

Beitrag von zatzen »

Ich antworte später ausführlicher.
Nur schon einmal so viel:
Ich habe etwas experimentiert und ZVID2 für lohnenswert und funktional befunden.
Die Freepascal Routine funktioniert mit clipping an allen Bildschirmseiten und spiegeln,
ich muss jetzt nur noch das Format strukturieren, und dafür muss ich folgendes klären:

Wenn ich mit getmem Speicher reserviere, in welchem Rahmen wird sich dann das zugeordnete
Offset befinden? Bleibt es unterhalb von 16, unterhalb von 8 oder sogar verlässlich bei 0 ?
Oder kann es sogar passieren, dass es Werte >= 16 annimmt, etwa wenn man nur kleine
Mengen reserviert?

Ich würde das ganze gern im Zweifelsfall so zurechtstutzen beim Laden eine ZV2 Datei,
dass ich in der ASM Routine von absoluten Verhältnissen ausgehen kann.
Also bspw. mit getmem den benötigten Speicher + 16 reservieren, und Die Daten
dann an (segment+1:0000) laden.
mov ax, 13h
int 10h

while vorne_frei do vor;
Antworten