Keyboard-Event

Fragen zu Tkinter.
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo !

Bin an einem Space Invaderspiel und versuche ein Dauerfeuer zu verhindern, doch alle Versuche haben nicht den gewünschten Erfolg. In Zeile 120 und 130 versuche ich mittels eines Schalters dieses Verhalten zu erzeugen. Es wird bei einem Tastendruck, ohne die Taste loszulassen, ein Keyrelease - Event gesendet.

Hier der Code: http://www.python-forum.de/pastebin.php?mode=view&s=103

Gruß Frank
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Nein, das Event wird nicht ausgelöst, du hast nur ein zweites Event mit FIRE_PRESS drin, welches schon vorher ausgelöst wird.

Übrigens sieht schon verdammt gut aus, kompliment :D
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo Xynon1 !

Danke für das Kompliment !

Verstehe gerade nicht was du mir sagen möchtest ?

Gruß Frank
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Du hast doch folgende Events gesetzt:

Code: Alles auswählen

RIGHT_PRESS = "<Right>"
LEFT_PRESS = "<Left>"
RIGHT_RELEASE = "<KeyRelease-Right>"
LEFT_RELEASE = "<KeyRelease-Left>"
FIRE_PRESS = ("<space>")
FIRE_RELEASE = ("<KeyRelease-space>")
Und in einer Schleife werden die gesetzt.
Nun hast du aber das Problem das wenn du ein KeyPress (Also ein normales Tasten-Event) nutzt, nämlich "<space>", bedeutet dass das folgende KeyRelease-Event "<KeyRelease-space>" ignoriert wird.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Wenn der KeyRelease-Event ignoriert werden würde, dann würde doch der Schalter "self.fire_button_release" auf False bleiben und ich könnte nicht mehr "ballern". Mein Gedanke war, beim Loslassen der Taste den Schalter auf True zu setzen und das Dauerfeuer abzuschalten.

Gruß Fank
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ok, moment, vieleicht liegt es am OS, hatte bei Tkinter Key-Bindings dort schon häufiger unterschiede, welches nutzt du ?

Ich teste es gleich mal noch mit einem anderen System.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Ich verwende Xubuntu !
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ist tatsächlich unterschiedlich,
ich seh mir dein Programm gleich nochmal genau an was an den einzelnen Stellen passiert, aber dürfte ich vieleicht einen anderen Stop-Trigger vorschlagen.

Wenn ich mich recht erinnere war das bei Space Inviders immer so, das du nur einen Schuss abgeben konntest und erst wenn der weg war den nächsten.
Also als Stop-Trigger statt KeyRelease das "sterben" des Schusses nehmen.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Danke dir für deine Hilfe ! Eine andere Möglichkeit nehme ich sehr gerne an - stehe gerade selbst auf dem Schlauch.

Gruß Frank
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ok, follgendes wäre meine Idee:

1. mach aus "set_canon_fire" einfach nur "fire"
2. Falls du den Player als Invader Objekt betrachtest, solltest du bei "fire" angeben, ob nach oben oder unten.
Besser wäre es aber wenn du ein "Sprite" Objekt machen würdest, welches die Bewegung steuert und du einmal Player und einmal Invader ableitest und sie damit trennst
3. Dein Schuss muss ein Objekt werden, da du ja nicht nur diesen sehen willst sonder ja auch was bei diesem passieren soll, also hier könnte man ein "Bullet" Objekt von Sprite ableiten
4. Dann kannst du bei "fire" eine Bullet Instanz erzeugen, welches die Kollision auslöst und sich dann auflöst.
5. Dann kannst du auch einfach prüfen, wenn die Schießen geklickt ob ein Projektil existiert, wenn nicht erzeugst du ein neues.

Im Prinzip wirst du hier nicht weit herum kommen um solche Logik, ich wunder mich nur warum du Tkinter und nicht pygame genommen hast, doch prinzipiell macht das keinen allzugroßen unterschied. Nur gäbe es in pygame sowas wie Sprites, etc schon.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Wenn ich das richtig verstanden habe, hast ein Problem mit dem Autorepeat, genauer das hier:
http://wiki.tcl.tk/20299

Vor einiger Zeit hatte ich das gleiche Problem, mein Testprogramm dafür:

Code: Alles auswählen

#!/usr/bin/env python

####

import Tkinter as tk

####
  
class KeyTest(object):


  def __init__(self):

    self.root = tk.Tk()
    self.svar = tk.StringVar()
    self.root.config(takefocus=1)
    
    self.label = tk.Label(self.root, textvariable=self.svar, width=60, height=5)
    self.label.pack()
    
    self.root.bind_all('<KeyRelease>', self.release)
    self.root.bind_all('<KeyPress>', self.press)
    # extra treatment else WM controls the tab
    self.root.bind_all('<KeyPress-Tab>', self.press)
    self.root.focus_set()
    self.reset()
    self.check()
    

  def check(self):

    if self.key:   
      s = '%s   >%s<   >%s<  ===> %d' % (self.key, ('-', 'PRESSED')[self.pressed],
                                         ('-', 'PUSHED')[self.push], self.counter)
      self.svar.set(s)

    self.push = 0
    self.root.after(50, self.check)

                  
  def reset(self):

    self.key = None
    self.push = 0
    self.pressed = 0
    self.old_ser = -1
    self.new_ser = -2
    self.counter = 0

    
  def release(self, ev):

    try:
      self.new_ser = ev.serial    
    except AttributeError:
      print 'ups'
      return
    
    print 'release', ev.serial
  
    self.pressed = 0
    self.old_ser = self.new_ser
    self.key = str(ev.keysym)


  def press(self, ev):

    if self.old_ser == ev.serial:
      self.push = 0
      print 'same key'
    else:
      self.push = 1
      self.counter += 1

    self.pressed = 1    
    self.old_ser = ev.serial
    self.key = str(ev.keysym)

 
  def run(self):

    self.root.mainloop() 


####
    
if __name__ == '__main__':

  KeyTest().run()
Programm starten und beliebig die Tasten drücken. Bei einem Tastendruck erscheint ganz kurz das Wort "PUSHED". Zur besseren Kontrolle wird noch ein Zähler hochgezählt. An dieser Stelle im Programm kannst Du Deine Fire-Methode einklinken, damit pro Tastendruck jeweils nur ein Schuss abgefeuert wird.

:wink:
yipyip
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Aber dies ist keine Lösung für das eigentliche Problem, dies ist nur eine Verlagerung.
Denn Zeitverzögert ist nur gut wenn man eine bestimmte Pulsrate hat, und das könnte man dann nochmal einfacher machen.
zB. nur mit der ".after(...)"-Methode einfach eine Variable ändern, welche das Schießen zulässt, dann ist es völlig egal wie oft das KeyEvent ausgelöst wird.

Und meine Frage wäre jetzt noch unter welchen Systemen du das bereits getestet hast, sonst ist dies genauso hinfällig wie der normale KeyEvent.

Edit:
Achso, trotzdem ein schönes Snippet, sollte höchstens nochmal etwas aufgearbeitet werden :wink:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Ich habe es nur unter Ubuntu getestet (deshalb auch das X11 im Link). Von Zeitverzögerung oder Pulsrate ist meinens Erachtens überhaupt nicht die Rede. Mit meinem Beispiel kann man ein einmaliges Event auslösen, auch wenn man die Taste länger gedrückt hält und somit das Autorepeat unter X11 umgehen. So hatte ich das Problem verstanden (damit ein Dauerfeuer verhindert wird).

:wink:
yipyip
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ja, das ist mir bewusst, aber sein Ziel ist ja nicht "Autorepeat unter X11 umgehen", auch wenn das sein momentanes Problem ist.
Sondern sein Ziel ist ja "Space Invaders" und in Space Invaders, hing das nicht vom Tastendruck ab, sondern ob das Projektil noch existiert.
Denn ansonsten kann man auch dauerfeuer erzeugen in dem man ganz schnell hinter einander Klickt.

Dennoch danke, brauchbar ist deine Info auf jedenfall und das möchte ich hier auch nicht gering schätzen.
Zu mindest versteh ich den Unterschied in der Interpretation des KeyEvents endlich.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Hallo yipyip!

Habe deinen Tip eingebaut und es klappt - danke!

@Xynon

Danke für deine Tips - habe schon mal Snake mit Pygame gemacht, doch irgendwie mag ich Tkinter.

Gruß Frank
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ich habe ja auch nichts dagegen, das du Tkinter nutzt, aber du musst dennoch etwas weiter in OOP streben, ansonsten fällt dir dein schon guter Ansatz auf die Füße. :wink:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

Ihr habt ja schon mehr geschrieben - ich war noch am Code von yipyip. Eigentlich wollte ich wirklich nur das Dauerfeuer abstellen, doch ich habe jetzt mal Space-Invaders gespielt und muss, dass Xynon1 recht hat. Man kann nur einen Schuß abgeben, wenn der eine getroffen hat oder aus dem Spielfeld ist. Muss ich noch ändern und dadurch ensteht das Problem mit dem Dauerferuer nicht.

Gruß und Dank Frank
Benutzeravatar
kaytec
User
Beiträge: 608
Registriert: Dienstag 13. Februar 2007, 21:57

kaytec hat geschrieben:Ihr habt ja schon mehr geschrieben - ich war noch am Code von yipyip. Eigentlich wollte ich wirklich nur das Dauerfeuer abstellen, doch ich habe jetzt mal Space-Invaders gespielt und musste feststellen, dass Xynon1 recht hat. Man kann nur einen Schuß abgeben, wenn der eine getroffen hat oder aus dem Spielfeld ist. Muss ich noch ändern und dadurch ensteht das Problem mit dem Dauerferuer nicht.

Gruß und Dank Frank
Wollte eigentlich mich nicht zitieren, sondern den Text verbessern.

Gruß Frank
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

@Xinon1:
Daß jeweils nur ein Schuss aktiv sein darf, war aus der ursprünglichen Frage aber auch nicht so ersichtlich. ;-)
Sollte programmiertechnisch aber auch nicht so ein großes Problem sein.
Kann mich nicht mehr erinnern, wann ich das letzte mal Space Invaders gespielt habe...

@kaytec
Auch wenn wir hier alle Tkinter Liebhaber sind, in diesem Fall hätte ich es doch mit Pygame gemacht.
Willst Du nicht auch noch Sound dazu haben?

:wink:
yipyip
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Sound kann man doch einbauen zB mit tkSnack.
Wenn man allerdings pygame sound haben willst dann importier doch das einzelne Modul von pygame, das ändert doch nichts daran das die GUI mit Tkinter gebaut ist.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten