Tilemaps mal objektorientiert...

Übersicht BlitzMax, BlitzMax NG Allgemein

Neue Antwort erstellen

bruZard

Betreff: Tilemaps mal objektorientiert...

BeitragMo, Feb 06, 2006 21:09
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo Folks, ich habe hier mal wieder ein Problem welches nicht wirklich problematisch ist, also das Projekt nicht aufhält, mir aber auf den Nägeln brennt da ich derzeit glaube zuviel Code für zuwenig Action zu schreiben.
Es geht um das verwalten von Tilemaps mit einer definierten Anzahl von Layern innerhalb von Klassen.

Bei mir sieht es derzeit so aus dass ich 5 Layer verwende:

  • ground = alles was ein Bodentile, eine Wand etc. sein kann, also prinzipiell die Hintergrundgrafik darstellt
  • decals = Schmuckgrafiken die auch animiert sein dürfen. Dazu zählt bspw. Laub auf dem Boden, Blut an der Wand etc.
  • entities = alles was irgendetwas tut ... der Player, die Gegner, eine Kiste, ein WasWeißIch
  • overlays = Grafiken die sich verhalten wie grounds und nur dazu da sind alle anderen Grafiken zu überlagern und transparent zu werden sobald der Player sich darunter befindet.
  • collision = Eine einfache Bitmaske die sagt ob der Player oder ein anderes Objekt auf diesem Tile sein darf oder nicht.

Nun ist es so dass nur die Layer ground und overlay an das Mapraster gebunden sind, alle anderen Layer beziehen sich nur auf dieses Raster.
Beispiel:

Der Player befindet sich in Spalte 5, Zeile 10 und hat dazu einen X-Offset von 12 Pixeln und einen Y-Offset von 8 Pixeln. Sein Handle ist Top-Left. Alle Decals und Entities werden so behandelt. Mein Ansatz derzeit dafür ist folgender:

Alles was ins Mapraster passt (ground, overlay) wird in einer Bank gesichert: 1 Integer für die ID des Tilesets, 1 Integer für die Framenummer des Tiles. Beim auslesen gehe ich alle Instanzen der Klasse TTileset durch um die ID zu finden die in der Bank gesichert ist. Allerdings finde ich diese Geschichte schon extrem lame.

Alle Objekte die relativ zu einem Tile stehen sind in einer Linked List gesichert welche an eine Klasse gebunden ist. Das bedeutet dass ich bei jedem Tile die komplette Liste der Decals und Entities durchgehen muss um zu gucken ob sich bei der aktuellen Mapzelle ein Objekt befindet. Auch das ist nicht nur lame sondern auch lahm.

Was soll dieser Post bezwecken? Ich bräuchte mal einen Schlag gegen den Hinterkopf und eine Idee wie man das einfacher und schneller lösen kann.

Für jede Idee bin ich dankbar Very Happy
PIV 2,4GHz - 1GB DDR 333 - ATI Radeon9600 - WinXP - DX9.0c - BMax 1.14 - B3D 1.91 - 1280x1024x32

User posted image

rema

BeitragMo, Feb 06, 2006 21:53
Antworten mit Zitat
Benutzer-Profile anzeigen
Kannst du mal Codefragmente zeigen, also nur als Beispiel. Hatte auch schon so ändliches Problem, wie ich die Daten optimal handhaben kann. Den die Suche von in TypeList sind lahm. Den lieber parent/child und so mehr Speicher abverlangen...
 

Ava

Gast

BeitragDi, Feb 07, 2006 10:14
Antworten mit Zitat
Hallo Bruzard,

mal schauen, ob ich Dir helfen kann, ich habe schon einige objektorientierte Tilemaps in BMax geschrieben. Ich würde Tiles prinzipiell nicht als Integer-Werte in einer Bank speichern. Einen Pointer-Array, der direkt auf das Tile-Objekt oder die Grafik zeigt (ich hatte mir dafür immer extra einen Type angelegt), fand ich da sehr praktisch. Wenn Du die Tiles als Objekte verwaltetst, hat das den Vorteil, dass Du denen sehr einfach viele schöne Eigenschaften einstellen kannst (zb. für Animationen, Einfluss auf die Geschwindigkeit von Charakteren, event. Schaden bei Betreten / Bewegen auf dem Tile und und und). Meine Map-Layer hatte ich auch immer als Objekte angelegt, so das ich dann immer eine völlig belebige Anzahl von ihnen einstellen konnte. Dabei hatte ich unterschiedle Typen von Layern (Tile-Layer, Object-Layer, Fill-Layer für Tiles oder Farben). So wie ich es aus Deinem Post herauslese, zeichnest Du deine Map, indem Du die x/y Koordinaten abgehst und dann dort die Layer übereinander einzeichnest? - das ist natürlich sehr ungünstig! Du solltest die Layer durchgehen, die x/y Tiles für das komplette Layer einzeichnen und dann zum nächsten wechseln. Wenn Du es so machst, hast Du auch keine Probleme mehr mit Deinen Objekt-Listen, da Du diese nur noch ein einziges Mal durchgehen musst! Wink

Vielleicht konnte ich Dir den einen oder anderen brauchbaren Denkanstoss geben. Smile

Gruss, Ava

bruZard

BeitragDi, Feb 07, 2006 18:26
Antworten mit Zitat
Benutzer-Profile anzeigen
Bin derzeit etwas knapp an Zeit und musste eben noch schnell Xaron "anpissen", aber heute abend poste ich nochwas was die Sache verdeutlicht und auch eine Erklärung warum die Pointer-Geschichte so nicht funktioniert.
PIV 2,4GHz - 1GB DDR 333 - ATI Radeon9600 - WinXP - DX9.0c - BMax 1.14 - B3D 1.91 - 1280x1024x32

User posted image

bruZard

BeitragDi, Feb 07, 2006 21:28
Antworten mit Zitat
Benutzer-Profile anzeigen
Aaaalso ... die Geschichte mit den Pointern auf Instanzen hatte ich schon, das hat aber nicht so funktioniert wie ich es mir gedacht hatte.
Zeichnen in die Map und Wiedergabe derselben funktionierte wunderbar, aber wehe ich kam auf die verwegene Idee das aktuelle Tileset zu wechseln.
Wie man den Pointer auf eine Instanz in eine Bank und wieder heraus bekommt hatten wir ja schon in diesem Topic abgehandelt: https://www.blitzforum.de/viewtopic.php?t=14196

Mein Ansatz ist dann folgernder (Dummy-Code)
Code: [AUSKLAPPEN]

Type TDummy
   Field _list:TList
   
   Function Add:TDummy()
      Local dummy:TDummy = New TDummy
      If dummy._list:TList = Null Then dummy._list:TList = New TList
      dummy._list.AddLast(dummy)
      Return dummy
   End Function
End Type

Die erzeugende Methode/Funktion übernimmt dann die Instanz und generiert daraus einen Zeiger welcher widerum in ein Integer gecastet wird
Code: [AUSKLAPPEN]

Local myDummy:TDummy = TDummy.Add
Local pointer:TDummy Ptr = Varptr myDummy
Local toBank:Int = Int(pointer)

Soweit so lustig, das gecastete Integer kann nun problemlos per PokeInt() in die Bank geschrieben werden. Beim auslesen muss dann halt wieder gecastet werden...
Code: [AUSKLAPPEN]

Local fromBank:TDummy Ptr = TDummy Ptr(PeekByte(bank,offset))

...auf das "wiedergewonnene" Object kann dann via fromBank[0].EinZugriff zugegriffen werden. Trotz dieser (eigentlich funktionierenden) Geschichte kam es immer wieder zu Seiteneffekten.

Leider ist dieser Versuch schon eine Weile her und ich kann nicht mehr jedes Detail reproduzieren, aber ich kann ja mal einen kleinen Testcode schreiben und schauen ob der Fehler immer noch auftritt.

Zum Problem mit den Objekten:
Es können theoretisch beliebig viele Objekte pro Tile existieren, das bedeutet dass ich diese nicht als Raster in einer Bank ablegen kann da dies bedeuten würde dass immer nur exakt ein Objekt auf einem Tile sein darf ... sehr blöd Neutral Bedeutet: Ich muss die komplette Liste der Objekte durchgehen und nachschauen ob sich auf dem gerade zu verarbeitenden Tile eines oder mehrere dieser Objekte befindet. Ich denke ich müsste das irgendwie per Sortierung lösen, habe da aber noch keinen wirklichen Ansatz.
PIV 2,4GHz - 1GB DDR 333 - ATI Radeon9600 - WinXP - DX9.0c - BMax 1.14 - B3D 1.91 - 1280x1024x32

User posted image

Jolinah

BeitragMi, Feb 08, 2006 0:15
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich versteh nicht ganz wieso du das über Bank und Pointer lösen willst, wäre sowas in der Art nicht einfacher?

Ist Pseudocode, funktioniert nicht, und soll nur ein Beispiel sein:
Code: [AUSKLAPPEN]
Type TLevel
   Field Layer:TLayer[5]
   
   Field Width:Int, Height:Int
   
   Method Draw()
      For l:TLayer = EachIn Layer
         l.Draw()
      Next
   End Method
End Type

Type TLayer
   Field Map:TObject[,]
   
   Method Draw()
      For Local y:Int = 0 To 24
         For Local x:Int = 0 To 32
            Map[x,y].Update()
            Map[x,y].Draw(x,y)
         Next
      Next
   End Method
End Type

Type TObject
   Field TileID
   
   Field offX:Int, offY:Int
   
   Method Draw(x:Int, y:Int)
      DrawImage Tileset, x + offX, y + offY, TileID
   End Method
   
   Method Update()
      'Normale Tiles machen nix
   End Method
End Type

Type TMovingTile Extends TObject

   Field timer:Int

   'Update Methode überschreiben
   'Dieses Tile bewegt sich irgendwie
   Method Update()
      If MilliSecs() - timer > 4000
         offX = 0
         timer = MilliSecs()
      Else If MilliSecs() - timer > 2000
         offX :+ 1
      EndIf
   End Method

End Type


BMax-Intern sind die Objekthandles sowieso Pointer, ich glaub wenn du da noch dauernd hin und her casten musst, dann wird es eher langsamer als schneller Wink
 

Ava

Gast

BeitragMi, Feb 08, 2006 1:56
Antworten mit Zitat
bruZard hat Folgendes geschrieben:
Zum Problem mit den Objekten:
Es können theoretisch beliebig viele Objekte pro Tile existieren, das bedeutet dass ich diese nicht als Raster in einer Bank ablegen kann da dies bedeuten würde dass immer nur exakt ein Objekt auf einem Tile sein darf ... sehr blöd Neutral Bedeutet: Ich muss die komplette Liste der Objekte durchgehen und nachschauen ob sich auf dem gerade zu verarbeitenden Tile eines oder mehrere dieser Objekte befindet. Ich denke ich müsste das irgendwie per Sortierung lösen, habe da aber noch keinen wirklichen Ansatz.


Ich verstehe immer noch nicht genau, warum Du Dir dieses Problem machst? Zeichne nicht Tile für Tile sondern Layer für Layer. Für jedes Tile die komplette Objekt-Liste durchzugehen ist doch völlig unnötig (und wenn nicht, dann erkläre es mir bitte noch einmal *zwinker*).

Also ich bin fast geneigt, meine TileEngine hier zu posten, um's mal zu verdeutlichen, wie ich es meine und Dir zu zeigen, dass es läuft (und gut läuft und schnell läuft). Twisted Evil Nur wäre das wohl etwas übertrieben viel Code... Confused

Im Übrigen geht Jolinahs Vorschlag schon in etwa in die Richtung, wie ich es eigentlich gemeint hatte.

bruZard

BeitragMi, Feb 08, 2006 18:16
Antworten mit Zitat
Benutzer-Profile anzeigen
Dass ich meine Daten in einer Bank ablege hat einen Grund: Ich bin kein besonders guter Programmierer, sondern Designer. D.h. dass ich mir Dinge immer räumlich vorstellen muss. Das klappt bei Banks sehr gut, funktioniert mit verzwickten "hier ein Type und da ein Type" Lösungen aber nur solange wie ich vor dem Quelltext sitze. Habe ich eine Nacht über den Kram geschlafen kann ich ihn nicht mehr oder kaum noch lesen.

@Ava: Es ist schon so dass ich Layer für Layer zeichne ... erst den Boden, dann die Decals, dann die Entities etc. Ich weiß nicht was mir Dein Hinweis bringen soll Very Happy

Allerdings habt Ihr mich da auf eine Idee gebracht: Ich schreibe nicht mehr den Zeiger auf ein Tileset in die Bank, sondern den Zeiger auf eine Map-Cell. Das spart ein Integer, also die Hälfte der Bank Wink
In dieser Map-Cell (nennen wir das Type einfach mal TCell) sichere ich dann Tileset, Tile-ID etc. als Instanzen auf die dahinter liegenden Klassen
Code: [AUSKLAPPEN]

-> Bank
   -> Zeiger auf TCell Instanz
      -> Instanzen von TTileset
      -> Instanzen von TObject (für Decals und Entities)
      -> andere Infos über die Zelle

...muss ich zuhause mal checken ob das so machbar ist.
PIV 2,4GHz - 1GB DDR 333 - ATI Radeon9600 - WinXP - DX9.0c - BMax 1.14 - B3D 1.91 - 1280x1024x32

User posted image

bruZard

BeitragMi, Feb 08, 2006 20:24
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe jetzt hier mal einen rudimentären, noch nicht funktionierenden und denke dass dieser die Lösung sein könnte.

Das Level erzeugt aus der Klasse TLayer noch immer eine neue Bank und diese werden mit den gecasteten Integers der Instanzen der Klasse TCell gefüllt aus welcher man widerum weitere Informationen über diverse Links bekommt.
Code: [AUSKLAPPEN]

Type TLevel
   Field cell:TCell
   Field layer:TLayer
   
   
   Function Init:TLevel(width:Int, height:Int)
      Local level:TLevel = New TLevel
      
      Return level
   End Function
EndType


Type TCell
   Global _list:TList
   Global count:Int
   
   Field id:Int
   Field tileset:TTileset
   Field frame:Int
   
   Function Add:TCell(tileset:TTileset,frame:Int)
      Local cell:TCell = New TCell
      
      cell.count:+1
      
      If cell._list:TList = Null Then cell._list:TList = New TList
      cell._list.AddLast(cell)
      
      cell.id            = cell.count
      cell.tileset   = tileset
      cell.frame      = frame
      
      Return cell
   End Function
End Type


Type TTileset
   Global _list:TList
   Global count:Int
   
   Field id:Int
   Field filename:String
   Field typ:String
   Field image:TImage
   
   Field tile_count:Int
   Field tile_vars:Int
   Field tile_anim:Int
   
   Function Load:TTileset(filename:String,typ:String,tile_count:Int,tile_vars:Int,tile_anim:Int,tile_width:Int,tile_height:Int)
      Local tileset:TTileset = New TTileset
      
      tileset.count:+1
      
      If tileset._list:TList = Null Then tileset._list:TList = New TList
      tileset._list.AddLast(tileset)
      
      tileset.id               = tileset.count
      tileset.filename      = StripDir(filename)
      tileset.typ               = typ
      tileset.tile_count   = tile_count
      tileset.tile_vars      = tile_vars
      tileset.tile_anim      = tile_anim
      tileset.image            = LoadAnimImage(filename,tile_width,tile_height,0,tile_count)
      
      Return tileset
   End Function
End Type



Type TLayer
   Global _list:TList
   Global count:Int
   
   Field id:Int
   Field width:Int
   Field height:Int
   Field data:TBank
   Field name:String
   Field ui:String
   
   Function Add:TLayer(width:Int, height:Int, name:String, ui:String)
      Local layer:TLayer = New TLayer
      
      layer.count:+1
      
      If layer._list:TList = Null Then layer._list:TList = New TList
      layer._list.AddLast(layer)
      
      layer.id         = layer.count
      layer.width      = width
      layer.height   = height
      layer.name      = name
      layer.ui         = ui
      
      layer.data      = CreateBank((width*height)*4)
      
      Return layer
   End Function
End Type

Is'n bissl viel und nicht dokumentiert, aber ich baue mal an dieser Idee weiter.
PIV 2,4GHz - 1GB DDR 333 - ATI Radeon9600 - WinXP - DX9.0c - BMax 1.14 - B3D 1.91 - 1280x1024x32

User posted image
 

suberror

BeitragMi, Feb 08, 2006 23:17
Antworten mit Zitat
Benutzer-Profile anzeigen
gelöscht

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group