programmier fehler tkinter "pong"

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
phbrue
User
Beiträge: 1
Registriert: Donnerstag 31. August 2017, 22:23

ich arbeite erst seit kurzem mit Python und habe versucht mit einer "step by step Anleitung" ein pong-ähnliches spiel zu programmieren. Leider startet der Ball nicht von der Plattform, kann mir jemand helfen?

Code: Alles auswählen

from tkinter import *
def move():
    global x1, y1, dx, dy,playing, gwidth, gheight, ballsize, rfb
    global lplayer, rplayer, nextplayer, counter, rpos, lpos, rs
    # position the ball on the playground
    if playing > 0:
        x1, y1 = x1 +dx, (y1+dy)
    else:     # follow racket
        if nextplayer == 0:  # if the next player is the right player give him the ball
            x1,y1,dx = rfb+10, lpos+(rs-ballsize)/2, abs(dx)
        else: # otherwise give the left player the ball
            x1,y1,dx = gwidth-rfb-10, rpos+(rs-ballsize)/2,-abs(dx)
    if y1 > (gheight-ballsize):
        y1, dy = gheight-ballsize, -dy
    if y1 < 0:
        y1, dy= 0, -dy
    # give the left player a point
    if x1 > (gwidth-ballsize):
        lplayer=lplayer+1     # increment score for left player
        x1, y1 = rfb, (gheight/2)   # give right player the ball
    nextplayer = 0
    playing = 0  # stop the game
    if x1 < 0:
        rplayer=rplayer+1   # increment score for right player
        x1, y1 = gwidth-rfb, (gheight/2)  # give left player the ball
        nextplayer = 1
        playing = 0  # stop the game
    if x1 < 0:
        x1, y1 = gwidth-rfb, (gheight/2)
    # test if the ball hits the left racket let it bounce back
    if x1 <= rfb:
        if y1>lpos and y1 < (lpos+rs):
            x1, dx = rfb+5, -dx
    # test if the ball hits the right racket let it bounce back
    if x1 >= (gwidth-rfb-ballsize):
        if y1 >= rpos and y1 <= (rpos+rs):
            x1, dx = (gwidth-rfb-ballsize-5), -dx

    can.coords(oval1, x1, y1, x1+ballsize, y1+ballsize)
    # draw current score
    score = str(lplayer) + ":" + str(rplayer)
    can.itemconfigure(counter, text=score)
    # Change the positon of the two rackets
    can.coords(rracket, gwidth-rfb, rpos, gwidth-rfb, rpos+rs)
    can.coords(lracket, rfb, lpos, rfb, lpos+rs)
    win.after(50, move)
#­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­-------------------------------------------------
def lup(event):
    global lpos, rspeed
    if lpos > rspeed:
        lpos=lpos-rspeed
def ldown(event):
    global lpos, gheight, rspeed, rs
    if lpos < (gheight-rs-rspeed):
        lpos=lpos+rspeed
def rmove(event):
    global rpos, gheight, rs
    ypos = event.y
    if ypos > 0 and ypos < (gheight-rs):  # if in range
        rpos = ypos
#------------------------------------------------------
def startit(event):
    global playing
    playing = 1
#---------------------------------------------------------
dx, dy = 15, 15  # Directions of the ball
gwidth = 800; gheight = 400  # Size of the playground
ballsize = 20; rfb = 20; rs = 60 # Ball size, racket from border, racket size
rspeed = 20   # Speed of the racket
lplayer=rplayer=0 # Start with no points
#---------------------------------------------------------
x1, y1 = rfb, (gheight-ballsize)/2
rpos=lpos=(gheight-rs)/2
nextplayer = 0  # next player: 0=right, 1=left
playing    = 0  # Do not play when starting the program
win = Tk();
win.title("Pong")
can = Canvas(win, bg='black',height=gheight, width=gwidth)
can.pack(side=LEFT)
oval1 = can.create_oval(x1, y1, x1+ballsize, y1+ballsize, width=2, fill='white')
line = can.create_line(gwidth/2, 0, gwidth/2, gheight, width=4, fill="white", dash=(4, 8))
lracket  = can.create_line(rfb, lpos, rfb, lpos+rs, width=10, fill="white")
rracket  = can.create_line(gwidth-rfb, rpos, gwidth-rfb, rpos+rs, width=10, fill="white")
# Create the score text
font=('courier', 20)
counter=can.create_text(gwidth/2, 20, text='0:0', font=font, fill="white")

# Use the following keys to move the racket
win.bind('q', lup)
win.bind('p', ldown)
win.bind('<B1-Motion>', rmove)
win.bind('<space>', startit) # start game by hitting space

move()  # draw next sequence
win.mainloop()
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Irgendwie habe ich den Eindruck, Du könntest den ersten Preis gewonnen haben, in Bezug auf die Anzahl von globalen Variablen, deren Verwendung man eigentlich vermeiden sollte. Und wenn ich mir das Script kopiere, sieht es recht unübersichtlich aus. Tue Dir selbst einen Gefallen und fange noch einmal von vorne an und schaue Dich in dieser Zeit ältere Script-Vorstellungen an und achte darauf, was bei diesen bemängelt wurde.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@phbrue: knapp hundert Zeilen unstrukturierter Code, da ist Fehlersuche ein Glücksspiel. Der erste was Du lernen solltest, sind Funktionen, also abgeschlossene Einheiten, die eine bestimmte Aufgabe ausführen. Dabei bekommen sie alles was sie brauchen, als Argumente und ihr Ergebnis werden per `return` zurückgegeben; also kein global.

