Archiv verlassen und diese Seite im Standarddesign anzeigen : Pixelmovement Terrain-Kollisionsabfrage
Guten Abend, eine weitere Frage meinerseits:
Ich habe mir ein kleines eigenes Pixelmovement-System zusammengebastlelt, es funktioniert auch, so weit ich das nachverfolgen kann, einwandtfrei.
Was ich allerdings ändern will ist die Kollision mit dem Gelände, anstatt, dass für meinen Character an jedem Felsblock ein 32x32 Pixel großes Quadrat geblockt wird würde ich lieber eine möglichst genaue, annähernd dem Felsblock entsprechende Blockung vorziehen.
Um dies zu erreichen dachte ich mir aus anderen Spielen, von welchen ich die Umsetzung teilweise kenne, Ideen aus zu leihen, ich dachte an eine Art Pathing Map. Diese wäre eine Bilddatei welche mit farblichen Markierungen die derzeitige Map auf welcher sich mein Character befindet representiert.
Würde mein Character sich bewegen wird seine Position auf der Pathing Map angeglichen und untersucht ob die Farbe des Pixels an der Position meines Characters Schwarz (sprich nicht begehbar) beziehungsweise Weiß (das heißt begehbar) ist.
Nun frage ich mich über die Performance da dies natürlich ein sehr wichtiger Bestandteil eines Spieles ist:
1). Soll ich beim betreten der Karte eine Pathing Map für die gesamte Karte anlegen?
2). Soll ich von Spielbeginn an alle Pathing Maps für alle Karten im Vorheraus erstellen?
3). Soll ich bei jeder einzelnen Bewegung nur eine winzige Pathing Map für das Tile anlegen auf welcher sich der Character gerade befindet/zu welcher er sich bewegt?
4). Gibt es eine bessere Lösung mit ähnlichem Effekt?
Ich danke im Vorraus allen Hilfreichen Community-Mitgliedern.
Shining Advances
14.06.2010, 20:56
ich denke am einfachsten geht es, wenn du diese pathing map speziell für das tileset erstellst anstelle für die karte.
also eine art 2. tileset mit denselben ausmaßen des richtigen tilesets, welches die passierbarkeit des tiles an der selben stelle auf dem richtigen tileset darstellt.
leicht verwirrender satz...ich hoffe du verstehst was ich meine xD
dieses 2. tileset lädst du dann einfach mit dem normalen tileset. am besten irgendwo in der setup methode von game_map.
dadurch brauchst du nicht für jede map eine eigene pathing map machen, was schonmal arbeit spart.
...und sofern die karte größer ist als das tileset (was eigtl immer der fall ist ;)) sparst du dir sogar noch ein wenig ram und ladezeit.
das zusätzliche umrechnen von mappixelkoordinate auf tilesetpixelkoordinate geht eigentlich ganz schnell und birgt keine performance probleme:
tileid = $game_map.data[pixel-x-koordinate / 32, pixel-y-koordinate / 32,0] - 384 #errechnet die tileid des feldes unter den koordinaten
#die letzte 0 steht dabei für die unterste ebene der karte...also brauchst du eine zählschleife um alle ebenen zu überprüfen
farbe = tileset2.get_pixel((tileid % 8) * 32 + (pixel-x-koordinate % 32),tileid / 8 * 32 + (pixel-y-koordinate % 32))
nachteil des ganzen ist halt, dass das nicht für die autotiles funktioniert.
diese lassen sich nämlich nicht durch eine einfache formel errechnen sondern benötigen eine spezielle tabelle (siehe diverse nachgeschriebene Tilemap klassen)
Ich habe mich wohl etwas missverständlich ausgedrückt, tut mir sehr leid, natürlich hatte ich niemals vor für jede Karte eine eigene Pathing Map zu zeichnen sondern für das Tileset welches die Karte benutzt und mit diesem Tileset dann eine Karte vom Editor zeichnen zu lassen, als würde ich eine Schwarz-Weiß Kopie erstellen lassen.
was ich bis jetzt erreicht habe ist folgendes Script:
(In Bezug auf meinen ersten Post ist die Möglichkeit Nummer 3) welche ich in Betracht ziehe.)
[Edit: Dieser Code funktioniert nicht!]
def pathing_test(real_x,real_y)
int_x = real_x / 32
int_y = real_y / 32
tile = Bitmap.new(32,32)
for i in [2, 1, 0]
tile_id = data[int_x, int_y, i] - 383 #tileset tiles beginnen mit 384
next if tile_id < 1 #auto tile
ry = tile_id / 8
rx = tile_id - (ry * 8) -1
rect = Rect.new(rx*32,ry*32,32,32)
tile.blt(0,0,RPG::Cache.tileset("PathingMap-"+@tileset_name),rect)
end
return tile.get_pixel(real_x - (int_x * 32), real_y - (int_y * 32)) == Color.new(0,0,0,0)
end
#real_x / real_x sind die tatsächlichen x und y Werte des Characters auf der Map
[Edit: Hier die verbesserte Version.]
def pathing_test(real_x,real_y)
int_x = real_x / 32
int_x -= 1 if real_x % 32 == 0 && real_x != 0
int_y = real_y / 32
int_y -= 1 if real_y % 32 == 0 && real_y != 0
tile = Bitmap.new(32,32)
for i in [2, 1, 0]
tile_id = data[int_x, int_y, i] - 383
next if tile_id < 1
ry = tile_id / 8
ry -= 1 if tile_id % 8 == 0
rx = tile_id - (ry * 8) -1
rect = Rect.new(rx*32,ry*32,32,32)
tile.blt(0,0,RPG::Cache.tileset("PathingMap-"+@tileset_name),rect)
end
return tile.get_pixel(real_x - (int_x * 32), real_y - (int_y * 32)) == Color.new(0,0,0,0)
end
#real_x / real_x sind die tatsächlichen x und y Werte des Characters auf der Map
gibt es hierfür Verbesserungsvorschläge? Erkennt jemand Fehler?
Edit: Ich habe selbst einige, teilweise kritische, Fehler im ersten Code entdeckt und ausgemergelt. Ich bitte weiterhin um Kommentare und Kritik.
Vielen Dank.
Cornix.
Shining Advances
14.06.2010, 21:38
das einzige was auszusetzen wäre ist, dass du das gesammte tile kopierst, obwohl du ja eigentlich nur ein pixel benötigst.
zudem übermalst du das selbe bitmap mit den 3 ebenen und fragst erst am ende ab ob das feld passierbar ist.
sofern passierbare felder weiß und nicht transparent sind, fragst du also nur die unterste ebene ab.
ansonsten kann man diverse zwischenschritte übrspringen und rechnungen durch mod verkürzen.
def pathing_test(real_x,real_y)
block = Color.new(0,0,0,255)#damit muss man das object/die farbe nicht 3 mal in der schleife erzeugen
tile = RPG::Cache.tileset("PathingMap-"+@tileset_name)
int_x = real_x / 32
int_y = real_y / 32
add_x = real_x % 32#damit es nicht 3 mal in der schleife ausgerechnet werden muss
add_y = real_y % 32#siehe oben
for i in [2, 1, 0]
tile_id = data[int_x, int_y, i] - 384 #tileset tiles beginnen mit 384
next if tile_id < 0 #achtung nicht 1! dadurch würdest du das erste tile überspringen
ry = tile_id / 8
rx = tile_id % 8 #geht mit mod einfacher und vermutlich schneller (wenn auch unwesentlich, sind ja nur kleine berechnungen)
return false if tile.get_pixel(rx+add_x, ry+add_y) == block
#schleife bricht ab sobald das feld nicht passierbar ist -> weniger schleifendurchläufe = weniger rechenzeit
end
return true#
end
das einzige was auszusetzen wäre ist, dass du das gesammte tile kopierst, obwohl du ja eigentlich nur ein pixel benötigst.
zudem übermalst du das selbe bitmap mit den 3 ebenen und fragst erst am ende ab ob das feld passierbar ist.
sofern passierbare felder weiß und nicht transparent sind, fragst du also nur die unterste ebene ab.
ansonsten kann man diverse zwischenschritte übrspringen und rechnungen durch mod verkürzen.
def pathing_test(real_x,real_y)
block = Color.new(0,0,0,255)#damit muss man das object/die farbe nicht 3 mal in der schleife erzeugen
tile = RPG::Cache.tileset("PathingMap-"+@tileset_name)
int_x = real_x / 32
int_y = real_y / 32
add_x = real_x % 32#damit es nicht 3 mal in der schleife ausgerechnet werden muss
add_y = real_y % 32#siehe oben
for i in [2, 1, 0]
tile_id = data[int_x, int_y, i] - 384 #tileset tiles beginnen mit 384
next if tile_id < 0 #achtung nicht 1! dadurch würdest du das erste tile überspringen
ry = tile_id / 8
rx = tile_id % 8 #geht mit mod einfacher und vermutlich schneller (wenn auch unwesentlich, sind ja nur kleine berechnungen)
return false if tile.get_pixel(rx+add_x, ry+add_y) == block
#schleife bricht ab sobald das feld nicht passierbar ist -> weniger schleifendurchläufe = weniger rechenzeit
end
return true#
end
Vielen vielen Dank, werde ich so benutzen, aber
"#tileset tiles beginnen mit 384"
und "#achtung nicht 1! dadurch würdest du das erste tile"
treffen nichtmehr zu wenn man das erste Tile durch die Subtraktion von 383 auf 1 setzt und nicht auf 0. Ist natürlich Geschmackssache.
Außerdem hatte ich das Tile ursprünglich absichtlich überzeichnet , nur war meine Begehbar-Farbe nicht 255,255,255,255 sondern 0,0,0,0 sprich transparent. Das Tile mit transparenz zu überschreiben hätte keinen Effekt.
Edit: es funktioniert nicht, ich bekomme für jedes Tile "true" und außerdem einen Error für maximale x-, y-Werte.
Edit2: Ich habe es mit folgendem Script zum laufen bringen können:
def test(real_x,real_y)
block = Color.new(0,0,0,255)
tile = RPG::Cache.tileset("PathingMap-"+@tileset_name)
int_x = real_x / 32
int_x -= 1 if real_x % 32 == 0 # die Koordinaten (32/32) gehören noch zum ersten Tile (0/0)
int_y = real_y / 32
int_y -= 1 if real_y % 32 == 0
add_x = real_x % 32
add_y = real_y % 32
for i in [2, 1, 0]
tile_id = data[int_x, int_y, i] - 384
next if tile_id < 0
ry = tile_id / 8
rx = tile_id % 8
return false if tile.get_pixel(rx*32+add_x, ry*32+add_y) == block
end
return true
enddie Fett geschriebenen unterstrichenen Stellen habe ich verändert.
Das Problem mit den Maximal Werten ist, dass das letzte Tile die max-X-Wert 640 und max-Y-Wert 480 was mit dem alten Script ein Tile ergeben würde welches außerhalb der Karte liegt.
Der Fehler in der Pixel-Farben Abfrage ist ziemlich offensichtlich.
Ich wollte keinen neuen Thread aufmachen welcher den selben Titel trägt wie einer von mir der bereits besteht daher werde ich diesen erneut benutzen.
Mein neues Problem lautet wie folgt:
Ich versuche ein eigenes Pixelmovement System zu erstellen.
Für die Terrain-Kollision benutze ich eine art "PathingMap" sprich, das benutzte Tileset auf welchem die Map spielt ist als Schwarz-Weiß Kopie nocheinmal vorhanden, schwarze stellen sind hierbei unpassierbar, weiße können passiert werden. Soweit funktioniert alles sehr gut.
Hier ein Beispiel für diese Anfrage falls es für jemanden von Interesse ist:
def pathing_test(real_x,real_y)
block = Color.new(0,0,0,255)
tile = RPG::Cache.tileset("PathingMap-"+@tileset_name)
int_x = real_x / 32
int_x -= 1 if real_x % 32 == 0
int_y = real_y / 32
int_y -= 1 if real_y % 32 == 0
add_x = real_x % 32
add_y = real_y % 32
for i in [2, 1, 0]
tile_id = data[int_x, int_y, i] - 384
next if tile_id < 0
ry = tile_id / 8
rx = tile_id % 8
return false if tile.get_pixel(rx*32+add_x, ry*32+add_y) == block
end
return true
end
Nun, des folgenden will ich, dass Objekte in meinem Spiel auch eine kreisrunde Kollision besitzen, sprich: Einen Kollisionsradius.
Auch der Kollisionsradius ist soweit fertig implementiert damit Objekte sich gegenseitig blocken können.
if object.collision_size < 0
return true #air
end
if object.collision_size > 0
for blocking_object in @blocking_objects
if blocking_object != object
return false if self.collide?(blocking_object, object)
end
end
end
def collide?(objectA, objectB)
return Math.hypot(objectA.x - objectB.new_x, objectA.y - objectB.new_y) < objectA.collision_size + objectB.collision_size
end
Wobei ich nun jedoch Probleme habe ist, diesen Kollisionsradius auch auf die Passierbarkeit des Geländes zu übertragen. Meine Gelände-Passierbarkeits-Abfrage funktioniert derzeit nur mit den x- und y-Koordinaten des Zielpunktes zu welchem sich mein Charakter bewegen will, allerdings soll auch auf dem Weg abgefragt werden ob unpassierbares Gelände den Kollisionsradius meines Charakters schneidet.
bisher kam ich lediglich auf sehr große, komplizierte Funktionen. Kennt jemand eine einfache, effektive Implementierungsmöglichkeit?
P.S.: Eine kleine zusätzliche Schwierigkeit ist, dass mein Character selbstständig wenn er auf eine unpassierbare Stelle trifft versucht das Hinderniss seitlich zu umgehen, sprich:
Wenn der Spieler die Pfeiltaste nach oben drückt und der Held auf einen Stein trifft versucht der Held nicht weiterhin geradeaus durch den Stein zu marschieren sondern nach Linksoben und anschließend auch nach Rechtsoben selbstständig aus zu weichen.
Hoffe auf weitere nützliche Beiträge der Community.
Cornix.
Powered by vBulletin® Version 4.2.3 Copyright ©2025 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.