Pygame - Tasteneingabe für Jump'n Run

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

Hi @ all,

Vorneweg: Nein, ich bin kein Anfänger und ja, ich traue mir diese Aufgabe durchaus zu ;) .

Problembeschreibung: Ich möchte ein kleines Jump'n Run-Spielchen mit (ist klar) einem steuerbaren Helden erstellen, der auch bei Sprüngen kontrollierbar bleibt. Hier habe ich das Problem, dass Python bei mehr als einer gedrückten Taste die erste "vergisst", sobald die zweite Taste ausgelassen wird.

Etwas blöd erklärt, daher folgendes Beispiel:
- Ich drücke "Pfeil-Rechts" und halte sie gedrückt; der Held läuft nach Rechts.
- Ich drücke "Pfeil-oben" und halte sie gedrückt (während "Pfeil-Rechts" ebenfalls immer noch gedrückt wird); der Held läuft jetzt schräg oben-rechts
- Ich lasse "Pfeil-oben" los; der Held bleibt - entgegen meinen Willen - komplett stehen (obwohl "Pfeil-Rechts" immer noch gedrückt wird)

---

Ich habe mal einen (extrem vereinfachten) Code erstellt, der aufzeigt, wie es genau abläuft. Zum Ausführen wird im selben Verzeichnis leider eine Grafikdatei namens "grafik.bmp" benötigt - ich weiß es leider noch nicht, wie man es anders macht:

Code: Alles auswählen

# -*- coding: cp1252 -*-

# Voreinstellungen, nix besonderes
import pygame, sys
pygame.init()
screen = pygame.display.set_mode((640,480))
screen.fill((250,250,250))

try:
    player = pygame.image.load('grafik.bmp').convert()
except:
    print 'Bitte Datei "grafik.bmp" mit irgendeinem Inhalt erstellen.'
    sys.exit(1)
playerpos = player.get_rect()
clock = pygame.time.Clock()

# "Meine" Methode, um überhaupt sowas wie dauerhafte Tasten zu ermöglichen:
# Tastenwiederholung lt. pygame-Doku aktivieren und jeweils eine Variable
# für jede Richtung deklarieren.
pygame.key.set_repeat(1,30)
plinks = 0
prechts = 0
punten = 0
poben = 0

schleife = -1
while schleife:
    screen.fill((250,250,250))
    screen.blit(player, playerpos)
    pygame.display.update()
    clock.tick(30)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            schleife = 0
            break
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT or plinks == 1:
                playerpos = playerpos.move(-5,0)
                plinks = 1
            if event.key == pygame.K_RIGHT or prechts == 1:
                playerpos = playerpos.move(5,0)
                prechts = 1
            if event.key == pygame.K_UP or poben == 1:
                playerpos = playerpos.move(0,-5)
                poben = 1
            if event.key == pygame.K_DOWN or punten == 1:
                playerpos = playerpos.move(0,5)
                punten = 1
            if event.key == pygame.K_ESCAPE:
                schleife = 0
                break
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                plinks = 0
            if event.key == pygame.K_RIGHT:
                prechts = 0
            if event.key == pygame.K_UP:
                poben = 0
            if event.key == pygame.K_DOWN:
                punten = 0

pygame.quit()
Wie man sieht, mache ich folgendes: Ich speichere für jede Bewegungsart je eine Variable. Für weitere Bewegungen des "Helden" reicht es dann, wenn die jeweilige Variable auf "1" gesetzt ist (OR-Abfrage). Erst, wenn die Tasten wieder losgelassen werden (KEYUP-Block), sollte theoretisch die weitere gleiche Bewegung des Helden erst wieder möglich sein, wenn die entsprechende Richtungstaste gedrückt wird.

Leider tritt dabei der beschriebene Effekt auf: Druck rechts, Druck oben, Druck oben loslassen = Stillstand.

Tjoar, ich brauche einen Anstoß: Wie löse ich das Problem? Ich hänge momentan komplett und weiß nicht, wie ich weitermachen soll... Ich hätte mir schon andere Quellcodes angesehen, die meiner Interpretation nach nicht viel anders machen, als ich... Need help^^.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Das Problem tritt nur dann bei dem loslassen der Unten oder Oben Taste auf. Bei allen anderen Kombinationen von Tasten scheinbar nicht.

Du weist wie man debuggt?

1. print Statements und sich Meldungen und Dinge ausdrucken lassen
2. das pdb Modul benutzen. Ganz einfach über pdb.set_trace() an einer wichtigen Stelle im Code setzen, Modul starten, wenn man dort ankommt wird man dann gleich in den Debugger "geworfen". Sehr praktisch. Funktioniert bei Modulen, die irgendwie was mit Grafik machen, natürlich nicht so gut, da man beim Fortsetzen oder "steppen" des Programmflusses nicht zwischendurch ins Grafikfenster wechseln kann.

Wenn man Print's benutzt, sieht man, das nach dem Loslassen von Unten oder Oben plötzlich keine Meldungen mehr erscheinen, also plötzlich alle Eventquellen versiegen. Lässt man Rechts dann auch los, geht der Programmfluss nach dem Zurücksetzen von prechts normal weiter. Ziemlich komisch.

- Normalerweise verwendet man keinen (oder nur sehr trivialen) Code auf Modulebene. IdR legt man eine main() Funktion an und ruft diese beim direkten Start des Modules automatisch auf, so:

Code: Alles auswählen

if __name__ == "__main__": main()
__main__ ist immer ein direkt gestartetes Modul.

- Warum verwendest du 1 und 0? Python kennt die Boolean Typen True und False. Sollte man für Wahrheitswerte und Zustände (Ist Oben gedrückt? -> True / False) benutzen.

- Kommt natürlich auf die Grösse des Projektes an, aber direkt mit dem Bildschirm als dem Spielfeld zu arbeiten empfinde ich als unschön. Damit bist du gezwungen, wie man hier auch sehen kann, Spiellogik in die Darstellung mitreinzuschmeißen. Der Spieler bewegt sich nicht, er wird von pygame bewegt. Ich würde der Darstellung die Möglichkeit geben, von der Logik Rohdaten anzufragen und mithilfe deren die Situation auf dem Bildschirm zu rekonstruieren.

- Die ganzen If Schl.. Abfragen :lol: samt Code Duplikation kannst du auch durch eine Forschleife aushebeln, die p* Variablen würde ich in eine Liste stecken und mit enumerate() zum Neusetzen über den Index arbeiten.

ADD:
<Event(2-KeyDown {'key': 273, 'unicode': u'', 'mod': 0})>
Set prechts
Set poben
<Event(2-KeyDown {'key': 273, 'unicode': u'', 'mod': 0})>
Set prechts
Set poben
<Event(2-KeyDown {'key': 273, 'unicode': u'', 'mod': 0})>
Set prechts
Set poben
<Event(3-KeyUp {'key': 273, 'mod': 0})>
Release poben
<Event(3-KeyUp {'key': 275, 'mod': 0})>
Release prechts
Anscheinend taucht ein "Rechts gedrückt" Event gar nicht mehr auf, wenn du beide Tasten gedrückt hälst, prechts wird also nur durch ein vorheriges "1" von prechts neu gesetzt. Lässt du dann die Oben Taste los, taucht das Event nicht wieder auf. Du rennst also irgendwie gegen Pygame's Event System. Schau dich am besten mal in Pygame's Event Dokumentation um.
BlackJack

Ich würde das mit der Tastenwiederholung sein lassen und ausschliesslich mit den Ereignissen für's Taste runterdrücken und Taste loslassen Arbeiten. Dafür muss man die Auswertung der Tastendrücke von der Bewegung trennen, also in der Schleife erst nach der Ereignisverarbeitung die Zustände auswerten und den Spieler bewegen.
sea-live
User
Beiträge: 440
Registriert: Montag 18. Februar 2008, 12:24
Wohnort: RP

da hätte ich was für dich mein panzersim
hier der quelltext
http://paste.pocoo.org/show/53316/

und die images
Bild
Bild

such mal hier rumm nach panzer dann findest du auch die restlichen images
Astorek
User
Beiträge: 72
Registriert: Samstag 24. Januar 2009, 15:06
Kontaktdaten:

BlackJack hat geschrieben:Ich würde das mit der Tastenwiederholung sein lassen und ausschliesslich mit den Ereignissen für's Taste runterdrücken und Taste loslassen Arbeiten. Dafür muss man die Auswertung der Tastendrücke von der Bewegung trennen, also in der Schleife erst nach der Ereignisverarbeitung die Zustände auswerten und den Spieler bewegen.
Danke, das war der Denkanstoß, den ich brauchte. Hätt ich eigentlich selber drauf kommen können^^.
str1442 hat geschrieben:Das Problem tritt nur dann bei dem loslassen der Unten oder Oben Taste auf. Bei allen anderen Kombinationen von Tasten scheinbar nicht.
Leider nicht, das Problem trat mit allen Kombinationen auf. Statt Pfeil-Oben und Pfeil-Rechts hätte ich wohl besser allgemeingültig Taste1 und Taste2 schreiben sollen^^.

Wegem Debuggen: Ich habe print-Befehle vorher genutzt. Deswegen war es auch so seltsam, weil die Grafik (der "Held") stillstand, obwohl der KEYUP-Block eben nicht abgearbeitet wurde... (Hätt ich zugegeben vorher sagen sollen, sorry) Aber Danke für den Tipp mit pdb, hätt ich wieder was zum Einlesen gefunden^^.

Ansonsten natürlich Danke für deine allgemeinen Programmiertipps. Ich habe das Beispiel nur auf die Schnelle hingeklatscht um mein Problem zu verdeutlichen, mein "wahres" Projekt sieht (auch in Bezug auf Trennung von Steuerung/Spielfeld) da sowieso etwas anders aus ;) . Das Ganze in mehrere IFs zu unterteilen und das (unabsichtliche) Vermeiden von Bool-Werten sind wohl noch meine eigenen Relikte aus Zeiten vor Python (habe früher in QBasic programmiert^^).

Anbei noch das korrigierte Listing, und ein Dankeschön nochmal :)

Code: Alles auswählen

# -*- coding: cp1252 -*-

# Voreinstellungen, nix besonderes
import pygame, sys
pygame.init()
screen = pygame.display.set_mode((640,480))
screen.fill((250,250,250))

try:
    player = pygame.image.load('grafik.bmp').convert()
except:
    print 'Bitte Datei "grafik.bmp" mit irgendeinem Inhalt erstellen.'
    sys.exit(1)
playerpos = player.get_rect()
clock = pygame.time.Clock()

# KEINE Tastenwiederholung, stattdessen "Bewegungsvariablen"
#pygame.key.set_repeat(1,30)
x = 0
y = 0

schleife = -1
while schleife:
    screen.fill((250,250,250))
    screen.blit(player, playerpos)
    pygame.display.update()
    clock.tick(30)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            schleife = 0
            break
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                x = -5
            if event.key == pygame.K_RIGHT:
                x = 5
            if event.key == pygame.K_UP:
                y = -5
            if event.key == pygame.K_DOWN:
                y = 5
            if event.key == pygame.K_ESCAPE:
                schleife = 0
                break
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                x = 0
            if event.key == pygame.K_RIGHT:
                x = 0
            if event.key == pygame.K_UP:
                y = 0
            if event.key == pygame.K_DOWN:
                y = 0
    playerpos = playerpos.move(x,y)

pygame.quit()
Antworten