Zufallsgenerierter Ort

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Zufallsgenerierter Ort

    Hey Leute,
    ich arbeite gerade an einem Spiel(wer hätte es gedacht) und wollte eine einigermaßen Zufallsgenerierte Karte haben.
    Mein Spiel ist in 2D Top-Down mit einem 16x16p Raster und meine Vorstellungen sind folgende:
    Es sollen vorgefertigte "Räume" entstehen, diese werden anschließend miteinander verbunden.
    "Räume" bedeutet, dass ich einen großen Raum(das was unter dem Ordner Rooms zu finden ist) habe, in dem Objekte(="Rooms") platziert werden und diese Objekte wieder rum erschaffen dann den Inhalt wie Gegner des "Raumes".
    Bisher habe ich jedoch nur die Möglichkeit "Räume" auf dem Raster zu erstellen, jedoch überschneiden sich diese und sie sind nicht miteinander verbunden.
    Meine Fragen sind dementsprechend: Wie könnte ich es schaffen,dass sich die "Räume" nicht überschneiden lassen und vor allem wie ich sie miteinander verbinde?
    Ganze Codebeispiele sind eigentlich nicht nötig, eine Erklärung genügt vorerst.

    Danke im Voraus
  • Je nach gewünschter Funktionalität kann Zufallsgenerierung extremst komplex werden (am meisten für Programmieranfänger).
    Die besten Ergebnisse, die ich bis jetzt erzielen konnte, sehen ungefähr so aus:

    (Entschuldigung für die sehr verwirrende ASCII-artige Grafik)

    Wenn ich mich in den Code noch mal hineinarbeiten würde, könnte ich vielleicht auch Gänge usw. machen (für meine Zwecke hat diese Generierung aber gereicht).
    Die Größe des "Gebäudes" ist komplett dynamisch, generell ist alles sehr anpassbar, sodass man z.B. die Räume sehr zerstört oder nicht-zerstört machen kann.
    Den Source Code habe ich noch, wenn also "Interesse" besteht, kann ich versuchen ihn bzw. die Mechanismen etwas zu erklären.
    Es funktioniert alles über ds_grid und ein paar Objekten, die aber nur optional sind (bzw. die ich für mein Projekt gebraucht habe).
  • Ich habe selber nochnicht viel Erfahrung mit zufällig generierten Räumen, aber wenn ich mich daran versuchen müsste würde ich es wie folgt machen:

    Zu Beginn des Raumes wird mithilfe einer global. variable festgelegt, wie viele Räume die generierte Fläche haben soll. Um das ganze etwas simpel zu halten mach ich das Level etwas kleiner, sagen wir "6" ist die maximale Raumzahl. Zudem müssen wir noch "vorgebaute" Räume festlegen (einfachhalber 3 auf 3 objekte breit, wobei es nur Wand-objekte(schwarz) und "raumöffnungs"-objekte(gelb) gibt).


    Das sind damit alle Räume, die für unsere Generierung nötig sind.
    Neben dem global.Raumgröße-Wert würde ich noch einen lokalen "zeit" wert einplanen. Damit es keine Probleme damit gibt, was im StepEvent vorher oder nachher passiert, wird ein Raum nur alle paar Steps über den "zeit" Wert erweitert.

    Und jetzt geht es ans Programmieren: um das ganze zu Steuern wird ein unsichtbares Generierungsobjekt benutzt, dass sich später selbst zerstört, wenn der Level beginnt.

    GML-Quellcode

    1. //Create-Event Generierungsobjekt
    2. global.Raumgröße=6// <-anzahl der räume, die noch generiert werden dürfen
    3. zeit=0// <- zeitwert, um probleme mit step zu vermeiden
    4. raumnext=0//<-raumnext ist der Raum, der generiert wird, die Nummern und der zugehörige Raum stehen oben
    5. global.spiel=0//wenn spiel=1 kann der spieler sich bewegen, dh das Level ist fertiggeneriert
    6. custom_x=Generierungsobjekt.x
    7. custom_y=Generierungsobjekt.y
    8. //custom_x/y is die Stelle, von der aus ein Raum generiert wird. Die Stelle wird auf ein zufälliges raumöffnungsobjekt gelegt, welches dann bestimmt in welche Richtung ein Raum generiert wird


    Damit haben wir unsere mathematischen Größen, was jetzt wichtig ist, ist zu bestimmen, welche Räume generiert werden dürfen.
    Wenn ein Raum generiert wird, wird mit ihm auch eines der "raumöffnungs"objekte generiert. Für jede Raumöffnung wird mindestens ein Raum generiert, esseiden, die raumöffnungen liegen am selben Raumteil an(weswegen ein Raum zwei Raumöffnungen entfernen würde), damit weiß das Generierungsobjekt, welche Räume noch generiert werden dürfen. Fängt das Programm etwa damit an eine Kreuzung mit 4 Öffnungen zu generieren, sinkt der global.Raumöffnungswert von 6 ( für 6 Räume insgesamt) auf 1 (für einen Raum, der zusätzlich noch generiert werden darf) (, das heißt von den 4 Räumen, die an die Kreuzung anliegen, darf nur einer zwei Ausgänge (also keine Sackgasse) haben, da wir den ersten, Kreuzungsraum, die 4 Räume, die an die Kreuzung anliegen, und dann noch einen zusätzlichen benötigen, um auf die 6 räume zu kommen.


    GML-Quellcode

    1. //Step-Event Generierungsobjekt
    2. var gridX, gridY;
    3. gridX=10*irandom(300);
    4. gridY=10*irandom(300);
    5. while (place_meeting(gridX,gridY,raumöffnungsobjekt))
    6. {
    7. gridX=10*irandom(300);
    8. gridY=10*irandom(300);
    9. }
    10. custom_x=gridX; custom_y=gridY;//mit dem obenstehenden codefragment kenne ich mich ehrlichgesagt nicht gut aus, es hat mir allerdings in der Vergangenheit schonmal geholfen. 10 ist die breite eines Objekts (ein block in unserem 3x3 großen raumteilen), 300 die Raumbreite/höhe (womit wir 30 "blöcke" hoch und breit bauen können, ganze 10x10 Raumteile). Der Code setzt custom_x und custom_y auf ein zufällig bestimmtes raumöffnungs-objekt in deinem Raum, bevor von da aus ein Raumteil generiert wird
    11. zeit += 0.5
    12. if zeit >=4// sorgt dafür, dass einige Steps vorbeigehen, bevor die Generierung in Kraft tritt, damit Stepevents wie die Zerstörung der Raumöffnungsobjekte vorher geschehen
    13. {
    14. zeit=0// setzt den zeittimer zurück
    15. if place_meeting(custom_x,custom_y,raumöffnungsobjekt)//wenn ein öffnungsobjekt existiert, ansonsten ist noch kein raum erstellt worden -> ein zufälliger raum muss als erstes erstellt werden
    16. {
    17. if place_meeting(custom_x-10,custom_y,wand) and place_meeting(custom_x+10,custom_y-20,wand)//wenn das raumöffnungsobjekt sich mittig unten in einem anderen raumobjekt befindet
    18. {
    19. custom_y+=20 // damit custom_x/y die mitte des zu erstellenden raumes sind und nichtmehr die untere mitte des existierenden raumes
    20. if global.Raumgröße=instance_number(raumöffnungsobjekt) and !place_meeting(custom_x+20,custom_y,raumöffnungsobjekt) and !place_meeting (custom_x-20,custom_y,raumöffnungsobjekt) and !place_meeting(custom_x,custom_y+20,raumöffnungsobjekt) //wenn nurnoch soviele räume erstellt werden können, wie es raumöffnungsobjekte gibt, und nur ein raumöffnungsobjekt mit einem raumteil entfernt werden kann, muss jeder raum eine sackgasse sein
    21. {
    22. raumnext=3
    23. }
    24. // an dieser stelle müsste der ganze code dafür stehen, dass zum beispiel, wenn links rechts über und unter dem raum jeweils ein gelbes öffnungsobjekt sitzt, zwangsweise die Kreuzung mit 4 ausgängen entsteht, allerdings bin ich mir ehrlich gesagt zu faul das alles runterzuschreiben, deswegen steht hier nur der Code für den fall, das es keine weiteren anliegenden räume gibt
    25. else if global.Raumgröße>=4// hier müsste noch ein else davor, wenn darüber der code für die kreuzungen usw steht
    26. {
    27. raumnext=choose(6,7,8,11,12,14,15)//ist raumgröße >=4 kann jeder raum platziert werden, der oben einen ausgang hat, da selbst bei 3 weiteren anliegenden Räumen die Raumzahl nur min um 4 steigt (den erstellten raum an sich mit einbezogen)
    28. }
    29. else if global.Raumgröße=3
    30. {
    31. raumnext=choose(6,7,8,11,12,14) // 15 bleibt aus, weil zu wenig raumgröße vorhanden ist
    32. }
    33. else if global.Raumgröße=2
    34. {
    35. raumnext=choose(6,7,8)//dito
    36. }
    37. }
    38. else if place_meeting(custom_x-20,custom_y+10,wand) and place_meeting(custom_x,custom_y-10,wand) // wenn der ausgang rechts mittig ist
    39. {
    40. custom_x+=20 // wieder in die mitte rutschen
    41. //hier das gleiche mit dem "wenn global.Raumgröße=instance_number"für die sackgasse
    42. //hier das gleiche mit dem "wenn links und rechts und oben und unten dann kreuzung"s zeug
    43. //hier das gleiche mit dem "wen keine nebenliegenden räume dann choose was zufälliges, abhängig von der verbleibenden raumgröße"
    44. }
    45. //und das ganze nochmal 2 mal für die ausgänge links und oben
    46. }
    47. else if !place_meeting(x-10,y-10,wand)//wenn am generierungsobjekt nochnichts generiert wurde, wird der erste raum generiert
    48. {
    49. if global.Raumgröße>=5
    50. {
    51. raumnext=choose(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)//ist raumgröße >=5 kann jeder raum platziert werden, da selbst bei 4 anliegenden Räumen die Raumzahl nicht zwangsweise größer 5 ist
    52. }
    53. else if global.Raumgröße=4
    54. {
    55. raumnext=choose(1,2,3,4,5,6,7,8,9,10,11,12,13,14) // 15 bleibt aus, da durch 4 ausgänge mehr als 4 räume generiert werden würden
    56. }
    57. else if global.Raumgröße=3
    58. {
    59. raumnext=choose(1,2,3,4,5,6,7,8,9,10)// räume mit 2 ausgängen
    60. }
    61. else if global.Raumgröße=2
    62. {
    63. raumnext=choose{1,2,3,4)// aus welchem grund auch immer ein generierter level nur 2 räume haben sollte
    64. }
    65. }
    66. if raumnext=1
    67. {
    68. //die raumobjekte werden um die custom_x/y koordinaten herumerstellt
    69. instance_create(custom_x-10,custom_y-10,wand)//linke obere ecke des raumteils1
    70. instance_create(custom_x,custom_y-10,wand)//mitte oben
    71. instance_create(custom_x+10,custom_y-10,wand)//rechts oben
    72. //undsoweiter, das halt mit allen wandobjekten in dem raumteil
    73. global.Raumgröße-=1 // ein raum wird erstellt, deswegen wird die gesamtzahl der Räume, die noch erstellt werden, eins herabgesetzt
    74. raumnext=0 //damit nicht irgendein raum generiert wird wenn was schiefläuft
    75. }
    76. else if raumnext=2
    77. {
    78. //das selbe zeug für die blöcke von raumteil2
    79. }
    80. //undsoweiter, halt mit allen raumnext-werten die man hat
    81. else
    82. {
    83. global.spiel=1// spiel startet, wenn startraum erschaffen ist und es keine raumöffnungen mehr gibt
    84. instance_destroy()
    85. }
    86. }
    Alles anzeigen



    Da der Ort, an dem die Räume generiert werden, von den gelben "raumöffnungs"objekten abhängt, müssen wir, bevor wir den nächsten Raum generieren, die Raumöffnungsobjekte zerstören, an denen bereits ein nächster Raum generiert wurde. Am einfachsten geht das denke ich, indem wir einfach im Step event die Raumöffnungs-Objekte zerstören, die neben einem Raumöffnungsobjekt sind, da ja sowohl Raumaus- und eingang eines der Objekte hat.

    GML-Quellcode

    1. //Step-Event raumöffnungsobjekt
    2. if place_meeting(x+10,y,raumöffnungsobjekt) or place_meeting(x-10,y,raumöffnungsobjekt) or place_meeting(x,y+10,raumöffnungsobjekt) or place_meeting(x,y-10,raumöffnungsobjekt)
    3. {
    4. instance_destroy()
    5. }




    Ich bin mir sicher, dass es bessere Lösungsansätze gibt, aber der hier ist mir spontan eingefallen, falls darin Fehler sind entschuldige ich mich schonmal im vorraus.

    Ich hoffe ich konnte trotzdem ein bisschen helfen und einige Anhaltspunkte liefern, obwohl ich selber nochnicht so gut bin und der Code so gut wie absolut keine Struktur hat :)

    Mfg
    Falcfire

    Edit: Mehr Struktur im Code
    Im Internet weiß niemand, dass du ein Truthahn bist.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Falcfire ()

  • derteast schrieb:

    Da ich keinen Gebäudekomplex generieren möchte, benötige ich deine Methode vorerst nicht, da mein größtes Problem beim Versuch die einzelnen "Räume" zu verbinden besteht. Da habe ich nämlich keinen Anhaltspunkt wie ich das irgendwie machen könnte.

    Inwiefern sind den die einzelnen "Räume"(raum-objekte?) nicht verbunden?
    Wenn sie einfach zu weit voneinander entfernt generiert werden, dass pass doch einfach den Code so an, dass sie direkt nebeneinander generiert werden, bzw dass der Abstand, in dem sie generiert werden, von der größe des "Raum"-Objekts abhängig ist.
    Im Internet weiß niemand, dass du ein Truthahn bist.
  • Mein Ziel war es, ein paar Objekte zu platzieren und diese dann miteinander zu verbinden, ohne das zwischen diesen beiden Objekten weitere da sind. Ein Controllerobjekt oder dergleichen sollte den Weg freiräumen(vorerst besteht der ganze Raum aus soliden Wänden). Jedoch ist deine Methode denke ich mal besser für mein Vorhaben, deshalb vielen Dank!