Im Internet, genauer gesagt hier fand ich ein Tutorial welches beschreibt wie man recht einfach Landschaften im Worms Stil erstellen kann.

Ich möchte auf keinen Fall den Eindruck erwecken als wäre die Idee von mir, lediglich die Umsetzung ist meine Interpretation des beschriebenen Verfahrens.

Also, los gehts. Beschreiben wir mal was hier passiert. Als erstes erstellen wir ein Bild und füllen es mit Pink als Hintergrundfarbe. Hier hinein wird nun ein Terrain am unteren Bildschirmrand gezeichnet und einige Wolkenartige Strukturen werden auch noch hinzugefügt damit das Bild nicht so leer ist. Das Terrain sowie die "Wolken" werden in Schwarz dargestellt.

Zum Vergrößern hier klicken.

Kommen wir zum Code der das ganze realisiert. Diesen habe ich in 2 Bereiche unterteilt. Wie schon erwähnt in das Terrain am unteren Bildschirmrand und eben die "Wolken". Beginnen wir mit dem Terrain.

Zunächst, und um sicher zu stellen das keine großen Lücken im Terrain aufreten, wird ein großes Rechteck gezeichnet welches den unteren freien Bereich abdeckt. Das ganze natürlich in schwarzer Farbe. Nun folgt eine Schleife über die gesamte Bildschirmbreite (und darüber hinaus). Bei jedem Step wird eine Zufallszahl für Y ermittelt welche vom unteren Bildschirmrand eine gewisse Höhe haben darf. Hier wird nun jedesmal eine Ellipse mit 100 x 100 Pixeln gezeichnet. Fertig ist das Terrain. Natürlich ziemlich simpel aber das Verfahren kann sich ja jeder ausbauen - hier geht es schließlich um das Prinzip.
Color 0, 0, 0: Rect 0, height - 50, width, height, 1
For x = -100 To width + 100 Step 20
   y = Rand( height - 150, height )
   Oval x, y, 100, 100, 1
Next
Weiter gehts mit den "Wolken". Hier wird es schon etwas komplizierter. Als erstes kommt eine Schleife die zwischen 1 und 4 Durchläufe vollführt, also eine zufällige Anzahl an Wolken, erzeugt. Nun ermitteln wir 2 Zufallswerte, jeweils für X und Y. Diese benutzen wir in nachfolgender Schleife als Ursprungskoordinaten um um diesen herum zwischen 6 und 20 Ellipsen in zufälliger Größe zu zeichnen. Fertig ist die Wolke. Auch hier wieder die Anmerkung das es sicher besser geht.
For t = 0 To Rand( 1, 3 )
   x = Rand( 0, width )
   y = Rand( 0, height - 350 )
   For i = 0 To Rand( 5, 19 )
      r = Rand( -100, 100 )
      Oval x+r, y+r, Rand(50, 100), Rand( 50, 150 ), 1
   Next
Next
Kommen wir nun aber zum Kern der Sache. Wir tauschen nun unsere schwarze Farbe gegen eine Textur. Diese muß Kachelbar sein, also nahtlos aneinanderpassen. Ich habe hier die Textur der Tutorialseite benutzt:



Zunächst legen wir nun ein Bild an welches exakt so groß ist wie unser Landschaftsbild. In dieses wird bei TileBlock die Textur hineingekachelt.
stone = LoadImage("stone.png")
grabber = CreateImage( width, height )
SetBuffer ImageBuffer( grabber )
TileBlock stone, 0, 0
Nun durchlaufen wir wieder eine Schleife die uns das Landschaftsbild ausliest. Finden wir einen schwarzen Pixel wird an diese Position der Pixel aus dem Texturenbild kopiert. Das in den Code noch LockBuffer etc. gehört versteht sich von selbst - ich habe es hier nur weggelassen da es ja ums Prinzip geht.
For x = 0 to width - 1
   For y = 0 to height - 1
      pix = ReadPixelFast( x, y )
      If pix = black Then
         WritePixelFast x, y, ReadPixelFast( x, y, ImageBuffer( grabber ) )
      End If
   Next
