Spiel in pygame sauber beenden

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
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo,
ich lese gerade ein Python-Buch (bin ein Anfänger), in dem man ein Spiel nach "Space Invaders" programmieren kann. Ein Raumschiff wird vom Benutzer horizontal bewegt. Dort ist eine Tastatursteuerung vorgesehen. Die hat mir aber nicht gefallen und ich habe es hinbekommen, dass man das Raumschiff mit der Maus horizontal bewegen kann.
Als Editor verwende ich Visual Studio 2019 Community. Das Spiel soll mit pygame laufen.


Meine eigentliche Frage ist:
Wie beendet man so ein Spiel sauber? Habe hier im Forum gelesen, dass man "sys.exit()" vergessen soll (so war es im Buch gelöst)...

Ich poste nur den (aus meiner Sicht) relevanten Code, der für das Schließen des Spiels nötig ist.

#Alien_Invasion.py

Code: Alles auswählen

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf

def run_game():
    ende = False
    # initialisiert das Spiel und erstellt ein screen-Objekt.
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    # Erstellt ein Schiff
    ship = Ship(screen)
    spielaktiv=True
    #Startet die Hauptschleife des Spiels.
    while spielaktiv:
        gf.check_events(ship)
        gf.update_screen(ai_settings, screen, ship)
        
        # Zeichnet den Bildschirm bei jedem Schleifendurchlauf neu.
        screen.fill(ai_settings.bg_color)
        ship.blitme()

run_game()
#game_functions.py

Code: Alles auswählen

import sys 
import pygame
import pygame.locals as pyl

def check_events(ship):
    # Lauscht auf Tastatur- und Mausereignisse.
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            spielaktiv = False    
        elif event.type == pyl.MOUSEMOTION:
            x=event.rel[0]
            if x > 0:
                ship.rect.centerx +=1    
            elif x<0:
                ship.rect.centerx -=1    
        
def update_screen(ai_settings, screen, ship):
    #Zeichnet den Bildschirm bei jedem Schleifendurchlauf neu.
    screen.fill(ai_settings.bg_color)
    ship.blitme()

    #Macht den zuletzt gezeichneten Bildschirm sichtbar.
    pygame.display.flip()
Ich bitte höflichst um Hilfe...

Viele Grüße,
kwon
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst doch nur aus run_game raus. Wenn spielkative False wird, dann sollte das doch alles passen.
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Ich muss es leider fragen (schäm...)
wie ist das normale Vorgehen?
Muss ich das X von dem pygame-Fenster klicken oder das X von Python?
sorry, kenne mich mit pygame nicht aus...
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Python hat kein X. Das ist deine IDE. Und dein Spiel beendest du durch das Spielfenster, oder durch einen Mechanismus im Spiel. Fuer das Fenster geht das so:

Code: Alles auswählen

 for event in pygame.event.get():
            if event.type == pygame.QUIT:
                .... # zb spielaktiv = False
Nachtrag: ah, jetzt sehe ich, was schief laeuft. Du kannst nicht einfach irgendwo eine lokale Variable benutzen, und woanders wird die dann magisch sichtbar.

Du kannst zB in deiner Ereignisschleifenbehandlung True oder False zurueckgeben, abhaengig davon, ob QUIT ausgeloest wurde, oder nicht.
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

if event.type == pygame.QUIT:
spielaktiv = False

habe ich in der Funktion check_events()...

Wenn ich aber auf das X von pygame klicke, dann passiert leider nichts...

Hättest du einen Hinweis?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Habe ich gerade nochmal nachgetragen. Du kannst nicht einfach irgendwo eine Variable definieren, und woanders ist das dann magisch sichtbar.

Werte gelangen *in* eine Funktion als Parameter (wie ship), und *aus* einer Funktion via return-Anweisung. Also zB so:

Code: Alles auswählen


def check_events(ship):
    # Lauscht auf Tastatur- und Mausereignisse.
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            return False    
        elif event.type == pyl.MOUSEMOTION:
            x=event.rel[0]
            if x > 0:
                ship.rect.centerx +=1    
            elif x<0:
                ship.rect.centerx -=1   
    return True

....
 while gf.check_events(ship):
     ...
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Lieber __deets__,
dankeschön, jetzt tut es, was es soll...

Werde ich noch in Ruhe nachvollziehen.
Ich bin eben noch ein Anfänger...

Ich wünsche dir einen schönen Abend!

Viele Grüße,
kwon

finde den "Thema gelöst-Button" nicht, wie kann man eine Frage als gelöst markieren?
Danke...
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Kann man nicht, an dieses Konzept glaube ich persoenlich auch nicht.
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Okay,
dankeschön...
Sirius3
User
Beiträge: 17761
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Module `settings` und `ship` sehen so aus, als ob Du pro Datei nur eine Klasse hast. Das macht man in Python nicht, das ist ja nicht Java, sondern fasst in Modulen thematisch alles zusammen, was zusammengehört. `game_functions` ist ein sehr generischer Name, und man sollte keine kryptischen Abkürzungen, wie `gf` benutzen. Pack erst einmal alles in eine Datei und falls die irgendwann zu groß wird, kann man sich immer noch überlegen, in welche Module man teilt.
In `run_game` wird `ende` gar nicht verwendet. Was soll das `ai` bei `ai_settings` bedeuten?
In `game_functions.py` wird `sys` importiert, aber nicht benutzt.
`update_screen` besteht aus drei Zeilen, die die drei Argumente unabhängig voneinander benutzen. Da stellt sich mir die Frage, ob das überhaupt eine sinnvolle Funktion sind. Die gleichen Zeilen hast Du auch nochmal in der while-Schleife direkt.