Für ein GUI-Programm mußt Du dann als nächstes Lernen, wie man Klassen schreibt. GUI-Programme arbeiten ereignisbasiert, rufen also für verschiedene Ereignisse verschiedene Methoden auf. Man muß also zwischen diesen Aufrufen Daten irgendwo speichern, und das passiert über Instanzen für Klassen.

Bei einem Pong-Spiel bietet es sich an eine Klasse für's Spielfeld, eine für den Ball und eine für den Schläger, von der dann zwei Instanzen (rechter Schläger und linker Schläger) erzeugt werden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

phbrue hat geschrieben:ich arbeite erst seit kurzem mit Python und habe versucht mit einer "step by step Anleitung" ein pong-ähnliches spiel zu programmieren. Leider startet der Ball nicht von der Plattform, kann mir jemand helfen?
Wieso der Ball startet doch! Du musst nur die Leertaste drücken und dann sie gedrückt halten. Solange Du die Leertaste drückst, bewegt sich der Ball.

Oder möchtest Du, dass sich der Ball auch noch bewegt, wenn Du die Leertate nicht mehr drückst? Dann mußt Du die Bewegung eben mit etwas anderem triggern als durch Drücken der Leertaste. Dazu gibt es einen Timer. Die Methode heißt after.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: dass der Ball nur bei gedrückter Leertaste fliegt, ist doch ein Fehler. Wie kann man nur denken, das wäre gewollt?? Eine `after`-„Schleife“ hat der TE auch schon. Liest Du überhaupt die Beiträge, oder willst Du einfach nur überall Deinen Senf dazugeben?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

phbrue hat geschrieben:ich arbeite erst seit kurzem mit Python und habe versucht mit einer "step by step Anleitung" ein pong-ähnliches spiel zu programmieren. Leider startet der Ball nicht von der Plattform, kann mir jemand helfen?
In Zeile 22 hast Du stehen:

playing = 0 # stop the game

Daher läuft das Spiel nur solange, wie Du die Leertaste gedrückt hältst.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@phbrue:

Man kann den Code auch so schreiben, dass man ihn versteht. Ein paar Kommentare machen den Code nicht übersichtlicher.
Und bei lauter x1,y1,dx,dy, usw blickt keiner durch. Da zum Beispiel blickt man durch:

Code: Alles auswählen

def move():

    if wenn_das_spiel_nicht_zuende_ist():
        bewegt_sich_der_ball_ueber_das_spielfeld()

    if wenn_das_spiel_zu_ende_ist():
        if der_naechste_spieler() == der_rechte():
            positioniere_den_ball_auf_der_startposition_fuer_den_rechten_spieler()
        else:
            positioniere_den_ball_auf_der_startposition_fuer_den_linken_spieler()
        
    if wenn_der_ball_am_oberen_rand_anstoesst():
        dann_soll_der_ball_sich_wieder_nach_unten_bewegen()

    if wenn_der_ball_am_unteren_rand_anstoesst():
        dann_soll_der_ball_sich_wieder_nach_oben_bewegen()

    if wenn_der_ball_ins_rechte_aus_geht():
        dann_erhoehe_die_trefferzahl_fuer_den_linken_spieler()
        positioniere_den_ball_auf_der_startposition_fuer_den_rechten_spieler()

    jetzt_kommt_der_rechte_spieler_dran()
    und_das_spiel_ist_zu_ende()    
    
    if wenn_der_ball_ins_linke_aus_geht():
        dann_erhoehe_die_trefferzahl_fuer_den_rechten_spieler()
        positioniere_den_ball_auf_der_startposition_fuer_den_linken_spieler()
        jetzt_kommt_der_linke_spieler_dran()
        und_das_spiel_ist_zu_ende()    
        
    if wenn_der_ball_auf_den_linken_schlaeger_knallt():
        dann_soll_er_vom_linken_schlaeger_zurueckprallen()

    if wenn_der_ball_auf_den_rechten_schlaeger_knallt():
        dann_soll_er_vom_rechten_schlaeger_zurueckprallen()

    schlaeger_ball_und_treffer_darstellen()
    
    win.after(50, move)
Und der Fehler ist: bei Zeile 22 und 23 das Einrücken vergessen

Außerdem sind die Zeilen 6 bis 10 unnötig, denn das hast Du schon, wenn der Ball ins Aus geht.

Das wäre schon einmal eine Verbesserung:

Code: Alles auswählen

from tkinter import *

def wenn_die_runde_nicht_zu_ende_ist():
    return playing

def bewegt_sich_der_ball_ueber_das_spielfeld():
    global x1,y1
    x1, y1 = x1 +dx, (y1+dy)
    
def positioniere_den_ball_auf_der_startposition_fuer_den_rechten_spieler():
    global x1,y1,dx
    x1,y1,dx = rfb+10, lpos+(rs-ballsize)/2, abs(dx)

def positioniere_den_ball_auf_der_startposition_fuer_den_linken_spieler():
    global x1,y1,dx
    x1,y1,dx = gwidth-rfb-10, rpos+(rs-ballsize)/2,-abs(dx)

def wenn_der_ball_am_oberen_rand_anstoesst():
    return y1 > (gheight-ballsize)

def dann_soll_der_ball_sich_wieder_nach_unten_bewegen():
    global y1,dy
    y1, dy = gheight-ballsize, -dy

def wenn_der_ball_am_unteren_rand_anstoesst():
    return y1 < 0

def dann_soll_der_ball_sich_wieder_nach_oben_bewegen():
    global y1,dy
    y1, dy= 0, -dy

def wenn_der_ball_ins_rechte_aus_geht():
    return x1 > (gwidth-ballsize)

def wenn_der_ball_ins_linke_aus_geht():
    return x1 < 0

