6
\$\begingroup\$

Simple game where you control space shuttle with mouse click and try to avoid meteors moving in random speed toward you. There are some things I am still developing like rotation and menus, but wanted to get some tips/recommendations on what I have so far.

Thank you in advance!

# Import pygame and random
import pygame
import random
# Initialize the game engine 
pygame.init()
#Define colors
WHITE = (255, 255, 255)
# Define done 
done = False
# Define function to create new meteor
def create_meteor():
 meteor = Meteor(WHITE, width, height)
 meteor_sprites_list.add(meteor)
# Define player class
class Player(pygame.sprite.Sprite):
 def __init__(self, filename, color, HW, HH):
 super().__init__()
 # Set height, width
 self.image = pygame.image.load("player.png").convert_alpha()
 # Set background color to transparent
 self.image.set_colorkey(color)
 # Make top-left corner the passed in locatin 
 self.rect = pygame.rect.Rect((HW, HH), self.image.get_size())
 # Gravity
 self.dy = 0 
 def ignite(self):
 self.dy = -400
 def update(self, dt, screen):
 # 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): #ground
 self.rect.y = (526-self.rect.height)
 #blit image to screen
 screen.blit(self.image, self.rect)
# Define new clas for meteor
class Meteor(pygame.sprite.Sprite):
 def __init__(self, color, width, height):
 # Takes in parameters for color, width (x position) , and height (y postion)
 # Call the parent class 
 super().__init__()
 # Make list of image file location
 self.meteor_list = ["meteors/meteor1.png"]
 # Randomly select meteor from above list
 self.new_meteor = random.choice(self.meteor_list)
 # Load graphic that is in file folder 
 self.image = pygame.image.load(self.new_meteor).convert_alpha()
 # Set background to transparent
 self.image.set_colorkey(color)
 # Fetch the rectangle object that has the dimensions of the image
 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)
 # Set angle for rotate attribute
 # ---- Attributes 
 def reset_pos(self, screen):
 # List of meteors
 self.meteor_list = ["meteors/meteor1.png"]
 # Pick random meteor from above list
 self.new_meteor = random.choice(self.meteor_list)
 # Load graphic that is in file folder 
 self.image = pygame.image.load(self.new_meteor).convert_alpha()
 # Set background to transparent
 self.image.set_colorkey(WHITE) 
 # Fetch the rectangle object that has the dimensions of the image
 self.rect = self.image.get_rect()
 # Reset postion of bad block when they reach the bottom of the screen 
 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)
 # What meteor does each cycle through
 def update(self): 
 # Add rotation 
 #pygame.transform.rotate(self.image, 115)
 # Move bad block down 3 at a time 
 self.rect.x += self.change_x
 self.rect.y += self.change_y
 #blit image to screen
 screen.blit(self.image, self.rect)
 # 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)
# Class to show the how far rocketship has travelled 
class Distance(pygame.sprite.Sprite):
 def __init__(self):
 # Call the parent class 
 super().__init__()
 # 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)
 # Format the font to be used 
 font = pygame.font.SysFont ("transistor", 25, True, False)
 # Create text to be displayed
 text = font.render("You've travelled " + str(travel_distance) + " kms", True, WHITE)
 # Center text 
 text_rect = text.get_rect(center=(HW, HH))
 # Blit text to the screen
 screen.blit(text, text_rect)
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
background_size = pygame.image.load("background.png")
# Get dimensions of background
width = background_size.get_width()
height = background_size.get_height() 
HW, HH = width/2, height/2
size = (width, height)
screen = pygame.display.set_mode(size)
x = 0 
# Load image for star background 
background = pygame.image.load("background.png").convert()
# Seperate becuase error when placed before screen
#Set caption
pygame.display.set_caption("")
# Creates a list of sprites. Each object in program is added to list. Managed by a class called "group"
meteor_sprites_list = pygame.sprite.Group()
# Create spaceship 
player = Player("player.png", WHITE, HW, HH)
# Create meteor sprites on the screen 
for i in range(4):
 create_meteor()
#-----Main Program Loop 
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
 # Display text on screen
 Distance()
 # Update Sprites 
 # Update meteor sprite
 meteor_sprites_list.update()
 # Update player sprite
 player.update(dt/1000. , screen)
 # Check to see if player has collided with meteor
 meteor_hit_list = pygame.sprite.spritecollide(player, meteor_sprites_list, True, pygame.sprite.collide_circle)
 # Event if player collides with meteor 
 for item in meteor_hit_list:
 print("Shuttle hit")
 create_meteor()
 meteor_hit_list.remove(item)
 # Make into game over screen and display distance
 # Update the screen with what we've drawn.
 pygame.display.flip()
# Make sure to quit 
pygame.quit()
asked Jul 1, 2018 at 15:27
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Good job! Here are my suggestions (roughly ordered from important to less important):

Separate the game logic from the drawing/blitting code. That will make your main loop cleaner and the code will probably be easier to extend. So don't blit the sprite images in their update methods.

Sprites in a sprite group can be blitted by calling the draw method of the group: meteor_group.draw(screen). I've renamed the meteor_sprites_list to meteor_group, because pygame groups are not lists.

If you want to keep the player sprite separate, just blit it in the while loop: screen.blit(player.image, player.rect).


Load the images globally or in a separate module, because it's inefficient to load them repeatedly from the hard disk in the main loop. The self.meteor_list can also be a global list:

PLAYER_SURFACE = pygame.image.load("player.png").convert_alpha()
METEOR_LIST = [
 pygame.image.load("meteors/meteor1.png").convert_alpha(),
 pygame.image.load("meteors/meteor2.png").convert_alpha(),
 # etc.
 ]
# If you have a lot of images, better load them with a for loop and append them.

Then choose and assign a random image in the __init__ method:

self.image = random.choice(METEOR_LIST)

I'm not sure if your images already have transparent backgrounds, if yes, you can just call convert_alpha() and omit this line:

self.image.set_colorkey(color)

You're loading the background image twice. Just replace background_size with background. And don't forget to convert the surfaces/images, because unconverted surfaces can slow the game down a lot: background = pygame.image.load("background.png").convert().


The Distance class can just be a function. Also, if you pass the coords as the topleft argument it doesn't jitter so much: text.get_rect(topleft=(HW-100, HH)). Or you could just blit the text at these coordinates: screen.blit(text, (HW-100, 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)
 text = font.render("You've travelled " + str(travel_distance) + " kms", True, WHITE)
 screen.blit(text, (HW-100, HH))

The while loop and the corresponding variables could be put into a main function or a class to clean up the global namespace.


You've got a lot of comments in your example which make the code a bit more difficult to read. Add comments only if something could be unclear (explain why the code is needed). Some comments are even wrong or outdated.

This one is unnecessary, since the code is self-explanatory:

# Set caption
pygame.display.set_caption("")

This one is more useful:

# Reset if falls off screen
if self.rect.right < 0:
 self.reset_pos(screen)

You don't need to remove the hit meteor from the meteor_hit_list because you'll create a new list in the next frame anyway:

meteor_hit_list.remove(item) # Just remove this line.

To stop the player at the bottom of the screen:

elif self.rect.bottom >= height:
 self.rect.bottom = screen.get_height()

There are some unnecessary parentheses:

if(self.rect.top <= 0):
self.rect.y = (526-self.rect.height)

Take a look at PEP 8.

And happy coding!

answered Jul 1, 2018 at 17:42
\$\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.