Code: Alles auswählen

import pygame
import pygame.locals
from settings import Settings
from ship import Ship

def check_events(ship):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            return False    
        elif event.type == pygame.locals.MOUSEMOTION:
            x = event.rel[0]
            if x > 0:
                ship.rect.centerx += 1    
            elif x < 0:
                ship.rect.centerx -= 1
    return True

def run_game():
    pygame.init()
    settings = Settings()
    screen = pygame.display.set_mode((settings.screen_width, settings.screen_height))
    pygame.display.set_caption("Alien Invasion")

    ship = Ship(screen)
    while check_events(ship):
        screen.fill(settings.bg_color)
        ship.blitme()
        pygame.display.flip()

if __name__ == "__main__":
    run_game()
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo liebe Profis,
ich lerne Python mit Hilfe eines Buches und möchte eine Aufgabe darin nachprogrammieren (ein Spiel wie Space Invaders).
Mit der Aufteilung des Codes möchte ich mich ans Buch halten, damit ich den gleichen Weg gehe. Lediglich bei der Steuerung möchte ich vom Buch abweichen und eine Maussteuerung verwenden.
Mir ist es geglückt eine Maussteuerung zu basteln, diese funktioniert aber sehr hakelig.
Was ich erreichen möchte ist:
* dass sich das Schiff nach rechts bewegt wenn ich die Maus nach rechts bewege
* dass sich das Schiff nach links bewegt wenn ich die Maus nach links bewege
* dass die Bewegung "sanft" ist (nicht hakelig)

Mir kam nur die Idee sich die relative Mausbewegung anzuschauen:
x = event.rel[0]
Ist x positiv dann bewege nach rechts
Ist x negativ dann bewege nach links

Vielleicht ist jemand so freundlich und könnte mir bei der Maussteuerung etwas helfen...
Leider weiß ich keinen anderen Ansatz, möchte es natürlich möglichst einfach halten...
Ich poste den kompletten Code damit der Kontext klarer wird.
Meine Frage bezieht sich erst einmal nur auf die Realisierung einer Maussteuerung (an der Aufteilung des Codes, wie von Sirius3 vorgeschlagen, möchte ich - wenn möglich - nicht so viel ändern, damit ich mit dem Buch konform bin).
Mein Ansatz zur Maussteuerung ist in game_functions.py in der Funktion check_events().

Ich bitte um etwas Hilfe...
Vielen Dank!

Alien_Invasion.py

Code: Alles auswählen

import pygame
from settings import Settings
from ship import Ship
import game_functions as gf

def run_game():
    # Initialisiert das Spiel + erstellt ein screen-Objekt
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
    pygame.display.set_caption("Alien Invasion")
    # Erstellt das Schiff
    ship = Ship(ai_settings,screen)
    # Startet die Hauptschleife des Spiels.
    while gf.check_events(ship):
        ship.update()
        gf.update_screen(ai_settings, screen, ship)
run_game()
game_functions.py

Code: Alles auswählen

import pygame
import pygame.locals as pyl

def check_events(ship):
    # Lauscht auf Mausereignisse.
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            return False    
        elif event.type == pyl.MOUSEMOTION:
            x = event.rel[0]
            if x == 0:
                ship.moving_right = False
                ship.moving_left = False
            else:
                if x > 0:
                    ship.moving_right = True    
                if x < 0:
                    ship.moving_left = True
    return True

def update_screen(ai_settings, screen, ship):
    #Zeichnet den Bildschirm bei jedem Schleifendurchlauf neu
    screen.fill(ai_settings.bg_color)
    ship.blitme()
    # Macht den als letztes gezeichneten Bi1dschirm sichtbar
    pygame.display.flip()
         
ship.py

Code: Alles auswählen

import pygame
class Ship():
    def __init__(self,ai_settings,screen):
        self.screen = screen
        self.ai_settings = ai_settings
        # lädt das Bi1d des Schiffes und ruft dessen umgebendes Rechteck ab
        self.image=pygame.image.load('images/ship.bmp')
        self.rect=self.image.get_rect()  
        self.screen_rect= screen.get_rect()
        # Platziert jedes neue Schiff mittig am unteren Bildschirmrand
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom
        # speichert einen Kommawert für den Schiffsmittelpunkt
        self.center=float(self.rect.centerx)

        self.moving_right=False
        self.moving_left=False
        
    def update(self):
        #Aktualisiert den Wert für den Mittelpunkt des Schiffs, nicht des Rechtecks
        if self.moving_right:
            self.center += self.ai_settings.ship_speed_factor
        if self.moving_left:
            self.center -= self.ai_settings.ship_speed_factor
        #Aktualisiert das rect-Objekt auf der Grundlage von self.center
        self.rect.centerx=self.center

    def blitme(self):
        self.screen.blit(self.image, self.rect)
settings.py

Code: Alles auswählen

class Settings():
    def __init__(self):
        # Bildschirmeinstellungen
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (80,80,255)
        self.ship_speed_factor = 1
kwon
User
Beiträge: 43
Registriert: Samstag 2. Mai 2020, 11:48

Hallo,
ich habe mich entschieden, mich komplett ans Buch zu halten.
Frage wird zurückgezogen.
Antworten