Roguelike-Diary (WIP ...)

Spiele, Demos, Grafikzeug und anderes unterhaltendes.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Bild

Theoretisch ist auch sowas möglich (berechnet mir das Programm in ~ 150 Millisekunden). 8)
Ob sowas spielbar ist, steht auf einem anderen Blatt ... :wink:
6-15% freie Felder von (xMax*yMax) fühlt sich am besten an, wobei schon 15% ziemlich ausladend wirken können (das da oben sind übrigens 45% Spielfeld-Füllung).

To-Do List für die nächsten 3 Wochen:
- Loops (perfekte Dungeons sind langweilig, und zuviel Backtracking ist auch nicht gut für's Spielgefühl: da müssen unbedingt ein paar Schleifen in die Architektur)
- Das Layout mit interessanter "Innenarchitektur" füllen = Mauer-Konstellationen, die dem Pathfinding-Test standhalten.
- Start- und Zielfeld generieren
- Abschließbare Türen und die zugehörigen Schlüssel im Dungeon verstecken.
- Einen Teil des Dungeons abtrennen und über Teleporter verknüpfen.
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: Roguelike-Diary (WIP ...)

Beitrag von Mijikai »

Das Projekt gefällt mir :)
Bleib am Ball.

Ich hab leider nicht durchgehalten und mein Spiele-Projekt vorerst auf Eis gelegt.
Aber deine Beiträge sind Motivation - vielleicht bekomme ich ja doch wieder Lust :)
(Video: https://www.youtube.com/watch?v=vBXwB-SDz7k)
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Oh, das sieht toll aus! :)
Sollte das auch ein Roguelike werden? Du solltest weiter machen!
Die Entscheidung, die mir bislang am meisten beim Durchhalten geholfen hat, war der Fokus auf "Vertical Building" - mich nicht mit grafischer Darstellung und Features aufhalten, stattdessen die Core-Mechanics so schnell wie möglich zu etablieren. Das nächste fertigzustellende Ziel (für die ich mir Deadlines setze) sollte niemals Content oder Feature sein, sondern ein Meilenstein!

Just do it!
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Jetzt mit 100% mehr Loops in den Layouts. :)

Roguelike Diary 10: Procedural Generation Part II. - Loops

Und so funktioniert's:
• Ich gehe alle freien Felder durch (ohne Türen), und schaue für jedes Feld einmal in jede der 4 Himmelsrichtungen, ob eine Mauer angrenzt.
• Wenn ja, kommt das Feld, inklusive Info über die getestete Richtung, auf eine offene Liste.
• Anschließend picke ich mir in einer Endlos-Schleife ein zufälliges Element von der offenen Liste, und fange an, in der für dieses Feld gespeicherten Richtung einen Tunnel zu "graben" (zunächst nur virtuell), bis ich entweder das Spielfeld verlasse, oder auf ein weiteres freies Feld, welches kein Türfeld ist, stoße.
• Dabei überprüfe ich bei jedem Schritt, abhängig von der getesteten Achse, zusätzlich einmal rechts und links, daß sich dort kein freies Feld befindet (schließlich will ich Gänge graben, und keine Räume erweitern).
• Anzahl der gegrabenen Tiles darf 10 nicht überschreiten.
• Wenn die Funktion einen gültigen Übergang finde, wird der Tunnel "real" gegraben
• Aktualisieren der Map-Info (Liste der freien Felder)
• Löschen des aktiven Elementes von der offenen Liste
• Solange wiederholen, bis eine maximale Anzahl von Loops erstellt wurde (max = Random(dungeonSize,dungeonSize/2), wobei dungeonSize = Prozentwert des effektiv gefüllten Spielfeldes), bzw. die offene Liste ein #Null zurückgibt.
• Schleife verlassen
• Sicherstellen, daß alle Türen geschlossen sind (Kollision <> 0)
• Jedes Türfeld einzeln aufrufen, und mit A*-Pfadsuche versuchen, einen Weg von der einen Seite der Tür zur anderen zu finden.
• Falls ein Weg gefunden wird, ist die Tür obsolet und wird gelöscht
• Aktualisierung der Map-Info