def dann_erhoehe_die_trefferzahl_fuer_den_linken_spieler():
    global lplayer
    lplayer=lplayer+1     # increment score for left player

def dann_erhoehe_die_trefferzahl_fuer_den_rechten_spieler():
    global rplayer
    rplayer=rplayer+1   # increment score for right player

def jetzt_kommt_der_rechte_spieler_dran():
    global nextplayer
    nextplayer = 0

def jetzt_kommt_der_linke_spieler_dran():
    global nextplayer
    nextplayer = 1

def wenn_der_ball_auf_den_linken_schlaeger_knallt():
    return x1 <= rfb and y1>lpos and y1 < (lpos+rs)

def dann_soll_er_vom_linken_schlaeger_zurueckprallen():
    global x1,dx
    x1, dx = rfb+5, -dx

def und_die_runde_ist_zu_ende():
    global playing
    playing = 0  # stop the game

def wenn_der_ball_auf_den_rechten_schlaeger_knallt():
    return x1 >= (gwidth-rfb-ballsize) and y1 >= rpos and y1 <= (rpos+rs)

def dann_soll_er_vom_rechten_schlaeger_zurueckprallen():
    global x1,dx
    x1, dx = (gwidth-rfb-ballsize-5), -dx

def schlaeger_ball_und_treffer_darstellen():
    can.coords(oval1, x1, y1, x1+ballsize, y1+ballsize)
    # draw current score
    score = str(lplayer) + ":" + str(rplayer)
    can.itemconfigure(counter, text=score)
    # Change the positon of the two rackets
    can.coords(rracket, gwidth-rfb, rpos, gwidth-rfb, rpos+rs)
    can.coords(lracket, rfb, lpos, rfb, lpos+rs)

def move():

    if wenn_die_runde_nicht_zu_ende_ist():
        bewegt_sich_der_ball_ueber_das_spielfeld()

    if wenn_der_ball_am_oberen_rand_anstoesst():
        dann_soll_der_ball_sich_wieder_nach_unten_bewegen()

    if wenn_der_ball_am_unteren_rand_anstoesst():
        dann_soll_der_ball_sich_wieder_nach_oben_bewegen()

    if wenn_der_ball_auf_den_linken_schlaeger_knallt():
        dann_soll_er_vom_linken_schlaeger_zurueckprallen()

    if wenn_der_ball_auf_den_rechten_schlaeger_knallt():
        dann_soll_er_vom_rechten_schlaeger_zurueckprallen()

    if wenn_der_ball_ins_rechte_aus_geht():
        dann_erhoehe_die_trefferzahl_fuer_den_linken_spieler()
        positioniere_den_ball_auf_der_startposition_fuer_den_rechten_spieler()
        jetzt_kommt_der_rechte_spieler_dran()
        und_die_runde_ist_zu_ende()    
    
    if wenn_der_ball_ins_linke_aus_geht():
        dann_erhoehe_die_trefferzahl_fuer_den_rechten_spieler()
        positioniere_den_ball_auf_der_startposition_fuer_den_linken_spieler()
        jetzt_kommt_der_linke_spieler_dran()
        und_die_runde_ist_zu_ende()    
        
    schlaeger_ball_und_treffer_darstellen()
    
    win.after(50, move)
# ---------------------------------------------------------------

def lup(event):
    global lpos, rspeed
    if lpos > rspeed:
        lpos=lpos-rspeed

def ldown(event):
    global lpos, gheight, rspeed, rs
    if lpos < (gheight-rs-rspeed):
        lpos=lpos+rspeed
def rmove(event):
    global rpos, gheight, rs
    ypos = event.y
    if ypos > 0 and ypos < (gheight-rs):  # if in range
        rpos = ypos
#------------------------------------------------------
def startit(event):
    global playing
    playing = 1
#---------------------------------------------------------
dx, dy = 15, 15  # Directions of the ball
gwidth = 800; gheight = 400  # Size of the playground
ballsize = 20; rfb = 20; rs = 60 # Ball size, racket from border, racket size
rspeed = 20   # Speed of the racket
lplayer=rplayer=0 # Start with no points
#---------------------------------------------------------
x1, y1 = rfb, (gheight-ballsize)/2
rpos=lpos=(gheight-rs)/2
nextplayer = 0  # next player: 0=right, 1=left
playing    = 0  # Do not play when starting the program
win = Tk();
win.title("Pong")
can = Canvas(win, bg='black',height=gheight, width=gwidth)
can.pack(side=LEFT)
oval1 = can.create_oval(x1, y1, x1+ballsize, y1+ballsize, width=2, fill='white')
line = can.create_line(gwidth/2, 0, gwidth/2, gheight, width=4, fill="white", dash=(4, 8))
lracket  = can.create_line(rfb, lpos, rfb, lpos+rs, width=10, fill="white")
rracket  = can.create_line(gwidth-rfb, rpos, gwidth-rfb, rpos+rs, width=10, fill="white")
# Create the score text
font=('courier', 20)
counter=can.create_text(gwidth/2, 20, text='0:0', font=font, fill="white")

# Use the following keys to move the racket
win.bind('q', lup)
win.bind('p', ldown)
win.bind('<B1-Motion>', rmove)
win.bind('<space>', startit) # start game by hitting space

move()  # draw next sequence
win.mainloop()
Es ist noch ein Fehler darin: die Ballstartpositionen für linken und rechten Spieler sind vertauscht. Habe ich nicht berichtigt.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: oh nein, bitte nicht! Du benutzt immer noch globale Variablen, die alles unübersichtlich machen. Und die Funktionsnamen zeigen schon, dass es keine Funktionen sein sollten. Wenn... - Dann...-Funktionen sind ein `if` und keine Funktionen!


@phbrue: bitte schnell vergessen, was Alfons da schreibt, es sei denn Du willst bleibende Schäden beim Programmieren behalten.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: oh nein, bitte nicht! Du benutzt immer noch globale Variablen, die alles unübersichtlich machen. Und die Funktionsnamen zeigen schon, dass es keine Funktionen sein sollten.
Ich benutze keine globalen Variablen. Die waren schon da. Ich schreibe doch nicht das ganze Programm um. Außerdemj, was ändert es, wenn man eine Klasse macht und dann überall self davorschreibt? Oder wenn man Objekte macht und dann position.x1 schreibt oder spieler.links.?

Und in der Funktion move taucht keine einzige globale Variable auf. Und was zeigen die Funktionsnamen? Dass es keine sein sollen? Was spricht dagegen, eine lange Bezeichnung zu wählen, die jedermann verständlich ist. Warum nicht so etwas, wie einen ganzen Satz?
Man kann natürlich auch irgendeine kurze Bezeichnung auf englisch wählen, bei der nur der Programmierer weiß, was dabei gemeint ist. So weiß es aber jeder.
Sirius3 hat geschrieben:Wenn... - Dann...-Funktionen sind ein `if` und keine Funktionen
Ich habe doch ein if geschrieben. Eigentlich hätte das Wenn nicht in den Funktionsnamen gehört, weil ja das if schon da ist. Aber ein Satz, der mit if beginnt, klingt blöd, wenn er dann auf Deutsch weiter geht. Also habe ich das if eben noch mal als wenn im Funktionsnamen wiederholt. Das klingt besser.
Sirius3 hat geschrieben:@phbrue: bitte schnell vergessen, was Alfons da schreibt, es sei denn Du willst bleibende Schäden beim Programmieren behalten.
@phbrue: Vergiß was Sirius3 schreibt. Die Funktion move ist 1a verständlich, weiß jeder was gemeint ist. Wenn Du das auf englisch umschreiben willst, dann natürlich das wenn nicht als if übersetzen, denn das if ist ja schon da.
Außerdem kann man auch die anderen Variablen besser benennen als eine kryptische Abkürzung wie rfb oder rs. ballsize dagegen ist gut gewählt.
Zuletzt geändert von Alfons Mittelmeyer am Samstag 2. September 2017, 11:21, insgesamt 2-mal geändert.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Das mit dem nächsten Spieler wird gar nicht benutzt. nextplayer ist nur eine Variable, die auf 0 oder 1 gesetzt wird und gar nicht abgefragt wird.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: eine Funktion, die Funktionen aufruft, die globale Variablen benutzen nutzt indirekt auch globale Variablen. Das ganze ist einfach kein sinnvoller Programmierstiel, und damit sollte man erst gar nicht anfangen. Wenn Du keine guten Programme postest, sondern nur Quatsch, dann lass das bitte sein.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: eine Funktion, die Funktionen aufruft, die globale Variablen benutzen nutzt indirekt auch globale Variablen. Das ganze ist einfach kein sinnvoller Programmierstiel, und damit sollte man erst gar nicht anfangen. Wenn Du keine guten Programme postest, sondern nur Quatsch, dann lass das bitte sein.
Findest Du es sinnvoll, für ein Spiel, das aus einem Canvas besteht und nur Variablen für das Spiel hat, vor jede Variable ein self davorzusetzen. Zu was soll das gut sein?

Aber man kann auch das machen, obwohl das keinerlei Vorteil bringt, nur mehr Schreibarbeit:

Code: Alles auswählen

# -*- coding: utf-8 -*-
try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.title('Pong')
        # widget definitions ===================================
        Spielfeld(self).pack(side='left')

