Generierung einer 2D-Landschaft
Inspiriert von der Diskussion in der Multimedia-Sparte, habe ich mich entschlossen, hier ein kleines Tutorial zur Generierung von 2D-Landschaften zu schreiben.
Es geht um eine Landschaft wie in Scorched Earth. Sie besteht aus Bergen, auf denen Panzer stehen, die sich becshießen. (Ein bekanntes Beispiel ist das bei MS-DOS mitgelieferte Gorillas.bas (QBasic))
WIr müssen es also erreichen, dass zufallsgenerierte Berge schnell und effizient generiert werden.
An die Berge haben wir folgende Ansprüche:
- Variable Anzahl
- Variable Steigungen
- Variable höhe
- schnelle Generierung
Ausgangbasis soll Timage Komponente beliebiger Größe sein (am besten in der Höhe größer 100) und ein Button.
Dann benötigen wir 2 Variablen vür die X- und die Y-Achse des Bildes
Delphi-Quelltext
EIne Variable, in der gespeichert wird, in welche richtung die Berge gehen (nach oben, oder nach unten) und eine, in der ein Zufallswert gespeichert wird.
Delphi-Quelltext
1: 2:
| plusminus:boolean; auswahl:integer; |
Zuerst muss das Image gelöscht und die Variablen initialisiert werden.
Delphi-Quelltext
1: 2: 3: 4: 5:
| bild.Picture.Free; bild.Picture := tpicture.Create; randomize; y := 100; steigung := 1; |
Wir beginnen nun damit, dass wir jede Spalte des Bildes durchlaufen.
Und für jede Spalte muss erneut per zufall generiert werden, ob der Berg eine neue Richtung einschlagen, oder die derzeitige beibehalten soll.
in 99 der hindert Fälle behält der Berg die Richtung bei und in einem fall ändert er es. Verringert man die Variable "bergspitzen", so wird es wahrscheinlicher, dass sich die Steigungsrichtung ändert.
In der Var plusminus wird die Steigungsrichtung gespeichert. Es gibt nur "nach oben" oder "nach unten" also ist sie vom Typ boolean.
Bei jedem Durchgang wird nun je nach Strigungsrichtung etwas von abgezogen bzw. dazugezählt.
am ende wird noch strich auf dem jeweiligen x Wert in y-Länge gezeichnet. Aber da der Berg so auf dem Kopüfstehen würde, muss der y-Wert von der Bild-Höhe abgezogen werden.
Das Ergebnis kann so aussehen.:
Und der Quelltext dazu:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:
| procedure TForm1.BitBtn2Click(Sender: TObject); var x:integer; steigung,y:real; plusminus:boolean; auswahl:integer; bergspitzen:integer;
const berg = 50;
begin bild.Picture.Free; bild.Picture := tpicture.Create; randomize; y := 100; steigung := 1; bergspitzen := 100; for x := 1 to bild.Width do begin auswahl := random(bergspitzen+1); if auswahl = bergspitzen then if plusminus = true then begin plusminus := false; end else begin plusminus := true; end;
if plusminus = true then begin y := y + steigung; if y > bild.Height-50 then y := bild.Height-50; if y < 0 then y := 0; end else begin y := y - steigung; if y < 0 then y := 0; if y > bild.Height-100 then y := bild.Height-100; end; bild.Canvas.Polygon([point(x,bild.Height),point(x,bild.Height-round(y))]); end; bild.repaint; end;
end. |
Problem:
Sehen wir uns unsere Ansprüche an, so muss ich sagen, dass unser erstes Ergebnis dem nicht Gerecht wird. Wir haben etwas generiert, was Ähnlichkeiten von Bergen hat, jedoch ist die Steigung immer 1 und die Oberfläche sehr glatt.
Wir müssen die Steigung also Abhängig vom Zufall machen.
Weiterhin wollen wir auch Steigungen haben, die kleiner als 45 Grad sind. Z.Z. ist die Steigung jedoch eine integer Variable und damit die kleinste Steigung "1" (also 45Grad). Erst unter 1 (z.B. 0.2) wir die Steigung flacher. Die Var "steigung" wird also, ebenso wie die Var y zu Real-Werten.
Weiterhin wird bei Jeder Änderung der Steigungsrichtung die Steigung neu per Zufall generiert.
Das Ergebnis kann dann so aussehen:
Und der Quelltext dazu:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48:
| procedure TForm1.BitBtn1Click(Sender: TObject); var x:integer; steigung,y:real; plusminus:boolean; auswahl:integer; bergspitzen:integer; const berg = 50; begin bild.Picture.Free; bild.Picture := tpicture.Create; randomize; y := random(bild.Height div 2); steigung := random(20) / (random(30)+1); bergspitzen := random(berg); for x := 1 to bild.Width do begin auswahl := random(bergspitzen+1); if auswahl = bergspitzen then if plusminus = true then begin plusminus := false; steigung := random(10) / (random(30)+1); end else begin plusminus := true; steigung := random(10) / (random(30)+1); end; if plusminus = true then begin y := y + steigung + (random(2)-1); if y > bild.Height-50 then y := bild.Height-50; if y < 0 then y := 0; end else begin y := y - steigung - (random(2)-1); if y < 0 then y := 0; if y > bild.Height-100 then y := bild.Height-100; end; bild.Canvas.Polygon([point(x,bild.Height),point(x,bild.Height-round(y))]); end; bild.repaint; end; |
Zum Abschluss muss ich noch sagen, dass es auch noch die Möglichkeiten über Sinuskurven gibt. "thebe", von dem auch das nachfolgende Bild ist hat diesen Weg vorgeschlagen. Hier muss jeder selber wissen, was er bevorzugt.
Die weiteren Veränderungen dieser Generierung sind der Phantasie überlassen.
Gruß Umpani