Python variable

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.
Antworten
Eleocraft
User
Beiträge: 10
Registriert: Mittwoch 19. September 2018, 11:16

Ich will in Python(ohne Pygame)ein jump and run programmieren.
aus irgendeinem Grund ist die variable UP (zur Feststellung ob die Figur auf festem Boden steht oder nicht) immer auf 1.
kann mir jemand sagen was ich falsch gemacht habe?

Code: Alles auswählen

#tkinter
from tkinter import *
from time import *
screen = Tk()
screen.title('jump and run')
c = Canvas(screen, width=1400, height=700)
c.pack()
UP = 0





#hintergrund
himg = PhotoImage(file='Hintergrund.gif')
c.create_image(0, 0, anchor=NW, image=himg)

#Player
img = PhotoImage(file='Player.gif')
Player1 = c.create_image(100, 600, anchor=NW, image=img) 
img2 = PhotoImage(file='Player2.gif')
Player2 = c.create_image(100, 600, anchor=NW, image=img2)
c.itemconfig(Player2, state=HIDDEN)



def Player(event):
    key = event.keysym
    if key == "Left":
        c.move(Player1, -10, 0)
        c.move(Player2, -10, 0)
        c.itemconfig(Player1, state=HIDDEN)
        c.itemconfig(Player2, state=NORMAL)
    elif key == "Right":
        c.move(Player1, 10, 0)
        c.move(Player2, 10, 0)
        c.itemconfig(Player1, state=NORMAL)
        c.itemconfig(Player2, state=HIDDEN)
c.bind_all('<Key>', Player)

def Player_sp(event):
    UP = 1
    
    for i in range(50):
        c.move(Player1, 0, -2)
        c.move(Player2, 0, -2)
        screen.update()
        sleep(0.01)
    UP = 0
    
c.bind_all('<Up>', Player_sp)

 
#Plate
def screen_plate(PX, PY, Länge):
    c.create_rectangle(PX, PY, PX + Länge, PY - 10, fill='lime')


while True:
    pos = c.coords(Player1)

    if pos[1] > 599:
        UP = 1
            if UP == 0:
        c.move(Player1, 0, 2)
        c.move(Player2, 0, 2)
    screen.update()
    sleep(0.01)
 
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Bei ereignisgesteuerter Programmierung darf es keine Schleifen geben, die längere Zeit arbeiten, ohne die Kontrolle an die Hauptschleife zurückzugeben. Um eine gewisse Frame-Rate einzuhalten, kann man bei Tk after benutzen; screen.update ist dagegen nicht so toll.
Um komplexere GUI Programme zu schreiben, braucht man eigentlich auch objektorientierte Programmierung, um den Zustand des Spiels zwischen den Methoden teilen zu können.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Code: Alles auswählen

    if pos[1] > 599:
        UP = 1
            if UP == 0:
Wenn die if-Bedingung wahr ist, wird UP auf 1 gesetzt,
direkt danach wird gefragt, ob UP gleich 0 ist.
Kann diese Bedingung jemals wahr sein?
Außerdem stimmt da was mit der Einrückung nicht.
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Eleocraft
User
Beiträge: 10
Registriert: Mittwoch 19. September 2018, 11:16

thx: da war ein Leerzeichen zu viel...
funktioniert trotzdem nicht.
Eleocraft
User
Beiträge: 10
Registriert: Mittwoch 19. September 2018, 11:16

Sirius3 hat geschrieben: Mittwoch 19. September 2018, 14:38 Bei ereignisgesteuerter Programmierung darf es keine Schleifen geben, die längere Zeit arbeiten, ohne die Kontrolle an die Hauptschleife zurückzugeben. Um eine gewisse Frame-Rate einzuhalten, kann man bei Tk after benutzen; screen.update ist dagegen nicht so toll.
Um komplexere GUI Programme zu schreiben, braucht man eigentlich auch objektorientierte Programmierung, um den Zustand des Spiels zwischen den Methoden teilen zu können.
was meinst du mit Tk after?
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Eleocraft
User
Beiträge: 10
Registriert: Mittwoch 19. September 2018, 11:16

Problem gefunden: da der Player bei y:600 spawnt, wird die variable UP auf 1 gesetzt und nicht mehr zurück...

Code: Alles auswählen


#tkinter
from tkinter import *
from time import *
screen = Tk()
screen.title('jump and run')
c = Canvas(screen, width=1400, height=700)
c.pack()
UP = 0





