Pixelabfrage

Fragen zu Tkinter.
Antworten
Hyrule
User
Beiträge: 3
Registriert: Mittwoch 27. Juni 2012, 20:59

Hallo, ich bin noch ein blutiger Anfänger was Python angeht und programmiere in der Schule momentan ein Tower Defence Spiel. Mein Problem:
Ich möchte einen Gegner laufen lassen, der von Türmen beschossen wird, jeder Turm hat eine bestimmte Reichweite und die Gegner können nur auf einem bestimmten Pfad wandeln. Meine Idee wäre, einen imaginären Kreis um den Turm zu ziehen und mit einem Thread abzufragen, ob sich ein Gegner darin befindet, den Schuss werde ich dann durch Sinus/Kosinus und Gegnergeschwindigkeit errechnen.
Aber wie kann ich überprüfen, ob sich mein Gegner im Kreis befindet ?
Anbei mein Code:

Code: Alles auswählen

from Tkinter import*
from math import*
from thread import*
from time import*
#from ModulGegner import*
#from ModulTuerme import*

class TURMnormal:
    def __init__(self,feld):
        self.Feuerkraft=20
        self.Feuerrate=2
        self.Reichweite=10
        self.canvas=feld
        self.X=0
        self.Y=0
        self.Turmgrafik=self.canvas.create_oval(200,200,210,210,fill='blue')

class GEGNERnormal:
    def __init__(self,feld,Ge):
        self.v= 5   #(1:39)
        self.t= 0.1
        self.Leben= 100
        self.canvas=feld
        self.posy = 5
        self.Gegnergrafik=self.canvas.create_oval(255,0,245,10,fill='red')
        if (Ge==False):
            Ge=True
            start_new_thread(self.Gegnerbewegung,())
            
        
    def Gegnerbewegung(self):
        
        while (True):
            self.canvas.move('g',0,self.v*self.t)
            sleep(self.t)
            self.posy += self.v*self.t
            print self.posy


class SPIEL:
    def __init__(self):
        self.Ge=False
        self.Spielfeld=Tk()
        self.feld=Canvas(width=500,height=500,bg='yellow')
        self.feld.pack(expand=YES, fill=BOTH)
        self.feld.create_line(225,0,225,500,fill='black')
        self.feld.create_line(275,0,275,500,fill='black')
        self.feld.create_rectangle(225,450,275,500,fill='blue')
        frame=Frame(self.Spielfeld)
        frame.pack()
        hm=Button(frame,text='Turm',command=self.TURMnormalerzeugung)
        hm.pack(side=LEFT)
        self.Spielfeld.bind('<B1-Motion>',self.Turmbewegung)
        hm1=Button(frame,text='Gegner',command=self.Gegnererzeugung)
        hm1.pack(side=LEFT)
        self.tag=''
        self.nummer=0
        self.tuerme= []
        self.Spielfeld.mainloop()
    
    def TURMnormalerzeugung(self):
        self.tag=self.tag+'t'
        self.TURMnormal=TURMnormal(self.feld)
        self.tuerme.append(self.TURMnormal)
        self.feld.addtag_withtag(self.tag,self.TURMnormal.Turmgrafik)
        

    def Turmbewegung(self,event):
        if event.x<225 or event.x>275:
            self.feld.delete(self.TURMnormal.Turmgrafik)
            self.TURMnormal.Turmgrafik=self.feld.create_oval(event.x-5,event.y-5,event.x+5,event.y+5,fill='black')
        self.feld.addtag_withtag(self.tag,self.TURMnormal.Turmgrafik)
        self.tuerme[self.nummer].X=event.x
        self.tuerme[self.nummer].Y=event.y
        

        
        

    def Gegnererzeugung(self):
        self.GEGNERnormal=GEGNERnormal(self.feld, self.Ge)
        self.feld.addtag_withtag('g',self.GEGNERnormal.Gegnergrafik)
        self.Ge=True



Towerdefense=SPIEL()
Zuletzt geändert von Anonymous am Mittwoch 27. Juni 2012, 22:13, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@Hyrule: Schau Dir mal bitte PEP 8 -- Style Guide for Python Code an und ändere die Form des Quelltextes entsprechend.

Sternchenimporte sind keine gute Idee. Es gibt ja einen Grund warum man Code in Modulen organisiert. Diese Grenzen reisst man mit den Sternchenimporten wieder nieder. Man weiss dann am Ende nicht mehr welcher Name eigentlich wo definiert wurde. Dann können auch noch subtile Probleme auftreten wenn verschiedene Module *gleiche* Namen definieren.

