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()
-
\$\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\$skrx– skrx2018年07月07日 17:45:48 +00:00Commented Jul 7, 2018 at 17:45
1 Answer 1
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
.