Unreal Engine Procedural Level Generation System

  • Tutorial
Bild

Hallo, ich heiße Dmitry. Ich mache Computerspiele auf der Unreal Engine als Hobby. Heute werde ich Ihnen erzählen, wie ich die prozedurale Generierung von Levels für mein Spiel „The Future City Project“ erstellt habe. Alle Quellen werden am Ende des Artikels vorgestellt. Und es wird eine Überraschung geben.

Algorithmus


Zuerst müssen Sie klarstellen, dass mein Spiel ein Ego-Action-Spiel mit Parkour ist. Daher sollte das Niveau nicht flach sein, dh es muss einige Höhenunterschiede geben, zu denen Sie im Gegenteil klettern oder springen können. Als Kulisse für das Spiel wählte ich außerdem die Stadt der Zukunft mit riesigen Wolkenkratzern und fliegenden Hovercars, was auch Anforderungen an den Level-Generierungsalgorithmus stellte.

  1. Da es sich bei dem Level um eine Stadt handelt, muss ein Straßennetz erstellt werden. Mein Straßennetz besteht aus zwei Arten von Straßen: Dies sind die Hauptstraßen und normale Straßen. Die Hauptstraßen unterscheiden sich von den üblichen dadurch, dass sie erstens breiter sind und zweitens die Stadt durchqueren, während gewöhnliche Straßen nicht länger als ein Viertel sind. Als Ergebnis erhalten wir genau ein solches Bild (ich entschuldige mich dafür, dass ich schief bin):

    Bild
  2. Jetzt haben wir Straßen und Inseln zwischen ihnen. Diese Inseln müssen mit etwas gefüllt werden. Dazu generiere ich ein Diagramm, das ein rechteckiges Gitter ist. Jeder Knoten dieses Diagramms ist ein Raum, der sich zufällig im Raum einer bestimmten Insel befindet (natürlich kann der nächste Knoten innerhalb bestimmter Grenzen nicht vor dem vorherigen liegen). Die Zimmer bestimme ich im Voraus. In diesem Fall kann es mehrere Diagrammebenen geben, um eine mehrstufige Struktur zu erhalten. Und damit sich diese Ebenen in der Nähe von Graphen überlappen, haben sie gemeinsame Knoten (Räume), die zufällig bestimmt werden.
  3. Jetzt müssen nur noch diese Knoten (Räume) entsprechend ihrer Position im Diagramm mit Korridoren verbunden werden. Dazu habe ich den "gierigen" Pfadfindungsalgorithmus verwendet. Das heißt, ich nehme einen der Abschnitte des Korridors (Abschnitte des Korridors sowie die Räume werden im Voraus festgelegt) und lege ihn zum Ausgang des Raums. Dann mache ich dies mit allen anderen Abschnitten und wähle den Abschnitt aus, dessen Ausgang dem gewünschten Punkt am nächsten liegt (der Eingang zu einem anderen Raum). Danach mache ich dasselbe, aber ich wende bereits Abschnitte auf den Abschnitt an, den Korridor, der während der vorherigen Iteration gefunden wurde, und so weiter bis zum siegreichen Ende.
  4. Nach dem Erstellen der Diagrammstruktur innerhalb der Inseln. Es ist notwendig, die Inseln miteinander zu verbinden. Zu diesem Zweck befindet sich auf den Nachbarinseln um den Raum herum, die einander am nächsten liegen. Dann sind diese Räume genauso verbunden wie die anderen.
  5. Wir haben also bereits ein Netzwerk, das aus Räumen besteht, die durch Korridore verbunden sind. Aber sie hängt in der Luft, um etwas zu tun. Nun, erstens können Sie jede der „Inseln“ zu einem Wolkenkratzer machen, und die Räume und Korridore werden jeweils die Räume und Korridore dieses Wolkenkratzers sein. Im Prinzip habe ich das mit 1/5 aller Inseln gemacht. Aber dies mit allen zu tun, wird sehr langweilig sein.
  6. Bei den restlichen 4/5 Inseln habe ich das getan. Die oberen Räume und Korridore bilden eine Art Dach. Die Räume, die unter ihnen erschienen, erscheinen im Innenraum der „Insel“, aber ich fülle den Raum zwischen ihnen nicht wie bei Wolkenkratzern aus, sondern lasse ihn leer, sodass wir „Plattformen“ haben, zwischen denen Sie leicht springen können. Lassen Sie mich einige Beispiele nennen:

    Bild

    Hier können Sie sehen, wie die oberen Räume und Korridore zu einem einzigen Dach verschmelzen.

    Bild

    Und hier können Sie den Innenraum einer solchen Insel sehen.
  7. Aber es stellt sich heraus, dass eine solche Insel nur in der Luft schwebt. Um dies loszuwerden, habe ich den Inseln Türme hinzugefügt. Welches wird sie unterstützen.
  8. Eigentlich ist das Level bereits fertig, aber es würde nicht schaden, ein paar Elemente hinzuzufügen, um es schöner zu machen. Zunächst habe ich unten ein Modell des Gebäudes hinzugefügt, damit es nicht so aussieht, als ob sich die Stadt über der Wüste befindet. Hier sind sie:

    Bild
  9. Außerdem habe ich Hovercars hinzugefügt, die über die Straßen fahren.