#hintergrund
himg = PhotoImage(file='Hintergrund.gif')
c.create_image(0, 0, anchor=NW, image=himg)

#Player
img = PhotoImage(file='Player.gif')
Player1 = c.create_image(100, 600, anchor=NW, image=img) 
img2 = PhotoImage(file='Player2.gif')
Player2 = c.create_image(100, 600, anchor=NW, image=img2)
c.itemconfig(Player2, state=HIDDEN)



def Player(event):
    key = event.keysym
    if key == "Left":
        c.move(Player1, -10, 0)
        c.move(Player2, -10, 0)
        c.itemconfig(Player1, state=HIDDEN)
        c.itemconfig(Player2, state=NORMAL)
    elif key == "Right":
        c.move(Player1, 10, 0)
        c.move(Player2, 10, 0)
        c.itemconfig(Player1, state=NORMAL)
        c.itemconfig(Player2, state=HIDDEN)
c.bind_all('<Key>', Player)

def Player_sp(event):
    UP = 2
    
    for i in range(50):
        c.move(Player1, 0, -2)
        c.move(Player2, 0, -2)
        screen.update()
        sleep(0.01)
    UP = 0
    
c.bind_all('<Up>', Player_sp)

 
#Plate
def screen_plate(PX, PY, Länge):
    c.create_rectangle(PX, PY, PX + Länge, PY - 10, fill='lime')

#Hauptschleife
while True:
    pos = c.coords(Player1)
    if pos[1] > 599:
        UP = 1
    if pos[1] < 600 and UP != 2:
        UP = 0
    if UP == 0:
        c.move(Player1, 0, 2)
        c.move(Player2, 0, 2)
    screen.update()
    sleep(0.01)
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Eleocraft: Noch ein paar Anmerkungen zum Quelltext:

Um das „screen.update ist dagegen nicht so toll“ von Sirius3 noch mal zu vertiefen: Du hast das in der `Player_sp()`-Funktion stehen, die aufgerufen wird wenn der Benutzer eine Taste drückt. Während die Funktion läuft, sorgt das `update()` dafür das weitere Tasteneingaben verarbeitet werden, was dazu führen kann, das wieder die `Player_sp()`-Funktion aufgerufen wird. Tk garantiert nicht das man während der Abarbeitung eines Handlers wieder Handler auslösen kann ohne das komische Sachen passieren. Das ist also ein Programmierfehler.

Sternchenimporte sind Böse™. Damit holst Du Dir bei `tkinter` mehr als 100 Namen in das aktuelle Modul, von denen Du nur einen Bruchteil benötigst. Es ist schwerer nachvollziehbar wo Namen eigentlich herkommen, und es besteht die Gefahr von Namenskollisionen.

Kommentare sind dazu da dem Leser mitzuteilen *warum* etwas gemacht wird. Sofern das nicht klar ist. Denn *was* gemacht wird, steht ja bereits als Code dort. Sollte man dem Code das nicht entnehmen können, dann kann das auch an schlechten Namen liegen, die unpassend oder nichtssagend sind.

Namen sollten nicht kryptisch abgekürzt werden, sondern dem Leser klar vermitteln was der Wert bedeutet. Also keine einbuchstabigen Namen (ausser der Gültigkeitsbereich ist sehr begrenzt („comprehension“-Syntax, ``lambda``-Ausdrücke)) oder es ist eine der gängigen Ausnahmen wie beispielsweise `i`, `j`, `k` für ganze Zahlen die als Schleifenlaufvariable und/oder Index verwendet werden, oder `x`, `y`, `z` für Koordinaten.

`c` für ein `Canvas`-Objekt geht eher nicht, das sollte man `canvas` nennen (wenn einem nichts passenderes einfällt). `himg` hiesse besser `hintergrund_image` oder um in einer Sprache zu bleiben `background_image`.

Namen durchnummerieren ist ein Warnzeichen das man sich entweder passendere Namen aussuchen sollte, oder die Einzelwerte in einer Datenstruktur besser aufgehoben wären.

An die üblichen Schreibweisen und Namenskonventionen sollte man sich auch halten. Das sind kleinbuchstaben_mit_unterstrichen für alles ausser Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Funktionen und Methoden werden üblicherweise nach der Tätigkeit benannt die sie ausführen. `Player` ist keine Tätigkeit. Und `UP` ist keine Konstante. Zudem ist bei der Variablen nicht so wirklich leicht zu verstehen was die magischen Zahlwerte 0 und 1 die an sie gebunden werden, und 2 das wahrscheinlich auch an `UP` gebunden werden soll, aber nur an ein lokales `UP` in der `Player_sp()`-Funktion gebunden wird, eigentlich bedeuten sollen. Das wäre ein Fall wo man Konstanten für diese Zahlen definieren würde. Da die alle von einem Typ sind, am besten per `enum.Enum` um sie nicht mit anderen Zahlen verwechseln zu können. Da kann man dann sowohl den Zahlen einen sinnvollen Namen geben, als auch den Oberbegriff für diese drei Werte.

Wegen `UP` kommst Du übrigens IMHO hier schon nicht mehr ohne objektorientierte Programmierung aus, denn das muss aus der Funktion für den Sprung nach draussen kommuniziert werden, die ist aber eine Rückruffunktion.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mal schnell überarbeitet aber komplett ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from enum import auto, Enum
from functools import partial


class HorizontalDirection(Enum):
    LEFT = auto()
    RIGHT = auto()


class VerticalDirection(Enum):
    DOWNWARDS = auto()
    NONE = auto()
    UPWARDS = auto()


class Player:
    
    HORIZONTAL_MOVING_DISTANCE = 10
    VERTICAL_MOVING_DISTANCE = 2
    
    def __init__(self, canvas, left_image_id, right_image_id):
        self.canvas = canvas
        self.image_ids = [left_image_id, right_image_id]
        self.vertical_direction = VerticalDirection.DOWNWARDS
        self.canvas.itemconfig(left_image_id, state=tk.HIDDEN)
    
    @property
    def coordinates(self):
        return self.canvas.coords(self.image_ids[0])
    
    def set_direction(self, direction):
        if direction == HorizontalDirection.LEFT:
            states = [tk.HIDDEN, tk.NORMAL]
        elif direction == HorizontalDirection.RIGHT:
            states = [tk.NORMAL, tk.HIDDEN]
        else:
            raise ValueError(f'wrong direction {direction}')
        
        for image_id, state in zip(self.image_ids, states):
            self.canvas.itemconfig(image_id, state=state)
        
    def move(self, x_delta=0, y_delta=0):
        if x_delta < 0:
            self.set_direction(HorizontalDirection.LEFT)
        elif x_delta > 0:
            self.set_direction(HorizontalDirection.RIGHT)
        
        for image_id in self.image_ids:
            self.canvas.move(image_id, x_delta, y_delta)
    

def do_jump(player, ticks):
    if ticks > 0:
        player.move(y_delta=-Player.VERTICAL_MOVING_DISTANCE)
        player.canvas.after(10, do_jump, player, ticks - 1)
    else:
        player.vertical_direction = VerticalDirection.DOWNWARDS


def start_jump(player, _event=None):
    player.vertical_direction = VerticalDirection.UPWARDS
    do_jump(player, 50)
    

def do_tick(player):
    y_position = player.coordinates[1]
    if y_position > 599:
        player.vertical_direction = VerticalDirection.NONE
    
    if (
        y_position < 600
        and player.vertical_direction != VerticalDirection.UPWARDS
    ):
        player.vertical_direction = VerticalDirection.DOWNWARDS
    
    if player.vertical_direction == VerticalDirection.DOWNWARDS:
        player.move(y_delta=Player.VERTICAL_MOVING_DISTANCE)
    
    player.canvas.after(10, do_tick, player)
    

def main():
    root = tk.Tk()
    root.title('jump and run')
    
    canvas = tk.Canvas(root, width=1400, height=700)
    canvas.pack()

    background_image = tk.PhotoImage(file='Hintergrund.gif')
    canvas.create_image(0, 0, anchor=tk.NW, image=background_image)

    right_player_image = tk.PhotoImage(file='Player.gif')
    player_right_id = canvas.create_image(
        100, 600, anchor=tk.NW, image=right_player_image
    )
    left_player_image = tk.PhotoImage(file='Player2.gif')
    player_left_id = canvas.create_image(
        100, 600, anchor=tk.NW, image=left_player_image
    )
    player = Player(canvas, player_left_id, player_right_id)
    
    canvas.bind_all(
        '<Left>', lambda _e: player.move(-Player.HORIZONTAL_MOVING_DISTANCE)
    )
    canvas.bind_all(
        '<Right>', lambda _e: player.move(Player.HORIZONTAL_MOVING_DISTANCE)
    )
    canvas.bind_all('<Up>', partial(start_jump, player))

    do_tick(player)
    root.mainloop()


if __name__ == '__main__':
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten