5 Punkte von GN⁺ 2024-08-05 | 1 Kommentare | Auf WhatsApp teilen
  • high_impact, das die Struktur der JavaScript-Engine Impact aus dem Jahr 2010 wiederaufleben lässt, ist eine C-Engine für 2D-Actionspiele und unterstützt Windows, Mac, Linux sowie WASM für das Web
  • Impact wurde in einer Zeit entwickelt, als iOS Flash ausschloss, um zu zeigen, dass Webspiele auch mit Canvas2D möglich sind; nach dem Verkauf für 99 US-Dollar wurden mehr als 3.000 Lizenzen verkauft, bevor die Engine als kostenloses Open Source veröffentlicht wurde
  • Die neue Engine ist ein kleines Framework, das Tilemaps, Entities, Physik und Kollisionen, Sprite-Animationen, Text und Sound zusammenfasst und SDL- oder Sokol-Backends nutzt
  • Die Implementierung bewahrt ihre Einfachheit mit einem Entity-Speicher fester Größe, QOI-/QOA-Assets, einem einzelnen Hunk-Speicher, OpenGL- und Software-Renderer sowie dem JavaScript-basierten Level-Editor Weltmeister
  • Biolab Disaster und Drop konnten mit Quellcode, der sehr nah am ursprünglichen JS blieb, portiert und ausgeführt werden; über Plattform- und Renderer-Erweiterungen lässt sich die Engine auf weitere Systeme ausdehnen

Überblick über high_impact

  • high_impact ist eine kleine Game-Engine für 2D-Actionspiele
  • Sie ist in C geschrieben und wird für Windows, Mac, Linux und als WASM für das Web kompiliert
  • Sie ist von der JavaScript-Game-Engine Impact aus dem Jahr 2010 inspiriert; der Name verweist auf eine Zeit, in der C als Hochsprache galt
  • Sie steht unter der MIT-Lizenz und der Quellcode liegt auf GitHub

Warum Impact entstand

  • Im April 2010 veröffentlichte Steve Jobs den offenen Brief „Thoughts on Flash“, in dem er erklärte, dass iOS Flash nicht unterstützen werde
  • Flash war damals das Zentrum der Browserspiel- und Webanimationskultur auf Basis von Browser-Plugins, und Seiten wie Newgrounds und Kongregate waren stark von Flash-Inhalten abhängig
  • Die Flash-Unterstützung auf Android war problematisch, und Adobe habe die Schwächen auf Mobilgeräten nicht ernsthaft zu beheben versucht
  • Zwar herrschte die Ansicht, dass es ohne Flash keine Browsergames gibt, doch mit der Canvas2D API konnten Bilder und Formen auf <canvas> gezeichnet werden
  • Canvas2D wurde von Apple/Safari ursprünglich zum Rendern von Desktop-Widgets entwickelt und später von Google und Mozilla unterstützt, während Microsofts Internet Explorer zurückfiel
  • In diesem Umfeld entstand Biolab Disaster, und dafür wurden zugleich die Game-Engine und der Level-Editor entwickelt

Verkauf und Einsatz von Impact

  • Nach Aufräumen des Codes und Dokumentation wurde Impact für 99 US-Dollar verkauft; trotz Kritik an der Bezahlentscheidung wurden mehr als 3.000 Lizenzen verkauft
  • Mehrere Webspiele wurden mit Impact entwickelt, und die Engine kam auch in kommerziellen plattformübergreifenden Titeln zum Einsatz
  • Gegen Ende ihres Lebenszyklus wurde Impact als kostenloses Open Source veröffentlicht
  • high_impact ist ein Projekt, das Impact von Grund auf neu erschafft – nicht in JavaScript, sondern in C

Warum C

  • C wird als einfache, aber tiefgründige Sprache beschrieben, die den Eigenschaften von Spielen ähnelt: „leicht zu lernen, schwer zu meistern“
  • Über mehrere Projekte hinweg wuchs das Interesse an C erneut
    • Portierung eines JavaScript-MPEG1-Decoders in die Single-Header-Bibliothek pl_mpeg
    • VR-Implementierung für Quake auf dem Oculus Rift
    • Entwicklung des Bildformats QOI und des Audioformats QOA
    • Reimplementation von wipEout
  • Impact hatte ursprünglich nicht den Umfang von Engines wie Godot, Unreal oder Unity, diente aber als solide Grundlage für viele Spiele
  • Die Neuschreibung von Impact in C begann als unterhaltsame Übung

Engine-Struktur und Assets

  • high_impact ist so einfach wie möglich umgesetzt und verfolgt das Ziel, mit möglichst wenig Code auszukommen
  • Die Grundfunktionen entsprechen der ursprünglichen JavaScript-Engine
    • Laden von Tilemaps
    • Erzeugen, Aktualisieren und Zeichnen von Entities als Spielobjekten
    • Physik- und Kollisionsbehandlung zwischen Entities
    • Kollisionen mit der Collision Map
    • Sprite-Sheet-Animationen
    • Textausgabe
    • Wiedergabe von Soundeffekten und Musik
  • Sie ist eher ein Framework als eine Bibliothek; die Spiel-Logik wird innerhalb des Frameworks geschrieben
  • Darunter liegt ein platform-Backend, das derzeit mit SDL oder Sokol kompiliert wird
  • Der Spielcode liegt in einer oder mehreren „scenes“, wobei eine scene ein struct mit Funktionszeigern ist
    • Nach dem Aufruf von engine_set_scene(&scene_game) setzt die Engine die neue scene
    • scene_game.init() wird einmal aufgerufen
    • scene_game.update() und scene_game.draw() werden in jedem Frame aufgerufen
  • Tilemaps und anfängliche Entities können aus .json-Dateien geladen oder dynamisch erzeugt werden
  • JSON wurde als Level-Format wegen der Abwärtskompatibilität mit dem ursprünglichen Impact gewählt
  • high_impact verwendet QOI für Bilder und QOA für Sound und Musik
    • Das Makefile der Demo-Spiele konvertiert PNG automatisch nach QOI und WAV nach QOA
    • Dadurch müssen keine separaten Bibliotheken zum Dekodieren von Bildern oder Audio eingebunden werden
  • Andere Asset-Formate könnten künftig unterstützt werden, aber die Einfachheit von QOI/QOA passt gut zur Ausrichtung des Projekts

Entity-System

  • Alle Entities teilen sich dieselbe entity_t struct, die die von high_impact benötigten Eigenschaften wie Position, Geschwindigkeit und Größe enthält
  • Da alle Entities dieselbe Byte-Größe haben, werden Speicherung und Verwaltung vereinfacht
  • Beim Bewegen von Entities werden Geschwindigkeit oder Beschleunigung gesetzt; den Rest übernimmt die Engine
  • Über Makros lassen sich spielspezifische Eigenschaften zur Basis-Entity-Struktur hinzufügen
    • Biolab Disaster verwendet ein union mit Structs pro Entity-Typ
    • Drop braucht keine zusätzlichen Eigenschaften
  • Jeder Entity-Typ muss ein entity_vtab_t mit Funktionszeigern bereitstellen
    • update wird in jedem Frame aufgerufen
    • touch wird aufgerufen, wenn sich die Entity mit einer anderen passenden Entity überlappt
    • Alle Einträge sind optional
  • Der Entity-Speicher hat feste Größe
    • Standardmäßig sind 1.024 aktive Entities vorgesehen
    • Das lässt sich über ENTITIES_MAX konfigurieren
    • Die Engine kommt problemlos mit bis zu 64k Entities zurecht
  • Um Entity-Referenzen länger als einen Frame zu behalten, wird entity_ref_t verwendet
    • entity_ref_t ist ein Struct mit uint16_t id und index
    • Mit entity_by_ref() lässt es sich wieder in einen Pointer auflösen
    • So kann unterschieden werden, ob an derselben Speicheradresse inzwischen eine andere Entity liegt
    • Wegen des uint16_t-Index ist die Zahl aktiver Entities auf 64k begrenzt
  • In C ist die Umsetzung von einfachem OOP, Klassen oder Single Inheritance etwas unbeholfen, aber high_impact versucht, dies so bequem wie möglich zu machen
  • Der „naive“ OOP-Ansatz, Entity-Logik je Typ an einer Stelle zu bündeln, war in den bisherigen Spielen gut verständlich und funktionierte zuverlässig

Kollisionserkennung und -reaktion

  • Eine einfache Kollisionsbehandlung prüft nur, ob die Bewegung an eine neue Position möglich ist; wenn nicht, stoppt sie, was bei schnellen Objekten zu seltsamem Verhalten führen kann
  • Wenn ein Spieler in einem 2D-Plattformer 16 px über dem Boden steht und der nächste Bewegungsschritt ihn in den Boden bringen würde, kann das wie eine weiche Landung wirken: erst schwebt er kurz in der Luft, dann fällt er im nächsten Frame weiter
  • high_impact verfolgt die Entity-Box durch die Tilemap und berechnet den exakten Kollisionspunkt
  • Das ist komplexer als eine einfache Ja/Nein-Prüfung, liefert aber bessere Ergebnisse und unterstützt auch Schräge-Tiles
  • Nach einem Zusammenstoß mit einem Tile kann mit verbleibender Geschwindigkeit ein zweites Tracing nötig sein
    • Trifft man zum Beispiel schräg auf den Boden, wird vel.y zu 0, während vel.x erhalten bleibt, damit das Objekt am Boden entlanggleiten kann
  • Kollisionen zwischen Entities werden separat behandelt
    • Partikel können mit der Tilemap kollidieren, aber nicht mit anderen Entities
    • Bewegliche Plattformen können mit anderen Entities kollidieren, sollen aber durch die Kollisionsreaktion selbst nicht bewegt werden
  • Für die Broad-Phase-Kollisionserkennung werden Entities nach pos.x sortiert
    • Da sie vom vorherigen Frame her meist schon weitgehend sortiert sind, ist der Aufwand für Insertion Sort gering
    • Anschließend wird von links nach rechts gescannt, und es werden nur Entities zwischen pos.x und pos.x + size.x geprüft
  • Dieses Sweep-and-Prune-Verfahren ist schnell, solange sich nicht viele Entities mit ähnlicher x-Position überlagern
  • Bei einem gestapelten Kistenturm etwa, wo viele Entities denselben x-Bereich teilen, tritt der Worst Case ein
  • Für Fälle wie vertikale Shooter, in denen die andere Achse besser geeignet ist, kann die Sweep-Achse mit #define ENTITY_SWEEP_AXIS y umgestellt werden

Rendering

  • high_impact enthält derzeit einen OpenGL-Renderer und einen unvollständigen Software-Renderer
  • Das gesamte Rendering läuft über eine sehr schmale API, und die eigentlichen Draw-Calls erfolgen in einer einzigen Funktion, was andere Backends relativ einfach macht
  • Ein zusätzliches Rendering-Backend muss im Kern Initialisierung, Aufräumen, Bildschirmgrößen-Setup, Frame-Beginn und -Ende sowie das Zeichnen von Quads unterstützen
  • Für die Texturverarbeitung werden außerdem Funktionen für mark, reset und create benötigt
  • Der Funktionsumfang ist bewusst schlicht: Es können nur Quads gezeichnet werden, Shader-Effekte gibt es nicht, aber für das Ziel der Engine reicht das aus
  • Der Software-Renderer umfasst 140 Zeilen Code und unterstützt nur achsenbündige Quads
  • Der OpenGL-Renderer versucht, das Rendering eines ganzen Frames in einen einzigen OpenGL-Draw-Call zu packen
    • Alle Quads werden in einem großen Buffer gesammelt und per glDrawElements() auf einmal übergeben
    • Alle Texturen werden in einem einzigen Texture Atlas zusammengeführt, um wiederholtes Rebinding zu vermeiden
  • Texture Atlases sind eine ältere Technik mit Nachteilen, werden aber verwendet, weil bindless texture nicht überall unterstützt wird
  • high_impact unterstützt nur einen einzigen Texture Atlas, dessen Größe aber per #define eingestellt werden kann
    • Mobile GPUs unterstützen typischerweise 8k×8k-Texturen
    • Moderne Desktop-GPUs scheinen bis zu 32k×32k zu unterstützen
    • Biolab Disaster und Drop verwenden einen 512×512-Atlas

Sound

  • Die Soundausgabe wird von SDL2 oder Sokol übernommen, während die Engine Laden, Dekodieren und Mischen mehrerer Sounds übernimmt
  • Das Soundsystem ist in sound_source_t mit Samples und sound_t für aktuell abgespielte Sounds aufgeteilt
  • Es basiert auf einem System, das bei der wipEout-Reimplementation entstand, und kann QOA bei Bedarf dekomprimieren
  • Alles ist statisch alloziert
    • Die Zahl ladbarer Sources ist fest
    • Die Zahl gleichzeitig abspielbarer Sounds ist fest
    • Abgespielte Sounds werden nach dem Ende automatisch verworfen und wiederverwendet
  • Für Sounds lassen sich Lautstärke, Stereo-Panning und Pitch ändern
  • Bei negativem Pitch wird ein Sound rückwärts abgespielt
  • Das für variablen Pitch nötige Resampling ist eine qualitativ einfache Variante mit Nearest-Neighbor-Interpolation

Speicherverwaltung

  • high_impact geht davon aus, dass sich der benötigte Speicher exakt bestimmen lässt, wenn ein Spiel keine von Nutzern erzeugten Assets hat
  • Die Engine alloziert statisch ein einziges Byte-Array namens „hunk“, und dies ist der gesamte Speicher, den high_impact verwendet
  • Die Größe des hunk lässt sich mit #define ALLOC_SIZE festlegen
  • Innerhalb des hunk gibt es zwei Arten der Speichervergabe
    • Einen Bump Allocator, auch Arena, der vom Anfang nach oben wächst und Spiel-Assets, Entities sowie Daten der aktuellen scene enthält
    • Einen temporären Allocator, der vom Ende nach unten wächst und sich wie malloc() und free() verhält, etwa für Zwischenspeicher nach dem Dekomprimieren von Bildern und vor dem Upload auf die GPU
  • Der Bump Allocator besitzt mehrere „High Water Marks“ und springt zu bestimmten Zeitpunkten automatisch zurück
  • Bump-allozierter Speicher muss nicht explizit mit free() freigegeben werden
  • Die konzeptionelle Lebensdauer ist in game, scene und frame unterteilt
    • Vor dem Setzen der ersten scene allozierter Speicher wird erst beim Programmende freigegeben
    • Während scene.load() allozierter Speicher wird beim Ende der scene freigegeben
    • Während der Laufzeit einer scene allozierter Speicher wird am Ende des Frames freigegeben
  • Das typspezifische load() von Entities wird in Phase 1 aufgerufen, da vorher nicht bekannt ist, welche Entities in einer scene verwendet werden
  • Zusätzliche Allokationskontexte lassen sich mit alloc_pool() kapseln; intern ist das eine Kurzform für bump_mark() und bump_reset(mark)

Der Level-Editor Weltmeister

  • Zum ursprünglichen Impact gehörte der Level-Editor Weltmeister, und auch high_impact enthält ihn
  • Er ist weiterhin in JavaScript geschrieben, nutzt noch viel vom ursprünglichen Code, wurde aber an moderne Browserfunktionen angepasst
  • Weltmeister arbeitet vollständig eigenständig
    • Ein Doppelklick auf weltmeister.html genügt, um mit dem Erstellen von Levels zu beginnen
    • Früher war zum Laden und Speichern von Dateien eine Backend-API mit PHP oder NodeJS nötig
    • Jetzt kann über die FileSystemAPI Zugriff auf einen bestimmten Ordner angefordert werden
  • Da Safari und Firefox insbesondere showDirectoryPicker() noch nicht vollständig unterstützen, wird ein Chromium-basierter Browser benötigt
  • Weltmeister liest C-Quelldateien ein und sammelt die Entity-Typen
  • high_impact bietet Makros, die der Editor versteht, im C-Code selbst aber keine Wirkung haben
    • EDITOR_SIZE(X, Y): Größe im Editor, Standard ist (8, 8)
    • EDITOR_RESIZE(RESIZE): Ob die Größe im Editor veränderbar ist
    • EDITOR_COLOR(R, G, B): Box-Farbe im Editor, Standard ist (128, 255, 128)
    • EDITOR_IGNORE(IGNORE): Ob die Erzeugung im Editor erlaubt ist

Demo-Spiele

  • Um zu prüfen, ob high_impact als echte Game-Engine funktioniert, wurden zwei ursprüngliche Impact-Spiele nach C portiert
  • Die Portierung war eher eine Art „Transliteration“ des vorhandenen JS-Quellcodes, wobei die bestehenden Assets wiederverwendet wurden
  • Dass dabei nur wenige Herausforderungen auftraten, kann als Hinweis gelten, dass high_impact wie beabsichtigt funktioniert
  • Biolab Disaster

  • Drop

Erweiterbarkeit

  • high_impact ist wie eine traditionelle Game-Engine aufgebaut, bei der spielspezifischer Code ergänzend geschrieben wird
  • Der Engine-Quellcode muss dabei nicht verändert werden, ist aber bewusst einfach genug gehalten, dass man ihn bei Bedarf direkt anpassen kann
  • Plattformen und Renderer sind so entworfen, dass sie sich erweitern lassen, ohne den restlichen Code zu ändern
  • Wer Interesse hat, ist mit Pull Requests für Vulkan-, DirectX- oder Metal-Renderer sowie Plattform-Backends für PSX, N64 oder Dreamcast willkommen
  • Da die Engine in C geschrieben ist, soll sie grundsätzlich überall laufen können