Die gesamte prozedural erzeugte Stadt ist fertig.

Wie man es benutzt


Nun erkläre ich Ihnen, wie Sie das Plugin verwenden, das Sie von GitHub herunterladen. Nachdem Sie das Projekt geöffnet haben, wird nur eine Karte angezeigt, auf der sich nur ein LevelGenerator-Objekt befindet (Wenn Sie dieses Objekt auf die Karte ziehen möchten, es jedoch nicht im Inhaltsbrowser sehen, aktivieren Sie das Kontrollkästchen ShowPluginContent).

Also hier sind seine Einstellungen:

  1. Generieren, Rebild-Ebene, Löschen-Ebene - Schaltflächen werden zum Debuggen verwendet. Mit deren Hilfe können Sie eine Ebene generieren, neu erstellen oder löschen.
  2. Set Generate Seed - Verwenden Sie eine Zufallszahl zum Generieren oder eine bestimmte
  3. MainMenuLevel - Wird für die Rückseite des Hauptmenüs vom Hauptmodus verwendet und unterscheidet sich darin, dass es keinen Renderzyklus gibt, der die Stadt zeichnet, wenn sich der Charakter bewegt
  4. Reihenfolge generieren - Ermöglicht das Festlegen des Zeichenbereichs für verschiedene Module (die Module selbst können in der Datei LevelGenerator.cpp festgelegt werden). Die Module sind in "Datenfüller" und "Füllstandsbilder" unterteilt, die ersten Daten werden generiert und die zweiten Daten verbergen den Füllstand vor diesen Daten. Bereichsnummern geben den Radius an, in dessen Mitte sich das Zeichen befindet.
  5. Als nächstes folgen die Materialeinstellungen für bestimmte Umgebungselemente.
  6. RoadFrequency - Die durchschnittliche Anzahl von Zellen zwischen Straßen. Je größer die Zahl, desto seltener die Straße.
  7. MainRoadFrequency - Die durchschnittliche Anzahl regulärer Straßen, die die Hauptstraßen teilen.
  8. RoadSize - Breite einer normalen Straße in Zellen
  9. MainRoadSize - Breite der Hauptstraße in Zellen
  10. TowerFrequency - wie viele Inselzellen auf einen tragenden Turm fallen.
  11. FullBildingTowersFrequency - Die Anzahl der Inseln, die einen Wolkenkratzer im Verhältnis zum Rest darstellen. Je größer die Zahl, desto weniger sind sie.
  12. FloorNum - Die Anzahl der Stockwerke der Ebene. Es ist besser, diese Zahl als Vielfaches der Anzahl der Ebenen des Diagramms + 1 zu nehmen
  13. CellSize - Die Größe der Zelle.
  14. CellHeight - Zellenhöhe
  15. FirstCityFloor und SecondCityFloor - Diese Werte bestimmen, wie hoch der Pegel vom Boden ist.
  16. ActorTag - Generierte Ektoren werden in einem Ordner mit diesem Namen abgelegt.
  17. GraphNodsFrequency - Bestimmen Sie den durchschnittlichen Abstand zwischen Diagrammknoten. Je größer die Zahl, desto seltener das Diagramm.
  18. GraphLayerNum - Die Anzahl der Diagrammebenen.
  19. HoverCarTrackLayers - Enthält Ebenen mit Hovercars. Jede Schicht hat Parameter wie: Höhe des Luftkissenfahrzeugs, Flugrichtung des Luftkissenfahrzeugs und durchschnittliche Entfernung zwischen ihnen.
  20. Wandstärke - Wandstärke der erzeugten Netze. Ich möchte hinzufügen, dass die Dicke der Decke ebenfalls dieser Zahl entspricht.
  21. ComplicitySpawnForTick - Die Gesamtkomplexität der generierten Ektoren in einem Tick. Je höher die Zahl, desto größer kann die Verzögerung beim Spawn und Ruckeln während des Spiels sein. Sie fragen, warum nicht einfach die Anzahl der Ektoren festlegen, die pro Tick generiert werden können. Und weil verschiedene Ektoren unterschiedliche Zeit für ihren Spawn benötigen. Sie können die Komplexität für jeden einzelnen Ektor in der Datei VirtualSpawner.h sehen.
  22. DeltaCellForGeneration - Die Anzahl der Zellen, die der Charakter verschieben muss, um mit der Rekonstruktion der Ebene zu beginnen.
  23. MinBildingZoneSize - Die Mindestgröße der Insel in Zellen. Wenn eine Insel plötzlich
    breit oder kleiner als dieser Wert erscheint, wird sie mit der benachbarten verbunden.

