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()
\$\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.

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.