Optimierungsbedarf:
- Überprüfen, ob ein Gang an jeder Seite eine Tür hat; in diesem Fall eine Seite zufällig wählen und die Tür löschen.
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Roguelike Diary 11: PCG Part III. - Improved Dungeon Layouts

Verbesserte Layouts:
• Beim Spawnen der initialen Architektur ist zunächst jede Instanz ein Raum, welcher über Türen mit dem Rest des Dungeons verknüpft ist.
• Jede Instanz bekommt eine einzigartige ID, wobei 1 für Türfelder reserviert ist; gerade Zahlen für Gänge, ungerade für Räume.
• Anschließend werden sämtliche Türen gezählt und in der Variable maxDoors gespeichert.
• Jetzt wird das Layout "gefixt", das heißt primär werden Sackgassen entfernt (das geht einfach: jedes Tile, welches in exakt 3 von 4 Kardinalrichtungen an eine Mauer angrenzt, ist eine Sackgasse und wird auf Null gesetzt. Solange wiederholen, bis die Routine ein noError = #True zurückgibt).
• In derselben Schleife werden Türen entfernt, welche auf beiden Seiten eine GangID (gerader Instanz-Index) haben.
• Immer wenn eine Tür entfernt wird, werden mittels eines FloodFill-Verfahrens alle Tile-Übergänge mit dem am nächsten verfügbaren Raum-Index überschrieben (falls möglich).
• Ansonsten wird auf die nächst verfügbare Gang-ID zurückgegriffen.
• Außerdem werden die an beiden Seiten des Türfeldes befindlichen Mauerfelder so weit wie möglich eingerissen.
• Jetzt werden Gänge lokalisiert, welche an mehr als an einer Seite eine Tür haben:
• Durch alle Türfelder cyclen und die Koordinaten, sowie die nächstliegende Gang-ID, auf eine offene Liste setzen.
• Das erste Element herauspicken und auf eine sekundäre offene Liste setzen.
• Mit dem Rest der Liste vergleichen: jede weitere Tür mit derselben Gang-ID kommt auf die sekundäre offene Liste.
• Falls ListSize(pickTile()) > 1, wird zufällig ein Element aus der sekundären offenen Liste gepickt, und gelöscht.
• FloodFill, Map-Info aktualisieren, alle offene Listen löschen, Schleife solange wiederholen, bis keine Türen mit doppelten Gang-IDs existieren.
• Abhängig von dungeonSize eine Mindestmenge an Loops kreieren (an dem Verfahren hat sich nichts geändert - ein Tweak erlaubt jetzt aber, daß neu gefundene Verbindungen ebenfalls auf die offene Liste gesetzt, und so rekursiv als Startpunkt für weitere Loops genutzt werden dürfen).
• Weitere Türen reduzieren, bis eine am Anfang festgelegte prozentuale Höchstmenge unterschritten wurde (maxDoors als Referenz).
• Durch alle Türfelder cyclen und mit A*-Pfadsuche obsolete Türen finden (siehe oben) und ggf. einreißen.
• Isolierte Mauerfelder finden und löschen.
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Roguelike Diary 12: PCG Part IV. - Interesting Shapes

So funktionierts:
• Jeder Dungeon-Level bekommt zwei Prozentwerte zugewiesen, jeweils für direkte (WallSpawn) und invertierte (TileSpawn) Manipulation.
• Direkte Manipulation:
• Der Creator cyclet durch alle freien Tiles und markiert alle Eckpunkte einer Raum-Instanz, welche keinen Pfad blockieren.
• Anschließend hat jeder markierte Punkt eine maximale Chance (Prozentwert), in ein Mauerfeld umgewandelt zu werden.
• Dieser Prozess wird insgesamt 3x durchgeführt, wobei der Prozentwert, gegen den gewürfelt wird, in jeder Generation exponentiell abnimmt.
• Indirekte Manipulation:
• Dasselbe, aber statt freier Felder werden Mauerfelder markiert, welche in diagonaler Richtung ein freies Feld "sehen".
• Die beiden Manipulationsarten sind kumulativ und werden nur durch die initial bestimmten Prozentwerte reguliert.
• Anschließend muß ein weiteres Mal die fixDeadEnd()-Prozedur aufgerufen werden.
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Bild

--> Roguelike Diary 13: Anatomy of a Dungeon

Der letzte Schritt im Video wirkt antiklimaktisch, ist aber essentiell:
• Zuvor habe ich mit dem Prim-Algorithmus einen Minimum Spanning Tree erstellt, welcher jedes Feld im Dungeon mit dem anderen verknüpft (den Prozess lasse ich invertiert ablaufen, das heißt ich "male" Mauern anstatt wie sonst üblich, Tunnel zu graben - so bleiben Korridor/Raum-Übergänge erhalten).
• Alle dabei erstellten Mauer-Tiles werden auf einer sogenannten Black Shopping List gespeichert.
• Alle übrigen "Nicht Tür"-Tiles kommen auf die White Shopping List.
• Als nächstes "bombe" ich einen speziell für diesen Dungeonlevel geseedeten Prozentsatz an Tiles, welche sich auf der Black Shopping List befinden weg - aktualisiere die Änderungen aber nur für die worldMap(), die Informationen bleiben auf der Black Shopping List erhalten!
• Jetzt wird aufgeräumt, indem lose und diagonal verknüpfte Tiles ebenfalls weggebombt werden (auch hierbei werden Informationen auf der Black Shopping List nicht angerührt).
• Und, voila, jetzt kann ich lustig den Dungeon mit Dekorationen und Schatztruhen bevölkern, indem ich mirzufällige Koordinaten von der Black Shopping List grabsche, und ich kann mir 100% sicher sein, das keines dieser Objekte den direkten Weg versperrt (Items bedienen sich übrigens von der White Shopping List).

Und was Effizienz angeht ...
Das oben veranschaulichte Verfahren liefert mir 1000 Dungeon-Layouts in rund 50 Sekunden! 8)

