Kleines Game mit Tkinter - Brauche Hilfe

Fragen zu Tkinter.
Saob
User
Beiträge: 23
Registriert: Mittwoch 12. Dezember 2007, 20:11
Kontaktdaten:

Mittwoch 12. Dezember 2007, 20:29

Hallo erstmal 8)
ich lerne siet einem guten halben Jahr Python im Informatik Unterricht. Bisher haben wir so sachen wie Buttons, Checkbuttons, Radiobuttons, etc gemacht, und da mir das was zu langweilig ist und Python mir spaß macht, möchte ich gerne ein kleines Game schreiben. Einen relativ großen teil habe ich bereits fertiggestellt, nachdem ich mir ein paar Tuts zu der objektklasse "Canvas" durchgelesen habe.
Hier der momentane Stand des Programmes:
http://filebeam.com/e07584c4ec156ab0f01a0c3af72e41a6

Ziel des Spieles: möglichst viele Donuts einsammeln :lol:
Der "New Donut" Button dient momentan nur, um zu gucken ob die neu setzung von Donuts funktioniert, nachdem man einen Donut eingesammelt hat. Hier schon mein erstes Problem, ich weiß nicht wie man den alten Donut zerstört (habs mit Try: versucht, kenne aber den Befehl nicht)

Code: Alles auswählen

def random_image():
    Position=randint(1,5)
    
    try:
        random_image.destroy 
    
    except:
        if Position==1:
            x=275
            y=75
            Field.create_image(x, y, image=DonutImage)
        elif Position==2:
            x=375
            y=275  
            Field.create_image(x, y, image=DonutImage)
        elif Position==3:
            x=525
            y=225 
            Field.create_image(x, y, image=DonutImage)
        elif Position==4:
            x=475
            y=525  
            Field.create_image(x, y, image=DonutImage)
        else:
            x=175
            y=375  
            Field.create_image(x, y, image=DonutImage)
  
Das eher wichtige was mir momentan fehlt: wie verbiete ich koordinaten? Also da wo eine Wand ist, sollte man auch logischerweise nicht langgehen können :wink:
Sowie, das event, wenn der Donut eingesammelt wird, das der zähler um eins hoch geht, und ein neuer platziert wird zu dem man hinmuss.

Wäre sehr dankbar wenn ihr mir helfen könntet :)
Zuletzt geändert von Saob am Donnerstag 13. Dezember 2007, 13:53, insgesamt 1-mal geändert.
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Donnerstag 13. Dezember 2007, 09:09

Bitte nimm den kompletten Quellcode wieder raus, leider hat das Forum Probleme mit sowas und man kann kaum noch antworten. Runterladen ist voellig ok. :)

Ich verstehe nicht, was das hier soll:

Code: Alles auswählen

def random_image():
    Position=randint(1,5)
   
    try:
        random_image.destroy
   
    except:
random_image ist deine Funktion, und in dieser Funktion willst du random_image.destroy aufrufen, dabei hat deine Funktion kein solches Attribut. Klar gibt das eine Ausnahme, aber dadurch, dass du einen allgemeinen except-Block drumrum hast, wird dieser Programmierfehler verdeckt, sowas sollte man nicht machen. Was willst du damit bewirken?

Das Erzeugen des Spielfeldes kannst du etwas einfacher machen. Idee:

Code: Alles auswählen

wall_coordinates = [(25, 25), (75, 25), (125, 25), ...]
for (x, y) in wall_coordinates:
    Field.create_image(x, y, image=SteelWallImage)
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Saob
User
Beiträge: 23
Registriert: Mittwoch 12. Dezember 2007, 20:11
Kontaktdaten:

Donnerstag 13. Dezember 2007, 14:09

Ne, da soll einfach der Befehl hin, das der alte Donut zerstört wird, aber den kenn ich halt nicht genau, ist somit einfach nen platzhalter :lol: . weil wenn man einen einsammelt. soll der alte verschwinden, und ein neuer auftauchen zu dem man dann hinsoll.
ich hab mir bisher auch gedanken gemacht, wie ich das gehen durch wände verhindern könnte, weiß aber nicht wie ich die koorinaten des mario-bildes auslesen kann. dein vorschlag mit den wandkoordinaten passt dann auch perfekt rein:

Code: Alles auswählen

mario_coordinates=[( #Koordinaten von Mario x, y )]
wall_coordinates = [(25, 25), (75, 25), (125, 25), ...]
if (x, y) in wall_coordinates==mariocoordinates:
    Field.move(1, 0, -50)
#somit soll das mario bild wieder zurückgesetzt werden auf die alte position
aber wie lese ich die aktuellen koordinaten vom mario-bild aus? und dann würd ich gern die if abfrage wissen, die in dem code oben wird sicherlich nicht funktionieren :roll:
sowie, sollte mario 50 pixel in die richtung zurück gesetzt werden, aus der er auch kam.

danke schonmal bisher an dich rebecca :)
Benutzeravatar
Rebecca
User
Beiträge: 1662
Registriert: Freitag 3. Februar 2006, 12:28
Wohnort: DN, Heimat: HB
Kontaktdaten:

Donnerstag 13. Dezember 2007, 14:41

So kann man ein Item loeschen:

Code: Alles auswählen

>>> l = Field.create_text(3, 3, text="Hallo")
>>> Field.delete(l)
D.h. wenn du das Donut-Bild in das Canvas einfuegst, musst du die Referenz darauf an einen Namen binden ("in einer Variable speichern"), damit du an spaeterer Stelle dieses Objekt entfernen kannst.

Mit dem "Durch Waende gehen": Du musst, bevor du das Mario-Bild bewegst, schauen, ob auf der neuen Stelle eine Wand ist oder nicht. Wenn du die Waende wir von mir vorgeschlagen erzeugst, hast du ja schon die Koordinaten der Waende in einer Liste. Du musst also schauen, ob die neuen Koordinaten von Mario in der Liste der Wandkoordinaten drin sind oder nicht. Ist die Frage, wie du an die Mario-Koordinaten kommst. Ich kenne mich mit TKinter nicht gut genug aus, um zu wissen, ob du die aus dem Canvas oder dem Mario-Objekt rausbekommen kannst. Eventuell musst du einfach selbst zwei Zahlen mitrechnen.

Ach ja, und *-Importe sollte man nicht verwenden.
Offizielles Python-Tutorial (Deutsche Version)

Urheberrecht, Datenschutz, Informationsfreiheit: Piratenpartei
Saob
User
Beiträge: 23
Registriert: Mittwoch 12. Dezember 2007, 20:11
Kontaktdaten:

Donnerstag 13. Dezember 2007, 17:13

so habe die wände nun neu eingerichtet nach deinem vorschlag. nur müsste ich jetzt wissen, wie ich marios koordinaten auslesen kann, sie dann vergleichen mit denen von steelwall_coordinates und den stonewall_coordinates um dann sagen zu können, ob eine bewegung möglich ist

Code: Alles auswählen

#SteelWalls
steelwall_coordinates=[(25, 25), (75, 25), (125, 25), (175, 25), (225, 25), (275, 25), (325, 25), (375, 25), (425, 25), (475, 25), (525, 25), (575, 25),             #Top
                    (25, 575), (75, 575), (125, 575), (175, 575), (225, 575), (275, 575), (325, 575), (375, 575), (425, 575), (475, 575), (525, 575), (575, 575),    #Bottom
                    (25, 75), (25, 125), (25, 175), (25, 225), (25, 275), (25, 325), (25, 375),(25, 425), (25, 475), (25, 525), (25, 575),                           #Left
                    (575, 75), (575, 125), (575, 175), (575, 225), (575, 275), (575, 325), (575, 375), (575, 425), (575, 475), (575, 525), (575, 575)]               #Right
for (x, y) in steelwall_coordinates:
    Field.create_image(x, y, image=SteelWallImage)

#StoneWalls
stonewall_coordinates=[(225, 75),                                                                               #Line1
                       (125, 125), (225, 125), (275, 125), (375, 125), (425, 125), (475, 125),                  #Line2
                       (125, 175), (375, 175),                                                                  #Line3
                       (125, 225), (175, 225), (225, 225), (275, 225), (325, 225), (375, 225), (475, 225),      #Line4
                       (325, 275), (475, 275), (525, 275),                                                      #Line5
                       (125, 325), (175, 325), (225, 325), (325, 325), (375, 325),                              #Line6
                       (125, 375), (225, 375), (475, 375),                                                      #Line7
                       (125, 425), (225, 425), (275, 425), (325, 425), (425, 425), (475, 425),                  #Line8
                       (275, 475), (425, 475),                                                                  #Line9
                       (75, 525), (125, 525), (175, 525), (375, 525), (425, 525), (525, 525)]                   #Line10
for (x, y) in stonewall_coordinates:
    Field.create_image(x, y, image=StoneWallImage)

Edit: habe nun auch das mit dem Field.delete versucht, allerdings wird der donut dabei nicht zerstört, wenn ich in die klammer aber ne 1 machen, ist mario weg(wie es dann auch sein sollte), aber mit dem donut gehts nicht

Code: Alles auswählen

def new_donut():
    Position=randint(1,5)
    
    try:
        Field.delete(Donut)
        
        if Position==1:
            x=275
            y=75
            Donut=Field.create_image(x, y, image=DonutImage)
        elif Position==2:
            x=375
            y=275  
            Donut=Field.create_image(x, y, image=DonutImage)
        elif Position==3:
            x=525
            y=225 
            Donut=Field.create_image(x, y, image=DonutImage)
        elif Position==4:
            x=475
            y=525  
            Donut=Field.create_image(x, y, image=DonutImage)
        else:
            x=175
            y=375  
            Donut=Field.create_image(x, y, image=DonutImage)
      
      
    except:        
        if Position==1:
            x=275
            y=75
            Donut=Field.create_image(x, y, image=DonutImage)
        elif Position==2:
            x=375
            y=275  
            Donut=Field.create_image(x, y, image=DonutImage)
        elif Position==3:
            x=525
            y=225 
            Donut=Field.create_image(x, y, image=DonutImage)
        elif Position==4:
            x=475
            y=525  
            Donut=Field.create_image(x, y, image=DonutImage)
        else:
            x=175
            y=375  
            Donut=Field.create_image(x, y, image=DonutImage)
      
      
BlackJack

Donnerstag 13. Dezember 2007, 18:04

Du solltest vielleicht überhaupt nicht mit den Tkinter-Koordinaten arbeiten, sondern eigene Spielkoordinaten verwenden, die in Koordinaten der GUI umgerechnet werden. Bei solchen Spielen hat man üblicherweise eine "zweidimensionale" Liste der Karte, wo vermerkt ist was sich an jedem Feld befindet. Und die Position der Spielerfigur und zusätzlichen Objekten, wie zum Beispiel den Donuts merkt man sich.

Warum ist denn immer noch dieses ``except`` dort? Wenn Du das mal weglässt, bekommst Du auch eine Fehlermeldung warum das mit dem `delete()` nicht funktioniert. Ausserdem habe ich den Verdacht Du solltest erst einmal die Grundlagen von Funktionen und Namensräumen lernen, bevor Du so etwas komplexes wie eine GUI angehst. `Donut` ist ein lokaler Name in der Funktion.

Ein Blick in den Style Guide wäre auch nicht schlecht.
Saob
User
Beiträge: 23
Registriert: Mittwoch 12. Dezember 2007, 20:11
Kontaktdaten:

Donnerstag 13. Dezember 2007, 18:48

Leider kenn ich mich soweit noch nicht aus, wie ich selbst koordinaten erstellen soll und diese dann in GUI umwandel, und was ist an denen von Tkinter falsch?
und wenn ich except weglasse, krieg ich ja den error weils ja ne alternative zu "Try:" geben muss. habe das ganze auch mal abgeändert nun, vorher diente ja try und except nur dazu, da beim starten des proggs noch kein donut da ist. um zu versuchen ihn zu zerstören war dann halt das try da, und als alternative das unter except: aber da das nun wegfällt sieht das ganze nun so aus. bezüglich des objektes "Donut" welches zerstört werden soll, weiß ich leider nicht wieso dies nicht geschieht und was du sagen wolltest, wieso dies nicht geschieht :(

also es wird nun ein standart donut erstellt, welcher beim start des spieles immer an der gleichen stelle ist, und durch den befehl "new_donut" (welcher später beim einsammeln eines Donuts geschieht) wird der momentane zerstört, und ein neuer random gesetzt (so solls jedenfalls geschehen)

Code: Alles auswählen

Donut=Field.create_image(275, 75, image=DonutImage)

def new_donut():
    Position=randint(1,5)

    Field.delete(Donut)
        
    if Position==1:
        x=275
        y=75
        Donut=Field.create_image(x, y, image=DonutImage)
    elif Position==2:
        x=375
        y=275  
        Donut=Field.create_image(x, y, image=DonutImage)
    elif Position==3:
        x=525
        y=225 
        Donut=Field.create_image(x, y, image=DonutImage)
    elif Position==4:
        x=475
        y=525  
        Donut=Field.create_image(x, y, image=DonutImage)
    else:
        x=175
        y=375  
        Donut=Field.create_image(x, y, image=DonutImage)

      
achja: learning by doing :wink:
ich hoffe meine unkenntniss ist nicht allzuschlimm :oops:
BlackJack

Donnerstag 13. Dezember 2007, 19:37

Die Tkinter Koordinaten sind halt irgendwelche relativ willkürliche GUI-Koordinaten die von Faktoren abhängen die mit der Spiellogik nichts zu tun haben.

Ohne das ``except`` sollte jetzt ein Fehler kommen, weil Du in der Funktion auf `Donut` zugreifst bevor der Name an ein Objekt gebunden wurde. Das `Donut` in der Funktion ist ein anderes `Donut` als das auf Modulebene. Wenn man einen Namen innerhalb einer Funktion an ein Objekt bindet, egal wo das in der Funktion steht, wird daraus ein lokaler Name.

Werte sollten Funktionen möglichst nur als Argumente betreten und als Rückgabewerte verlassen. Auf Modulebene wird für ein sauberes Programm viel zu viel definiert.
Saob
User
Beiträge: 23
Registriert: Mittwoch 12. Dezember 2007, 20:11
Kontaktdaten:

Freitag 14. Dezember 2007, 09:38

Nun weiß ich aber leider nicht, wie ich den Donut dann zerstören kann, da er ja Lokal drinne ist, aber wie krieg ich ihn dann wieder auf die Lokale oberfläche?

Des weiteren hab ich mir nun gedacht, mit den Mario koordinaten könnt man es ja so machen:
Anfangs sind die koordinaten (275, 275), und bei jedem move werden dann die X und Y koordinaten verändert. hier ist das ganze natürlich lokal, was ich ja nicht hinkriege :roll: und wie genau das mit dem donut nun funktioieren kann

Code: Alles auswählen

MarioX=275
MarioY=275

def move(event):

    
    if event.keysym == "Up":
        Field.move(1, 0, -50)
        MarioY=float(MarioY) - 50     
    elif event.keysym == "Down":
        Field.move(1, 0, 50)
        MarioY=float(MarioY) + 50
    elif event.keysym == "Left":
        Field.move(1, -50, 0)
        MarioX=float(MarioX) - 50
    else:
        Field.move(1, 50, 0)
        MarioX=float(MarioX) + 50
BlackJack

Freitag 14. Dezember 2007, 11:47

Dir fehlen einfach die Grundlagen. Um so ein Programm sauber hin zu bekommen braucht man IMHO alles bis (und inklusive) Objektorientierung. Mit GUIs kommt dazu dann noch ereignisgetriebene Programmierung. Mit allem zusammen sollte man vielleicht nicht anfangen.
Saob
User
Beiträge: 23
Registriert: Mittwoch 12. Dezember 2007, 20:11
Kontaktdaten:

Samstag 15. Dezember 2007, 22:30

So, ich bin inzwischen weiter gekommen mit dem Spiel.
Momentane Features:
-Mario lässt sich per Pfeiltasten bewegen
-Donuts lassen sich einsammeln
--->Anzahl der gesammelten Donuts wird angezeigt
--->Neuer Donut Spawnt jedes mal beim einsammeln des momentanen
--->Der Donut kann nicht an der gleichen Stelle neu spawnen
-Exit Knopf :P

In Arbeit: Verhindern, durch Wände gehen zu können

Download: http://filebeam.com/00235f51949da1bc6da25594908775be
Screen: http://img233.imageshack.us/img233/3209/gamelx3.jpg

Also ich versuch nun das gehen durch wände zu verhindern, aber das klappt net so ganz. Meine idee sieht so aus, weiß aber nicht ganz wie ich die if schleife einleite. hier der code wie ich das umsetzten will, die comments im code stellen die verhinderung dar, wie ich se umsetzen will:

Code: Alles auswählen

def move(event):
    
    global MarioX, MarioY
    
    if event.keysym == "Up":
        Field.move(1, 0, -50)
        MarioY=MarioY - 50
        if MarioY == DonutY and MarioX == DonutX:
            new_donut()

        #if MarioX == (x) in stonewall_coordinates and MarioY == (y) in stonewall_coordinates:
        #   Field.move(1, 0, 50)
 
    elif event.keysym == "Down":
        Field.move(1, 0, 50)
        MarioY=MarioY + 50
        if MarioY == DonutY and MarioX == DonutX:
            new_donut()

        #if MarioX == (x) in stonewall_coordinates and MarioY == (y) in stonewall_coordinates:
        #   Field.move(1, 0, -50)
                
    elif event.keysym == "Left":
        Field.move(1, -50, 0)
        MarioX=MarioX - 50
        if MarioY == DonutY and MarioX == DonutX:
            new_donut()

        #if MarioX == (x) in stonewall_coordinates and MarioY == (y) in stonewall_coordinates:
        #   Field.move(1, 50, 0)
    
    else:
        Field.move(1, 50, 0)
        MarioX=MarioX + 50
        if MarioY == DonutY and MarioX == DonutX:
            new_donut()

        #if MarioX == (x) in stonewall_coordinates and MarioY == (y) in stonewall_coordinates:
        #   Field.move(1, -50, 0)
       
nur die "if MarioX == (x) in stonewall_coordinates...." Zeile weiß ich leidern icht genau wie ich die auslese aus den stonewall_coordinates
Saob
User
Beiträge: 23
Registriert: Mittwoch 12. Dezember 2007, 20:11
Kontaktdaten:

Donnerstag 17. Januar 2008, 23:41

So, es hat sich eine menge getan bei dem Spiel. Habe ab und zu weiterprogrammiert und mich durch die Welt der ganzen Python Commands durchgekämpft (leider bisher nur einen kleinen Teil dieser Welt :) )
Hier die aktuelle Version:
http://filebeam.com/2e6b6531fba751678512e9aea20fcd15
Den Ghost ignoriere, den bau ich am Wochenende ein das der rumrennt etc.
Mein Problem:
Beim Spielen stürtz das Programm oft ab, schließt sich einfach oder "Python.exe hat ein Problem festgestellt und muss beendet werden..."
woran liegt das?
Und bitte nicht die antworten dazu missbrauchen, mir mitzuteilen wie unangenehm mein Programierstil ist :oops:
Thanks in advance
BlackJack

Freitag 18. Januar 2008, 10:23

Was soll denn der Quatsch in den ersten beiden Kommentarzeilen? Das führt bei Python 2.5 wegen fehlendem Kodierungskommentar erst einmal zu einem `SyntaxError`, dass Programm lässt sich deswegen also so gar nicht starten.

Du weisst, dass "unangenehmer Programmierstil" bedeutet, dass der Quelltext schwerer zu lesen und verstehen ist, als er sein müsste? Darum werden den sich wahrscheinlich nicht so viele durchlesen wollen, also bekommst Du auch weniger Hilfe als bei saubererem Stil und verständlicherem Quelltext.

Gut das gleich relativ weit oben ein verdächtiges ``import thread`` steht. Man darf bei den meisten GUI-Toolkit, so auch bei `Tkinter`, nur von dem Thread auf die GUI zugreifen, in dem auch die `mainloop()` läuft. Sonst gibt's komische Effekte, bis zu "harten" Programmabstürzen.

Schau Dir mal die `after()`-Methode auf Widgets an und verzichte auf Threads.
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Freitag 18. Januar 2008, 11:16

Wenn du ein freies Archivformat wie zip, tar.gz, tar.bz2 oder 7z verwendest, haben vielleicht noch mehr Leute Lust, deinen Code auszuprobieren. rar ist obsolet, da nicht durch Freie Software zu implementieren.
skypa
User
Beiträge: 97
Registriert: Freitag 5. Januar 2007, 03:13

Freitag 18. Januar 2008, 11:30

Hey Saob, und willkommen!

Cooles Programm, bringt mich direkt auf eine neue Idee :D
Kannst bitte die aktuelle Version nochmal hochladen, bei deinem letzten Link dazu ist die Datei verschollen.

Cya
Antworten