1 Kommentare

 
GN⁺ 2024-08-05
Meinungen auf Hacker News
  • Ein großer Teil der Programmierarbeit, bei der ich am meisten gelernt habe, war Impact zu verdanken.
    Impact war seiner Zeit wirklich weit voraus, und ich bin stolz darauf, einer der 3000 Lizenzinhaber gewesen zu sein. Es war der beste Kauf, den ich je getätigt habe, und das einzige Spiel, das ich wirklich bis zum Ende fertiggestellt habe, habe ich mit Impact gebaut.
    Mir gefiel, dass die Lizenz den Quellcode enthielt, und ich habe Engine und Editor selbst an meine Bedürfnisse angepasst. Dadurch habe ich über mehrere Jahre meine eigene JS-Game-Engine gebaut; die Fertigstellung von Spielen rückte dabei zwar in den Hintergrund, aber ich habe in diesem Prozess viel gelernt und auch viele Spiele für Game Jams gemacht.
    Auch Ejecta, Impacts native iOS-Unterstützung, hat mich inspiriert, aber damals frustrierte mich, dass es unter Android nicht lief. Also habe ich JVM-Bindings für V8 gebaut und Teile von WebGL implementiert, um meine Engine ohne WebView unter Android laufen zu lassen. Das öffentlich veröffentlichte Repository mit den V8-Bindings wurde unerwartet in kommerzieller Software eingesetzt: https://github.com/namuol/jv8
    Inspiriert von Impacts Geschäftsmodell habe ich sogar versucht, ein gebootstrapptes Startup aufzubauen, das Zugriff auf private GitHub-Repositories verkauft, aber diese Geschichte würde wohl länger werden. Jedenfalls wird mir warm ums Herz und es freut mich, zu sehen, wie Impact durch die Portierung nach C für das „moderne“ Web aktualisiert wird. Ich würde sagen, das Web ist gerade in einer seltsamen Phase, aber ich kann mich an keine Zeit erinnern, in der das Web nicht seltsam war.

    • Ursprünglich war Impact doch eine JavaScript- und browserbasierte Engine, daher sollte sie doch eigentlich auch unter Android mit einer einfachen WebView gut laufen, oder?
  • CrossCode ist ein hervorragendes Spiel. Ich wusste, dass es Web-Technologien nutzt, und war immer wieder überrascht, wie gut die Performance auf der Nintendo-Switch-Hardware ist.
    Diese Engine dürfte daran wohl auch einen gewissen Anteil haben.

    • Fairerweise muss man sagen, dass das CrossCode-Team Impact wirklich stark modifiziert hat. In einigen Entwicklungsstreams sieht man, wie Weltmeister, der Level-Editor von Impact, stark erweitert wurde: https://youtu.be/4lZfnM9Ubeo?t=3215
      Genau das ist eigentlich cool. Es ist gut, wenn Entwickler die Engine passend zu ihrem Spiel verändern können; ebenso sollte man high_impact eher als bequemen Ausgangspunkt betrachten und nicht als „feature-komplette“ Game-Engine.
    • Die Switch-Portierung war, wie schon jemand gesagt hat, mit viel Aufwand verbunden und hat mit dem Standard-impact.js überhaupt nichts mehr zu tun.
      Eine nette Anekdote: Alle wollten eine Switch-Version, aber wegen technischer Grenzen antwortete das Team: „Wenn Hedgehags lernen zu fliegen, erscheint CrossCode auf der Switch“: https://www.radicalfishgames.com/?p=6581
      Als die Portierung schließlich zustande kam, wurde eine zusätzliche Quest namens „A switch in attitude“ eingebaut, und wie erwartet tauchen darin fliegende Hedgehags auf: https://www.radicalfishgames.com/?p=6668
    • Es gibt einen Vortrag über den Prozess, CrossCode auf die Switch zu portieren: https://www.youtube.com/watch?v=KfBzlzvt8RU
    • Für die Switch-Portierung war, soweit ich mich erinnere, ein geradezu heroischer Aufwand nötig.
  • „Thoughts on Flash“ hat das Web vielleicht genau in dem Moment gerettet, in dem die Webplattform es am dringendsten brauchte: als sich die Dominanz einer einzelnen Software langsam ausweitete.
    Darin schwang wohl auch Frust über Adobe mit, das die MacOS-Unterstützung zu vernachlässigen schien, weil die deutlich größere Windows-Nutzerbasis Priorität hatte. Zum Beispiel hinkte die Mac-Version der Windows-Version immer hinterher.
    Jobs könnte das Gefühl gehabt haben, dass Adobe auch Apple etwas schuldete, so wie Apple Adobe möglich gemacht hatte, aber das ist eher Spekulation. Das Spiel selbst sieht wirklich geschmeidig aus.

    • Auf der PSP gab es auch Flash, und das war ziemlich ordentlich. Ich frage mich, wie viel Aufwand und Geld Sony da hineingesteckt hat.
    • Dieser Artikel hat mich wieder dazu gebracht, mit C herumzuspielen. Ursprünglich komme ich eher aus der ECMAScript-Ecke und habe vor langer Zeit ein wenig Lingo gemacht, aber mich ziehen ressourcenschonende Dinge an, die nah an der Hardware sind.
      Allerdings nicht so nah wie Assembler; ich bin eher dabei, hier und da Dinge zu reduzieren, die mich dazu bringen, tiefer einzusteigen.
  • Als jemand, der irgendwann aus dem Unternehmensjob ausbrechen und sich ernsthaft in Side Projects stürzen möchte, würde ich gern mehr über den Teil hören, in dem es darum geht, durch Monetarisierung selbstständig zu werden.
    Der Gedanke, Geld für etwas zu nehmen, das man ursprünglich aus Spaß gemacht hat, fühlt sich seltsam belastend an, aber ich weiß auch, dass genau das mir ermöglichen könnte, das, was ich liebe, in Vollzeit zu tun.

    • Wenn es gute Arbeit ist, die jemandes Problem löst, ist es völlig legitim, dafür entlohnt zu werden, und das weißt du vermutlich schon.
      Deshalb ist es wichtig herauszufinden, warum sich dieser Gedanke belastend anfühlt. Häufige Gründe sind, dass Menschen im Umfeld einen oft entmutigen, dass einem die Fähigkeiten fehlen, das, was man tun möchte, gut umzusetzen, dass es einem peinlich ist, um Hilfe zu bitten, oder man glaubt, anderen damit zur Last zu fallen, dass man Angst davor hat, dass die eigene Arbeit bewertet wird, oder davor, die „Sicherheit“ des aktuellen Einkommens zu verlieren, besonders wenn man Angehörige zu versorgen hat.
      In den meisten Fällen sind diese Gründe weniger wirklich gute Gründe als vielmehr etwas, das eine gewisse Neujustierung braucht und sich deshalb wie „Risiko“ anfühlt, sodass es schwer ist, die Komfortzone zu verlassen. Mit so einer Haltung erscheint jede Gelegenheit als Risiko, und es wird sehr schwer, den richtigen Zeitpunkt zu finden, um mit dem anzufangen, was man wirklich tun möchte.
      In diesem Zusammenhang: Es ist wichtig, einfach die Dinge zu tun, die man interessant findet, und sie der Welt zu zeigen; daraus aber etwas zu machen, das den Lebensunterhalt trägt, ist eine völlig andere Herausforderung. Die meisten schaffen es nicht, das, was sie lieben, zum Beruf zu machen, und selbst wenn es gelingt, können die Erwartungen zahlender Kunden und der Druck, Umsätze aufrechtzuerhalten, diese Liebe verdrängen. Das heißt nicht, dass man es lassen sollte, aber es ist gut, das zu wissen, bevor man hineinspringt.
  • Ich habe mich extra in meinen kaum genutzten HN-Account eingeloggt, weil ich Biolab Disaster vor ein paar Jahren immer wieder gespielt hatte, den Namen aber vergessen hatte.
    Es ist ziemlich erstaunlich, es zufällig wiedergefunden zu haben.

  • Normalerweise hätte ich das deutlich negativer formuliert. Denn ich sah ein „Framework“ einfach als eine Bibliothek, die sich nicht gut mit anderen Dingen verträgt.
    Trotzdem ist es schön, eine so einleuchtende positive Formulierung zu hören.

    • Die Erklärung, die ich gehört hatte, war ungefähr: Eine Bibliothek rufe ich auf, ein Framework ruft mich auf.
    • Ich habe Software Engineering studiert, aber den Unterschied zwischen Bibliotheken und Frameworks erst wirklich verstanden, als ich in meinem ersten Job mit WebObjects entwickelt habe.
      Es war angenehm, ein reichhaltiges, gut entworfenes Framework zu verwenden, das 99 % dessen, was man brauchte, wirklich gut erledigte. Eigenen Code hinzuzufügen gehörte zu den einfachsten Entwicklungserfahrungen, die ich je hatte, und es funktionierte einfach. Es fühlte sich magisch an, und ich vermisse es immer noch.
    • Ich sehe darin weiterhin auch eine negative Seite.
      Mein ideales Framework ist intern eine Bibliothek oder ein Bündel zusammenarbeitender Bibliotheken, und der Framework-Charakter ist so gering wie möglich.
      Qt zum Beispiel ist ein Framework und Qt „ruft mich auf“, aber man kann QPainter-Code ausführen, ohne die Qt-Event-Loop zu starten oder sich intensiv mit QObject zu beschäftigen. Idealerweise sollte man die Event-Loop nutzen können, ohne gleich Signale und Slots vollständig zu übernehmen, auch wenn sich das dann weniger gut anfühlen kann.
      Das ist nicht immer möglich und nicht immer lohnend, aber bei sonst gleichen Bedingungen bevorzuge ich die Variante ganz ohne Framework.
      Bei Game Engines verstehe ich, warum ein gewisser Framework-Anteil nötig ist. Wenn man für besondere Plattformen wie Smartphones oder Konsolen kompiliert, muss die Engine in den Build-Prozess und manchmal sogar in libc-nahe Teile eingreifen; man kann also nicht einfach eine Win32-Executable bauen und sie ein PlayStation-Spiel nennen.
  • Kombiniert man das verlustfreie QOI-Dateiformat mit 7Zip, ist die Performance besser als bei verlustfreiem PNG. Beeindruckende Arbeit.

    • Mit BMP plus 7Zip wäre es ebenfalls besser, und der Unterschied könnte wahrscheinlich sogar größer sein. Am Ende läuft es auf den Unterschied zwischen gzip und einem gzip-Ersatzkompressor hinaus.
  • „for No Reason“ war vielleicht dazu gedacht, die Akkulaufzeit der Spieler zu schonen.

  • Der Teil zur Speicherverwaltung gefällt mir. Arena Allocation ist wirklich simpel.
    Mein Spielzeug-Webserver hat anfangs ebenfalls mit einer Arena begonnen, aber bald habe ich gemerkt, dass es gar keinen Bedarf gibt, Speicher zu vergrößern oder zu verkleinern.
    Heute allokiere ich den benötigten Speicher am Anfang komplett und teile ihn dann in Stücke auf, die die einzelnen Module verwenden. Beim Programmieren gehen wir oft davon aus, dass wir eine beliebige Menge Speicher benötigen könnten, aber das muss nicht so sein.
    Viele Dinge haben tatsächlich klare Grenzen, und für den Rest lassen sich meist Grenzen definieren. Wenn man sie auflistet, kennt man die benötigte Speichermenge. Solche Grenzen vorab zu durchdenken und festzulegen macht Spaß, gibt Sicherheit und fördert eine gesunde Sparsamkeit.

    • Es nervt mich jedes Mal, wenn Leute sagen, die Performance von std::vector sei schrecklich, besonders in Games.
      Wenn man mit einem leeren Vector beginnt und ohne Reservierung Tausende Einträge hinzufügt, kann das natürlich passieren. Aber sehr oft kann man das tatsächlich benötigte Maximum ermitteln und vorab allokieren, dann ist alles in Ordnung.
    • Die Datenbank TigerBeetle wurde auf diese Weise gebaut, und das Konzept gefällt mir ziemlich gut: https://tigerbeetle.com/blog/a-database-without-dynamic-memo...
  • Mir gefällt sehr, wie mit einer union eine ENTITY-Datenstruktur für polymorphe Typen gebaut wurde. Gutes Design.
    Ich mag es immer noch, mit C herumzuspielen. Es war die erste Sprache, die ich gelernt habe, und ich habe mich jahrelang wirklich damit abgemüht. Wie im Originalbeitrag gesagt wird: C ist eine knappe Sprache, und das ist großartig; wenn man will, kann man beliebig tief eintauchen.
    Das Spiel gefiel mir, weil es eine Atmosphäre wie das alte Commander Keen hatte, und ich mochte diese Franchise, die Carmack in der Vor-3D-Ära gemacht hat, früher ziemlich gern.