Und dann gibt es drei Repositorys (die Repository-Daten werden durch Klicken mit der rechten Maustaste auf den Unterabschnitt LevelGen erstellt):

Towerstorage


In TowerStorage - Es gibt zwei Arten von Objekten:

  1. Twers sind Objekte, die von der LevelTowerActorTower-Klasse abgeleitet sind. Sie werden bestimmen, wie die Türme durch die Inseln aussehen werden.
  2. Bilder sind Objekte, die von der LevelTowerActorBilding-Klasse abgeleitet sind. Bestimmen Sie das Erscheinungsbild von Gebäuden, die am unteren Rand des Levels erscheinen.



Bild

Wenn Sie also eine der Blaupausen erstellen, sehen wir Folgendes : Das BordersShower-Objekt ist im Konstruktionsbaum vorhanden. Es wird benötigt, damit Sie verstehen können, ob Ihr Modell in den darunter ausgewählten Bereich fällt oder außerhalb der Grenzen kriecht.

  • Größe - Größe in Zellen bemerkt.
  • BildingHeight - Bestimmt die Höhe des Gebäudes, wenn der Wert 1 ist, dann sollte er SecondCytiFlore (rote Farbe) nicht überschreiten, wenn 2, dann sollte er FistCytiFlore + SecondCytiFlore nicht überschreiten.
    Dies ist notwendig, da nur Gebäude mit BildingHeight = 1 unter den Straßen erscheinen, um den Hovercars Platz zu geben.
  • CellSize - Die Zellengröße wird nur benötigt, damit die BorderComponent korrekt angezeigt wird, da CellSize beim Generieren eines Levels aus den LevelGenrator-Einstellungen übernommen wird.
  • FistCytiFlore, SecondCytiFlore - genau das gleiche wird nur benötigt, damit die BorderComponent korrekt angezeigt wird.

Raumlagerung


In RoomStorage gibt es Objekte, die die Parameter von Räumen bestimmen, die auf der Ebene platziert sind.

  1. NodeRooms - Räume in den Knoten des Diagramms. Abgeleitet von LevelRoomActorNode
  2. GroundLinkRooms - Dies sind Abschnitte der Korridore, die NodeRoom verbinden. Abgeleitet von LevelRoomActorLink
  3. RoadLinkRooms - Dieser Abschnitt wird eingefügt, wenn der Korridor die Straße überquert. Abgeleitet von LevelRoomActorRoadLink
  4. TerraceLinkRoom - Dies sind Abschnitte von Korridoren, die sich oberhalb der Straße entlang der Wände befinden. Abgeleitet von LevelRoomActorTerraceLink

Nachdem wir einen der Räume erstellt haben, sehen

Bild

wir Folgendes : Wie im vorherigen Fall sehen wir BordersShower - aber es gibt bereits andere Parameter:

  • JointSlots - Bestimmen Sie, wo andere Räume beitreten sollen. Um sie festzulegen, müssen Sie die relative Koordinate und Richtung von 4 bestimmen. Wenn sie nicht richtig definiert sind, wird der Steckplatz in WrongSlotsMaterial gezeichnet.
  • RoomSize - Die Größe des Raums in Zellen. Alle Räume sind rechteckige Parallelepipeds.
  • RoomWals - Sie können eine raumbegrenzende Wand wie JointSlot hinzufügen. Warum werden sie gebraucht? Je nach Standort hat der Raum möglicherweise keine Wände, und wenn hier Wände hinzugefügt werden, sind dies immer der Fall.
  • CanPlayerSpanw - Kann ein Charakter in diesem Raum erscheinen?
  • CellSize, CellHeight, WallTicness - Ebenso werden sie nur für das Rendern von BordersShower benötigt, sie haben keinen Einfluss auf die Levelgenerierung.

Hovercarstorage


HoverCarStorage enthält Objekte, die Parameter von Hovercars definieren. Diese Objekte werden von HoverCarActor abgeleitet. Folgendes werden

wir beim Erstellen eines solchen Objekts sehen:

Bild

Dieses Objekt verfügt bereits nicht über BordersShower und der einzige Parameter ist Speed, der die Geschwindigkeit des Browsers bestimmt. Der ForwardCarDistance-Parameter wird in keiner Weise verwendet.

Das ist alles.

Plugin-Link herunterladen.

Überraschung


Diejenigen, die noch bis zum Ende lesen, warten auf die versprochene Überraschung, und sie werden mein Spiel „The Future City Project“. Das Spiel ist ein Ego-Action-Spiel mit Parkour in einer prozedural erzeugten Welt.

Download-Link "The Future City Project"

Jetzt auch beliebt: