Hey, Mich würde mal interessieren ob jemand von euch Erfahrung damit hat 2D Karten zu generieren.Wie macht ihr das, welchen Algorithmus benutzt ihr? Ich habe mir mal den "diamond square algorithmus" angeschaut ihn aber nicht ganz verstanden , es wäre super wenn mir jemand von euch erklären könnte wie man sowas angeht , zumal mein Englisch nicht das beste ist aber die wenigen Tutorials die es gibt auf englisch sind.Bei solchen Themen ist es dann schwer für mich nach zu vollziehen was genau gemacht wird.Vielen Dank im Vorraus
2D Terrian Generator & diamond square algorithmus
- VB.NET
- .NET (FX) 4.5–4.8
Sie verwenden einen veralteten Browser (%browser%) mit Sicherheitsschwachstellen und können nicht alle Funktionen dieser Webseite nutzen.
Hier erfahren Sie, wie einfach Sie Ihren Browser aktualisieren können.
Hier erfahren Sie, wie einfach Sie Ihren Browser aktualisieren können.
Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.
-
-
Also -auch wenn dein Englisch nicht so gut ist- eine sehr gute Tutorialreihe gibt es bei Brackeys: youtube.com/user/Brackeys
Wo hast du dir den DSA angeschaut? Bei Wikipedia ist der -finde Ich- gut erklärt: de.wikipedia.org/wiki/Diamond-square_Algorithmus
Wikipedia schrieb:
Ausgangspunkt für die Generierung einer fraktalen Landschaft auf Basis des Diamond-square Algorithmus ist ein Quadrat. Jeder Ecke des Quadrats wird ein Höhenwert zugeordnet. Der Algorithmus zerlegt das Quadrat rekursiv in kleinere Quadrate, wobei der Höhenwert des Mittelpunkts als Mittelwert der vier Eckpunkte, plus einer zufälligen Verschiebung, definiert wird. Analog wird der Höhenwert der Seitenhalbierenden eines Quadrats als Mittelwert der vier horizontal umgebenden Punkte, plus einer zufälligen Verschiebung, definiert. Die Verschiebung ist Normalverteilt mit einem Mittelwert von 0 und nimmt mit der Größe der Rechtecke ab. Die Mittelpunkte und Seitenhalbierende bilden die Eckpunkte der neuen Rechtecke. Ausnahme von der Regel zur Generierung der neuen Punkte bilden die vier Außenseiten des ursprüngliche Rechtecks, die jeweils nach der eindimensionalen Mittelpunktverschiebung generiert werden.
LG -
@ichduersie Hättest du vl einen Pseudo Code der mir hilft das ganze besser zu verstehen? Es ist immer noch recht kompliziert, ich wüsste nicht wie ich es umsetzen sollte
-
Thorstian schrieb:
ich wüsste nicht wie ich es umsetzen sollte
Du bist so nicht in der Lage, Fehler selbstständig zu finden und zu beheben.Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch
Ein guter .NET-Snippetkonverter (der ist verfügbar).
Programmierfragen über PN / Konversation werden ignoriert! -
-
Hi
aber es handelt sich in diesem Fall nicht um Hilfe zur Selbsthilfe, sondern um "Zeig' mir wie's geht und ich kann's kopieren". Kopieren und kapieren bringt für zukünftige Algorithmen wenig.
"Ausgangspunkt für die Generierung einer fraktalen Landschaft auf Basis des Diamond-square Algorithmus ist ein Quadrat. Jeder Ecke des Quadrats wird ein Höhenwert zugeordnet."
--> Stelle dir ein Quadrat vor, beachte im Speziellen die Eckpunkte
"Der Algorithmus zerlegt das Quadrat rekursiv in kleinere Quadrate, wobei der Höhenwert des Mittelpunkts als Mittelwert der vier Eckpunkte, plus einer zufälligen Verschiebung, definiert wird."
--> Füge einen Mittelpunkt hinzu, wodurch du das erste Quadrat zu 4 zerlegen kannst. Wenn du nun das 2D-Modell in ein 3D-Modell überträgst (wobei z die dritte Achse sei), wird z als Mittelwert der zs der anderen Eckpunkte aufgefasst (d.h. A1.Z/4 + A2.Z/4 + A3.Z/4 + A4.Z/4) und ein zufälliger "offset" draufaddiert, sodass eben eine Verschiebung stattfindet.
"Analog wird der Höhenwert der Seitenhalbierenden eines Quadrats als Mittelwert der vier horizontal umgebenden Punkte, plus einer zufälligen Verschiebung, definiert."
--> Mache das gleiche für die Seitenhalbierenden des Quadrats. Die Seitenhalbierenden ergeben sich eben, wenn du das Quadrat am Mittelpunkt viertelst. Es wird immer nur die z-Achse um ein zufälliges z verschoben
Wiederhole den Algorithmus bis zu einer gewissen Tiefe (z.B. bei einer Tiefe von 8 hättest du bereits 2^8*2^8 = 2^9 Felder, bzw. eben bei einer Tiefe von k hättest du 2^k*2^k = 2^(k+1) Felder)
"Die Verschiebung ist Normalverteilt mit einem Mittelwert von 0 und nimmt mit der Größe der Rechtecke ab."
--> Der offset soll immer kleiner werden, je kleiner das betrachtete Quadrat wird, bzw. je tiefer du in das Rechteck reinwanderst. Normalverteilt heißt, dass, große Abweichungen immer unwahrscheinlicher werden (d.h. mit der Wahrscheinlichkeit e^-m^2 auftreten), siehe Anmerkungen.
"Die Mittelpunkte und Seitenhalbierende bilden die Eckpunkte der neuen Rechtecke."
--> Bereits erläutert
"Ausnahme von der Regel zur Generierung der neuen Punkte bilden die vier Außenseiten des ursprüngliche Rechtecks, die jeweils nach der eindimensionalen Mittelpunktverschiebung generiert werden."
--> Ist klar, denn irgendwoher musst du das ursprüngliche Rechteck ja auch bekommen. Die berechnest du aber nach dem gleichen Schema: Vier Eckpunkte, eine verschobene z-Koordinate (d.h. mit Offset). Wenn du bspw. einfach nur ein Rechteck mit festem z befüllen wollen würdest, müsstest du das nat. nicht weiter angeben.
Ich kannte den Algorithmus btw. vorher nicht, aber so ähnlich hätte ich das ebenfalls angegangen.
Anmerkungen:
- Statt der normalverteilten Abweichung kannst du auch einfach 1/2^tiefe als maximale Abweichung nehmen und das mit einer Zufallszahl multiplizieren, glaube ich.
Und für Ganzzahlen gilt: 2^k = 1 << k, wobei 0 <= k < n, n ist für Byte 8, für Short 15, für Integer 31, für Long 63 (für SByte 7, für UShort 16, für UInteger 32, für ULong 64). Um auf die Gleitkommazahl zu schließen, einfach konvertieren.
Das sollte einfacher sein, als noch einen Algorithmus durchzuarbeiten ;p.
Noch ein kleiner Schnipsel:
- Ein Single-Array eignet sich sehr gut zur Darstellung eines solchen Systems. Das Array hat dann halt (2^k+1) * (2^k+1) = 2^(k+1)+2*(2^k)+1 = 2^(k+2)+1 Einträge (kann das sein?), wobei y*(2^k +1)+x, wobei 0 <= x < 2^k+1 gilt... Ich hoffe mal, du kennst diese Art der Notation. Du speicherst auf jeden Fall einfach nur den z-Wert der Koordinaten, x und y ergeben sich durch die zugehörigen x und y der Indexierung (y = index/(2^k+1), x = index mod (2^k+1)) Die 1 wird also aufaddiert, weil du ja nicht nur den Anfangspunkt, sondern auch den Endpunkt speichern musst.
Gruß
~blaze~Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „~blaze~“ ()
-
@~blaze~ vielen dank das hat mir jetzt schon sehr geholfen ich glaube ich habe es jetzt Grundlegend verstanden.Ich werde versuchen es um zu setzen. Bei Rückfragen melde ich mich wieder
Edit: Wie kann ich denn ein Quadrat am besten rekursiv zerlegen?
Mein bisheriger Code:
Spoiler anzeigen
VB.NET-Quellcode
- Option Strict On
- Imports System.Math
- Public Class Form1
- Dim map(17, 17) As Double
- Dim rnd As New System.Random
- Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
- Select Case e.KeyCode
- Case Keys.R
- gen()
- End Select
- End Sub
- Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
- Me.DoubleBuffered = True
- End Sub
- Private Sub gen()
- 'Eckpunkte
- map(1, 1) = 1.0
- map(17, 1) = 2.0
- map(1, 17) = 8.0
- map(17, 17) = 10.0
- 'Mitte
- map(9, 9) = Math.Round(map(1, 1) / 4 + map(17, 1) / 4 + map(1, 17) / 4 + map(17, 17) / 4 + rnd.NextDouble, 2)
- End Sub
- Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
- With e.Graphics
- Dim s As Integer = 50
- For x = 1 To 16
- For y = 1 To 16
- .DrawRectangle(Pens.Red, x * s, y * s, s, s)
- Next
- Next
- For x = 1 To 17
- For y = 1 To 17
- If Not map(x, y) = 0 Then
- .FillEllipse(Brushes.Red, x * s - 5, y * s - 5, 10, 10)
- Else
- .FillEllipse(Brushes.Blue, x * s - 5, y * s - 5, 10, 10)
- End If
- Next
- Next
- For x = 1 To 17
- For y = 1 To 17
- .DrawString(map(x, y).ToString, Font, Brushes.Green, x * s, y * s + 10)
- Next
- Next
- End With
- End Sub
- Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
- Me.Invalidate()
- End Sub
- End Class
Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Thorstian“ ()
-
Du verwendest jetzt ein 18x18-Array. Vergiss nicht, dass
Private map(17, 17) As Double
ein Array mit 18 * 18 Elementen bereitstellt.
Du würdest die Randwerte, d.h. map(0, 0) und map(0, 16), map(16, 16), map(16, 0) festlegen, dann eine rekursive Funktion aufrufen, die rekursiv alle festlegt, die dazwischen liegen, d.h. anfangs zwischen 0 und 16, dann zwischen 0 und 7, 7 und 16 (d.h. du berechnest genau welchen Wert hier?), dann zwischen 0 und 3 und 3 und 7, dann 7 und 11 und 11 und 16... irgendwas passt da nicht... Das ist mir gerade aber zu hoch; ich hab' für heute keine Lust mehr.
Überleg' mal selber etwas, das Grundprinzip sollte ja eigentlich klar sein.
Gruß
~blaze~ -
Ahoi,
also der Diamond Square ist wohl deine beste Wahl wenn es darum geht was zu lernen und zu verstehen. Allerdings habe ich mal für jemand anderen der eine recht ähnliche frage hatte folgende Lösung ins Auge gefasst: MyIsland UPDATE [Pre Beta 0.1] (Permalink zu meinem Beitrag zu der Frage)
Das ist in keinem Fall eine wirklich gute oder besondere Lösung aber eine Möglichkeit voranzukommen und eine gute Basis zu schaffen, guck es dir an, versuche es zu verstehen und evtl. gibts es dir genug Ideen auf Grundlage vn der Methode deine eigene zu entwickeln die für dich am besten funktioniert
Mfg
Paul
"yippieh! it compiles - ship it!" -
-
-
Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „nafets“ ()
-
Ich wollte auch mal ne total überflüssige Signatur:
---Leer--- -
-
Würde mir jemand helfen bluh.org/code-the-diamond-square-algorithm/ in Vb2010 um zu setzen bzw mir zu erklären? Es gibt einiges was ich nicht ganz verstehe da es java und englisch ist...
-
-
-
Überleg' dir doch mal, was du tun müsstest, um ein Rechteck mit einer Karte zu befüllen. Stell dir vor, du nimmst ein Rechteck, halbierst es jeweils an beiden Kanten. Dann wiederholst du das ganze jeweils für jedes Unterrechteck, ab einer gewissen Tiefe hörst du auf. Jetzt überlege, wie du das auf einen rekursiven Algorithmus übertragen könntest. Von mir aus speichere auch erst mal alle Rechtecke und erst später die Eckpunkte. Danach ordnest du noch jedem Rechteck einen z-Wert zu, sodass eine bestimmte Tiefe enthalten ist. Wenn du das gemacht hast, erhöhst du die Komplexität und passt den Z-Wert an.
Wenn du es dir einfach machen willst, verwende eine LinkedList(Of T), sobald das implementiert ist, kannst du überlegen, wie du es in ein Array packst.
Gruß
~blaze~ -
Ich habe jetzt mal folgendes Zusammengebastelt,habe ich etwas übersehen bis auf das was im Kommentar steht oder ist es soweit jetzt richtig @~blaze~ ?
Spoiler anzeigen
VB.NET-Quellcode
-
- Imports System.Math
- Public Class Form1
- Declare Function AllocConsole Lib "kernel32" () As Int32
- Declare Function FreeConsole Lib "kernel32" () As Int32
- Dim map(9, 9) As Double
- Dim map_x As Integer = 9
- Dim map_y As Integer = 9
- Dim rnd As New System.Random
- Dim A As Integer = 10
- Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
- Select Case e.KeyCode
- Case Keys.R
- map(0, 0) = rnd.Next(0, 11)
- map(8, 0) = rnd.Next(0, 11)
- map(0, 8) = rnd.Next(0, 11)
- map(8, 8) = rnd.Next(0, 11)
- DoAllTheWork(0, 8, 0, 8)
- End Select
- End Sub
- Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
- Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
- Me.Location = New Point(0, 0)
- Me.Size = New Point(500, 500)
- Me.DoubleBuffered = True
- MsgBox(rnd.NextDouble)
- AllocConsole()
- End Sub
- Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
- With e.Graphics
- Dim f As New Font("Arial", 16)
- For x = 0 To map_x - 1
- For y = 0 To map_y - 1
- .FillEllipse(Brushes.Red, x * 40 + 32, y * 40 + 32, 10, 10)
- .DrawString(map(x, y), f, Brushes.Green, x * 40 + 32, y * 40 + 40)
- Next
- Next
- End With
- End Sub
- Private Sub DoAllTheWork(ByVal x1 As Integer, ByVal x2 As Integer, ByVal y1 As Integer, ByVal y2 As Integer)
- 'Zufallswerte ggf plus oder minus und mit der Zeit geringer werdent
- Console.WriteLine("X1: " & x1)
- Console.WriteLine("X2: " & x2)
- Console.WriteLine("Y1: " & y1)
- Console.WriteLine("Y2: " & y2)
- 'MsgBox("")
- If x1 - x2 > 1 Or x1 - x2 < -1 Then
- map((x1 + x2) / 2, (y1 + y2) / 2) = Math.Round((map(x1, y1) + map(x1, y2) + map(x2, y1) + map(x2, y2)) / 4 + rnd.NextDouble, 1)
- map(x1, (y1 + y2) / 2) = Math.Round((map(x1, y1) + map(x1, y2)) / 2 + rnd.NextDouble, 1)
- map(x2, (y1 + y2) / 2) = Math.Round((map(x2, y1) + map(x2, y2)) / 2 + rnd.NextDouble, 1)
- map((x1 + x2) / 2, y1) = Math.Round((map(x1, y1) + map(x2, y1)) / 2 + rnd.NextDouble, 1)
- map((x1 + x2) / 2, y2) = Math.Round((map(x1, y2) + map(x2, y2)) / 2 + rnd.NextDouble, 1)
- DoAllTheWork(x1, (x1 + x2) / 2, y1, (y1 + y2) / 2)
- DoAllTheWork(x1, (x1 + x2) / 2, y2, (y1 + y2) / 2)
- DoAllTheWork(x2, (x1 + x2) / 2, y1, (y1 + y2) / 2)
- DoAllTheWork(x2, (x1 + x2) / 2, y2, (y1 + y2) / 2)
- End If
- End Sub
- Private Sub FPSMaker_Tick(sender As Object, e As EventArgs) Handles FPSMaker.Tick
- Me.Invalidate()
- End Sub
- End Class
-
Sicher, dass Option Strict auf On ist? (x1 + x2) / 2 ist eigentlich ein Double. Es wäre eher (x1 + x2) \ 2 oder, zur Überlaufvermeidung x1 + (x2 - x1) \ 2. Ich glaube nicht, dass es ganz richtig ist. wenn du das Rechteck in Teile schneidest erhältst du folgende Rechtecke:
VB.NET-Quellcode
- Dim rect As Rectangle = '...
- Dim tl As New Rectangle(rect.Left, rect.Top, rect.Width \ 2, rect.Height \ 2)
- Dim tr As New Rectangle(rect.Left, rect.Top + rect.Height \ 2, rect.Width \ 2, rect.Height \ 2)
- Dim bl As New Rectangle(rect.Left, rect.Top + rect.Height \ 2, rect.Width \ 2, rect.Height \ 2)
- Dim br As New Rectangle(rect.Left + rect.Width \ 2, rect.Top + rect.Height \ 2, rect.Width \ 2, rect.Height \ 2)
Ohne die exakte Benennung blicke ich da aber nicht ganz durch.
Pro diesem Rechteck (pro Rekursionsschritt) definierst du dann das z. Übrigens: rnd.NextDouble() gibt einen Wert zwischen 0 und 1 zurück. Und gib doch beim Rekursionsschritt bereits ein z mit an und die Tiefe der Rekursion (brauchst du später)
Natürlich kannst du das Rechteck auch so angeben, wie du es jetzt gemacht hast.
Sieht auf jeden Fall schon mal vielversprechend aus.
Gruß
~blaze~
-
Ähnliche Themen
-
Name für Steam-Ähnliche Plattform gesucht
MarcoQuinten - - Off-Topic
-
9 Benutzer haben hier geschrieben
- Thorstian (8)
- ~blaze~ (5)
- RodFromGermany (1)
- paulkoch95 (1)
- nafets (1)
- jvbsl (1)
- ichduersie (1)
- hellmaster159 (1)
- Counterbug (1)