Das `thread`-Modul sollte man sowieso nicht benutzen — steht auch in der Dokumentation — aber im Zusammenhang mit GUIs sind Threads eh problematisch. Zumindest auf die GUI darf man nur aus dem Hauptthread heraus zugreifen. Sonst passieren sehr abenteuerliche Sachen bis hin zu harten Programmabstürzen. Auch `time.sleep()` ist nahezu unbrauchbar, weil das die GUI für die Wartezeit einfriert. GUI-Programmierung ist bei allen üblichen Toolkits ereignisbasierte Programmierung. Wenn man etwas regelmässig oder zeitverzögert ausgeführt haben möchte, dann erzeugt man dafür mit der entsprechenden Methode eine Ereignisquelle. Bei `Tkinter` geht das mit der `after()`-Methode, die es auf jedem Widget gibt.

Klammern um Bedingungen bei ``if``, ``while``, und Co sind in Python nicht nötig und auch nicht üblich. Wahrheitswerte vergleicht man nicht explizit mit `True` oder `False`. Da kommt ja eh nur wieder ein Wahrheitswert heraus. Den hat man aber ohne einen expliziten Vergleich sowieso schon.

Ansonsten sollte man das Modell und die Spiellogik von der GUI trennen. Du willst ja eigentlich wissen wie weit ein Gegner von einem Turn entfernt ist. Dann rechne die Entfernung einfach aus. Das hat nichts mit Pixeln oder der angezeigten Grafik zu tun.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

hi Hyrule

Hier ein erster Wurf zum studieren:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import Tkinter as tk

def enemy_in_action():

    canvas.move('enemy_spy', 2, 2)

    overlapped_ids = canvas.find_overlapping(*canvas.coords('enemy_spy'))
    tags = [canvas.itemcget(id, 'tag') for id in overlapped_ids]
    
    if 'defence_zone' in tags:
        canvas.itemconfig('alarm', fill='red')
    else:
        canvas.itemconfig('alarm', fill='brown')
        
    canvas.after(100, enemy_in_action)

class App(object):
    
    def __init__(self):
        self.win =  tk.Tk()
        self.win.geometry('{0}x{1}+{2}+{3}'.format(300, 300, 50, 50)) 
          
        self.move_flag = False

app = App()
app.win.title('Read canvas objects')

canvas = tk.Canvas(app.win, bg='khaki')
canvas.pack()

defence_zone = (80, 80, 250, 250)
canvas.create_oval(defence_zone, fill='red', outline='', stipple='gray12',
    tag='defence_zone')

enemy_spy_geometry = (20, 20, 25, 25)
canvas.create_oval(enemy_spy_geometry, fill='steelblue', outline='red',
    tag='enemy_spy')

alarm_geometry = (200, 20, 220, 40)
canvas.create_rectangle(alarm_geometry, fill='brown', outline='',
    tag='alarm')
    
canvas.create_text(190, 45, text='Alarm', fill='brown', anchor='nw')

enemy_in_action()

app.win.mainloop()
Gruß wuf :wink:
Take it easy Mates!
Hyrule
User
Beiträge: 3
Registriert: Mittwoch 27. Juni 2012, 20:59

Hallo, Wuf :)

Vielen Dank erstmal für deinen Code, ich hab wirklich viel dazugelernt, vor allem die after-Funktion und die itemcget-Funktion sind sehr nützlich !
Ich brauche aber leider auch die exakten Koordinaten des Mittelpunktes der Grafik, habe jetzt aber über Einführung 2 neuer Variablen und einer Kosinusfunktion dieses Problem gelöst. Zu der after-Funktion habe ich noch eine kleine Frage. Mein Thread hält das Programm etwas auf, so dass die Zeit praktisch langsamer vergeht, tut die after-Funktion das auch, oder arbeitet sie ohne Zeitverschiebung ?
So braucht mein Gegner 99 Sekunden um das Feld zu überqueren, wenn die Thread-rate bei einer Sekunde liegt. Liegt sie bei 0.1 Sekunden, so braucht er wesentlich länger, als er sollte, da mein Laptop länger als 0.1 Sekunden für die Berechnungen benötigt. Der Vorteil beim Thread wäre also, dass spätere Threads genauso betroffen sind und das Programm damit auf allen verschiedenen Rechnern laufen müsste, während die after-Funktion Hänger,Ruckler usw. verursachen würde......
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Hyrule
Hyrule hat geschrieben:Ich brauche aber leider auch die exakten Koordinaten des Mittelpunktes der Grafik, habe jetzt aber über Einführung 2 neuer Variablen und einer Kosinusfunktion dieses Problem gelöst.
Welche Grafik meinst du Enemy, Defence Zone oder Spielfeld? Ohne Glaskugel sehe ich nicht genau was du mit den zwei Variablen, der Kosinusfunktion und dem Thread machst.

Gruß wuf :wink:
Take it easy Mates!
Hyrule
User
Beiträge: 3
Registriert: Mittwoch 27. Juni 2012, 20:59

Hat sich als etwas schwieriger als gedacht herausgestellt, sobald ich's hab poste ich hier mal mein Ergebnis :) Is etwas sehr mathematisch und physikalisch, aber was soll man machen.... Ich muss noch einige Schleifen einbauen und zeitliches koordinieren, aber dann sollte es eigentlich klappen :)
Ich brauch den Mittelpunkt von enemy, da der Schuss des Turmes, der die defence errichtet, genau dorthin gehen soll.
Ich schätze ich habs morgen nachmittag fertig. Ich vermute, du wirst nicht draus schlau werden, aber hier ist mein bisheriger code:

