PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Python: Problem mit scrollbarer Tilemap


Capitano
29.07.2008, 03:45
Hallo!

Also, ich programmiere grade ein kleines Jump 'n run game mit python(+pygame).
Klappte auch alles wunderbar in meinen ersten Testläufen. Doch dann habe ich das scrollen in die Tilemap integriert. (Hab mich zum großteil an dieses (http://jnrdev.72dpiarmy.com/en/jnrdev3/) Tutorial gehalten. Auch wenns in C++ geschrieben ist.)

Zuerst werde ich kurz die Funktionsweise erläutern, und dann was am Ende schief läuft.

1) Meine Tilemap wird durch ein 2d array dargestellt.
Und meine methode zum füllen des Bildschirms sieht etwa so aus:
Also: scroll_y und scroll_x ist die Spielerposition - die Hälfte des Bildschirms(um herauszufinden, bei welcher Position die linke obere Ecke des Bildschirms sein muss)

for y von 0-(scroll_y%TILESIZE) bis Höhe des Bildschirms + TILESIZE
for x von 0-(scroll_x%TILESIZE) bis Breite des Bildschirms + TILESIZE
(Zählschritt ist bei beiden Schleifen die TILESIZE)
Tile mit den Koords[(x+scroll_x)/TILESIZE] und[(y+scroll_y)/TILESIZE] an der Position (x,y)blitten
(Ich hoffe mal das war ein wenig verständlich)
:D

-Bewegen tu ich meine Spielfigur pixelweise und nicht Tileweise..
-In meiner Methode, die sich um die Kollisionsabfrage der Spielfigur mit der Map(Also ob begehbar oder nicht) arbeitet einfahc mit den SPielerkoordinaten, die ja eigentlich World koordinaten sein müssten, und damit völlig unbeeinflusst von den scrollkoordinaten. Oder seh ich da was falsch?

Auf jedenfall scheint es so bei mir nicht zu funktinieren.

Starte ich das SPiel, startet meine SPielfigur ziemlich in der linken oberen Ecke. Hier wird noch nciht gescrollt und alles funktiniert wunderbar. laufe ich aber über die Bildschirmmitte hinaus(Ab hier wird gescrollt, da die Spielfigur immer mittig im Bildschirm sein soll) ist alles etwas verschoben.
Lassse ich mir die Koordinaten des Spielers und die Scrollkoordinaten ausgeben, fällt auf, dass der SPieler immer genau um diese Scrollkoordinaten falsch steht.
Das sieht dann so aus:
http://img299.imageshack.us/img299/827/jumperkf5.th.png (http://img299.imageshack.us/my.php?image=jumperkf5.png)
Wie wir sehen, steht die Spielfigur genau um diese 9px(Also genau dem Wertder scroll_y) zu tief.(oder der Hintergrund zu hoch??)

Deshalb meine Frage: erkennt ihr in dem was ich geschrieben habe irgendwo einen Denkfehler?..
Oder erkennt vllt ein pythonkundiger einen fehler in meinem Code?..
Weiß auf jedenfall nicht mehr weiter..


Hier der gesammte code..zum download (http://capigame.bplaced.de/stuff/scrollMap.rar)

Falls noch jemand irgendwelche zusätzlichen erläuterungen oder sonst was, steh ich gerne für Rückfragen zur Verfügung:)

greetings


butterkeks
29.07.2008, 22:04
wahrscheinlich ist es für mich einfacher, dir das einfach nochmal allgemein zu erklären:

Gehen wir erst mal aus, dass es keinen Rand gibt und somit immer gescrollt wird. Sagen wir, die Weltkoordinaten des SPielers stehen in

vec = Numeric.Array # alias
player.pos = vec(x,y)

ich verwende Numeric wegen der komfortableren Vektorschreibweise, aber du kannst das ganze natürlich auch komponentenweise machen. Außerdem geh ich davon aus, dass (0,0) oben links ist.

Die Figur soll nun allerdings in die Mitte, also schauen wir um wieviel sie dazu "verschoben" werden muss (Vektorpfeil von der Mitte des Sprites zur Bildschirmmitte):

offset = vec(win.width / 2, win.height / 2) - (player.pos + (player.size / 2))

Theoretisch wärst du fertig indem du alles um offset verschoben zeichnest (alles +offset), aber natürlich brauchst du aus Performancegründen auch Clipping. Dazu berechnen wir den Weltpunkt, der (0,0) in Ansichtskoordinaten entspricht, indem wir die Transformation quasi "umkehren", also subtrahieren:
pt = vec(0,0) - offset
Dieser Punkt liegt in tile
tile = pt / tilesize
Über die tilesize und die Fenstergröße kennen wir natürlich die Anzahl Tiles pro Zeile/Spalte und können die restlichen in Sichtweite befindlichen Tiles einfach ermitteln. Damit ergibt sich vorerst folgender code zum Zeichnen:

for y in range(win.width / tilesize):
for x in range(win.height / tilesize):
# das entsprechende tile an der per Parameter angegebenen Position blitten
tiles[tile[0] + x][tile[1] + y].blit(tile * tilesize + offset)

Wenn
pt % tilesize != 0
für x bzw. y, dann ist die Anzeige nicht "bündig" und du musst entsprechend ein tile mehr zeichnen:

bounds = [win.width / tilesize, win.height / tilesize]
if pt[0] % tilesize > 0: bounds[0] += 1
if pt[1] % tilesize > 0: bounds[1] += 1

for y in range(bounds[1]):
for x in range(bounds[0]):
# das entsprechende tile an der per Parameter angegebenen Position blitten
tiles[tile[0] + x][tile[1] + y].blit(tile * tilesize + offset)

Du kannst an dieser Stelle noch etwas Performance rausholen, indem du die tiles und so "lokal" clipst (d.h. wirklich nur die Hälfte zeichnest, wenn ein Tile nur halb drauf ist) und das automatische Clipping bei pygame ausschaltest, welches sonst bei jedem Blit unnötigerweise ausgeführt wird. Natürlich haut es dein Prog aus den Socken, wenn du irgendwo hinzeichnest wo nichts hin darf.

Um wieder deine Scrollbegrenzung einzubauen, musst du einfach nur schauen, dass offset bestimmte Werte nicht über- bzw. unterschreitet und bei Bedarf zurücksetzen:

if scroll[0] < 0: scroll[0] = 0
if scroll[0] > mapWidth * tileSize - win.width: scroll[0] = mapWidth * tileSize - win.width
# usw.


Bei Fragen einfach fragen

Capitano
30.07.2008, 00:10
Hallo!

erstmal vielen dank für deine ausführliche Antwort.

Allerdings hab ich grade vor 20Minuten den entscheidenden Tipp in einem anderen Forum bekommen.

Ich musste einfach nur die absoluten Weltkoordinaten der Spielfigur vor dem Zeichnen dieser Figur in relative Koordinaten umrechnen.

Ich wusste ja das es irgendetwas triviales sein wird, aber damit hab ich nun wirklich nicht gerechnet. Da muss ich wohl ne wirklich große Denkblockade gehabt haben..
Ich war die ganze so auf das rendern der map und die kollisionsabfrage fixiert, das ich garnicht drauf geachtet hab wohin ich den player-Sprite zeichne..

Naja, den Fehler werd ich nie wieder machen^^

Werd deinen Post trotzdem mal in aller ruhe und gründlichkeit durchgehen, denn ich glaube meine Tilemap ist noch ziemlich verbesserungsfähig.

greetings