I am making a spaceship game where you control a spaceship and fire bullets to defeat enemies. However, when I run my game, I get this:
Traceback (most recent call last):
File "C:/eqodqfe/t2.py", line 98, in enemies.draw(enemy)
File "C:\Users\linpang2018\AppData\Local\Programs\Python\Python38-32\lib\site-packages\pygame\sprite.py", line 474, in drawsurface_blit = surface.blit
AttributeError: 'Enemy' object has no attribute 'blit'
Why does this happen? How do I fix it? This is my current code: (Some parts omitted or replaced by --snip--)
class Spaceship(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("C:/eqodqfe/spaceship.png")
self.image = pygame.transform.scale(self.image, (175, 175))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.rect.center = (self.x, self.y)
class Bullet(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
--snip--
def update(self):
self.y -= 5
self.rect.center = (self.x, self.y)
if self.y < 0:
self.kill()
class Enemy(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen, self.x, self.y = s, x ,y
self.image = pygame.image.load("C:/eqodqfe/enemy.png")
self.image = pygame.transform.scale(self.image, (240, 210))
self.rect = self.image.get_rect()
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.rect.center = (self.x, self.y)
spaceship = Spaceship(screen, 400, 400)
bullets = pygame.sprite.Group()
enemies = pygame.sprite.Group()
clock = pygame.time.Clock()
running = True
while running:
--snip-- # (event loop and spawning bullets)
bullets.update()
key = pygame.key.get_pressed()
amount = 5
if key[pygame.K_a]:
spaceship.x -= amount
--snip--
spaceship.update()
screen.fill((255, 255, 255))
screen.blit(spaceship.image, spaceship.rect)
enemy = Enemy(screen, randint(-200, 800), -100)
enemies.add(enemy)
bullets.draw(screen)
enemies.draw(enemy)
pygame.display.update()
clock.tick(60)
pygame.sprite.Group() works like this:
Draws the contained Sprites to the Surface argument.
So the line
enemies.draw(enemy)
should be
enemies.draw(screen)
as the argument has to be a Surface, not a Sprite.
Related
My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)
My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)
My collide_rect function isn't working properly. It always returns True, when it's not suppose to. I have tried looking on the internet but nothing is working for me. I think the collide rect somehow did not use the actual coordinates for the two sprites. Can anyone help with this?
import pygame
import pygame.sprite
import sys
gameDisplay = pygame.display.set_mode((800,600))
pygame.display.set_caption("test_collision")
clock = pygame.time.Clock()
crashed = False
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect()
self.x = 280
self.y = 475
self.col = False
def update(self):
gameDisplay.blit(self.image, (self.x,self.y))
self.rect = self.image.get_rect()
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 1000
self.y = 483
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect()
def change_x(self):
self.time = pygame.time.get_ticks()
self.x = -(self.time/5) + 800
def update(self):
self.rect = self.image.get_rect()
gameDisplay.blit(self.image,(self.x,self.y))
obstacle = Obstacle()
ball = Ball()
while not crashed:
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
gameDisplay.fill((255,255,255))
ball.update()
obstacle.change_x()
obstacle.update()
ball.test_collisions(obstacle)
if ball.col:
print("colided")
pygame.display.flip()
clock.tick(1000)
pygame.quit()
sys.exit()
P.S This is my first post :)
pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, but it returns a rectangle that always starts at (0, 0) since a Surface object has no position.
The Surface is placed at a position on the display with the blit function.
You've to set the location of the rectangle, either by a keyword argument, e.g:
self.rect = self.image.get_rect(topleft = (self.x, self.y))
or an assignment to a virtual attribute (see pygame.Rect), e.g:
self.rect = self.image.get_rect()
self.rect.topleft = (self.x, self.y)
It is absolutely unnecessary to add some extra attributes self.x and self.y. Use the location of the rectangle instead. e.g:
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("ball.png")
self.rect = self.image.get_rect(topleft = (280, 475))
self.col = False
def update(self):
gameDisplay.blit(self.image, self.rect)
def test_collisions(self,sprite):
self.col = pygame.sprite.collide_rect(self,sprite)
class Obstacle(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("obstacle.png")
self.time = pygame.time.get_ticks()
self.rect = self.image.get_rect(topleft = (1000, 483))
def change_x(self):
self.time = pygame.time.get_ticks()
self.rect.x = -(self.time/5) + 800
def update(self):
gameDisplay.blit(self.image, self.rect)
Further note, that you can get rid of the methods Ball.update() respectively Obstacle.update() (you can delete them), if you use a pygame.sprite.Group and call .draw(), which uses the .image and .rect properties of the contained sprites, to draw them. e.g.:
obstacle = Obstacle()
ball = Ball()
all_sprites = pygame.sprite.Group([obstacle, ball])
while not crashed:
# [...]
gameDisplay.fill((255,255,255))
all_sprites.draw(gameDisplay)
pygame.display.flip()
clock.tick(1000)
I simply just want to delete the zombie sprite from my sprite group when the bullet collides with the zombie to create the sense the zombie is dying.
I already tried the Zombie(1,1).kill() but it did not work unfortunatley.
here is my code if it will be helpful...
import pygame
from sys import exit
from random import randint
import math
from pygame.math import Vector2
from pygame.constants import K_LSHIFT, K_SPACE, MOUSEBUTTONDOWN, MOUSEBUTTONUP, K_e
pygame.init()
import time
pygame.mouse.set_visible(True)
class Player(pygame.sprite.Sprite):
def __init__(self, x , y):
super().__init__()
self.x = x
self.y = y
self.image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
self.rect = self.image.get_rect()
self.orig_image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
self.rotate_vel = 1
self.cross_image = pygame.image.load('graphics/crosshair049.png')
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, self.rect)
dir_vec = pygame.math.Vector2()
dir_vec.from_polar((180, -self.rotate_vel))
cross_pos = dir_vec + self.rect.center
cross_x, cross_y = round(cross_pos.x), round(cross_pos.y)
surface.blit(self.cross_image, self.cross_image.get_rect(center = (cross_x, cross_y)))
def movement(self):
key = pygame.key.get_pressed()
self.rect = self.image.get_rect(center = (self.x, self.y))
dist = 3 # distance moved in 1 frame, try changing it to 5
if key[pygame.K_DOWN] or key[pygame.K_s]: # down key
self.y += dist # move down
elif key[pygame.K_UP] or key[pygame.K_w]: # up key
self.y -= dist # move up
if key[pygame.K_RIGHT] or key[pygame.K_d]: # right key
self.x += dist # move right
elif key[pygame.K_LEFT] or key[pygame.K_a]: # left key
self.x -= dist # move left
def rotate(self, surface):
keys = pygame.key.get_pressed()
if keys[K_LSHIFT]:
self.rotate_vel += 5
self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel)
self.rect = self.image.get_rect(center=self.rect.center)
surface.blit(self.image, self.rect)
if keys[K_SPACE]:
self.rotate_vel += -5
self.image = pygame.transform.rotate(self.orig_image, self.rotate_vel)
self.rect = self.image.get_rect(center=self.rect.center)
surface.blit(self.image, self.rect)
def update(self):
self.movement()
self.draw(screen)
self.rotate(screen)
class Bullet(pygame.sprite.Sprite):
def __init__(self, pos, angle):
super().__init__()
self.image = pygame.image.load('graphics/weapons/bullets/default_bullet.png')
self.image = pygame.transform.rotate(self.image, angle)
self.rect = self.image.get_rect(center = pos)
self.speed = 25
self.pos = pos
self.dir_vec = pygame.math.Vector2()
self.dir_vec.from_polar((self.speed, -angle))
def update(self, screen):
self.pos += self.dir_vec
self.rect.center = round(self.pos.x), round(self.pos.y)
class Zombie(pygame.sprite.Sprite):
def __init__(self, x , y):
super().__init__()
self.x = x
self.y = y
self.image = pygame.image.load('graphics/zombie/zoimbie1_hold.png').convert_alpha()
self.rect = self.image.get_rect(center = (x,y))
self.orig_image = pygame.image.load('graphics/Robot 1/robot1_gun.png').convert_alpha()
def draw(self, surface):
surface.blit(self.image, self.rect)
def update(self):
self.draw(screen)
#screen
clock = pygame.time.Clock()
FPS = 60
screen = pygame.display.set_mode((1200, 600))
#player
player_sprite = Player(600, 300)
player = pygame.sprite.GroupSingle()
player.add(player_sprite)
#bullet
bullet_group = pygame.sprite.Group()
#Zombie
zombie_sprite = Zombie(600, 300)
zombie = pygame.sprite.Group()
zombie.add(zombie_sprite)
#keys
keys = pygame.key.get_pressed()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
pos = player_sprite.rect.center
new_bullet = Bullet(pos, player_sprite.rotate_vel)
bullet_group.add(new_bullet)
if pygame.sprite.spritecollide(zombie_sprite,bullet_group , True):
Zombie(1,1).kill()
bullet_group.update(screen)
#screen
screen.fill('grey')
#player sprite funtions
player.update()
#buller group draw
bullet_group.draw(screen)
bullet_group.update(screen)
#Zombie update
zombie.update()
clock.tick(FPS)
pygame.display.update()
Try zombie_sprite.kill() instead of Zombie(1,1).kill()
You are instantiating a new object instead of destroying the original one.
I am making a spaceship game where you control a spaceship and fire bullets to destroy enemy spaceships. When you click the try again button when you lose, you should be able to both replay and close the game. However, even though I was able to play multiply times, I wasn't able to close the window. I think this has something to do with the if statement that checks if the try again button is clicked. Or maybe it has something to do with something else. How can I fix it so I can close the window at all times?
This is my current code:
import pygame
from pygame.locals import *
from random import randint, choice
from tools import *
pygame.init()
pygame.font.init()
SCREEN_X = 800
SCREEN_Y = 500
CENTER_POS = (400, 225)
screen = pygame.display.set_mode((SCREEN_X, SCREEN_Y))
class Spaceship(pygame.sprite.Sprite):
"""A spaceship object. Used for the player."""
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("spaceship.png")
self.image = pygame.transform.scale(self.image, (175, 175))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
"""Updates the spaceship rect"""
self.rect.center = (self.x, self.y)
class Bullet(pygame.sprite.Sprite):
"""A bullet object. Appears when the player clicks."""
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("bullet.png")
self.image = pygame.transform.scale(self.image, (100, 100))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
"""Let's the bullet move upward."""
self.y -= 5
self.rect.center = (self.x, self.y)
if self.y < 0:
self.kill()
class Enemy(pygame.sprite.Sprite):
"""An enemy object. The player's job is to destroy enemies."""
def __init__(self, s, x, y, t):
pygame.sprite.Sprite.__init__(self)
self.type = t
self.screen, self.x, self.y = s, x, y
self.image = pygame.image.load(get_enemy_image()[self.type])
# There is an if statement because the
# N1 Galaxy Fighter and M7 Comet Glider need different sizes
if self.type == "N1 Galaxy Fighter":
self.image = pygame.transform.scale(self.image, (235, 215))
elif self.type == "M7 Comet Glider":
self.image = pygame.transform.scale(self.image, (155, 215))
self.rect = self.image.get_rect()
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
self.score_given = get_enemy_given_score()[self.type]
def update(self):
if self.y < 0:
self.kill()
self.y += 3
self.rect.center = (self.x, self.y)
class GameOverBackground(pygame.sprite.Sprite):
"""The game over background object."""
def __init__(self, s, x, y, size=(100, 100)):
pygame.sprite.Sprite.__init__(self)
self.screen, self.x, self.y = s, x, y
self.size = size
self.image = pygame.image.load("Game_Over.jpg")
self.image = pygame.transform.scale(self.image, self.size)
self.rect = self.image.get_rect()
def blitme(self):
"""Blits the game over image on the screen"""
self.screen.blit(self.image, self.rect)
class Coin(pygame.sprite.Sprite):
"""A coin object."""
def __init__(self, pos=(0, 0), size=(100, 100)):
pygame.sprite.Sprite.__init__(self)
self.x, self.y = pos[0], pos[1]
self.size = size
self.image = pygame.image.load("coin.png")
self.image = pygame.transform.scale(self.image, self.size)
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
"""Updates the coin rect"""
self.rect.center = (self.x, self.y)
class StartButton(pygame.sprite.Sprite):
def __init__(self, s, x, y, size=None):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x = x
self.y = y
self.image = pygame.image.load("start_button.png")
if size is not None:
self.image = pygame.transform.scale(self.image, size)
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def blitme(self):
self.screen.blit(self.image, self.rect)
class TryAgainButton(pygame.sprite.Sprite):
def __init__(self, s, x, y, size=None):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x = x
self.y = y
self.image = pygame.image.load("try_again.png")
if size is not None:
self.image = pygame.transform.scale(self.image, size)
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def blitme(self):
self.screen.blit(self.image, self.rect)
def main():
bg = GameOverBackground(screen, 0, 0, size=(800, 500))
spaceship = Spaceship(screen, 400, 400)
start_button = StartButton(screen, CENTER_POS[0], CENTER_POS[1], size=(300, 195))
button_rect = start_button.image.get_rect(topleft=(start_button.x, start_button.y))
game_started = False
try_again_button = TryAgainButton(screen, CENTER_POS[0], CENTER_POS[1]+215, size=(300, 195))
button_rect_2 = try_again_button.image.get_rect(topleft=(try_again_button.x, try_again_button.y))
try_again = False
bullets = pygame.sprite.Group()
enemies = pygame.sprite.Group()
coins = pygame.sprite.Group()
clock = pygame.time.Clock()
enemy_interval = 2000
enemy_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_event, enemy_interval)
coin_interval = 3500
coin_event = pygame.USEREVENT + 1
pygame.time.set_timer(coin_event, coin_interval)
score = 0
lives = 3
with open("high_score.txt", "r") as file:
highscore = file.read()
font = pygame.font.SysFont("Arial", 30)
score_text_surface = font.render("Score: {:,}".format(score), True, (0, 0, 0))
lives_text_surface = font.render("HP: %s" % lives, True, (0, 0, 0))
high_score_text_surface = font.render("High Score: %s" % highscore, True, (0, 0, 0))
spaceship_collided = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == MOUSEBUTTONDOWN:
if not game_started:
if button_rect.collidepoint(event.pos):
game_started = True
if game_started is True and button_rect_2.collidepoint(event.pos):
try_again = True
if game_started and event.type == MOUSEBUTTONDOWN:
bullet = Bullet(screen, spaceship.x, spaceship.y - 20)
bullets.add(bullet)
if game_started and event.type == enemy_event and not lives <= 0:
enemy = Enemy(screen, randint(-100, 725), 0, choice(["N1 Galaxy Fighter", "M7 Comet Glider"]))
enemies.add(enemy)
if game_started and event.type == coin_event and not lives <= 0:
if len(coins) < 100:
coins.add(Coin((randint(-125, 750), randint(-200, 400))))
screen.fill((255, 255, 255)) # DO NOT DRAW ANYTHING IN FRONT OF THIS LINE, I'M WARNING YOU
if not game_started:
start_button.blitme()
if try_again:
main()
if game_started:
bullets.update()
key = pygame.key.get_pressed()
amount = 5
if key[pygame.K_a]:
spaceship.x -= amount
elif key[pygame.K_d]:
spaceship.x += amount
elif key[pygame.K_w]:
spaceship.y -= amount
elif key[pygame.K_s]:
spaceship.y += amount
spaceship.update()
if not lives <= 0:
screen.blit(spaceship.image, spaceship.rect)
if not lives <= 0:
bullets.draw(screen)
enemies.draw(screen)
coins.update()
coins.draw(screen)
for i in enemies:
i.update()
if pygame.sprite.spritecollide(i, bullets, True):
score += i.score_given
i.kill()
if spaceship_collided and lives <= 0:
bg.blitme()
if score > int(highscore):
with open("high_score.txt", "w") as file:
file.write(str(score))
if score >= 99999:
score = 99999
if not lives <= 0:
score_text_surface = font.render("Score: {:,}".format(score), True, (0, 0, 0))
screen.blit(score_text_surface, (590, 0))
lives_text_surface = font.render("HP: %s" % lives, True, (0, 0, 0))
screen.blit(lives_text_surface, (260, 0))
high_score_text_surface = font.render("High Score: %s" % highscore, True, (0, 0, 0))
screen.blit(high_score_text_surface, (360, 0))
if pygame.sprite.spritecollide(spaceship, enemies, dokill=True):
lives -= 1
spaceship_collided = True
if pygame.sprite.spritecollide(spaceship, coins, dokill=True):
score += 10
if lives <= 0:
try_again_button.blitme()
pygame.display.update()
clock.tick(60)
if __name__ == "__main__":
main()
You can't do it like that. Never run the main application loop recursively. The issue is
if try_again:
main()
What you are actually doing is starting a new game within the game in progress. If you want to close the game, you will need to close all games that you have started before. Hence, you have to press close several times.
Instead of calling main recursively, you either have to reset all game states or call main in a loop.
In your particular situation, the later solution is easier to implement. Remove the recursive call but set running = False, return try_again from main and call main while it returns True:
def main():
# [...]
try_again = False
running = True
while running:
# [...]
if try_again:
# main() <---- DELETE
running = False
# [...]
return try_again
if __name__ == "__main__":
keep_running = True
while keep_running:
keep_running = main()