Code: Alles auswählen

from Tkinter import*
from math import*
from thread import*
from time import*
#from ModulGegner import*
#from ModulTuerme import*

class Listen:
    def __init__(self):
        self.gnummer=0
        self.gegner=[]
        

class TURMnormal:
    def __init__(self,feld):
        self.Feuerkraft=20
        self.Feuerrate=2
        self.Reichweite=100
        self.canvas=feld
        self.X=250
        self.Y=5
        self.Turmgrafik=self.canvas.create_oval(200,200,210,210,fill='blue')
        start_new_thread(self.Gegnerbeschuss,())
    
    def Gegnerbeschuss(self):
        while True :
            if self.X<=Speicherbank.gegner[Speicherbank.gnummer-1].X+self.Reichweite and Speicherbank.gegner[Speicherbank.gnummer-1].X<= self.X+self.Reichweite:
                print Speicherbank.gegner[0].Y

                if  self.Y - cos((Speicherbank.gegner[Speicherbank.gnummer-1].X - self.X)/self.Reichweite)*self.Reichweite == Speicherbank.gegner[Speicherbank.gnummer-1].Y:
                    print 1
    sleep(1)

class GEGNERnormal:
    def __init__(self,feld,Ge):
        self.v= 5   #(1:39)
        self.t= 0.1
        self.Leben= 100
        self.canvas=feld
        self.X= 0
        self.Y= 0
        self.Gegnergrafik=self.canvas.create_oval(255,0,245,10,fill='red')
        if (Ge==False):
            Ge=True
            start_new_thread(self.Gegnerbewegung,())
            
        
    def Gegnerbewegung(self):
        
        while (True):
            self.canvas.move('g',0,self.v*self.t)
            sleep(self.t)
            Speicherbank.gegner[Speicherbank.gnummer-1].Y += self.v*self.t


class SPIEL:
    def __init__(self):
        self.Ge=False
        self.Spielfeld=Tk()
        self.feld=Canvas(width=500,height=500,bg='yellow')
        self.feld.pack(expand=YES, fill=BOTH)
        self.feld.create_line(225,0,225,500,fill='black')
        self.feld.create_line(275,0,275,500,fill='black')
        self.feld.create_rectangle(225,450,275,500,fill='blue')
        frame=Frame(self.Spielfeld)
        frame.pack()
        hm=Button(frame,text='Turm',command=self.TURMnormalerzeugung)
        hm.pack(side=LEFT)
        self.Spielfeld.bind('<B1-Motion>',self.Turmbewegung)
        hm1=Button(frame,text='Gegner',command=self.Gegnererzeugung)
        hm1.pack(side=LEFT)
        self.tag=''
        self.tnummer=-1
        self.tuerme= []
        self.Spielfeld.mainloop()
    
    def TURMnormalerzeugung(self):
        self.tag=self.tag+'t'
        self.TURMnormal=TURMnormal(self.feld)
        self.tuerme.append(self.TURMnormal)
        self.feld.addtag_withtag(self.tag,self.TURMnormal.Turmgrafik)
        self.tnummer += 1
        

    def Turmbewegung(self,event):
        if event.x<225 or event.x>275:
            self.feld.delete(self.TURMnormal.Turmgrafik)
            self.TURMnormal.Turmgrafik=self.feld.create_oval(event.x-5,event.y-5,event.x+5,event.y+5,fill='black')
        self.feld.addtag_withtag(self.tag,self.TURMnormal.Turmgrafik)
        self.tuerme[self.tnummer].X=event.x
        self.tuerme[self.tnummer].Y=event.y
        


    def Gegnererzeugung(self):
        self.GEGNERnormal=GEGNERnormal(self.feld, self.Ge)
        self.feld.addtag_withtag('g',self.GEGNERnormal.Gegnergrafik)
        Speicherbank.gegner.append(self.GEGNERnormal)
        Speicherbank.gnummer += 1
        self.Ge=True


Speicherbank=Listen()
Towerdefense=SPIEL()
BlackJack

@Hyrule: Neben den ganzen anderen Sachen die ich geschrieben und Du ignoriert hast: Man darf nicht von anderen als dem Haupthread in dem die Hauptschleife von `Tkinter` läuft, auf die GUI zugreifen. Dein Programm ist fehlerhaft. Falls das bei Dir zufällig bis jetzt noch nicht abgestürzt ist, ändert das nichts an dieser Tatsache.

So globale Sachen wie die `Speicherbank` sollte man vermeiden. Wobei die Klasse `Listen` (komischer Name) unnötig kompliziert ist. `gnummer` ist redundant weil Listen ihre eigene Länge kennen. Die muss man nicht noch mal separat irgendwo vermerken. Und man kann bei Listen auch negative Indizes verwenden.
Antworten