Next
Weiter im Text. Kommen wir nun zum Gras welches sich auf den Oberseiten des Terrains befinden soll und dem was an die Unterseite gehört, ich nenne es mal Sand. Hierzu wird schon beim Durchlaufen der "Texturierungsschleife" geprüft wann ein Pixelwechsel von Pink auf Schwarz und umgekehrt stattfindet. Bei jedem Wechsel wird die entsprechende Aktion ausgeführt, also entweder das Gras oder der Sand gezeichnet.

Die Gras bzw. Sandtexturen sind wesentlich kleiner als die Steintextur zum Füllen der Landschaft. Hier ist nur wichtig das die Texturhöhe ein vielfaches von 2 ist. Warum dazu kommen wir gleich, zuerst einmal die Texturen. Auch diese habe ich von der Tutorialseite übernommen:

 

Nun zum Prinzip. Nehmen wir hierzu mal das Gras. Es soll immer dann gezeichnet werden wenn ein Wechsel von Pink auf Schwarz stattfindet. Dabei wird es aber nicht von der Position des aufgefundenen schwarzen Pixels aus nach unten gezeichnet, sondern wir gehen noch einmal die halbe Texturhöhe zurück. Damit dies funktioniert sollte die Texturhöhe eben ein vielfaches von 2 sein. Der Grund hierfür liegt darin das sich später Sand und Gras nicht überschneiden - es sieht halt einfach besser aus. Ausserdem sollen auch vom Gras nur die nicht Pink farbenen Pixel dargestellt werden, so daß man auch "rauhe" Texturen benutzen kann.

Ich habe hier Pixel für Pixel kopiert, man könnte das ganze aber natürlich auch per DrawImageRect oder auf andere Weise realisieren.

Beim Sand wird ebenso verfahren, nur das hier natürlich bei einem Wechsel von Schwarz auf Pink gezeichnet wird und wir hier noch den unteren Bildschirmrand ausschließen weil es ebenfalls hässlich aussehen würde wenn auch hier die Sandtextur eingezeichnet würde.

Alles in allem sieht das Ergebnis danach so aus:

Zum Vergrößern hier klicken.

Ein recht anständiges Ergebnis wenn man den geringen Aufwand in Betracht zieht. Zu guter letzt noch der gesamte Quellcode. In diesem hab ich noch ein paar kleine "Explosionen" eingebaut welche zeigen sollen wie die fertige Map benutzt werden kann. Da diese nun ein normales maskiertes Image ist, kann auch per ImagesCollide eine Kollisionsabfrage durchgeführt werden.
width  = 800
height = 600
Graphics width, height, 0, 2

sand  = LoadImage("sand.png")
grass = LoadImage("grass.png")
stone = LoadImage("stone.png")

hsand  = ImageHeight( sand ) / 2
hgrass = ImageHeight( grass ) / 2

;--> Erstellt Bild mit gleicher Größe wie Landschaftsbild und fülle mit "stone" Textur.
grabber = CreateImage( width, height )
SetBuffer ImageBuffer( grabber )
TileBlock stone, 0, 0

;--> Erstelle "land"
land = CreateImage( width, height )

;--> Ermittle Referenzpixel
SetBuffer ImageBuffer( land )
LockBuffer ImageBuffer( land )
WritePixelFast 0, 0, 0
black = ReadPixelFast( 0, 0 )
WritePixelFast 0, 0, $FF00FF
pink = ReadPixelFast( 0, 0 )
UnlockBuffer ImageBuffer( land )

;--> Beginne Schleife
Repeat

   SeedRnd MilliSecs()

   SetBuffer ImageBuffer( land )
   Color $FF, 0, $FF: Rect 0, 0, width, height, 1

   ;--> Berechne Terrain am unteren Bildrand
   Color 0, 0, 0: Rect 0, height - 50, width, height, 1
   For x = -100 To width + 100 Step 20
      y = Rand( height - 150, height )
      Oval x, y, 100, 100, 1
   Next

   ;--> Berechne Terrain in Bildmitte (Wolkenstrukturen)
   For t = 0 To Rand( 1, 3 )
      x = Rand( 0, width )
      y = Rand( 0, height - 350 )
      For i = 0 To Rand( 5, 19 )
         r = Rand( -100, 100 )
         Oval x+r, y+r, Rand(50, 100), Rand( 50, 150 ), 1
      Next
   Next

   SetBuffer ImageBuffer( land )
   DrawBlock land, 0, 0

   ;--> locke alle Buffer
   LockBuffer ImageBuffer( land )
   LockBuffer ImageBuffer( sand )
   LockBuffer ImageBuffer( grass )
   LockBuffer ImageBuffer( grabber )

   ;--> Landschaft mit "stone" Textur füllen
   For x = 0 To width - 1

      transition = 0

      For y = 0 To height - 1

         ;--> Lies Pixel aus.
         pix = ReadPixelFast( x, y )

         ;--> Wenn Schwarz Pixel, ersetze ihn durch "stone" Pixel
         If pix = black Then
            ;--> erster Schwarz Pixel nach Pink? Speichere Y-Position
            If transition = 0 Then ytop = y: transition = 1
            WritePixelFast x, y, ReadPixelFast( x, y, ImageBuffer( grabber ) )
         End If

         ;--> Pink Pixel oder Bilrdschirmkante?
         If pix = pink Or y = height - 1 Then

            ;--> Erster Pink Pixel nach schwarz?
            If transition = 1 Then

               ;--> Zeichne "sand" Textur an unteren Rand wenn NICHT unterer Bildschirmrand
               If y < height - 1 Then
                  ysand = 0
                  For y1 = y - hsand * 2 To y - 1
                     If y1 > -1 And y1 < height Then
                        psand = ReadPixelFast( xsand, ysand, ImageBuffer( sand ) )
                        If psand <> pink Then WritePixelFast x, y1, psand
                        ysand = ysand + 1
                     End If
                  Next
                  xsand = xsand + 1: If xsand = ImageWidth( sand ) Then xsand = 0
               End If

               ;--> Zeichne "grass" Textur an oberen Rand wenn NICHT oberer Bildschirmrand
               If ytop > 0 Then
                  ygrass = 0
                  For y1 = ytop - hgrass To ytop + hgrass - 1
                     If y1 > -1 And y1 < height Then
                        pgrass = ReadPixelFast( xgrass, ygrass, ImageBuffer( grass ) )
                        If pgrass <> pink Then WritePixelFast x, y1, pgrass
                        ygrass = ygrass + 1
                     End If
                  Next
                  xgrass = xgrass + 1: If xgrass = ImageWidth( grass ) Then xgrass = 0
               End If

               transition = 0

            End If

         End If

      Next
   Next

   UnlockBuffer ImageBuffer( land )
   UnlockBuffer ImageBuffer( sand )
   UnlockBuffer ImageBuffer( grass )
   UnlockBuffer ImageBuffer( grabber )

   ;--> Maskieren wir nun das "land"
   MaskImage land, 0, 0, pink

   ;--> Erzeuge einen Hintergrund
   back = CreateImage( width, height )
   SetBuffer ImageBuffer( back )
   For y = 0 To height
      blue# = 255 / Float( height ) * y
      Color 0, 0, blue
      Rect 0, height-y, width, height-y, 1
   Next

   ;--> Nun machen wir noch einige Explosionen
   For i=0 To Rand( 5, 9 )

      ;--> suche nach einem Punkt im "land" der mit einer Textur belegt ist
      Repeat
         x = Rand( 0, width )
         y = Rand( 0, height )
         If ReadPixel( x, y ) <> pink Then Exit
      Forever

      ;--> Zeichne eine Ellipse als "Explosion"
      Color 0, 0, pink
      For r = 0 To 100

         SetBuffer ImageBuffer( land )
         Oval x-r/2, y-r/2, r, r, 1

         SetBuffer BackBuffer()
         DrawBlock back, 0, 0
         DrawImage land, 0, 0

         Flip

      Next

   Next
Until KeyHit(1)
End
Hey...weiterlesen ;-)

Der größte Clou kommt nämlich erst jetzt. Als kleiner Tipp: Diese Level werden ja durch "Zufall" erzeugt und wie man weis geschieht in einem Computer nichts wirklich zufällig. Das heißt das ein Level bei gesetztem Startwert für eine Zufallsberechnung jedesmal gleich aussieht.

Dies wiederum bedeutet das man z.B. bei einem Multiplayer Spiel nicht ein ganzes Level oder Bild übertragen muß sondern lediglich 4 Byte als Startwert der Zufallsberechnung. Auf allen Rechnern wird dann nämlich das gleiche Level erzeugt.

Zum Vergrößern hier klicken.

So das wars jetzt aber wirklich.