5
\$\begingroup\$

This is an ongoing development of a game. The player is controlled by mouse click to move upward. The goal is to avoid meteors for as long as possible. I would be grateful if someone could critique my code.

Version 1 can be found here. Since then I have:

  • Added intro menu screen
  • Added game loop to contain main game logic
  • Added health to game, game over screen when health == 0
  • Removed unnecessary and redundant comments
  • Implemented recommendations found in original post

Still working on:

  • Adding rotation to meteors
  • Display health with health bar
  • Buttons in intro menu
# All images found @ http://kenney.nl/assets/space-shooter-extension, http://kenney.nl/assets/space-shooter-redux, and https://www.goldstar.com/events/los-angeles-ca/the-sirens-of-titan-tickets
import pygame
import random
pygame.init()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
def game_intro():
 intro = False
 intro_background = pygame.image.load("assets/title_screen.png")
 # Get dimensions of background
 width = background.get_width()
 height = background.get_height() 
 HW, HH = width/2, height/2
 screen = pygame.display.set_mode((width,height))
 intro_background = intro_background.convert()
 
 while not intro: 
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
 done = True 
 if event.type == pygame.MOUSEBUTTONDOWN:
 game_loop(background)
 screen.blit(intro_background, (0,0)) 
 message_display("Click to take Constant home")
 pygame.display.update()
 clock.tick(15)
 
 pygame.quit()
def game_loop(background):
 done = False 
 x = 0 
 health = 5 
 while not done:
 dt = clock.tick(30)
 # Main event Loop 
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
 done = True
 if event.type == pygame.MOUSEBUTTONDOWN:
 player.ignite()
 
 #-----Game Logic 
 # Draw background and move to the left
 rel_x = x % width
 screen.blit(background, (rel_x - width, 0))
 if rel_x < width:
 screen.blit(background, (rel_x, 0))
 x -= 2
 
 # Check to see if player has collided with meteor
 meteor_hit_list = pygame.sprite.spritecollide(player, meteor_group, 
 True)
 # Event if player collides with meteor 
 for item in meteor_hit_list:
 health -= 1
 print("Shuttle hit", health)
 if health == 0:
 game_over()
 create_meteor() 
 
 distance(screen)
 all_sprites_group.draw(screen)
 meteor_group.update()
 player.update(dt/1000)
 
 pygame.display.flip()
 
 pygame.quit()
def game_over():
 exit = False 
 screen = pygame.display.set_mode((850,526))
 
 while not exit: 
 for event in pygame.event.get():
 if event.type == pygame.QUIT:
 pygame.quit()
 quit()
 if event.type == pygame.MOUSEBUTTONDOWN:
 game_intro()
 screen.fill(BLACK)
 message_display("GAME OVER! Click to restart")
 pygame.display.update()
 
 pygame.display.flip()
 clock.tick(30)
 
 pygame.quit()
def create_meteor():
 meteor = Meteor(width, height)
 meteor_group.add(meteor)
 all_sprites_group.add(meteor)
def message_display(text):
 font = pygame.font.SysFont("transistor", 25, True, False)
 travel_text = font.render(text, True, WHITE)
 screen.blit(travel_text, (HW-150, HH))
def distance(screen):
 """Show how far the rocketship has travelled."""
 # Get time since init was called
 time = pygame.time.get_ticks()
 # Convert milliseconds to seconds, 1 second = 1 km
 travel_distance = round(time/1000, 2)
 message_display("You have travelled " + str(travel_distance) + " lightyears")
class Player(pygame.sprite.Sprite):
 def __init__(self,PLAYER_SURFACE, HW, HH):
 super().__init__()
 self.image = PLAYER_SURFACE 
 self.rect = pygame.rect.Rect(((HW - (PLAYER_SURFACE.get_width())), HH), 
 self.image.get_size())
 # Gravity
 self.dy = 0 
 def ignite(self):
 self.dy = -400
 def update(self, dt):
 # Apply gravity
 self.dy = min(400, self.dy + 40)
 self.rect.y += self.dy * dt
 
 # What happens if go to border of screen
 if self.rect.top <= 0: # Top
 self.rect.y = 0
 self.dy = -4
 elif self.rect.bottom >= height: # Bottom
 self.rect.y = 526-self.rect.height
class Meteor(pygame.sprite.Sprite):
 def __init__(self, width, height):
 super().__init__()
 self.image = random.choice(METEOR_IMAGES) 
 self.rect = self.image.get_rect()
 # Random starting location
 self.rect.x = random.randrange(width, (width + 300))
 self.rect.y = random.randrange(0, height)
 
 # Random movement to the left
 self.change_x = random.randrange(-10,-5)
 self.change_y = random.randrange(-4,3)
 def reset_pos(self, screen):
 self.image = random.choice(METEOR_IMAGES)
 self.rect = self.image.get_rect()
 self.rect.x = random.randrange(width, (width + 100))
 self.rect.y = random.randrange(0, height)
 # Random movement to the left
 self.change_x = random.randrange(-10,-5)
 self.change_y = random.randrange(-4,3)
 
 def update(self): 
 # Move meteor
 self.rect.x += self.change_x
 self.rect.y += self.change_y
 
 # Reset if falls off screen
 if self.rect.right < 0:
 self.reset_pos(screen)
 if self.rect.top > height:
 self.reset_pos(screen)
 if self.rect.bottom < 0:
 self.reset_pos(screen)
clock = pygame.time.Clock()
background = pygame.image.load("assets/background.png")
# Get dimensions of background
width = background.get_width()
height = background.get_height() 
HW, HH = width/2, height/2
screen = pygame.display.set_mode((width,height))
background = background.convert()
PLAYER_SURFACE = pygame.image.load("assets/player.png").convert_alpha()
METEOR_IMAGES = []
METEOR_LIST = [
 "assets/meteors/meteor1.png"...
]
for image in METEOR_LIST:
 METEOR_IMAGES.append(pygame.image.load(image).convert_alpha())
pygame.display.set_caption("Return from Titan")
all_sprites_group = pygame.sprite.Group()
meteor_group = pygame.sprite.Group()
# Create spaceship 
player = Player(PLAYER_SURFACE, HW, HH)
all_sprites_group.add(player)
# Create meteor sprites on the screen 
for i in range(4):
 create_meteor()
game_intro()
toolic
14.5k5 gold badges29 silver badges203 bronze badges
asked Jul 6, 2018 at 14:50
\$\endgroup\$
1
  • \$\begingroup\$ Looks pretty good. For the scenes I'd use a finite state machine similar to this or if you want to keep your scene functions, take a look at this example. \$\endgroup\$ Commented Jul 7, 2018 at 17:45

1 Answer 1

1
\$\begingroup\$

Documentation

The PEP 8 style guide recommends adding docstrings for classes and functions.

Layout

Move the class to the top after the import lines. Move the other functions after the classes. Then move all executable code after the functions. Having them in the middle of the code interrupts the natural flow of the code (from a human readability standpoint).

Unused

The variable i is unused:

for i in range(4):

Typically, the underscore placeholder is used:

for _ in range(4):

Similarly for:

for item in meteor_hit_list:

In the game_intro function, the done variable is set, but it is never used. It can be deleted:

done = True 

The intro variable can be eliminated:

intro = False

Change:

while not intro: 

to:

while True: 

Naming

The variable named exit is the same as a builtin function. I suggest renaming the variable as something like exit_game.

answered Apr 18 at 10:12
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.