Nächser Milestone: Lock & Key-System!
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Roguelike Diary 14: Portals and improved Monster Navigation

Ihr habt länger nichts von mir gehört, was nicht heißt, ich wäre faul gewesen ... <)
Habe weiter an Routinen für prozedurale Dungeon-Generierung geschraubt (liege gut im Zeitplan meiner selbstgesetzten Deadline), ein paar Bugs gefixt, außerdem KI-Verhaltensmuster getestet, und es gibt jetzt Portale!
Das besondere: wenn Monster sehen, wie man durch ein Portal schreitet, updaten sie ihren letzten "Search"-Fixpunkt auf die Koordinaten des Portals, will heißen, sie folgen einem durch den Teleporter!
Und wie super ist eigentlich der RotateSprite()-Befehl? :shock: 8)
Für den Zielpunkt des Teleporters verzichte ich auf einen check(), ob dort bereits ein Monster steht. Das würde nur Tür und Tor für allerhand buggy emergent behavior öffnen; es kann zwar passieren, daß mehrere Monster nach einer Teleportation auf demselben Feld landen, aber spätestens nach den ersten Schritten löst sich die Kuschelei wieder auf, und sowohl Code als auch der KI-Decision Tree sind derart flexibel gestaltet, daß sie ohne weitere Regeln gut mit der Situation klar kommen (die Monster regeln das quasi "unter sich").
Portale können später alle möglichen Aufgaben erfüllen ... zum Beispiel sind später auch Treppen und Übergänge zu anderen Dungeon-Ebenen effektiv Portale (vong 1 funktionsweise her /:-> ).

Nächster Milestone: Lock & Key-System (diesmal wirklich)!
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Ich muß umdisponieren ... /:->
Eigentlich wollte ich ein paar Schlüssel-Items basteln und daraufhin das Lock&Key-System angehen, bevor mir gewahr wurde, daß man die Schlüssel aufsammeln muß, und mein starres Inventarsystem dies momentan nicht zulässt ... bzw. momentan braucht jeder Itemtyp einen festen Slot, an den er gehört (Melee-Slot, Ammo-Slot, etc.). Schlüssel bringen das System durcheinander. Bzw. wenn ich jetzt anfangen würde, zusätzliche Inventar-Funktionalitäten zu hardcoden (Slot für rote Schlüssel, Slot für gelbe Schlüssel, etc.), stehe ich irgendwann vor einem unübersichtlichen Code-Salat, der irgendwie funktioniert, aber keinerlei Modifikationen mehr zulässt. Und so ein System umzuwerfen und zu patchen ist eine Arbeit, die ich mir nicht antun möchte ... also warum das Problem nicht angehen, wenn die Komplexität noch überschaubar ist?

• Jedes Item wird gleichwertig behandelt.
• Items werden nicht mehr beim Aufsammeln "ausgerüstet", sondern landen im allgemeinen Inventar.
• Wenn man ein Item aufnimmt, wird der nächste freie slotIndex gesucht, bzw. für stapelbare Items der nächste slotIndex vom selben Itemtyp, welcher noch Kapazität übrig hat.
• Das Item bekommt den Platz zugewiesen, und der entsprechende Slot wird als "voll" geflaggt, bzw. die Stacks aufaddiert.
• Stapelbare Items können entweder unendlich viel Kapazität haben (Schlüssel), oder eine gewisse Höchstmenge pro Slot (Munition).
• Wenn der Spieler versucht, z.B. 20 Stacks Pfeile aufzuheben, aber die Höchstmenge nur 15 ist, wird jeder Pfeil in einer Schleife einzeln aufgehoben und nach jedem Stack+1 neu überprüft, wann der aktuelle Slot "voll" ist - wenn ja, wird der nächste freie Slot gesucht (ggf. Kopie des Items anlegen). Dies wird solange wiederholt, bis entweder alle Pfeile aufgehoben wurden, oder kein freier Slot mehr übrig ist. In jedem Fall gibt die Prozedur entweder ein #False (wenn nichts aufgehoben wurde), oder ein #True (wenn mindestens 1 Pfeil aufgehoben wurde) an den Requester zurück.
• Ein Rechtsklick auf ein Inventarfeld leert den Slot, und das Item wird fallengelassen - sofern die aktuelle Spieler-Koordinate frei ist.
• Container-Funktionalität steht weiter unten auf der Wunschliste - so etwas wasserdicht zu programmieren ist aber anders komplex :bluescreen: ... momentan ist es absolut ausreichend, wenn auf jedem Tile der worldMap maximal ein Item liegen darf.
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Roguelike-Diary (WIP ...)

Beitrag von diceman »

Und hier ist sie, meine Item-Pickup-Routine. :)
Und da jeder actor() vom Typ #monsterActor automatisch bei Erstellung ein Inventory()-Element zugeteilt bekommt (sofern slotsNum > 0), funktioniert die Routine theoretisch auch für Monster.

getFreeSlotIndex() liefert die Adresse für den nächsten gültigen Slot. Wenn das aufzuhebende Item stapelbar ist, sind auch bereits belegte Slots gültig, sofern Item vom selben Typ und Kapazität vorhanden (stacks < stackable).
stackable = -1 bedeutet, daß von diesem Item theoretisch unendlich viele in einem Slot stapelbar sind.

updateStacks() löscht primär "leere" itemActors (stacks = 0), gibt deren Slots frei, und regelt weitere, noch nicht implementierte Features (zusammenfassen von Items, wenn mehrere Stackables vom selben Typ auf einem Feld gedroppt werden ... erkennen ob mehrere Items auf einem Feld sind um ggf. ein Container-Item zu erstellen ... etc.).

Das Macro _EachActor(#isActorPointer) zusammen mit _EndEach() ist mein größter Freund geworden ...
Dahinter versteckt sich ein multiArray, welches *pointer-Adressen speichert, und mit dem ich blitzschnell die Actor-Liste auf bestimmte gültige Typen filtern kann.
Und da jeder neu erstellte actor() immer über die actorFactory() gehen muß, entfällt auch jeglicher Verwaltungsaufwand, da dies automatisch geregelt wird.
Ebenso wird das Array automatisch re-dimensioniert und neu aufgebaut, sobald ein actor() aus der Liste gelöscht wird.

Bild
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Antworten