Langsam neigt sich das Modul dem Ende zu und gleichzeigt ist auch mein Projekt annähernd Feature-Complete… /s
Diese Woche habe ich mich dazu entschieden, die World Generation nochmals komplett neu aufzusetzen. Zusätzlich habe ich einen animierten Loadingscreen gebaut.
World Generation
Architektur
Wie letzte Woche beschrieben, war ich schon länger etwas unzufrieden mit der Architektur des WorldGen Codes. Dieser war bisher in einem prozeduralen Stiel aufgesetzt. Um ein neues Generation-Level zu bauen, musste ich eine Menge Funktionen in verschiedenen Files schreiben, ich musste Variablen ändern, an 3 Stellen ein switch Statement erweitern und viel Boilerplate Code kopieren.
Im neuen System werden alle Generation-Levels als eigenes Struct dargestellt, welche jeweils zwei Methoden implementieren. Diese Structs können anschliessend ganz frei in einem Stack angeordnet und beliebig wiederholt werden.
Dieser Code konfiguriert, welche Layers wie oft und in welcher Reihenfolge ausgeführt werden sollen. Schön : )
world_gen.GenerationStack = []world_gen.LayerGenerator{ &layers.GenerateInitialGraph{ PointOfInterestSize: util.Vector2int{X: 5, Y: 3}, PointOfInterestDistance: util.Vector2int{X: 18, Y: 4}, }, &layers.RandomlyOffsetPoi{ MaxOffset: util.Vector2int{X: 7, Y: 3}, }, &layers.GenerateInitialPoiType{}, &layers.GenerateTypeGrid{}, &layers.CalculatePathData{}, &layers.RasterizePointsOfInterest{}, }
Limitierte Welt
Bisher war meine Welt unendlich gross generierbar (zumindest bis zum Integer Cap). Diesen Aspekt des Projekts fand ich zwar sehr cool, machte jedoch beispielsweise das Generieren von natürlichen Flüssen fast unmöglich.
Aus diesen Gründen habe ich mich entschieden die Welt nur limitiert gross zu generieren. Da ich so die Welt direkt am Anfang generieren kann, sollte der Map Screen auch effizienter laufen.
Points of Interest
Die Idee der alten Welt war, dass jeder Meter „begehbar“ sein sollte. Jeden Chunk auf dem Map Screen konnte man öffnen und in einer grösseren Darstellung sehen. Die Idee der neuen Welt ist Points of Interest zu generieren und diese mit Pfaden zu verbinden.
Um diese Points of Interest wird dann dazu passendes Terrain generiert – dieses ist nicht „begehbar“.
Mögliche Points of Interest wären: Dorf, Hütte im Wald, Schrein, Festung, verlassene Miene etc.
Diese Points of Interest sind als Graph abgespeichert. Da ich diesen jedoch noch nicht mit Pfaden visualisiert habe, werde ich die Erklärung dazu erst im nächsten Blog erklären.
Ach ja: die Welt seht jetzt auch etwas schöner aus : )
Loading Screen
Der zweite grössere Task diese Woche war dieser Loading Screen:
Die Schwierigkeit am Ladescreen war die Interaktion von WASM mit dem Browser. Da der Browser nur auf einem Thread läuft, würde eine lange laufende Funktion in Go einfach die Webseite blockieren und somit auch die Animation.
Go hat jedoch einen Scheduler, der Goroutines – auch in WASM – Abwechslungsweise ausführen kann. Problematisch ist dabei, dass dieses System separat vom Eventloop von JS funktioniert. Dieses System brauche ich jedoch um die Frames in den Browser zu rendern.
Dies führt dazu, dass ich in meinen langlaufenden Generationsfunktionen immer mal wieder an den Browser yielden muss:
for y := 0; y < world.Scale.Y; y++ { for x := 0; x < world.Scale.X; x++ { // world gen... } c.YieldExecutionToRendering() }
Nächste Ziele
Da mich diese Woche den Zielen der letzten Woche nicht nähergebracht hat, haben sich diese auch nicht gross verändert.
Nächste Woche werde ich die Weltengenerierung fertigstellen und damit beginnen dem Spieler das Erkunden der Welt möglich zu machen.