class Spielfeld(tk.Canvas):

    # variables for the game
    dx, dy = 15, 15  # Directions of the ball
    gwidth = 800; gheight = 400  # Size of the playground
    ballsize = 20; rfb = 20; rs = 60 # Ball size, racket from border, racket size
    rspeed = 20   # Speed of the racket
    lplayer=rplayer=0 # Start with no points
    x1, y1 = rfb, (gheight-ballsize)/2
    rpos=lpos=(gheight-rs)/2
    playing    = 0  # Do not play when starting the program

    def __init__(self,master,**kwargs):
        tk.Canvas.__init__(self,master,**kwargs)
        self.config(height='400', width='800', bg='black')

        # widget definitions ===================================
        self.ball = self.create_oval(self.x1, self.y1, self.x1+self.ballsize, self.y1+self.ballsize, width=2, fill='white')
        self.create_line(self.gwidth/2, 0, self.gwidth/2, self.gheight, width=4, fill="white", dash=(4, 8))        
        self.racket_left = self.create_line(self.rfb, self.lpos, self.rfb, self.lpos+self.rs, width=10, fill="white")
        self.racket_right  = self.create_line(self.gwidth-self.rfb, self.rpos, self.gwidth-self.rfb, self.rpos+self.rs, width=10, fill="white")
        self.score_text = self.create_text(self.gwidth/2, 20, text='0:0', font='courier 20', fill="white")

        # Use the following keys to move the racket
        self.master.bind('q', self.lup)
        self.master.bind('a', self.ldown)
        self.master.bind('<B1-Motion>', self.rmove)
        self.master.bind('<space>', self.startit) # start game by hitting space

        # init game =============================
        self.spielfeld_init()

    def startit(self,event):
        self.playing = 1

    def lup(self,event):
        if self.lpos > self.rspeed:
            self.lpos=self.lpos-self.rspeed

    def ldown(self,event):
        if self.lpos < (self.gheight-self.rs-self.rspeed):
            self.lpos=self.lpos+self.rspeed

    def rmove(self,event):
        ypos = event.y
        if ypos > 0 and ypos < (self.gheight-self.rs):  # if in range
            self.rpos = ypos


    def spielfeld_init(self):

        def wenn_die_runde_nicht_zu_ende_ist():
            return self.playing

        def bewegt_sich_der_ball_ueber_das_spielfeld():
            self.x1, self.y1 = self.x1 +self.dx, (self.y1+self.dy)
            
        def positioniere_den_ball_auf_der_startposition_fuer_den_linken_spieler():
            self.x1,self.y1,self.dx = self.rfb+10, self.lpos+(self.rs-self.ballsize)/2, abs(self.dx)

        def positioniere_den_ball_auf_der_startposition_fuer_den_rechten_spieler():
            self.x1,self.y1,self.dx = self.gwidth-self.rfb-10, self.rpos+(self.rs-self.ballsize)/2,-abs(self.dx)

        def wenn_der_ball_am_oberen_rand_anstoesst():
            return self.y1 > (self.gheight-self.ballsize)

        def dann_soll_der_ball_sich_wieder_nach_unten_bewegen():
            self.y1, self.dy = self.gheight-self.ballsize, -self.dy

        def wenn_der_ball_am_unteren_rand_anstoesst():
            return self.y1 < 0

        def dann_soll_der_ball_sich_wieder_nach_oben_bewegen():
            self.y1, self.dy= 0, -self.dy

        def wenn_der_ball_ins_rechte_aus_geht():
            return self.x1 > (self.gwidth-self.ballsize)

        def wenn_der_ball_ins_linke_aus_geht():
            return self.x1 < 0

        def dann_erhoehe_die_trefferzahl_fuer_den_linken_spieler():
            self.lplayer=self.lplayer+1     # increment score for left player

        def dann_erhoehe_die_trefferzahl_fuer_den_rechten_spieler():
            self.rplayer=self.rplayer+1   # increment score for right player

        def wenn_der_ball_auf_den_linken_schlaeger_knallt():
            return self.x1 <= self.rfb and self.y1>self.lpos and self.y1 < (self.lpos+self.rs)

        def dann_soll_er_vom_linken_schlaeger_zurueckprallen():
            self.x1, self.dx = self.rfb+5, -self.dx

        def wenn_der_ball_auf_den_rechten_schlaeger_knallt():
            return self.x1 >= (self.gwidth-self.rfb-self.ballsize) and self.y1 >= self.rpos and self.y1 <= (self.rpos+self.rs)

        def dann_soll_er_vom_rechten_schlaeger_zurueckprallen():
            self.x1, self.dx = (self.gwidth-self.rfb-self.ballsize-5), -self.dx

        def und_die_runde_ist_zu_ende():
            self.playing = 0  # stop the game

        def schlaeger_ball_und_treffer_darstellen():
            self.coords(self.ball, self.x1, self.y1, self.x1+self.ballsize, self.y1+self.ballsize)
            # draw current score
            score = str(self.lplayer) + ":" + str(self.rplayer)
            self.itemconfigure(self.score_text, text=score)
            # Change the positon of the two rackets
            self.coords(self.racket_right, self.gwidth-self.rfb, self.rpos, self.gwidth-self.rfb, self.rpos+self.rs)
            self.coords(self.racket_left, self.rfb, self.lpos, self.rfb, self.lpos+self.rs)
        
        def move():

            if wenn_die_runde_nicht_zu_ende_ist():
                bewegt_sich_der_ball_ueber_das_spielfeld()

            if wenn_der_ball_am_oberen_rand_anstoesst():
                dann_soll_der_ball_sich_wieder_nach_unten_bewegen()

            elif wenn_der_ball_am_unteren_rand_anstoesst():
                dann_soll_der_ball_sich_wieder_nach_oben_bewegen()

            if wenn_der_ball_auf_den_linken_schlaeger_knallt():
                dann_soll_er_vom_linken_schlaeger_zurueckprallen()

            elif wenn_der_ball_auf_den_rechten_schlaeger_knallt():
                dann_soll_er_vom_rechten_schlaeger_zurueckprallen()

            if wenn_der_ball_ins_rechte_aus_geht():
                dann_erhoehe_die_trefferzahl_fuer_den_linken_spieler()
                positioniere_den_ball_auf_der_startposition_fuer_den_rechten_spieler()
                und_die_runde_ist_zu_ende()    

            elif wenn_der_ball_ins_linke_aus_geht():
                dann_erhoehe_die_trefferzahl_fuer_den_rechten_spieler()
                positioniere_den_ball_auf_der_startposition_fuer_den_linken_spieler()
                und_die_runde_ist_zu_ende()    
                    
            schlaeger_ball_und_treffer_darstellen()
            
            self.after(50, move)

        move()

if __name__ == '__main__':
    Application().mainloop()
Vielleicht kannst Du ja mal erklären, zu was es gut sein soll, wenn man vor jede Variable ein 'self.' davorsetzt? Vielleicht würde das auch der TO gerne verstehen.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: es geht nicht darum, überall self davorzuschreiben, das hat nämlich in der jetzigen Form überhaupt keinen Vorteil. Aber ordentliche Funktionen zu schreiben, die eine Aufgabe haben und nicht nur Kommentar in Funktionsnamen sind, das macht Sinn. Du hast jetzt Funktionen in Funktionen in Methoden verschachtelt, das ist untestbar und unwartbar.

Ein Beispiel: statt »dann_erhoehe_die_trefferzahl_fuer_den_linken_spieler« als Spielerobjekt mit »left_player.score()« zu schreiben, ist zum einen viel aussagekräftiger, man braucht nicht alles Doppelt (einmal für Links und einmal für Rechts), kann für andere Spiele wiederverwendet werden und ist für sich testbar.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: es geht nicht darum, überall self davorzuschreiben, das hat nämlich in der jetzigen Form überhaupt keinen Vorteil.
Endlich richtig erkannt, aber zuvor, als ich das noch original gelassen hatte, dann hattest Du meine Verbesserung als Quatsch bezeichnet und dass ich so etwas lassen sollte.
Sirius3 hat geschrieben:Aber ordentliche Funktionen zu schreiben, die eine Aufgabe haben und nicht nur Kommentar in Funktionsnamen sind, das macht Sinn.
Jetzt schreibst Du Quatsch. Das sind ordentliche Funktionen, die eine Aufgabe haben. Ich habe sie nur so ausführlich benannt, dass sie einem Kommentar gleichen. Bei einem Top Down Entwurf fängt man mit gut benannten Funktionen an. Bei einem Bottom Up Entwurf fängt man unten an mit allerlei x,y und sonstigen Strukturen und sollte dann aber letztendlich bei verständlichem Top Code enden. Im vorliegenden Fall war das Bottom und es fehlte das Up. Es war also Bottom Code und daher nicht leicht verständlich.
Sirius3 hat geschrieben:Du hast jetzt Funktionen in Funktionen in Methoden verschachtelt, das ist untestbar und unwartbar.
Jetzt läßt Du, wie es Deine Art ist, nur wieder unbegründete Sprüche los. Starte das Spiel, drücke auf die Leertaste, bewege den Schläger, dann siehst Du dass es geht. Und dass ich das als Methoden mache und die schönen Funktionsnamen auch noch mit einem 'self.' davor verhunzen soll, fällt mir nicht im Traum ein.
Sirius3 hat geschrieben:Ein Beispiel: statt »dann_erhoehe_die_trefferzahl_fuer_den_linken_spieler« als Spielerobjekt mit »left_player.score()« zu schreiben, ist zum einen viel aussagekräftiger, man braucht nicht alles Doppelt (einmal für Links und einmal für Rechts), kann für andere Spiele wiederverwendet werden und ist für sich testbar.
Da hast Du recht, ich hatte auch schon daran gedacht, aber ich habe schon genug getan und würde sagen, dass Du jetzt auch mal an der Reihe bist.

PS: außerdem habe ich keinen Code geschrieben. Es ist der Originalcode vom TO, den ich nur in Funktionen kopiert hatte. Und jetzt hatte ich noch vor die Variablen ein 'self.' davor geschrieben. Und Melewo schrieb, dass der TO am Besten seinen Code wegwerfen und neu anfangen sollte. Aber der Code war in Ordnung. Es fehlte eine Einrückung (das war ein Versehen) und es fehlte eine leicht verständliche Top Code Komponente.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Alfons Mittelmeyer hat geschrieben:aber ich habe schon genug getan und würde sagen, dass Du jetzt auch mal an der Reihe bist.
Hier ist gar keiner an der Reihe, mit Ausnahme des Fragestellers. Der Fragesteller ist der einzige hier, der an der Reihe wäre und zwar damit, sich in den nächsten Wochen und Monaten erst einmal tiefgehender mit Python zu beschäftigen, bevor er dieses oder ein anderes Spiel erneut programmieren möchte. Wobei darauf zu achten wäre, hatte Sirius3 ihm in seiner ersten Antwort erklärt. Wenn er dazu weitere Fragen gehabt hätte, hätte er sich melden können. Doch das tat er nicht.

Als ich hier Anfang Mai einen ersten Entwurf von einem Script vorstellte, hatten mir BlackJack und Sirius3 Antworten gegeben, mit denen konnte man etwas anfangen, auch wenn man nicht alles gleich so umgesetzt, verstanden oder eingesehen hat. Aber man hat es im Hinterkopf behalten, um einen Teil davon zukünftig mit einfließen zu lassen. Wenn da einer so einen Zirkus wie Du veranstaltet hätte, dabei dann noch diese laufenden Streitereien, um nicht zu sagen fragwürdige Besserwisserei, dann hätte ich mich sicherlich auch so schnell nicht mehr in diesem Forum blicken lassen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Natürlich bietet eine Klasse auch einen Vorteil.

Wenn es keine Klasse ist, hat man nur ein PingPong Spiel. Aber bei einer Klasse kann man auch das machen:

Code: Alles auswählen

        Spielfeld(self).pack()
        Spielfeld(self).pack()
