[GELÖST] Tilemaps - Aktionsfelder erstellen?

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Neue Antwort erstellen

 

CO2

ehemals "SirMO"

Betreff: [GELÖST] Tilemaps - Aktionsfelder erstellen?

BeitragFr, Sep 16, 2011 15:20
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo,
Ich habe mal wieder ne frage: Ich habe eine Tilemap, und möchte, das einige tiles davon aktionsfelder sind. leider habe ich keine ahnung, wie ich das realisieren könnte, zumal je nach aktionsfeld eine neue tilemap geladen wird. Ich gebe mal ein beispiel: Der spieler rennt auf der hauptmap rum. nun steht er vor einem getileten haus. er geht auf das aktionsfeld, und der innenraum des hauses wird geladen. die hauptmap verschwindet, der innenraum wird sichtbar. dann geht er wieder aus dem haus raus, und stellt sich vor ein anderes haus. hier wird auch wieder der innenraum geladen, welcher sich aber vom anderen haus unterscheidet.

also:
1. Wie kann ich ein aktionsfeld als solches "definieren"?
2. Wie kann ich dem Rechner sagen, welche tilemap zu laden ist?

ganz nebenher:
3. wie kann ich bei tilemaps eine pixelangabe machen? also wenn ich ein objekt (meinetwegen einen baum) platzieren will, wie finde ich raus, welchen pixel ich nehmen muss?

mfG,
CO2
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti
  • Zuletzt bearbeitet von CO2 am Mo, Sep 26, 2011 21:06, insgesamt einmal bearbeitet

Ana

BeitragFr, Sep 16, 2011 15:25
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich hab das so gelöst, das ich jedem Feld einen String mit geben, mit der Map die es laden soll. Wenn der String leer ist, dann passiert halt nichts.

Nun überprüfe ich jedes mal wenn der Spieler gezeichnet wird (genau genommen nur wenn er sich gerade bewegt hat, aber das ist ja nicht so wichtig) ob x mod Tilesize = 0 und y mod Tilesize = 0 ist. Wenn er sich nicht pixel weise bewegt, dann musst du halt eine kleine Toleranz einbauen. Sollten die beiden Bedingungen Stimmen, neue Map laden und fertig.
Don't only practice your art,
but force your way into its secrets,
for it and knowledge
can raise human to divine

ZEVS

BeitragFr, Sep 16, 2011 15:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Das ist ein klares Anwendungsgebiet für die BMax-Funktionspointer!
Du speicherst jedes Tile als Objekt in einem zweidimensionalen Array (Bild allein reicht nicht).
Als ein Field gibst du das Bild an und als ein weiteres eine Funktion für die Aktion. GGf. kannst du so auch Aktualisierungen von Tiles (z.B. Graswachstum) implementieren.

In diesen annonymen Funktionen lässt du den Rechner einfach eine neue Tilemap laden. Woher die kommt, sei dir überlassen (entweder aus einem anderen Array oder aus einer Datei).

Die Kollisionsüberprüfung, ob das Aktionstile betreten wurde, hat Ana hervorragend berschrieben.

BlitzMax: [AUSKLAPPEN]
Type tile
Field image:TImage, action()
End Type
'...
Global tilemap:tile[][], mapwidth, mapheight
'laden
'wenn du die Tilemaps aus einem Array auslesen willst:
Global tilemaps:tile[][][] 'Dimensionen: 1. Tilemaps 2. Zeilen/Spalten 3.Tiles
'/wenn

'Beispiel für ein Aktions-Tile:
Local actiontile:tile = New tile
actiontile.image = teleporterImage
Function loadTilemap1()
loadTilemap 1
End Function
actiontile.action = loadTilemap1

Function loadTimemap(index)
Local dims% = tilemaps[index].dimensions()
mapwidth = dims[0]
mapheight = dims[1]
tilemap = tilemap[..mapwidth]
For x=0 To mapwidth-1
tilemap[x] = tilemap[x][..mapheight]
For y=0 To mapheight
tilemap[x][y] = tilemaps[index][x][y]
Next
Next
End Function

'Mainloop
Local t:tile = 'aktuelles Tile
If t.action Then t.action
'/Mainloop


ZEVS
 

PhillipK

BeitragFr, Sep 16, 2011 15:57
Antworten mit Zitat
Benutzer-Profile anzeigen
Das was du dir vorstellst, würde ich ganz stumpf als EventTrigger bezeichnen.

Meine umsetzung dafür wäre.. mh wie erklär ich das denn mal Very Happy

Ich würde es via Type machen.

BlitzMax: [AUSKLAPPEN]
Type TEventTrigger
Field onActivate(ev:TEventTrigger)
Field data:Object

Function Create:TEventTrigger(func(ev:TEventTrigger), obj:Object)
Local trigger:TEventTrigger= New TEventTrigger
trigger.onActivate = func
trigger.data = obj
Return trigger
End Function
Method Activate()
Self.onActivate(Self)
End Method
End Type


Erstmal so grob Smile
Diesem Trigger kannst du zb ein Field obj:Object und ein field id:Int mitgeben.

Nun ist es wichtig, das du solche 'aktionsfelder' entsprechend rausfinden kannst. Am besten wäre es, wenn du eine art LandschaftsTile hast wo jede kachel ihr eigenes objekt hat.

Allerdings reicht es, wenn du die entsprechdenen teile finden kannst und für dich auch weißt, wo was gemacht wird.
Wenn du die Trigger-objekte zuweist, kannst du das objekt zb als string nutzen (da string Objekt ist und somit ein extend von Object)
Dort schreibst du deine anweisung rein - zb nameDerMap

Ich weiß nicht genau ausm kopf raus, wie man eine funktion mit parameter pointern konnte, aber irgendwie ging das Smile Musst ein wenig rumprobieren - ansonsten schaue ich gleich noch einmal.

Du hast also deine Trigger-objekte und weißt, wo was zum einsatz kommt.
Als funktionspointer hast du zb die Funktion "ChangeMap" hinterlegt.

BlitzMax: [AUSKLAPPEN]
Function ChangeMap(ev:TEventTrigger)
'die funktion kann nur auf einem mapchange trigger sein. Dh sie weiß, das das objekt-field ein String ist!
Local zielMap:String = String(ev.data)
LoadMap(zielMap)
End Function


Tja und nun.. musst du einfach noch für jeden MapTrigger eine data erstellen.
Ich nutze das immer so, das das Objekt sich selbst als parameter übergibt, damit man aus der funktion heraus zugriff auf die Felder hat. Andererseits ist man an eine statische anzahl an parametern gebunden, was ich persönlich weniger bevorzuge.

Du kannst in ZielMap zb auch einen gewissen platz reservieren, zb 8 zeichen (8 byte), welche du mit zielMap[0] etc ausliest und per "Local x:short = (a shl 8) | (b)" und "Local y:short = (c shl 8) | (b)" wieder zusammen setzt.
So kannst du in dem string gleichzeitig auch integer etc speichern (natürlcih vor dem mapladen abschneiden!) und direkt den spieler an die position setzen.

edit:

Ach ZEVs hatte schon die selbe idee. Ich wurde beim schreiben unterbrochen, deshalb hat meine antwort etwas gedauert :3

Und - nicht zu vergessen:
Sobald der spieler oder was auch immer den Trigger auslösen soll, das feld betritt, muss die Methode "Activate()" ausgelöst werden.

Midimaster

BeitragFr, Sep 16, 2011 16:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Die Lösungen gereichen Euch zu Ehren!!!

aber dies ist ein Anfänger-Forum, und hier muss die Lösung auch so sein, dass ein Anfänger sie nachvollzierhen kann!

Ana's Vorschlag reicht vollkommen aus! Bei der Umsetzung kommt es nun darauf an, wie Du die MAP im Programm verwaltest. Als Array oder über Types?

Lass mal den Code zum Laden der Tilemap sehen, dann kann man dir auch konkrete Vorschläge machen , wie du das ganze um Aktionsmöglichkeiten erweitern kannst.

Angenommen die Tiles sind bei Dir so organisiert:

BlitzMax: [AUSKLAPPEN]
Global Map%[40,40]
...
irgendTile = Map[x,y]


dann bietet sich an, parallel ein Stringfeld mitlaufen zu lassen:
BlitzMax: [AUSKLAPPEN]
Global Map%[40,40] , ActionMap$[40,40]
...
irgendTile = Map[x,y,0]
Action= ActionMap[x,y]



oder etwas eleganter: die Map um eine weitere Dimension zu erweitern:

BlitzMax: [AUSKLAPPEN]
Global Map[40,40,2]
...
irgendTile = Map[x,y,0]
Action= Map[x,y,1]
Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe

Holzchopf

Meisterpacker

BeitragFr, Sep 16, 2011 16:56
Antworten mit Zitat
Benutzer-Profile anzeigen
Ein dreidimensionales Int-Array ist weitaus weniger elegant als ein zweidimensionales Type-Array. Auch zwei Arrays parallel zu führen finde ich in diesem Fall sehr fragwürdig.

Natürlich ist das die Beginners-Corner, aber es gibt nun mal eine Hand voll Leuten, die der Meinung sind, Hilfesuchenden von Anfang an die korrekte Antwort zu geben. Und auch diese kann man so formulieren, dass sie für Anfänger nachvollziehbar ist. Zudem gibt es Themen, um die kommt kein Anfänger herum. Und sowieso denke ich nicht, dass Type-Arrays CO2 überfordern werden.

Die 3. Frage verstehe ich nicht ganz, CO2. Willst du von der Mausposition (in Pixeln) zu den Array-Indizes gelangen? Dann einfach Code: [AUSKLAPPEN]
xIndex = (MouseX()-kartenOffsetX) / tileBreite
Die Ganzzahldivision alleine genügt, um das Resultat immer abzurunden und so bis an den äussersten Tile-Rand das richtige anzupeilen.

mfG
Holzchopf
Erledige alles Schritt um Schritt - erledige alles. - Holzchopf
CC BYBinaryBorn - Yogurt ♫ (31.10.2018)
Im Kopf da knackt's und knistert's sturm - 's ist kein Gedanke, nur ein Wurm
 

PhillipK

BeitragFr, Sep 16, 2011 17:09
Antworten mit Zitat
Benutzer-Profile anzeigen
Um mal meinen Senf zum Off-topic krams zu geben:
Klar, anfängerforum. aber nicht jeder anfänger mag anfänger bleiben Smile

ich gestehe, es mag oft vorkommen, das man themen, die man schon hunderte male selbst behandelt hat, etwas leichter von der hand beschreibt, als es gefordert ist.
Ich für meinen teil setze immer eines vorraus: Der hilfesuchende sollte nicht die scheu haben, nachzufragen.
Als ich in blitzmax groß durchgestartet bin, gab es viele, im nachhinein betrachtet, nichtige fragen die mich gequählt haben.
aber ich habe immer zu nachgefragt, auch wenns am ende peinlich war, wie einfach das alles doch ist. Nun versuche ich möglichst vielen leuten zu helfen und dem board ein klein wenig von dem zurückzugeben, was ich bekommen habe: Geteilte erfahrungen und ansätze.

Auch jetzt noch poste ich sachen ins Beginners Corner, wenn ich glaube, das diese frage elementar ist. Auch wenn ich schon ne ganze ecke mehr weiß wie zb vor 2 jahren Smile

Also vielleicht weiß auch CO2 schon den ein oder anderen Kniff, den andere nicht kennen.
@CO2: Wenn du etwas nicht verstehst, frag nach. Keine scheu, so einfach ist das. Auf fragen gibt es antworten, auf unklarheiten erklärungen. Und wenn doch wer eine biestige bemerkung macht, lass dich davon nicht einschüchtern - aber allen vor weg: Bring eigeninitiative und lernbereitschaft mit. Smile

@Midimaster:

Ich muss zustimmen, die laderoutine wird einiges helfen können.
Mich persönlich würde auchnoch das rausfinden einens Tiles interessieren. Es gibt 20millionen wege, seine Koordinaten etc zu organisieren ^^




---

Und nochetwas:
Zitat:
ganz nebenher:
3. wie kann ich bei tilemaps eine pixelangabe machen? also wenn ich ein objekt (meinetwegen einen baum) platzieren will, wie finde ich raus, welchen pixel ich nehmen muss?


Entweder wird gefragt, welches bild gezeichnet werden soll, das ist eine frage, die mit der Laderoutine für die Tilemap an sich erledigt werden kann.
Oder aber es wird nach der genauen position gefragt, wo der Baum denn hingezeichnet werden soll.
Auch das kann, wahrscheinlich, relativ leicht beantwortet werden. Mein größtes problem war immer die übersetzung der Screenkoordinaten in Weltkoordinaten - das ist eine sache der Organisation.
Poste uns doch einmal:
1) wie wird die spielerposition bestimmt?
2) wie findest du raus, welches tile unter dem spieler ist? (findest du das atm überhaupt schon raus?)
3) wie wird deine Map geladen / nachgeladen?

ZEVS

BeitragFr, Sep 16, 2011 17:19
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich fühle mich geschmeichelt, aber dennoch halte ich meine Lösung auch für anfängergerecht (wenn auch nicht unbedingt meine Erklärung).
Vielleicht sollte ich es mit einer graphischen Darstellung versuchen:
user posted image
Leicht hellgrün hab ich Funktionen dargestellt, die Tiles sind dagegen dunkelgrün. Links oben sieht man die graphische Verdeutlichung des tile-Types (aktion + Bild), im unteren Teil der Aufbau der Tilemap mit der referenzierten Funktion rechts oben.

Das globale Array Tilemaps enthält - wer hätte das gedacht - alle Tilemaps. Das Speichern der Tilemaps in einem Array hat den Vorteil der schnellen Ausführungszeit und der internen Speicherung von Tilemaps (wie viele Spiele gibt es, wo man in ein Haus gehen kann, alles auf den Kopf stellen kann, rein und wieser raus geht und alles ist so wie vorher).

Diese Funktionsreferenz "->2" hat PhillipK aber bereits besser durch Trigger gelöst.

Wieso soll man mit BMax programmieren, wenn man sich nur auf die primitive BB-Syntax beschränkt? OOP und Funktionsreferenzen sind ein Segen - auch für sog. Anfänger.

Bzw.: Midimasters Ansatz mag am einfachsten zu verstehen, am besten erklärt und vorerst am einfachsten zu implementieren sein - aber später wird es dann schwierig, wenn man an den Ausbau denkt, denn nicht alle Aktionsfelder müssen auch automatisch Türen sein (Schalter, Items etc. sind auch möglich). Mit dem OOP-Ansatz ist diese Implementierung kein Problem, bei Midimasters dagegen werden die Strings komlexer, müssen interpretiert werden, was man mit ein paar annonymen Funktionen schnell weglassen kann (natürlich nur mit PhillipKs Trigger-Engine).

ZEVS
 

CO2

ehemals "SirMO"

BeitragSa, Sep 17, 2011 2:52
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich bedanke mich herzlich für die zahlreichen Antworten und gleichzeitig entschuldige ich mich, das ich erst jetzt zurückschreiben kann. Wink

@ Ana:
Erstmal danke dir Wink

Zitat:
Ich hab das so gelöst, das ich jedem Feld einen String mit geben, mit der Map die es laden soll. Wenn der String leer ist, dann passiert halt nichts.
Was meinst du denn mit "Feld"? ein Array-Feld oder ein Type-Feld? Wink

Zitat:
(...) Sollten die beiden Bedingungen Stimmen, neue Map laden und fertig.
Das wäre jetzt schon die Kollision InGame, woll? Very Happy


@ ZEVS:
Ok, auch dir danke für die Hilfe,

- 1. Post: Jetzt habe ich einige Fragen zum Code: Was macht diese Funktion?
BlitzMax: [AUSKLAPPEN]
Function loadTilemap1()
loadTilemap 1
End Function


Was macht die Funktion "action()" im tile-Type? Also ist sie quasi eine kennung der aktionstiles, oder enthält sie code, welcher beim betreten eines aktionsfeldes ausgeführt wird?

Was heißt "teleporterImage", bzw. wofür steht es?

- 2. Post: Welche tiles wären denn aktionstiles?


@ PhillipK:
Auch dir danke ich für die Hilfe Wink

- 1. Post: Immer diese Types Very Happy Zum Code: Da müsste ich dir jetzt so viele Fragen stellen, das würde den rahmen sprengen Embarassed Ich kann ja die wichtigsten Stellen:
was macht die Funktion "func()"?
wofür steht "ev"? (Weil du das öfter im Code hast)
was macht die Zeile "Self.onActivate(Self)"
Das wären jetzt die allerwichtigsten, der Rest klärt sich, wenn ich die befehle kenne (Ich bin noch nicht so weit, lese nämlich parallel ein Tutorial, und da bin ich gerade bei "Method") Wink

- 2. Post: Zitat:
@CO2: Wenn du etwas nicht verstehst, frag nach.
Siehe oben Very Happy

Zitat:
3. wie kann ich bei tilemaps eine pixelangabe machen? also wenn ich ein objekt (meinetwegen einen baum) platzieren will, wie finde ich raus, welchen pixel ich nehmen muss?
Da wollte ich wissen, wie ich einen Baum oder andere Objekte an einem pixel genau zeichnen kann. Also ich wollte wissen, wie ich herausfinde, wie die pixelkoordinaten sind. wenn ich also z.b.: eine tilemap 10 x 10 tiles groß habe, möchte ich den pixel herausfinden, der sich in der mitte des tiles an posx = 2 und posy = 5 befindet. Wie geht das?

Zitat:
1) wie wird die spielerposition bestimmt?
Wie meinst du das? ob sich der spieler pixelgenau oder tilegenau bewegt? Wenn ja: pixelgenau Very Happy

Zitat:
2) wie findest du raus, welches tile unter dem spieler ist? (findest du das atm überhaupt schon raus?)
Das habe ich bisher noch nicht herausgefunden Embarassed mir würde jetzt spontan auch gar nicht einfallen, wie ich das lösen kann (Da habe ich noch gar nicht dran gedacht, das ich herausfinden muss, auf welchem tile der spieler steht Embarassed )

Zitat:
3) wie wird deine Map geladen / nachgeladen?
in einem Array, welches soviele felder hat, wie es maps gibt. dann wird einfach vor der hauptschleife eine For - Next schleife gemacht, in der das array mit den maps gefüllt wird. Oder wolltest du jetzt den Code haben?


@ Midimaster:
Natürlich auch dir Besten dank! Wink

Zitat:
Lass mal den Code zum Laden der Tilemap sehen, dann kann man dir auch konkrete Vorschläge machen , wie du das ganze um Aktionsmöglichkeiten erweitern kannst.
Habe den noch nicht in eine funktion gepackt...: BlitzMax: [AUSKLAPPEN]
Global map:Int[19, 19]
Global tileset:Int = LoadAnimImage("data\Tileset.png", 32, 32, 0, 40)
Global map_width:Int, map_height:Int
Global tilemap:TStream = ReadFile("data\map.tmd")

map_width = ReadInt(tilemap)
map_height = ReadInt(tilemap)

For x:Int = 0 To map_width
For y:Int = 0 To map_height
map[x, y] = ReadByte(tilemap)
DrawImage tileset, x * 32, y * 32, map[x, y]
Next
Next
Das ist jetzt der Grundcode, so wie ich ihn hatte (ohne die aktionstiles). Zum tilemapfile (.tmd - Datei): Diese ist so aufgebaut, das erst map_width und map_height als int eingetragen sind, danach die jeweiligen "IDs" im tileset als byte.

Zitat:
dann bietet sich an, parallel ein Stringfeld mitlaufen zu lassen:
Das würde dann die anderen Map-Pfade enthalten, oder wie?

Zitat:
oder etwas eleganter: die Map um eine weitere Dimension zu erweitern:
Nur zum Verständnis: "0" wäre kein Aktionsfeld, "1" wäre ein aktionsfeld, oder wie? Und wo wird der map-pfad gespeichert?


@ Holzchopf:
Auch dir Danke.

Zitat:
Willst du von der Mausposition (in Pixeln) zu den Array-Indizes gelangen?
Siehe Antwort PhillipK Very Happy


Ich hoffe ich habe keinen Vergessen, wenn doch, laut "Hier" schreien, dann werde ich das ändern Wink
mfG,
CO2
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti

Xeres

Moderator

BeitragSa, Sep 17, 2011 3:08
Antworten mit Zitat
Benutzer-Profile anzeigen
Wenn du die Basics wie Maus- und Spielerposition noch nicht raus hast, starte mit weniger. Fang mit einem simplen, unbewegtem 2D Array an und benutzte die BlitzBasic RPG Tutorials. Kümmere dich um die Grundlegenden Dinge - dann erst arbeite dich zu größeren Aktionen hoch.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

Ana

BeitragSa, Sep 17, 2011 3:59
Antworten mit Zitat
Benutzer-Profile anzeigen
Beides natürlich, ich würde ein Array von Types verwenden. Mit dem Array kann man durch Division mit Rest leicht die Position bestimmen ohne alle Felder ablaufen zu müssen, mit dem Type kannst du verschiedene Elementartypen verwenden. Denke es ist einfach super unbequem seinen Maps nur nummern zu geben und langsam (und irgendwie auch einfach zu wider) seine Koordinaten als String zu speichern.

Ungefähr sowas:

BlitzMax: [AUSKLAPPEN]

Global Map:Feld[,] = New Feld[MapWidth,MapHeight]
Type Feld
Field x:Int,y:Int,Begehbar:Byte
Field Image:TImage
Field MapLink:String

Method Draw()
...
End Method
End Type

Function GetFeld(X:Int,Y:Int)
Return map[X/Tilesize,Y/Tilesize]
End Function

Function CheckMapLink(X:Int,Y:Int)
If GetFeld/(x,y).maplink <> "" Then LoadMap(GetFeld/(x,y).maplink)
End Function


So ungefähr hätte ich es gelöst. Funktionspointer und sowas sind zwar schön und gut, aber es geht halt auch so und ich find es erstmal deutlich einfacher wenn man das Thema nicht kennt und nur eine Antwort will. Anderseits kann es sich bestimmt lohnen sich damit zu beschäftigen (auch wenn es wichtigeres gibt).
Don't only practice your art,
but force your way into its secrets,
for it and knowledge
can raise human to divine

Midimaster

BeitragSa, Sep 17, 2011 9:59
Antworten mit Zitat
Benutzer-Profile anzeigen
@CO2

danke für den Lade-Code. Jetzt kann man sehen, wie Du die Map in deinem Code "verwaltest". Das dachte ich mir schon, dass Du es mit einem ARRY machst: Map[x,y]


Zwei Arrays

Das Stringfeld ist die gedanklich leichteste Erweiterung deines Codes. Dabei läuft einfach ein zweites Feld neben dem ersten her. Es enthält diesmal halt nicht die IDs, sondern z.b. MapPfade, ActionFelder oder was sonst noch auf einem Feld passieren könnte. Es muss auch nicht unbedingt ein Stringfeld sein, sondern kann auch ein INTEGER-Feld mit neuen IDs sein. Du lädst es auf die gleiche Art wie deine Hauptmap aus einer zweiten Map-Datei
BlitzMax: [AUSKLAPPEN]
ActionMap:Int[19,19]
Global ActionMap:TStream = ReadFile("data\ActionMap.tmd")
....
For x:Int = 0 To map_width
For y:Int = 0 To map_height
ActionMap[x, y] = ReadByte(ActionMap)
Next
Next
....
Select ActionMap[SpielerX , SpielerY]
Case 0
' keine Action auf diesem Feld
Case 1
' beispiel für Action: Betreten eines Hauses:
NextMap ="MapHaus.tmd"
NextActionMap= "ActionMapHaus.tmd"
NeuSpielerX=10
NeuSpielerY=10
.... und was immer Du sonst noch regeln möchtest
Case 2
' und wieder zurück ins freie:
NextMap ="Map.tmd"
NextActionMap= "ActionMap.tmd"
NeuSpielerX=10
NeuSpielerY=10
Case 3
.....
End Select


So hast Du eine zweite Map, die für jede Position auf dem Original Map-Feld eine mögliche Action bereithält. die Actions sind wieder nur IDS, die dann in einer SELECT/CASE zu den dort genau beschriebenen Actions führen.

Dabei ist es Dir überlassen, welche Action dort durchgeführt wird. Das können Map-Wechsel sein, oder auch kleine Animationen (Explosionen ..., geheime Felder mitten im Gras, bei denen dann ganz wo anders in der Map was passiert ... ,

Ein dreidimensionales Array

statt nun 2 oder gar 3 (falls dir noch was an Erweiterung einfällt) Arrays nebeneinander zu vewalten, kann man auch geschickt alles in ein gemeinsames Array packen. Dazu erhält das Array eine dritte Dimension:

BlitzMax: [AUSKLAPPEN]
Global Map:IN[19,19,2]


Anders als Du glaubst sind aber alle Tile-Daten immer in:
BlitzMax: [AUSKLAPPEN]
Map[X,Y,0]


und die Action-Daten in:
BlitzMax: [AUSKLAPPEN]
Map[X,Y,1]


Die 0 und die 1 sind noch keine Werte, sondern nur die Nummer der "Unter-Map", so wie wir vorhin auch zwei Maps hatten. Die Tile-Map ist Untermap 0, die ActionMap ist Untermap 1.

Die eigentlichen IDs sind Werte in den Array-Zellen dort:

BlitzMax: [AUSKLAPPEN]
' Tile-Map:
DrawImage tileset, x * 32, y * 32, map[x, y,0]
....
'Action-Map:
Select Map[SpielerX , SpielerY,1]
....



Die LadeRoutine kann so wie im ersten Beispiel gelöst werden:
BlitzMax: [AUSKLAPPEN]
Global ActionMap:TStream = ReadFile("data\ActionMap.tmd")
Global tilemap:TStream = ReadFile("data\map.tmd")
....
For x:Int = 0 To map_width
For y:Int = 0 To map_height
Map[x, y, 0] = ReadByte(TileMap)
Map[x, y, 1] = ReadByte(ActionMap)
Next
Next


du kannst aber die Actions auch punktuell aus einer Text-Datei nachziehen, da ja wahrscheinlich sehr wenige Felder Actions erhalten sollen:

BlitzMax: [AUSKLAPPEN]
Global ActionListe:TStream = ReadFile("data\ActionListe.txt")
Repeat
WoActionX = ReadLine(ActionListe)
WoActionY = ReadLine(ActionListe)
ActionID = ReadLine(ActionListe)
Map[ WoActionX , WoActionY , 1] = ActionID
Until Eof(ActionListe)



Types statt Array-Dimensionen
Ich weiss nicht ,wie sicher Du mit TYPES bist. Aber man kann mit Types jedem Feld beliebig viele "Eigenschaften" geben. Der Vorteil: fällt einem später eine weitere "Eigenschaft" ein, dann kann man sie sehr leicht erweitern. Bisher habe Deine Felder folgende Eigenschaften:

X
Y
ID
Action

Ana hat gleich noch...

Begehbar
Image
MapLink

dazugenommen...

Es wäre ja durchaus denkbar, dass ein Feld zwar das gleiche Bild, aber unterschiedliche Eigenschaften besitzt. Etwa eine Mauer, die durchlässig ist, oder ein See, auf dem man laufen kann, oder Sand, in dem man einsinkt, etc... Daher zwei Eigenschaften "Image" und "ID". Mit "Begehbar" wirst Du die ständigen Abfragen los, bei welcher ID eine Begehbarkeit vorliegt. Ab sofort "weiss" jedes Feld, ob es begehbar ist. usw....

Code-technisch löst Du TYPES so:

BlitzMax: [AUSKLAPPEN]
Type Feld
Field X:Int , Y: Int , ID:Int
Field TileSet:Int
Field Begehbar:Int , Action:Int
....usw
End Type

'Zeichnen:
DrawImage tileset, x * 32, y * 32, map[x, y].TileSet
' Action herausfinden:
ActionHier = Map[SpielerX, SpielerY].Action

Gewinner des BCC #53 mit "Gitarrist vs Fussballer" http://www.midimaster.de/downl...ssball.exe
 

PhillipK

BeitragSa, Sep 17, 2011 10:40
Antworten mit Zitat
Benutzer-Profile anzeigen
Ohjemine, tut mir leid das ich mal wieder einfach stumpf vor mich hingeredet hab Very Happy

Ich erklärs mal etwas besser..

Vorraussetzung ist, das du mit dem begriff eines "Objektes" was anfangen kannst - Vergleichbar mit zb. Java.

Ein Blitzmax-Type (also ein beliebiges objekt) unterstützt eine schier unbegrenzte anzahl an werten die du als päckchen speichern kannst.
Unter anderem ist auch möglich, funktionen zu "verlinken" (stells dir wie eine art Hyperlink in WWW vor. Du rufst es auf und kommst beim ziel an Very Happy )

BlitzMax: [AUSKLAPPEN]
Field onActivate(ev:TEventTrigger)


Field beschreibt, das du ein weiteres feld in deinem Objekt haben möchtest.
Der Typ ist hier Int() (glaube ich) -> Funktionen werden indirekt als Integer-pointer gehandhabt.
Das ermöglicht es dir, deinen Hyperlink in das Feld "onActivate" einzuspeichern. ev:TEventTrigger ist der Parameter der funktion.

Ich bin mir nicht ganz sicher, wie es genau ist, ein Funktion mit parameter zu pointern, ich probiers gleich mal aus und schreib dir den Testcode Smile

Da ev:TEventTrigger beschreibt, das deine funktionen die du verlinken möchtest als ersten parameter einen Parameter vom typ 'TEventPointer' erwartet (ev ist hierbei ein willkürlich gewählter name ! wichtig ist das :TEventPointer), musst du auch eben solche funktionen da reinschreiben.

Hier mal der Test:
BlitzMax: [AUSKLAPPEN]
SuperStrict '-> Superstrict muss immer sein, zwingt zum ordentlichen Programmieren.

'<=============================================
'----- Anfang Programm



'Wir erstellen in der variable 'test' eine neue instanz von dem Typ 'TTest'
Local test:TTest = New TTest

'Und wir schreiben gleich mal eine funktion in das Feld 'onActivate' rein -> TestFunktion1!
test.onActivate = TestFunktion1

'nun führen wir das ganze einmal aus, und schauen was passiert..
test.Activate()
test.Activate()

test.Activate()
test.Activate()

test.Activate()
test.Activate()


Print "Der Test ist abgeschlossen. unserere Variable Test hat nun folgende werte:"
Print "Aufrufe der funktion 'TestFunktion1' -> " + test.Aufrufe1
Print "Aufrufe der funktion 'TestFunktion2' -> " + test.Aufrufe2

'---- Ende programm
'==============================================>

Type TTest
Field onActivate(abc:TTest) '=> Unserer 'Hyperlink'

Field Aufrufe1:Int = 0 'ein testWert- soll zählen, wie oft TestFunktion1 aufgerufen wurde!
Field Aufrufe2:Int = 0 'ein testWert- soll zählen, wie oft TestFunktion2 aufgerufen wurde!

Method Activate()
Self.onActivate(Self)
End Method
End Type


Function TestFunktion1(efg:TTest)
'Hier mal ein simples printen um nachzuvollziehen, was da passiert.
Print "TestFunktion1 wurde aufgerufen."

'und gleich mal das feld 'Aufrufe1' hochzählen!
efg.Aufrufe1 = efg.Aufrufe1 + 1 'den letzten inhalt + 1 -> hochzählen.

'Hier mal etwas komisches: nach ausführen der funktion soll der 'Hyperlink' geändert werden.
efg.onActivate = TestFunktion2
End Function

Function TestFunktion2(hij:TTest)
Print "TestFunktion2 wurde aufgerufen. "

'hochzählen von 'Aufrufe2'
hij.Aufrufe2 = hij.Aufrufe2 + 1

'wieder eine änderung..
hij.onActivate = TestFunktion1
End Function


Das ist nichts, was man auf anhieb verstehen muss, zumal ich nicht der beste erklärkünstler bin.

Bitte lass dich nicht von den Seltsamen namen verwirren, ich habe sie absichtlich gewählt, damit du siehst, das diese in keinem zusammenhang stehen.


Um das ganze nun noch einmal grob mit worten zu beschreiben, wie du es für dein Problem nutzen könntest:

Mit dieser Grundstruktur 'TEventTrigger' kannst du eine art schalter programmieren.
Für jede sache kannst du einen TEventTrigger anlegen, die data füllen und die funktion umleiten (hey, der Hyperlink!)

Bei einem geplanten TileSetwechsel / Mapchange / was auch immer könntest du nun zb für jeden Tür-TEventTrigger die daten in das feld 'data' reinschreiben und die funktion 'WechselMap' ausführen.

Wichtig ist hier, das das objekt als Parameter übergeben wird, damit die funktion 'WechselMap' später zugriff auf die zieldaten hat.

Allerdings könnte man damit auch etwas anderes machen.
Zb einen TEventTrigger erstellen, welcher als data -1 mitbringt (bzw da ist dann besser ein Field value:Int zu empfehlen - ich habe grade keine ahnung, wie man am gescheitesten einen integer-wert in ein object einspeist).

Dieser Trigger kriegt nun die funktion 'Mach_mir_aua" aus, was dem spieler die -1 an Hp abzieht. Und schon hast du eine falle Smile

ABER:
Du brauchst eine möglichkeit, zu wissen, wann und wo welcher TEventTrigger platziert wird.
--------------------------------------------------------

Nun gehe ich mal an deinen Lade code ran - ich finds gut das du bereits gebrauch von eigenen Lade-routinen machst Very Happy

Ich weiß ich weiß, ich habe schon weiß gott genug getippt. Aber egal Smile

nun, dein system sieht bis jetzt noch leicht erweiterbar aus. Aber vorsicht: Es kann sein, das du viel in deinem Code ändern musst , grade wenn du öfters auf die id zugreifst! (also zb Map[x,y] um die id zu erhalten!)

Ich beschreibe nun den weg, wie man das ganze etwas variable gestalten könnte.


Zu ersteinmal lagern wir die werte, die du mit deiner map verbindest, in ein Objekt aus.

BlitzMax: [AUSKLAPPEN]
Type TTile
Field id:Byte
End Type


Und dazu erstellen wir noch einen Landschafts-Type - also eine eigene Landschaftsinstanz.

Hier mal eine vorgefertigte version:

BlitzMax: [AUSKLAPPEN]
Type TLandschaft
Field tileset:TImage 'das alte TileSet!
Field width:Int 'map_width!
Field Height:Int 'map_height

Field tile:TTile[,]

Function LoadMap:TLandschaft(tileSetPfad:String, tileMapPfad:String)
'hier kommt deine laderoutine rein.

'da dies eine FUNKTION innerhalb eines Types ist, müssen wir ersteinmal die Instanz erstellen.
Local map:TLandschaft = New TLandschaft
'anmerkung: map ist wieder nur variable gewählt-

map.tileset = LoadAnimImage(tileSetPfad, 32, 32, 0, 40)

Local stream:TStream = ReadFile(tileMapPFad) 'entspricht "tilemap:TStream = ReadFile("data\map.tmd")"

map.width = ReadInt(stream) 'die beiden werte, gewohnt wie vorher, auslesen.
map.Height = ReadInt(stream)

'hier was anderes: ersteinmal den Array 'tile' vorbereiten.
map.tile = New TTile[map.width + 1, map.Height + 1] '+1 habe ich deshalb genommen, da du mit "von 0 BIS map_width" etc durchgehst - aber evtl ist es auch OHNE +1 richtig!

For Local x:Int = 0 To map.width
For Local y:Int = 0 To map.Height
map.tile[x, y] = New TTile 'da auch hier wieder eine Instanz erstellt werden muss, tun wir das dochmal. :)

map.tile[x, y].id = ReadByte(stream) 'und wie gewohnt: Den stream auslesen.
Next
Next

'Wir sind fertig? Okay, stream wieder schließen.
CloseStream(stream)

'finally: die map zurückgeben.
Return map
End Function

End Type


Ich habe lediglich deine Laderoutine in die Funktion ausgelagert.
Ansonsten ist es fast identisch.

Nun zum unterschied:

Du kannst nun über
BlitzMax: [AUSKLAPPEN]
Global map:TLandschaft = TLandschaft.LoadMap("data\Tileset.png", "data\map.tmd")

deine map 'Laden'

mehr Globale variablen brauchst du nicht! Smile

TLandschaft ist ein Objekt, LoadMap die funktion.
Der Punkt gibt an, das du auf die funktion LoadMap INNERHALB von TLandschaft zugreifen möchtest.

Eine methode funktioniert ähnlich, allerdings ist sie instanzgebunden. Das heißt: Hätte 'Type TLandschaft' nun noch zb 'Method Draw()' drinstehen, könntest du mit
BlitzMax: [AUSKLAPPEN]
map.draw()


eben diese draw-methode ausführen lassen.

Objekte des Types 'TTile' könnten nun eben diese TEVentTrigger halten. wenn ein Trigger vorhanden ist, dann könnte dieser aktiviert werden, sobald der Spieler "darüber" ist - ganz nach belieben.


Methoden haben hier den vorteil, das sie auf die eigenen werte von Map zugreifen können.

Aber bitte bitte bitte! Tausch nicht einfach etwas aus! Schau hier drüber, wenn du es verstehst kann ich mirn Keks freuen - ich konnte einmal was erklären. Wenn nicht - nicht einfach austesten! Das ist schon eine größere änderung an deinem Code und könnte mit Restlichen teilen nichtmehr Kompatibel sein.
Wenn du ein solches System einbauen möchtest, ersteinmal sicherheitskopie anfertigen und bei jedem Detail nachfragen, was unklar ist. Erst wenn du den umgang verstanden hast, übernehmen Surprised



Gruß, der viel schreibende (und kränkelnde) Phillipk Smile
 

CO2

ehemals "SirMO"

BeitragMi, Sep 21, 2011 18:02
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo,
Und entschuldigung, das ich erst jetzt zurückschreibe (wenig zeit, neues Schuljahr hat begonnen...). Ich danke wieder für die Zahlreichen Antworten.

@ Xeres:
Ich versuche ja gerade dieses Spieler position, maus Position auf einer Tilemap zu verstehen... Das RPG Tutorial von Rob's site habe ich fast durch, zumindest bis zu dem punkt "Tilemap laden". Und dann kommt das getue mit der Positionen, und da blicke ich nicht durch... Wink

@ Ana:
Ok, also wird die map anstatt mit einem Array vom typ "int" mit meinem definierten Typ gebildet.

@ Midimaster:
Zitat:
Zwei Arrays
Auf die Idee wäre ich gar nicht gekommen Very Happy Aber nur nochmal zum verständnis: Also deklariere ich für jede map 2 Arrays, einmal das Array, was die tilemap enthält, und einmal das Array, was die zu ladenen maps und die positionen der actionTiles enthält oder wie?

Zitat:
Ein dreidimensionales Array
An diese Methode hatte ich auch gedacht, nur wusste ich nicht, wie das aussehen würde...

Zitat:
Types statt Array-Dimensionen
Das wäre wohl wirklich die eleganteste Lösung. Und dann wird für die Tilemap wie Ana schon schrieb einfach ein Array mit dem Type als Typ deklariert.

@ PhillipK:
Zitat:
(und kränkelnde) Phillipk
- Gute Besserung Wink
Ich danke dir für diesen wirklich sehr ausführlichen Beitrag.

Ok, das grundzeug vom Type kriege ich hin. Ich war ein wenig verwirrt, weil du im 1. Post die variable "ev" benutzt hast, ich dachte das wäre irgendwie eine Konstante, vorgeben von BMax.

Jetzt ist auch der Groschen zu "onActivate" gefallen Wink Die Funktion testet nur, ob das Objekt "TEventTrigger" Aktiviert wurde... Achso.

Zitat:
Es kann sein, das du viel in deinem Code ändern musst
Ich denke ich werde den Code nochmal neu schreiben, habe jetzt ja soviele erkenntnisse gesammelt.

mfG,
CO2
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti

Xeres

Moderator

BeitragMi, Sep 21, 2011 18:18
Antworten mit Zitat
Benutzer-Profile anzeigen
Nimm Stift & Zettel zur Hand, und schreib bzw. mal dir das Problem auf. Berechne händisch, was im Programm passieren soll. Auf den Code starren und andere Werte ausprobieren hilft nicht immer und überall dem Verständnis. Wenn du dann erfasst hast, welchen Punkt genau du nicht mehr verstehst, lässt sich da um so leichter helfen.
Ohne Grundlagen wirst du nicht weit kommen.
Win10 Prof.(x64)/Ubuntu 16.04|CPU 4x3Ghz (Intel i5-4590S)|RAM 8 GB|GeForce GTX 960
Wie man Fragen richtig stellt || "Es geht nicht" || Video-Tutorial: Sinus & Cosinus
T
HERE IS NO FAIR. THERE IS NO JUSTICE. THERE IS JUST ME. (Death, Discworld)

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group