pygame code verbessern

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Ich habe mir eine Methode geschrieben mit der ich meine Sprites und Animationen lade. Mein Problem ist jetzt, das diese Methode nach einem Mausklick ausgeführt wird um mein Spielfeld neu anzuzeigen ( liegt also in der Hauptschleife des Spiels ). Dabei werden die Spites ja immer neu von der Platte geladen dadurch harkt das Spiel ein klein wenig. Zwar nur ganz minimal aber man merkt es eben. Jetzt stehe ich auf dem Schlauch, die Sache ein wenig eleganter zu lösen. Ich habe schon versucht, die Spites unter __init__ einmal zu laden, sprich:

Code: Alles auswählen

self.diagreen = sprites('gfx/sprites/diagreen.json', 'gfx/sprites/diagreen.png')
und dann einfach zu ersetzen in der Methode:

Code: Alles auswählen

self.spritemap.append(self.diagreen)
es wird dann auch nicht gemeckert, allerdings fehlen mir dann Sprites. sind einfach nicht zu sehen und das Spiel, spielt auch ein wenig verrückt. Kann mir dabei jemand nen Tip geben?
Achja, die Methode self.setFields ist dann dafür da um die Sprites mittig auf den Bildschirm zu setzen.

Code: Alles auswählen

    #
    # Alle Sprites laden ( in richtiger Reihenfolge )
    #
    def makePlayfield(self):
        self.spritemap = []
        for i in range(len(self.playmap)):
            if self.playmap[i] == 0:
                self.spritemap.append(sprites('gfx/sprites/diagreen.json', 'gfx/sprites/diagreen.png'))

            if self.playmap[i] == 1:
                self.spritemap.append(sprites('gfx/sprites/diayellow.json', 'gfx/sprites/diayellow.png'))

            if self.playmap[i] == 2:
                self.spritemap.append(sprites('gfx/sprites/diared.json', 'gfx/sprites/diared.png'))
        self.setFields()


Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Mit Deinem Codeausschnitt kann man die Frage nicht beantworten.
Wenn sich playmap nicht ändert, dann ist es kein Problem, das in __init__ einzubauen.
Methoden schreibt man, wie Variablen und Attribute komplett klein.
Über einen Index iteriert man nicht, weil man direkt über die Liste iterieren kann.
Wenn man sich gegenseitig ausschließende if`s hat, dann benutzt man elif.
Wenn man so eine simple 1:1-Zuordnung hat, dann braucht man gar kein if.

Code: Alles auswählen

    def make_playfield(self):
        self.spritemap = [
            sprites(f'gfx/sprites/{self.SPRITES[sprite_nr]}.json', f'gfx/sprites/{self.SPRITES[sprite_nr]}.png')
        ]
        self.set_fields()
Besser wäre es, die Sprites einmal als Wörterbuch zu speichern und dann nur darauf zu verweisen.
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und eine kleine Offtopic Bemerkung: man harkt seinen Garten. Dabei hakt es mit dem Fortschritt, weil Bier & Grillgut locken.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Ähm, da hat es einige Zeile nicht mitkopiert:

Code: Alles auswählen

    SPRITES = [
        "diagreen",
        "diayellow",
        "diared",
    ]

    def make_playfield(self):
        self.spritemap = [
            sprites(f'gfx/sprites/{self.SPRITES[sprite_nr]}.json', f'gfx/sprites/{self.SPRITES[sprite_nr]}.png')
            for sprite_nr in self.playmap
        ]
        self.set_fields()
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Doch, die playmap ändert sich. Da ist das Spielfeld drin aus dem die Sprites dann neu aufgebaut werden. Ich denke, ich muß das überdenken und in die self.set_fields() rein setzen. In der Methode werden die Sprites ja eigentlich angezeigt. Es war eben nur sehr viel einfacher, komplett alles neu zu bauen. Ich änder das mal lieber gleich, sonst wird es hinterher zu aufwendig. Aber danke ;)

Ich weiß, mein Programmierstiel ist und war nie der beste.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

@Sirius3:
leider komme ich mit deinem Code nicht zurecht. Liegt vielleicht da dran, das ich gerade noch Python2.7 nutze?
Hab dann aber weiter probiert. Und Zwar habe ich noch mal so getestet:
Jetzt sollten doch durch die Schleife, 10 Objekte angezeigt werden. Es werden aber nur 3 angezeigt. Wieso denn? Wenn ich die Schleife weg lasse und per Hand jedes einzelne append schreibe, geht es komischer Weise. Was mache ich denn da falsch?

Code: Alles auswählen

        self.counter = 10
        self.spritemap = []
        self.diagreen = sprites('gfx/sprites/diagreen.json', 'gfx/sprites/diagreen.png')
        self.diayellow = sprites('gfx/sprites/diayellow.json', 'gfx/sprites/diayellow.png')
        self.diared = sprites('gfx/sprites/diared.json', 'gfx/sprites/diared.png')
        for i in range(0, self.counter):
            self.spritemap.append(self.diagreen)
            self.spritemap.append(self.diared)
            self.spritemap.append(self.diayellow)
        for i in range(0, self.counter):
            self.spritemap[i].setxy(randrange(800), randrange(600))
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

ne, Komando zurück. Auch das append per Hand geht nicht :(
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie kommst Du nicht zurecht? Was für eine Fehlermeldung bekommst Du?
Zeige den gesamten relevanten Teil Deines Codes. Aus den Codefragementen können wir nichts rauslesen.
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Vielleicht sollte ich erstmal den Hintergrund erklären. Ich habe Sprites, mit 3 verschiedenen Farben ( die Dinger sind auch animiert ). Nun habe ich ein 3x3 großes Feld in dem die Sprite mit zufälliger Farbe gesetzt werden sollen und anklickbar sein sollen. Dabei sollen sie die Farbe wechseln. Das ist eigentlich alles. Mit meinem alten Code, wo immer die Sprites neu von der Festplatte geladen und aufgebaut werden funktioniert ja auch so weit alles. Außer eben das es dann hakt ( man sieht es kaum aber man merkt es ) wenn man das Sprite anklickt ( dann wird ja wieder auf die Platte zugegriffen).
Jetzt habe ich leider schon nen Knick im Kopf. Ich habe da bestimmt nur nen kleinen Denkfehler drin. Ist mein erstes Spiel-Projekt. Auf dem C64er in Assembler hätte ich es anders gelöst ;)
Ich schicke mal meine Sprite Klasse mit, die ich mir gebaut hatte.
So weit so gut. Kann mir also meine Sprites anzeigen lassen. Jetzt hänge ich wie gesagt an dem Spielfeld dran und komme irgendwie nicht weiter.

Code: Alles auswählen

#
# sprites einlesen.
# wichtig ist es hier den richtigen Pfad zu bauen damit die Dateien fuer
# die Grafiken und so weiter gefunden werden ( fuer Android ).
#
class sprites(object):
    def __init__(self, jsonfile, spritefile):
        self.screen_x = 0  # screen_x  # x Position vom Sprite auf dem Bildschirm X
        self.screen_y = 0  # screen_y  # y Position vom Sprite auf dem Bildschirm  Y
        self.sprite = []  # komplette Animation
        self.anicounter = 0  # Zaehler der Animation
        self.animax = 0  # maximale Bilder der Animation
        self.timer_range = 60  # Zeit des Timers bis die Animation gestartet wird
        self.timer_stop = \
            datetime.datetime.utcnow() + \
            datetime.timedelta(seconds=randrange(self.timer_range))
        self.sprite_w = 0  # Breite des aktuellen Sprites
        self.sprite_h = 0  # Hoehe des aktuellen Sprites
        self.audio_click = music('/gfx/audio/sfx/click_1.wav')

        with open(os.getcwd() + "/" + jsonfile, 'r') as myfile:
            data = myfile.read()
        obj = json.loads(data)
        # print ("Sprites: " + str((len(obj))))
        sprites_sheet = pygame.image.load(os.getcwd() + "/" + spritefile).convert()

        #
        # einzelne, leere Sprites erstellen in der Groesse der Sprites im .json File
        #
        for images in range(len(obj)):
            self.sprite.append(pygame.Surface((int(obj[images]['width']), int(obj[images]['height']))))

        #
        # die leeren Sprites, aus dem Spritesheet fuellen
        #
        for images in range(len(self.sprite)):
            self.sprite[images].blit(sprites_sheet, (0, 0),
                                     (int(obj[images]['x']), int(obj[images]['y']),
                                      int(obj[images]['width']), int(obj[images]['height'])))
            self.sprite[images].set_colorkey((0, 0, 0))
            self.sprite_w = int(obj[images]['width'])
            self.sprite_h = int(obj[images]['height'])
        self.animax = (len(self.sprite))

    #
    # die Logig der Sprites starten ( Animation usw. )
    #
    def doLogic(self, screen):
        screen.blit(self.sprite[self.anicounter], (self.screen_x, self.screen_y))
        self.rotate()

    #
    # Die Animation einmal abspielen und Timer wieder auf
    # Zufall stellen
    #
    def rotate(self):
        if datetime.datetime.utcnow() > self.timer_stop:
            self.anicounter += 1
            if self.anicounter >= self.animax:
                self.anicounter = 0
                self.timer_stop = datetime.datetime.utcnow() + \
                                  datetime.timedelta(seconds=randrange(self.timer_range))

    def getSize(self):
        return self.sprite_w, self.sprite_h

    #
    # fuer neu berechnete Sprites werden die Koordinaten dann hier wieder gesetzt
    #
    def setxy(self, x, y):
        self.screen_x = x
        self.screen_y = y

    def getxy(self):
        return (self.screen_x, self.screen_y)

    #
    # wurde ein Sprite angeclickt? Wird ueber die Mausposition berechnet und True
    # oder False zurueckgeben. Wenn Sprite angeclickt dann True an sonsten False
    #
    def checkclick(self):
        screen_x, screen_y = self.getxy()
        sprite_w, sprite_h = self.getSize()
        mouse_x, mouse_y = pygame.mouse.get_pos()

        if int(mouse_x) in range(int(screen_x), int(screen_x) + int(sprite_w)):
            if int(mouse_y) in range(int(screen_y), int(screen_y) + int(sprite_h)):
                self.audio_click.doLogic(0)
                return True
        return False
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Ich glaube ich habe die Lösung ... muß sie aber noch ausprobieren. Ich sage dann bescheid ...
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Klasse schreibt man mit großem Anfangsbuchstaben.
Für Timer benutzt man kein datetime, sondern time.time() (ab Python3 time.monotonic).
`json.load` lädt direkt aus einer geöffneten Datei, das `my` in `myfile` ist unsinnig, weil es kein ourfile gibt.
Pfade setzt man nicht per + zusammen, os.getcwd zu benutzen ist unsinn, weil Pfade automatisch relativ zum aktuellen Verzeichnis sind.
Wie schon oben geschrieben iteriert man nicht über einen Index. Der Name `images` für einen Index ist sehr verwirrend.
`width` und `height` sind hoffentich schon ints.
Ein Attribut innerhalb einer for-Schleife zu setzten ist selten sinnvoll, und ein Zeichen dafür, dass irgendwas nicht stimmt, denn nur die letzte Zuweisung bleibt erhalten. Wie ist das mit sprite_w und sprite_h?
`screen_x` und `screen_y` werden immer nur zusammen benutzt. Warum nicht gleich ein Tuple?

Ein `x in range(...)` ist seltsam, man vergleich normalerweise mit <.

Code: Alles auswählen

#
# sprites einlesen.
# wichtig ist es hier den richtigen Pfad zu bauen damit die Dateien fuer
# die Grafiken und so weiter gefunden werden ( fuer Android ).
#
class Sprites(object):
    def __init__(self, jsonfile, spritefile):
        self.screen_xy = (0, 0)  # x/y Position vom Sprite auf dem Bildschirm
        self.anicounter = 0  # Zaehler der Animation
        self.timer_range = 60  # Zeit des Timers bis die Animation gestartet wird
        self.timer_stop = time.time() + randrange(self.timer_range))
        self.sprite_w = 0  # Breite des aktuellen Sprites
        self.sprite_h = 0  # Hoehe des aktuellen Sprites
        self.audio_click = music('/gfx/audio/sfx/click_1.wav')

        with open(jsonfile, 'rb') as file:
            images = json.loads(file)
        # print ("Sprites: " + str((len(obj))))
        sprites_sheet = pygame.image.load(spritefile).convert()

        self.sprites = [] # komplette Animation
        for image in images:
            # einzelne, leere Sprites erstellen in der Groesse der Sprites im .json File
            sprite = pygame.Surface((image['width'], image['height']))
            # die leeren Sprites, aus dem Spritesheet fuellen
            sprite.blit(sprites_sheet, (0, 0), (image['x'], image['y'], image['width'], image['height']))
            sprite.set_colorkey((0, 0, 0))
            self.sprites.append(sprite)
            # komisch:
            self.sprite_w = image['width']
            self.sprite_h = image['height']

    #
    # die Logik der Sprites starten ( Animation usw. )
    #
    def do_logic(self, screen):
        screen.blit(self.sprites[self.anicounter], self.screen_xy)
        self.rotate()

    #
    # Die Animation einmal abspielen und Timer wieder auf
    # Zufall stellen
    #
    def rotate(self):
        if time.time() > self.timer_stop:
            self.anicounter += 1
            if self.anicounter >= len(self.sprites):
                self.anicounter = 0
                self.timer_stop = time.time() + randrange(self.timer_range)

    def get_size(self):
        return self.sprite_w, self.sprite_h

    #
    # fuer neu berechnete Sprites werden die Koordinaten dann hier wieder gesetzt
    #
    def set_xy(self, x, y):
        self.screen_x = (x, y)
        self.screen_y = y

    def get_xy(self):
        return self.screen_xy

    #
    # wurde ein Sprite angeclickt? Wird ueber die Mausposition berechnet und True
    # oder False zurueckgeben. Wenn Sprite angeclickt dann True an sonsten False
    #
    def checkclick(self):
        screen_x, screen_y = self.screen_xy
        sprite_w, sprite_h = self.get_size()
        mouse_x, mouse_y = pygame.mouse.get_pos()

        if screen_x <= mouse_x < screen_x + sprite_w:
            if screen_y <= mouse_y < screen_y + sprite_h:
                self.audio_click.do_logic(0)
                return True
        return False
Der Code hat jetzt aber auch nichts mit Deinem ursprünglichen Problem zu tun?
The Hit-Man
User
Beiträge: 435
Registriert: Montag 20. Februar 2006, 18:11
Wohnort: Menden / Sauerland
Kontaktdaten:

Hatte dann doch die Lösung. Es war einfach besser, Sprites auszuschalten wenn ich sie nicht brauche oder einzuschalten wenn ich sie braucht. Also entweder kamen sie mit in den Blit oder auch nicht. So gehts dann ... Und danke fürs Helfen.
Antworten