Oder mit grid lassen sich sogar vier PingPong Spiele auf dem Bildschirm unterbringen. Da könnten dann sogar acht Spieler spielen. Man muss dann allerdings die event Bindings mit zusätzlichem ,'+' machen und bei der Spielfeld Klasse muss man noch die Übergabe für die Bedienung implementieren, damit dann jedes Spiel seine eigenen Tasten hat. Könnte natürlich auf der Tastatur für acht Spieler ein bißchen eng werden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Melewo hat geschrieben:
Alfons Mittelmeyer hat geschrieben:aber ich habe schon genug getan und würde sagen, dass Du jetzt auch mal an der Reihe bist.
Hier ist gar keiner an der Reihe, mit Ausnahme des Fragestellers. Der Fragesteller ist der einzige hier, der an der Reihe wäre und zwar damit, sich in den nächsten Wochen und Monaten erst einmal tiefgehender mit Python zu beschäftigen, bevor er dieses oder ein anderes Spiel erneut programmieren möchte. Wobei darauf zu achten wäre, hatte Sirius3 ihm in seiner ersten Antwort erklärt. Wenn er dazu weitere Fragen gehabt hätte, hätte er sich melden können. Doch das tat er nicht.
Ist klar, dass er sich nicht mehr meldete. Es kommt ein neuer User in das Forum. Man sollte ihn doch herzlich willkommen heißen. Und man sollte ihm doch helfen, den Fehler zu finden, deshalb fragt er ja hier an. Und was tust Du?
Melewo hat geschrieben:Irgendwie habe ich den Eindruck, Du könntest den ersten Preis gewonnen haben, in Bezug auf die Anzahl von globalen Variablen, deren Verwendung man eigentlich vermeiden sollte.
Also ein freundliches Willkommen ist das nicht. Und was Du dann bringst, ist ja der absolute Hammer.
Melewo hat geschrieben:Tue Dir selbst einen Gefallen und fange noch einmal von vorne an
Der Code war vollkommen in Ordnung, bis auf den Fehler, dass etwas nicht eingerückt war. Und geklärt sollte jetzt auch sein, dass eine KLasse im vorliegenden Fall auch gar nichts bringt
Melewo hat geschrieben:Als ich hier Anfang Mai einen ersten Entwurf von einem Script vorstellte, hatten mir BlackJack und Sirius3 Antworten gegeben, mit denen konnte man etwas anfangen, auch wenn man nicht alles gleich so umgesetzt, verstanden oder eingesehen hat. Aber man hat es im Hinterkopf behalten, um einen Teil davon zukünftig mit einfließen zu lassen.
Wenn Dir BlackJack und Sirius3 wertvolle Antworten gegeben hatten, das gibt Dir lange nicht das Recht, Dich hier als Richter aufzuspielen, was bei einem Programm in Ordnung ist oder nicht. Und so etwas, wie dem User den Eindruck zu vermitteln, dass sein Kode Kacke sei, er sich hier nicht mehr blicken lassen solle, bis er es besser kann und er seinen Code wegwerfen und neu anfangen soll, schadet dem Forum extrem. Der User wird sich wahrscheinlich hier nicht mehr blicken lassen.
Melewo hat geschrieben:Wenn da einer so einen Zirkus wie Du veranstaltet hätte, dabei dann noch diese laufenden Streitereien, um nicht zu sagen fragwürdige Besserwisserei, dann hätte ich mich sicherlich auch so schnell nicht mehr in diesem Forum blicken lassen.
Wer veranstaltet hier einen Zirkus? Und wenn es um solche Programme geht, dann bin ich kein Prinzipienreiter, wie manch andere im Forum. Ich kann beurteilen, ob bei so einem Programm die Verwendung einer Klasse einen Vorteil bringt oder nicht. Und in diesem Fall bringt sie keinen, außer wenn man mehrere PingPong Spiele gerne gleichzeitig hätte.

Und wer fängt die Streitereien an? Schau doch was Sirius3 geschrieben hatte:
Sirius3 hat geschrieben:@phbrue: bitte schnell vergessen, was Alfons da schreibt, es sei denn Du willst bleibende Schäden beim Programmieren behalten.
Das war 1a Top Code, wie man ihn bei einem Top Down Entwurf schreibt. Meinst Du ich soll solche Äußerungen einfach hinnehmen? Natürlich nicht. Also entgegne ich etwas. Aber hört es damit auf? Nein, es geht weiter und weiter und weiter und immer kommen neue solche Sachen wie:
Sirius3 hat geschrieben:Wenn Du keine guten Programme postest, sondern nur Quatsch, dann lass das bitte sein.
Ich hatte lediglich für den bestehenden Code einen Teil verbessert, damit er lesbarer wird. Warum wird das jetzt als Quatsch bezeichnet, nur weil ich nicht das ganze Programm umgeschrieben habe? Ich kann doch dem TO zeigen, wie er sein Programm lesbarer gestaltet. Wieso soll ich da sein ganzes Programm umschreiben, damit Sirius3 das nicht als Quatsch bezeichet. Soll er es doch selber umschreiben.
Melewo
User
Beiträge: 320
Registriert: Mittwoch 3. Mai 2017, 16:30

Alfons Mittelmeyer hat geschrieben:Ist klar, dass er sich nicht mehr meldete. Es kommt ein neuer User in das Forum. Man sollte ihn doch herzlich willkommen heißen. Und man sollte ihm doch helfen, den Fehler zu finden, deshalb fragt er ja hier an. Und was tust Du?
Melewo hat geschrieben:Irgendwie habe ich den Eindruck, Du könntest den ersten Preis gewonnen haben, in Bezug auf die Anzahl von globalen Variablen, deren Verwendung man eigentlich vermeiden sollte.
Also ein freundliches Willkommen ist das nicht. Und was Du dann bringst, ist ja der absolute Hammer.
Melewo hat geschrieben:Tue Dir selbst einen Gefallen und fange noch einmal von vorne an
Der Code war vollkommen in Ordnung, bis auf den Fehler, dass etwas nicht eingerückt war. Und geklärt sollte jetzt auch sein, dass eine KLasse im vorliegenden Fall auch gar nichts bringt
Ein Einsteiger kann nicht viel von jemanden lernen, der versucht Pfuscharbeit auszubügeln, sondern nur vor von demjenigen, der ihm sagt, wenn etwas blanke Pfuscharbeit ist. Und bei über ein Dutzend globale Variablen in einem Code von unter 100 Zeilen, da war es halt nichts weiter als Pfusch und der Fragesteller muss sich halt überlegen, ob er Python lernen möchte oder nicht. Doch wenn er es lernen möchte, muss er noch einmal von vorn mit so einem Script beginnen.

Und wenn Du hier allen Ernstes behaupten möchtest, dass der Code bis auf einen Fehler völlig in Ordnung war, na dann kann wohl von Dir auch kaum einer etwas lernen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Melewo hat geschrieben:Ein Einsteiger kann nicht viel von jemanden lernen, der versucht Pfuscharbeit auszubügeln, sondern nur vor von demjenigen, der ihm sagt, wenn etwas blanke Pfuscharbeit ist. Und bei über ein Dutzend globale Variablen in einem Code von unter 100 Zeilen, da war es halt nichts weiter als Pfusch und der Fragesteller muss sich halt überlegen, ob er Python lernen möchte oder nicht. Doch wenn er es lernen möchte, muss er noch einmal von vorn mit so einem Script beginnen.
Wenn Du mit Pfuscharbeit daherkommst, dann mußt Du Dich vorher selbst aufs Korn nehmen. Hier hast Du nämlich bereits selber bei nur 15 Zeilen Code schon drei globale Variablen: viewtopic.php?f=1&t=41196#p314791

Das ist aber dann auch nichts weiter als Pfusch und Du mußt Dir überlegen, ob Du Python lernen möchtest oder nicht. Du erwartest von anderen dass sie etwas tun, was Du selber nicht tust. Aber bei denen ist es Pfusch und bei Dir völlig in Ordnung?
Melewo hat geschrieben:Und wenn Du hier allen Ernstes behaupten möchtest, dass der Code bis auf einen Fehler völlig in Ordnung war, na dann kann wohl von Dir auch kaum einer etwas lernen.
Dieser Code vom TO war kein Beispiel für Code, den man präsentiert, damit sich andere ein Beispiel nehmen. Es war Code wie man ihn für sich selber schreibt. Und da will man nicht so viel tippen.

Wenn man schreibt play_field_height, weiß jeder was gemeint ist, aber was ist gheight?
Oder gar wenn es um racket_width geht und man schreibt nur rs, dann kann sich keiner unter rs etwas vorstellen.

Wenn man lernen will, wie tkinter funktioniert, dann schreibt man Code für sich und spart sich Tipparbeit. Für Code, den man für sich selber zum Lernen schreibt, war der Code in Ordnung. Für Code, den man an andere weitergibt, wie etwa gar einen Kunden, war er natürlich nicht in Ordnung, weil sich da keiner auskennt.

Aber es sollte doch in Ordnung sein, wenn jemand hier fragt, wo der Fehler sein könnte. Da muß man ihn doch nicht gleich fertig machen, weil das kein Muster Code war.

Und zu globalen Variablen und globalen Zuständen: Wenn mehrere Objekte gegenseitig auf Variablen (sogenannte globale Variablen) zugreifen, das sollte man vermeiden. Wenn ein Objekt auf seine eigenen Variablen zugreift, dagegen gibt es doch nichts einzuwenden. Und hier gab es nur das eine Objekt, nämlich das PingPong Spiel mit einem Canvas als Spielfeld. Und es waren die Variablen für das Geschehen auf diesem Spielfeld. Man hätte natürlich die meisten Variablen vermeiden können, indem man sich umständlich die Koordinaten aus den Canvas Items holt. Aber mit Variablen geht es einfacher. Nur hätte man sie aussagekräftig benennen sollen, damit auch andere etwas damit anfangen können.

Dem TO zu schreiben, dass das Pfusch wäre und er zuerst Python lernen solle und von vorn beginnen, war total nicht in Ordnung. Ihn höflich zu bitten, die Variablen aussagekräftig zu benennen, damit man versteht was gemeint ist, das wäre richtig gewesen.

Das wäre ein guter Anfang gewesen und sicher hätte er den Gefallen getan. Da wäre er aber dann wohl schon selber auf den Fehler gekommen - ist er wahrscheinlich auch schon längst. Dann wäre eine gute Beziehung hergestellt gewesen und man hätte ihm weitere Tipps gehen können, damit er mehr lernt. Aber wenn man ihm gleich seinen Code als Pfusch vor die Ohren knallt und das völlig unberechtigt, dann ist es eben mit dem Gewinn eines neuen Mitgliedes vorbei.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Hier ein Vorschlag zur Benennung:

Code: Alles auswählen

class Var: pass

def main():

    # constants ====================

    FIELD_WIDTH = 800
    FIELD_HEIGHT = 400
    BALL_SIZE = 20
    RACKET_SPEEED = 20
    RACKET_HEIGHT = 60
    RACKET_WIDTH = 20
    
    # variables =====================

    score = Var()
    racket_ypos = Var()
    # es geht auch
    def ball(): pass

    score.left = score.right = 0

    racket_ypos.left = racket_ypos.right = (FIELD_HEIGHT - RACKET_HEIGHT)/2

    ball.x , ball.y = RACKET_WIDTH , (FIELD_HEIGHT - BALL_SIZE)/2
    ball.dx = ball.dy = 15
    ball.playing = 0

    # sonstiger Code würde folgen

main()
Man kann eine Var Klasse machen oder einfach als Funktion deklarieren, damit man Objekte bekommt, deren Werte man auch in einer Funktion verändern kann. Damit braucht man kein global und kann das jederzeit in einer Funktion verwenden.

Mit entsprechender Benennung wäre der Code vom TO auch verständlich und man kann mit den Objekten das auch in einer Funktion unterbringen und braucht kein global.

Und da muss man auch nicht von vorne anfangen, sondern kann mit search und replace die Variablen ersetzen.
Antworten