Mars Lander pygame
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
4
down vote
favorite
Here is my code about our final project in uni called 'Mars Lander'. The thing is that my code seems to be all over the place (more specifically in the 'main' function at the end of the code...) therefore I would like to get some help with putting things in place! It's my first time making a game using the 'pygame' library and this one was definitely worth the effort!
import pygame
import sys
from random import uniform, randint # used for the random starting velocity of the lander, random clock time etc.
from time import clock # to show the time elapsed
import math # used to calculate the magnitude of the gravity force applied on the lander
WIDTH = 1200 # width of the game window
HEIGHT = 750 # height of the game window
FPS = 20 # frames per second
pause = False # variable which is used to determine whether the game is paused or not
# Initialise pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock_game = pygame.time.Clock()
pygame.font.init() # you have to call this at the start if you want to use this module.
myfont = pygame.font.SysFont('Comic Sans MS', 15)
alert_large = pygame.font.SysFont("Comic Sans MS", 18)
large_text = pygame.font.SysFont("Comic Sans MS", 50)
class Background(pygame.sprite.Sprite): # class for the background image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
# obstacles landing pad meteors classes
class Lander(pygame.sprite.Sprite): # class for the lander image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.rot_image = self.image
self.angle = 0
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.altitude = 0
self.damage = 0
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
def reset_stats(self):
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def check_boundaries(self):
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom > HEIGHT:
self.reset_stats()
self.rect.left = randint(0, 1123)
return True
else:
return False
def get_fuel(self):
return self.fuel
def burn_fuel(self): # decreases the fuel when 'space' key is pressed
self.fuel -= 5
def start_engine(self):
self.burn_fuel()
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
def rotate_left(self):
self.angle += 1 % 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def rotate_right(self):
self.angle -= 1 % 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def to_ground(self):
self.altitude = 1000 - self.rect.top*1.436
return self.altitude
def get_damage(self):
return self.damage
def check_landing(self, pad):
check_velocity_y = [True if self.veloc_y < 5 else False]
check_velocity_x = [True if -5 < self.veloc_x < 5 else False]
check_angle = [True if -7 <= self.angle <= 7 else False]
check_above_pad = [True if (self.rect.left > pad.rect.left and self.rect.right < pad.rect.right) else False]
check_touch = [True if (self.rect.bottom == pad.rect.top) else False]
if check_above_pad[0] and check_angle[0] and check_velocity_x[0] and check_velocity_y[0] and check_touch[0]:
return True
else:
return False
class EngineThrust(pygame.sprite.Sprite): # class for the thrust image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rot_image = self.image
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.thrst_angle = lndr.angle
def rotate_thrust(self):
self.rot_image = pygame.transform.rotate(self.image, self.thrst_angle)
class LandingPad(pygame.sprite.Sprite): # class for the landing pad image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
class GameScore: # class for the score of the game
def __init__(self):
self.score = 0
def successful_land(self):
self.score += 50
def get_score(self): # Returns game score
return self.score
class Lives: # class for the lives of the player
def __init__(self, lives):
self.lives = lives # Holds lives left
def crashed(self): # Decrement lives by 1
self.lives -= 1
def get_lives(self): # Return lives number
return self.lives
def game_over(self): # Check if there are no lives left
return self.lives == 0
class SysFailure: # class for the lander system errors
def __init__(self):
self.random_alert = 0 # Will carry alert time
self.random_key = 0 # Will holds key value
def get_alert(self): # Set a new alert time and return it
self.random_alert = randint(int(clock()+5), int(clock() + 15))
return self.random_alert
def get_key(self): # Randomize and return key value
self.random_key = randint(1, 3)
return self.random_key
class Obstacle(pygame.sprite.Sprite): # class for the obstacle images
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.destroyed = False
def get_status(self): # Return the status of the obstacle
return self.destroyed
def obstacle_collision(self, lander): # Increment lander damage by 10 % if the meteor collides with the lander
if lander.rect.colliderect(self.rect):
lander.damage += 10
return True
else:
return False
class Meteor(pygame.sprite.Sprite): # Class for the meteor images
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # Call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.bottom = location # The location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
self.destroyed = False
def storm_fall(self): # Set y and x-axis speed of the meteor
self.rect.x += self.speed_x
self.rect.y += self.speed_y
def meteor_collision(self, lander): # Increment lander damage by 25 % if the meteor collides with the lander
if lander.rect.colliderect(self.rect):
lander.damage += 25
return True
else:
return False
def get_status(self): # Return the status of the meteor
return self.destroyed
def reset_stats(self): # Set the bottom of the sprite to its initial value
self.rect.bottom = 0
class Storm: # Class for the meteor storms
def __init__(self):
self.random_storm = 0 # Holds random storm time
def storm_time(self): # Randomize and return storm time
self.random_storm = randint(int(clock()+3), int(clock() + 12))
return self.random_storm
def resume(): # Resume the game
global pause
pause = False
def paused(): # Pause the game
global game_status
crash_msg = large_text.render('You Have Crashed!', False, (255, 0, 0))
screen.blit(crash_msg, (420, 300)) # Display crash message in the middle of the screen
while pause:
for event in pygame.event.get():
if event.type == pygame.QUIT: # Quit the game if the 'X' button is clicked
sys.exit()
if event.type == pygame.KEYDOWN: # Wait for a key to be pressed and if so resumes the game
resume()
pygame.display.update()
clock_game.tick(FPS)
obstacles = pygame.sprite.Group() # Create obstacle sprite group
meteors = pygame.sprite.Group() # Create meteor sprite group
bckgd = Background('mars_background_instr.png', [0, 0])
lndr = Lander('lander.png', [randint(0, 1123), 0])
pad_1 = LandingPad('pad.png', [randint(858, 1042), 732])
pad_2 = LandingPad('pad_tall.png', [randint(458, 700), 620])
pad_3 = LandingPad('pad.png', [randint(0, 300), 650])
"""
Create 5 obstacles each being placed on a fixed location
on the background image!
"""
obstacle_1 = Obstacle('pipe_ramp_NE.png', [90, 540])
obstacle_2 = Obstacle('building_dome.png', [420, 575])
obstacle_3 = Obstacle('satellite_SW.png', [1150, 435])
obstacle_4 = Obstacle('rocks_ore_SW.png', [1080, 620])
obstacle_5 = Obstacle('building_station_SW.png', [850, 640])
# Add to the sprite group 'obstacles'
obstacles.add(obstacle_1, obstacle_2, obstacle_3, obstacle_4, obstacle_5)
"""
Create 10 meteors using the Meteor class which are placed
at random x-axis locations starting with the bottom of the image rectangle
lying at 0 on the y-axis!
"""
meteor1 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor2 = Meteor('spaceMeteors_2.png', [randint(300, 900), 0])
meteor3 = Meteor('spaceMeteors_3.png', [randint(300, 900), 0])
meteor4 = Meteor('spaceMeteors_4.png', [randint(300, 900), 0])
meteor5 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor6 = Meteor('spaceMeteors_2.png', [randint(300, 900), 0])
meteor7 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor8 = Meteor('spaceMeteors_4.png', [randint(300, 900), 0])
meteor9 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor10 = Meteor('spaceMeteors_3.png', [randint(300, 900), 0])
# Add to the sprite group assigned to the 'meteors' variable
meteors.add(meteor1, meteor2, meteor3, meteor4, meteor5, meteor6, meteor7, meteor8, meteor9, meteor10)
storm = Storm() # Storm variable
lives_left = Lives(3) # Each time the player starts with 3 lives
lander_score = GameScore() # Holds the score of the game
alert_signal = SysFailure() # Holds the lander system failure causes
game_status = True # Holds the status of the game
def main(): # The main function which runs the game
global game_status, pause # change name
random_signal = alert_signal.get_alert() # Holds the randomized alert signal time
random_key = alert_signal.get_key() # Carries the randomized key used to decide which control failure will occur
# during the alert signal
random_storm = storm.storm_time() # Random meteor storm time
meteor_storm = False # Set to True whenever a storm should occur
meteor_shower = False # Set to True whenever a storm should occur
print(random_storm)
meteor_number = randint(1, 10) # Determines the number of meteors the storm will contain
print(meteor_number)
while game_status: # main game loop
clock_game.tick(FPS)
screen.fill([255, 255, 255]) # Fill the empty spaces with white color
screen.blit(bckgd.image, bckgd.rect) # Place the background image
screen.blit(pad_1.image, pad_1.rect) # Put the first landing pad on the background
screen.blit(pad_2.image, pad_2.rect) # Put the second landing pad on the background
screen.blit(pad_3.image, pad_3.rect) # Put the last landing pad on the background
for obstacle in obstacles: # draw every one of the obstacles
# if a collision occurs the obstacle gets destroyed and it is no longer shown
if not obstacle.get_status():
screen.blit(obstacle.image, obstacle.rect)
if obstacle.obstacle_collision(lndr):
obstacle.destroyed = True
# Waits for an event
for event in pygame.event.get(): # If the user clicks the 'X' button on the window it quits the program
if event.type == pygame.QUIT:
sys.exit()
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm: # As soon as the clock passes the random storm time it causes meteor rain
if clock() > random_storm:
meteor_storm = True
meteor_shower = True
if meteor_shower:
delay = 0 # Each meteor is drawn with 1 second delay
count = 0 # Counts the meteors number
for meteor in meteors:
if count < meteor_number:
delay += 1
if clock() > random_storm + delay:
if not meteor.get_status(): # Draw every one of the meteors
# if a collision occurs the meteor gets destroyed and it is no longer shown
meteor.storm_fall() # Give x-axis and y-axis velocity to the meteors
screen.blit(meteor.image, meteor.rect)
if meteor.meteor_collision(lndr):
meteor.destroyed = True
count += 1
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
game_status = False
elif lives_left.game_over(): # Terminate program if the player has no lives left
game_status = False
elif lndr.get_fuel() <= 0: # Remove lander controls if it is out of fuel
screen.blit(lndr.rot_image, lndr.rect)
else:
if not random_signal < clock() < random_signal+2: # While the clock is not in the 2 sec alert time
if lndr.get_damage() < 100: # While the lander hasn't sustained 100% damage
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left+31, lndr.rect.bottom-10]) # Create thrust
# sprite
lndr.start_engine() # Call 'start engine' function (Lander)
thrst.rotate_thrust() # Call 'rotate_engine' which rotates the thrust along with the lander
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'left' is pressed
lndr.rotate_right()
if lndr.check_landing(pad_1) or lndr.check_landing(pad_2) or lndr.check_landing(pad_3): # Call
# 'check_landing' method on each pad sprite which checks whether the lander has landed on
# the landing pad
lander_score.successful_land() # Increment score with 50 pts
for meteor in meteors:
meteor.destroyed = False
meteor.reset_stats()
random_signal = alert_signal.get_alert()
random_key = alert_signal.get_key()
random_storm = storm.storm_time()
meteor_number = randint(1, 10)
print(random_storm)
print(meteor_number)
meteor_shower = False
meteor_storm = False
for obstacle in obstacles:
obstacle.destroyed = False
lndr.reset_stats()
else:
lndr.damage = 100 # Stop lander damage at 100 %
else:
alert_msg = alert_large.render('*ALERT*', False, (0, 0, 255))
screen.blit(alert_msg, (190, 80)) # Display alert message
if random_key == 1:
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left + 31, lndr.rect.bottom - 10])
lndr.start_engine()
thrst.rotate_thrust()
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
elif random_key == 2:
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'right' is pressed
lndr.rotate_right()
else:
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left + 31, lndr.rect.bottom - 10])
lndr.start_engine()
thrst.rotate_thrust()
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'right' is pressed
lndr.rotate_right()
screen.blit(lndr.rot_image, lndr.rect)
time_passed = myfont.render(':.1f s'.format(clock()), False, (255, 0, 0))
screen.blit(time_passed, (72, 10)) # Display clock in seconds
velocity_y = myfont.render(':.1f m/s'.format(lndr.veloc_y), False, (255, 0, 0))
screen.blit(velocity_y, (280, 56)) # Display y-axis velocity (downward, meters per second)
velocity_x = myfont.render(':.1f m/s'.format(lndr.veloc_x), False, (255, 0, 0))
screen.blit(velocity_x, (280, 33)) # Display x-axis velocity (sideways, meters per second)
fuel_remaining = myfont.render(':d kg'.format(lndr.fuel), False, (255, 0, 0))
screen.blit(fuel_remaining, (72, 33)) # Display remaining fuel in kg
altitude = myfont.render(':.0f m'.format(lndr.to_ground()), False, (255, 0, 0))
screen.blit(altitude, (280, 10)) # Display altitude in meters
lander_damage = myfont.render(' %'.format(lndr.get_damage()), False, (255, 0, 0))
screen.blit(lander_damage, (95, 56)) # Display damage suffered by the mars lander
game_score = myfont.render(':.0f pts'.format(lander_score.get_score()), False, (255, 0, 0))
screen.blit(game_score, (77, 82)) # Display altitude in meters
lndr.free_fall() # Call 'free_fall' method in class 'Lander'
if lndr.check_boundaries(): # Call 'check_boundaries' method located in 'Lander' class
for meteor in meteors:
meteor.destroyed = False
meteor.reset_stats()
random_signal = alert_signal.get_alert() # Get a new random time for the alert
random_key = alert_signal.get_key() # Get a new random key for the lander control failure
random_storm = storm.storm_time() # Get a new random time for the storm
meteor_number = randint(1, 10) # Get a new random number for the meteors
lives_left.crashed() # Reduce lives with 1
print(random_storm)
print(meteor_number)
meteor_shower = False
meteor_storm = False
for obstacle in obstacles: # Reset all obstacles and make them visible
obstacle.destroyed = False
pause = True # Set 'pause' to True so the game pauses when 'paused' method is called
paused() # Call 'paused'
pygame.display.update() # Refresh (update) display
pygame.quit() # quit game if game_status = False
main()
python game pygame
add a comment |Â
up vote
4
down vote
favorite
Here is my code about our final project in uni called 'Mars Lander'. The thing is that my code seems to be all over the place (more specifically in the 'main' function at the end of the code...) therefore I would like to get some help with putting things in place! It's my first time making a game using the 'pygame' library and this one was definitely worth the effort!
import pygame
import sys
from random import uniform, randint # used for the random starting velocity of the lander, random clock time etc.
from time import clock # to show the time elapsed
import math # used to calculate the magnitude of the gravity force applied on the lander
WIDTH = 1200 # width of the game window
HEIGHT = 750 # height of the game window
FPS = 20 # frames per second
pause = False # variable which is used to determine whether the game is paused or not
# Initialise pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock_game = pygame.time.Clock()
pygame.font.init() # you have to call this at the start if you want to use this module.
myfont = pygame.font.SysFont('Comic Sans MS', 15)
alert_large = pygame.font.SysFont("Comic Sans MS", 18)
large_text = pygame.font.SysFont("Comic Sans MS", 50)
class Background(pygame.sprite.Sprite): # class for the background image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
# obstacles landing pad meteors classes
class Lander(pygame.sprite.Sprite): # class for the lander image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.rot_image = self.image
self.angle = 0
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.altitude = 0
self.damage = 0
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
def reset_stats(self):
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def check_boundaries(self):
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom > HEIGHT:
self.reset_stats()
self.rect.left = randint(0, 1123)
return True
else:
return False
def get_fuel(self):
return self.fuel
def burn_fuel(self): # decreases the fuel when 'space' key is pressed
self.fuel -= 5
def start_engine(self):
self.burn_fuel()
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
def rotate_left(self):
self.angle += 1 % 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def rotate_right(self):
self.angle -= 1 % 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def to_ground(self):
self.altitude = 1000 - self.rect.top*1.436
return self.altitude
def get_damage(self):
return self.damage
def check_landing(self, pad):
check_velocity_y = [True if self.veloc_y < 5 else False]
check_velocity_x = [True if -5 < self.veloc_x < 5 else False]
check_angle = [True if -7 <= self.angle <= 7 else False]
check_above_pad = [True if (self.rect.left > pad.rect.left and self.rect.right < pad.rect.right) else False]
check_touch = [True if (self.rect.bottom == pad.rect.top) else False]
if check_above_pad[0] and check_angle[0] and check_velocity_x[0] and check_velocity_y[0] and check_touch[0]:
return True
else:
return False
class EngineThrust(pygame.sprite.Sprite): # class for the thrust image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rot_image = self.image
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.thrst_angle = lndr.angle
def rotate_thrust(self):
self.rot_image = pygame.transform.rotate(self.image, self.thrst_angle)
class LandingPad(pygame.sprite.Sprite): # class for the landing pad image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
class GameScore: # class for the score of the game
def __init__(self):
self.score = 0
def successful_land(self):
self.score += 50
def get_score(self): # Returns game score
return self.score
class Lives: # class for the lives of the player
def __init__(self, lives):
self.lives = lives # Holds lives left
def crashed(self): # Decrement lives by 1
self.lives -= 1
def get_lives(self): # Return lives number
return self.lives
def game_over(self): # Check if there are no lives left
return self.lives == 0
class SysFailure: # class for the lander system errors
def __init__(self):
self.random_alert = 0 # Will carry alert time
self.random_key = 0 # Will holds key value
def get_alert(self): # Set a new alert time and return it
self.random_alert = randint(int(clock()+5), int(clock() + 15))
return self.random_alert
def get_key(self): # Randomize and return key value
self.random_key = randint(1, 3)
return self.random_key
class Obstacle(pygame.sprite.Sprite): # class for the obstacle images
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.destroyed = False
def get_status(self): # Return the status of the obstacle
return self.destroyed
def obstacle_collision(self, lander): # Increment lander damage by 10 % if the meteor collides with the lander
if lander.rect.colliderect(self.rect):
lander.damage += 10
return True
else:
return False
class Meteor(pygame.sprite.Sprite): # Class for the meteor images
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # Call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.bottom = location # The location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
self.destroyed = False
def storm_fall(self): # Set y and x-axis speed of the meteor
self.rect.x += self.speed_x
self.rect.y += self.speed_y
def meteor_collision(self, lander): # Increment lander damage by 25 % if the meteor collides with the lander
if lander.rect.colliderect(self.rect):
lander.damage += 25
return True
else:
return False
def get_status(self): # Return the status of the meteor
return self.destroyed
def reset_stats(self): # Set the bottom of the sprite to its initial value
self.rect.bottom = 0
class Storm: # Class for the meteor storms
def __init__(self):
self.random_storm = 0 # Holds random storm time
def storm_time(self): # Randomize and return storm time
self.random_storm = randint(int(clock()+3), int(clock() + 12))
return self.random_storm
def resume(): # Resume the game
global pause
pause = False
def paused(): # Pause the game
global game_status
crash_msg = large_text.render('You Have Crashed!', False, (255, 0, 0))
screen.blit(crash_msg, (420, 300)) # Display crash message in the middle of the screen
while pause:
for event in pygame.event.get():
if event.type == pygame.QUIT: # Quit the game if the 'X' button is clicked
sys.exit()
if event.type == pygame.KEYDOWN: # Wait for a key to be pressed and if so resumes the game
resume()
pygame.display.update()
clock_game.tick(FPS)
obstacles = pygame.sprite.Group() # Create obstacle sprite group
meteors = pygame.sprite.Group() # Create meteor sprite group
bckgd = Background('mars_background_instr.png', [0, 0])
lndr = Lander('lander.png', [randint(0, 1123), 0])
pad_1 = LandingPad('pad.png', [randint(858, 1042), 732])
pad_2 = LandingPad('pad_tall.png', [randint(458, 700), 620])
pad_3 = LandingPad('pad.png', [randint(0, 300), 650])
"""
Create 5 obstacles each being placed on a fixed location
on the background image!
"""
obstacle_1 = Obstacle('pipe_ramp_NE.png', [90, 540])
obstacle_2 = Obstacle('building_dome.png', [420, 575])
obstacle_3 = Obstacle('satellite_SW.png', [1150, 435])
obstacle_4 = Obstacle('rocks_ore_SW.png', [1080, 620])
obstacle_5 = Obstacle('building_station_SW.png', [850, 640])
# Add to the sprite group 'obstacles'
obstacles.add(obstacle_1, obstacle_2, obstacle_3, obstacle_4, obstacle_5)
"""
Create 10 meteors using the Meteor class which are placed
at random x-axis locations starting with the bottom of the image rectangle
lying at 0 on the y-axis!
"""
meteor1 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor2 = Meteor('spaceMeteors_2.png', [randint(300, 900), 0])
meteor3 = Meteor('spaceMeteors_3.png', [randint(300, 900), 0])
meteor4 = Meteor('spaceMeteors_4.png', [randint(300, 900), 0])
meteor5 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor6 = Meteor('spaceMeteors_2.png', [randint(300, 900), 0])
meteor7 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor8 = Meteor('spaceMeteors_4.png', [randint(300, 900), 0])
meteor9 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor10 = Meteor('spaceMeteors_3.png', [randint(300, 900), 0])
# Add to the sprite group assigned to the 'meteors' variable
meteors.add(meteor1, meteor2, meteor3, meteor4, meteor5, meteor6, meteor7, meteor8, meteor9, meteor10)
storm = Storm() # Storm variable
lives_left = Lives(3) # Each time the player starts with 3 lives
lander_score = GameScore() # Holds the score of the game
alert_signal = SysFailure() # Holds the lander system failure causes
game_status = True # Holds the status of the game
def main(): # The main function which runs the game
global game_status, pause # change name
random_signal = alert_signal.get_alert() # Holds the randomized alert signal time
random_key = alert_signal.get_key() # Carries the randomized key used to decide which control failure will occur
# during the alert signal
random_storm = storm.storm_time() # Random meteor storm time
meteor_storm = False # Set to True whenever a storm should occur
meteor_shower = False # Set to True whenever a storm should occur
print(random_storm)
meteor_number = randint(1, 10) # Determines the number of meteors the storm will contain
print(meteor_number)
while game_status: # main game loop
clock_game.tick(FPS)
screen.fill([255, 255, 255]) # Fill the empty spaces with white color
screen.blit(bckgd.image, bckgd.rect) # Place the background image
screen.blit(pad_1.image, pad_1.rect) # Put the first landing pad on the background
screen.blit(pad_2.image, pad_2.rect) # Put the second landing pad on the background
screen.blit(pad_3.image, pad_3.rect) # Put the last landing pad on the background
for obstacle in obstacles: # draw every one of the obstacles
# if a collision occurs the obstacle gets destroyed and it is no longer shown
if not obstacle.get_status():
screen.blit(obstacle.image, obstacle.rect)
if obstacle.obstacle_collision(lndr):
obstacle.destroyed = True
# Waits for an event
for event in pygame.event.get(): # If the user clicks the 'X' button on the window it quits the program
if event.type == pygame.QUIT:
sys.exit()
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm: # As soon as the clock passes the random storm time it causes meteor rain
if clock() > random_storm:
meteor_storm = True
meteor_shower = True
if meteor_shower:
delay = 0 # Each meteor is drawn with 1 second delay
count = 0 # Counts the meteors number
for meteor in meteors:
if count < meteor_number:
delay += 1
if clock() > random_storm + delay:
if not meteor.get_status(): # Draw every one of the meteors
# if a collision occurs the meteor gets destroyed and it is no longer shown
meteor.storm_fall() # Give x-axis and y-axis velocity to the meteors
screen.blit(meteor.image, meteor.rect)
if meteor.meteor_collision(lndr):
meteor.destroyed = True
count += 1
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
game_status = False
elif lives_left.game_over(): # Terminate program if the player has no lives left
game_status = False
elif lndr.get_fuel() <= 0: # Remove lander controls if it is out of fuel
screen.blit(lndr.rot_image, lndr.rect)
else:
if not random_signal < clock() < random_signal+2: # While the clock is not in the 2 sec alert time
if lndr.get_damage() < 100: # While the lander hasn't sustained 100% damage
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left+31, lndr.rect.bottom-10]) # Create thrust
# sprite
lndr.start_engine() # Call 'start engine' function (Lander)
thrst.rotate_thrust() # Call 'rotate_engine' which rotates the thrust along with the lander
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'left' is pressed
lndr.rotate_right()
if lndr.check_landing(pad_1) or lndr.check_landing(pad_2) or lndr.check_landing(pad_3): # Call
# 'check_landing' method on each pad sprite which checks whether the lander has landed on
# the landing pad
lander_score.successful_land() # Increment score with 50 pts
for meteor in meteors:
meteor.destroyed = False
meteor.reset_stats()
random_signal = alert_signal.get_alert()
random_key = alert_signal.get_key()
random_storm = storm.storm_time()
meteor_number = randint(1, 10)
print(random_storm)
print(meteor_number)
meteor_shower = False
meteor_storm = False
for obstacle in obstacles:
obstacle.destroyed = False
lndr.reset_stats()
else:
lndr.damage = 100 # Stop lander damage at 100 %
else:
alert_msg = alert_large.render('*ALERT*', False, (0, 0, 255))
screen.blit(alert_msg, (190, 80)) # Display alert message
if random_key == 1:
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left + 31, lndr.rect.bottom - 10])
lndr.start_engine()
thrst.rotate_thrust()
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
elif random_key == 2:
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'right' is pressed
lndr.rotate_right()
else:
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left + 31, lndr.rect.bottom - 10])
lndr.start_engine()
thrst.rotate_thrust()
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'right' is pressed
lndr.rotate_right()
screen.blit(lndr.rot_image, lndr.rect)
time_passed = myfont.render(':.1f s'.format(clock()), False, (255, 0, 0))
screen.blit(time_passed, (72, 10)) # Display clock in seconds
velocity_y = myfont.render(':.1f m/s'.format(lndr.veloc_y), False, (255, 0, 0))
screen.blit(velocity_y, (280, 56)) # Display y-axis velocity (downward, meters per second)
velocity_x = myfont.render(':.1f m/s'.format(lndr.veloc_x), False, (255, 0, 0))
screen.blit(velocity_x, (280, 33)) # Display x-axis velocity (sideways, meters per second)
fuel_remaining = myfont.render(':d kg'.format(lndr.fuel), False, (255, 0, 0))
screen.blit(fuel_remaining, (72, 33)) # Display remaining fuel in kg
altitude = myfont.render(':.0f m'.format(lndr.to_ground()), False, (255, 0, 0))
screen.blit(altitude, (280, 10)) # Display altitude in meters
lander_damage = myfont.render(' %'.format(lndr.get_damage()), False, (255, 0, 0))
screen.blit(lander_damage, (95, 56)) # Display damage suffered by the mars lander
game_score = myfont.render(':.0f pts'.format(lander_score.get_score()), False, (255, 0, 0))
screen.blit(game_score, (77, 82)) # Display altitude in meters
lndr.free_fall() # Call 'free_fall' method in class 'Lander'
if lndr.check_boundaries(): # Call 'check_boundaries' method located in 'Lander' class
for meteor in meteors:
meteor.destroyed = False
meteor.reset_stats()
random_signal = alert_signal.get_alert() # Get a new random time for the alert
random_key = alert_signal.get_key() # Get a new random key for the lander control failure
random_storm = storm.storm_time() # Get a new random time for the storm
meteor_number = randint(1, 10) # Get a new random number for the meteors
lives_left.crashed() # Reduce lives with 1
print(random_storm)
print(meteor_number)
meteor_shower = False
meteor_storm = False
for obstacle in obstacles: # Reset all obstacles and make them visible
obstacle.destroyed = False
pause = True # Set 'pause' to True so the game pauses when 'paused' method is called
paused() # Call 'paused'
pygame.display.update() # Refresh (update) display
pygame.quit() # quit game if game_status = False
main()
python game pygame
Could you upload the images somewhere, please? We can't run the code without the assets.
â Gareth Rees
Apr 17 at 9:47
Here you go: photos.app.goo.gl/y8mNYY9bLvbv9poM2
â Close Enough
Apr 19 at 12:04
add a comment |Â
up vote
4
down vote
favorite
up vote
4
down vote
favorite
Here is my code about our final project in uni called 'Mars Lander'. The thing is that my code seems to be all over the place (more specifically in the 'main' function at the end of the code...) therefore I would like to get some help with putting things in place! It's my first time making a game using the 'pygame' library and this one was definitely worth the effort!
import pygame
import sys
from random import uniform, randint # used for the random starting velocity of the lander, random clock time etc.
from time import clock # to show the time elapsed
import math # used to calculate the magnitude of the gravity force applied on the lander
WIDTH = 1200 # width of the game window
HEIGHT = 750 # height of the game window
FPS = 20 # frames per second
pause = False # variable which is used to determine whether the game is paused or not
# Initialise pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock_game = pygame.time.Clock()
pygame.font.init() # you have to call this at the start if you want to use this module.
myfont = pygame.font.SysFont('Comic Sans MS', 15)
alert_large = pygame.font.SysFont("Comic Sans MS", 18)
large_text = pygame.font.SysFont("Comic Sans MS", 50)
class Background(pygame.sprite.Sprite): # class for the background image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
# obstacles landing pad meteors classes
class Lander(pygame.sprite.Sprite): # class for the lander image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.rot_image = self.image
self.angle = 0
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.altitude = 0
self.damage = 0
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
def reset_stats(self):
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def check_boundaries(self):
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom > HEIGHT:
self.reset_stats()
self.rect.left = randint(0, 1123)
return True
else:
return False
def get_fuel(self):
return self.fuel
def burn_fuel(self): # decreases the fuel when 'space' key is pressed
self.fuel -= 5
def start_engine(self):
self.burn_fuel()
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
def rotate_left(self):
self.angle += 1 % 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def rotate_right(self):
self.angle -= 1 % 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def to_ground(self):
self.altitude = 1000 - self.rect.top*1.436
return self.altitude
def get_damage(self):
return self.damage
def check_landing(self, pad):
check_velocity_y = [True if self.veloc_y < 5 else False]
check_velocity_x = [True if -5 < self.veloc_x < 5 else False]
check_angle = [True if -7 <= self.angle <= 7 else False]
check_above_pad = [True if (self.rect.left > pad.rect.left and self.rect.right < pad.rect.right) else False]
check_touch = [True if (self.rect.bottom == pad.rect.top) else False]
if check_above_pad[0] and check_angle[0] and check_velocity_x[0] and check_velocity_y[0] and check_touch[0]:
return True
else:
return False
class EngineThrust(pygame.sprite.Sprite): # class for the thrust image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rot_image = self.image
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.thrst_angle = lndr.angle
def rotate_thrust(self):
self.rot_image = pygame.transform.rotate(self.image, self.thrst_angle)
class LandingPad(pygame.sprite.Sprite): # class for the landing pad image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
class GameScore: # class for the score of the game
def __init__(self):
self.score = 0
def successful_land(self):
self.score += 50
def get_score(self): # Returns game score
return self.score
class Lives: # class for the lives of the player
def __init__(self, lives):
self.lives = lives # Holds lives left
def crashed(self): # Decrement lives by 1
self.lives -= 1
def get_lives(self): # Return lives number
return self.lives
def game_over(self): # Check if there are no lives left
return self.lives == 0
class SysFailure: # class for the lander system errors
def __init__(self):
self.random_alert = 0 # Will carry alert time
self.random_key = 0 # Will holds key value
def get_alert(self): # Set a new alert time and return it
self.random_alert = randint(int(clock()+5), int(clock() + 15))
return self.random_alert
def get_key(self): # Randomize and return key value
self.random_key = randint(1, 3)
return self.random_key
class Obstacle(pygame.sprite.Sprite): # class for the obstacle images
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.destroyed = False
def get_status(self): # Return the status of the obstacle
return self.destroyed
def obstacle_collision(self, lander): # Increment lander damage by 10 % if the meteor collides with the lander
if lander.rect.colliderect(self.rect):
lander.damage += 10
return True
else:
return False
class Meteor(pygame.sprite.Sprite): # Class for the meteor images
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # Call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.bottom = location # The location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
self.destroyed = False
def storm_fall(self): # Set y and x-axis speed of the meteor
self.rect.x += self.speed_x
self.rect.y += self.speed_y
def meteor_collision(self, lander): # Increment lander damage by 25 % if the meteor collides with the lander
if lander.rect.colliderect(self.rect):
lander.damage += 25
return True
else:
return False
def get_status(self): # Return the status of the meteor
return self.destroyed
def reset_stats(self): # Set the bottom of the sprite to its initial value
self.rect.bottom = 0
class Storm: # Class for the meteor storms
def __init__(self):
self.random_storm = 0 # Holds random storm time
def storm_time(self): # Randomize and return storm time
self.random_storm = randint(int(clock()+3), int(clock() + 12))
return self.random_storm
def resume(): # Resume the game
global pause
pause = False
def paused(): # Pause the game
global game_status
crash_msg = large_text.render('You Have Crashed!', False, (255, 0, 0))
screen.blit(crash_msg, (420, 300)) # Display crash message in the middle of the screen
while pause:
for event in pygame.event.get():
if event.type == pygame.QUIT: # Quit the game if the 'X' button is clicked
sys.exit()
if event.type == pygame.KEYDOWN: # Wait for a key to be pressed and if so resumes the game
resume()
pygame.display.update()
clock_game.tick(FPS)
obstacles = pygame.sprite.Group() # Create obstacle sprite group
meteors = pygame.sprite.Group() # Create meteor sprite group
bckgd = Background('mars_background_instr.png', [0, 0])
lndr = Lander('lander.png', [randint(0, 1123), 0])
pad_1 = LandingPad('pad.png', [randint(858, 1042), 732])
pad_2 = LandingPad('pad_tall.png', [randint(458, 700), 620])
pad_3 = LandingPad('pad.png', [randint(0, 300), 650])
"""
Create 5 obstacles each being placed on a fixed location
on the background image!
"""
obstacle_1 = Obstacle('pipe_ramp_NE.png', [90, 540])
obstacle_2 = Obstacle('building_dome.png', [420, 575])
obstacle_3 = Obstacle('satellite_SW.png', [1150, 435])
obstacle_4 = Obstacle('rocks_ore_SW.png', [1080, 620])
obstacle_5 = Obstacle('building_station_SW.png', [850, 640])
# Add to the sprite group 'obstacles'
obstacles.add(obstacle_1, obstacle_2, obstacle_3, obstacle_4, obstacle_5)
"""
Create 10 meteors using the Meteor class which are placed
at random x-axis locations starting with the bottom of the image rectangle
lying at 0 on the y-axis!
"""
meteor1 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor2 = Meteor('spaceMeteors_2.png', [randint(300, 900), 0])
meteor3 = Meteor('spaceMeteors_3.png', [randint(300, 900), 0])
meteor4 = Meteor('spaceMeteors_4.png', [randint(300, 900), 0])
meteor5 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor6 = Meteor('spaceMeteors_2.png', [randint(300, 900), 0])
meteor7 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor8 = Meteor('spaceMeteors_4.png', [randint(300, 900), 0])
meteor9 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor10 = Meteor('spaceMeteors_3.png', [randint(300, 900), 0])
# Add to the sprite group assigned to the 'meteors' variable
meteors.add(meteor1, meteor2, meteor3, meteor4, meteor5, meteor6, meteor7, meteor8, meteor9, meteor10)
storm = Storm() # Storm variable
lives_left = Lives(3) # Each time the player starts with 3 lives
lander_score = GameScore() # Holds the score of the game
alert_signal = SysFailure() # Holds the lander system failure causes
game_status = True # Holds the status of the game
def main(): # The main function which runs the game
global game_status, pause # change name
random_signal = alert_signal.get_alert() # Holds the randomized alert signal time
random_key = alert_signal.get_key() # Carries the randomized key used to decide which control failure will occur
# during the alert signal
random_storm = storm.storm_time() # Random meteor storm time
meteor_storm = False # Set to True whenever a storm should occur
meteor_shower = False # Set to True whenever a storm should occur
print(random_storm)
meteor_number = randint(1, 10) # Determines the number of meteors the storm will contain
print(meteor_number)
while game_status: # main game loop
clock_game.tick(FPS)
screen.fill([255, 255, 255]) # Fill the empty spaces with white color
screen.blit(bckgd.image, bckgd.rect) # Place the background image
screen.blit(pad_1.image, pad_1.rect) # Put the first landing pad on the background
screen.blit(pad_2.image, pad_2.rect) # Put the second landing pad on the background
screen.blit(pad_3.image, pad_3.rect) # Put the last landing pad on the background
for obstacle in obstacles: # draw every one of the obstacles
# if a collision occurs the obstacle gets destroyed and it is no longer shown
if not obstacle.get_status():
screen.blit(obstacle.image, obstacle.rect)
if obstacle.obstacle_collision(lndr):
obstacle.destroyed = True
# Waits for an event
for event in pygame.event.get(): # If the user clicks the 'X' button on the window it quits the program
if event.type == pygame.QUIT:
sys.exit()
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm: # As soon as the clock passes the random storm time it causes meteor rain
if clock() > random_storm:
meteor_storm = True
meteor_shower = True
if meteor_shower:
delay = 0 # Each meteor is drawn with 1 second delay
count = 0 # Counts the meteors number
for meteor in meteors:
if count < meteor_number:
delay += 1
if clock() > random_storm + delay:
if not meteor.get_status(): # Draw every one of the meteors
# if a collision occurs the meteor gets destroyed and it is no longer shown
meteor.storm_fall() # Give x-axis and y-axis velocity to the meteors
screen.blit(meteor.image, meteor.rect)
if meteor.meteor_collision(lndr):
meteor.destroyed = True
count += 1
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
game_status = False
elif lives_left.game_over(): # Terminate program if the player has no lives left
game_status = False
elif lndr.get_fuel() <= 0: # Remove lander controls if it is out of fuel
screen.blit(lndr.rot_image, lndr.rect)
else:
if not random_signal < clock() < random_signal+2: # While the clock is not in the 2 sec alert time
if lndr.get_damage() < 100: # While the lander hasn't sustained 100% damage
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left+31, lndr.rect.bottom-10]) # Create thrust
# sprite
lndr.start_engine() # Call 'start engine' function (Lander)
thrst.rotate_thrust() # Call 'rotate_engine' which rotates the thrust along with the lander
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'left' is pressed
lndr.rotate_right()
if lndr.check_landing(pad_1) or lndr.check_landing(pad_2) or lndr.check_landing(pad_3): # Call
# 'check_landing' method on each pad sprite which checks whether the lander has landed on
# the landing pad
lander_score.successful_land() # Increment score with 50 pts
for meteor in meteors:
meteor.destroyed = False
meteor.reset_stats()
random_signal = alert_signal.get_alert()
random_key = alert_signal.get_key()
random_storm = storm.storm_time()
meteor_number = randint(1, 10)
print(random_storm)
print(meteor_number)
meteor_shower = False
meteor_storm = False
for obstacle in obstacles:
obstacle.destroyed = False
lndr.reset_stats()
else:
lndr.damage = 100 # Stop lander damage at 100 %
else:
alert_msg = alert_large.render('*ALERT*', False, (0, 0, 255))
screen.blit(alert_msg, (190, 80)) # Display alert message
if random_key == 1:
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left + 31, lndr.rect.bottom - 10])
lndr.start_engine()
thrst.rotate_thrust()
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
elif random_key == 2:
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'right' is pressed
lndr.rotate_right()
else:
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left + 31, lndr.rect.bottom - 10])
lndr.start_engine()
thrst.rotate_thrust()
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'right' is pressed
lndr.rotate_right()
screen.blit(lndr.rot_image, lndr.rect)
time_passed = myfont.render(':.1f s'.format(clock()), False, (255, 0, 0))
screen.blit(time_passed, (72, 10)) # Display clock in seconds
velocity_y = myfont.render(':.1f m/s'.format(lndr.veloc_y), False, (255, 0, 0))
screen.blit(velocity_y, (280, 56)) # Display y-axis velocity (downward, meters per second)
velocity_x = myfont.render(':.1f m/s'.format(lndr.veloc_x), False, (255, 0, 0))
screen.blit(velocity_x, (280, 33)) # Display x-axis velocity (sideways, meters per second)
fuel_remaining = myfont.render(':d kg'.format(lndr.fuel), False, (255, 0, 0))
screen.blit(fuel_remaining, (72, 33)) # Display remaining fuel in kg
altitude = myfont.render(':.0f m'.format(lndr.to_ground()), False, (255, 0, 0))
screen.blit(altitude, (280, 10)) # Display altitude in meters
lander_damage = myfont.render(' %'.format(lndr.get_damage()), False, (255, 0, 0))
screen.blit(lander_damage, (95, 56)) # Display damage suffered by the mars lander
game_score = myfont.render(':.0f pts'.format(lander_score.get_score()), False, (255, 0, 0))
screen.blit(game_score, (77, 82)) # Display altitude in meters
lndr.free_fall() # Call 'free_fall' method in class 'Lander'
if lndr.check_boundaries(): # Call 'check_boundaries' method located in 'Lander' class
for meteor in meteors:
meteor.destroyed = False
meteor.reset_stats()
random_signal = alert_signal.get_alert() # Get a new random time for the alert
random_key = alert_signal.get_key() # Get a new random key for the lander control failure
random_storm = storm.storm_time() # Get a new random time for the storm
meteor_number = randint(1, 10) # Get a new random number for the meteors
lives_left.crashed() # Reduce lives with 1
print(random_storm)
print(meteor_number)
meteor_shower = False
meteor_storm = False
for obstacle in obstacles: # Reset all obstacles and make them visible
obstacle.destroyed = False
pause = True # Set 'pause' to True so the game pauses when 'paused' method is called
paused() # Call 'paused'
pygame.display.update() # Refresh (update) display
pygame.quit() # quit game if game_status = False
main()
python game pygame
Here is my code about our final project in uni called 'Mars Lander'. The thing is that my code seems to be all over the place (more specifically in the 'main' function at the end of the code...) therefore I would like to get some help with putting things in place! It's my first time making a game using the 'pygame' library and this one was definitely worth the effort!
import pygame
import sys
from random import uniform, randint # used for the random starting velocity of the lander, random clock time etc.
from time import clock # to show the time elapsed
import math # used to calculate the magnitude of the gravity force applied on the lander
WIDTH = 1200 # width of the game window
HEIGHT = 750 # height of the game window
FPS = 20 # frames per second
pause = False # variable which is used to determine whether the game is paused or not
# Initialise pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock_game = pygame.time.Clock()
pygame.font.init() # you have to call this at the start if you want to use this module.
myfont = pygame.font.SysFont('Comic Sans MS', 15)
alert_large = pygame.font.SysFont("Comic Sans MS", 18)
large_text = pygame.font.SysFont("Comic Sans MS", 50)
class Background(pygame.sprite.Sprite): # class for the background image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
# obstacles landing pad meteors classes
class Lander(pygame.sprite.Sprite): # class for the lander image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.rot_image = self.image
self.angle = 0
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.altitude = 0
self.damage = 0
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
def reset_stats(self):
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def check_boundaries(self):
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom > HEIGHT:
self.reset_stats()
self.rect.left = randint(0, 1123)
return True
else:
return False
def get_fuel(self):
return self.fuel
def burn_fuel(self): # decreases the fuel when 'space' key is pressed
self.fuel -= 5
def start_engine(self):
self.burn_fuel()
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
def rotate_left(self):
self.angle += 1 % 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def rotate_right(self):
self.angle -= 1 % 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
def to_ground(self):
self.altitude = 1000 - self.rect.top*1.436
return self.altitude
def get_damage(self):
return self.damage
def check_landing(self, pad):
check_velocity_y = [True if self.veloc_y < 5 else False]
check_velocity_x = [True if -5 < self.veloc_x < 5 else False]
check_angle = [True if -7 <= self.angle <= 7 else False]
check_above_pad = [True if (self.rect.left > pad.rect.left and self.rect.right < pad.rect.right) else False]
check_touch = [True if (self.rect.bottom == pad.rect.top) else False]
if check_above_pad[0] and check_angle[0] and check_velocity_x[0] and check_velocity_y[0] and check_touch[0]:
return True
else:
return False
class EngineThrust(pygame.sprite.Sprite): # class for the thrust image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rot_image = self.image
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.thrst_angle = lndr.angle
def rotate_thrust(self):
self.rot_image = pygame.transform.rotate(self.image, self.thrst_angle)
class LandingPad(pygame.sprite.Sprite): # class for the landing pad image
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
class GameScore: # class for the score of the game
def __init__(self):
self.score = 0
def successful_land(self):
self.score += 50
def get_score(self): # Returns game score
return self.score
class Lives: # class for the lives of the player
def __init__(self, lives):
self.lives = lives # Holds lives left
def crashed(self): # Decrement lives by 1
self.lives -= 1
def get_lives(self): # Return lives number
return self.lives
def game_over(self): # Check if there are no lives left
return self.lives == 0
class SysFailure: # class for the lander system errors
def __init__(self):
self.random_alert = 0 # Will carry alert time
self.random_key = 0 # Will holds key value
def get_alert(self): # Set a new alert time and return it
self.random_alert = randint(int(clock()+5), int(clock() + 15))
return self.random_alert
def get_key(self): # Randomize and return key value
self.random_key = randint(1, 3)
return self.random_key
class Obstacle(pygame.sprite.Sprite): # class for the obstacle images
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location # the location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.destroyed = False
def get_status(self): # Return the status of the obstacle
return self.destroyed
def obstacle_collision(self, lander): # Increment lander damage by 10 % if the meteor collides with the lander
if lander.rect.colliderect(self.rect):
lander.damage += 10
return True
else:
return False
class Meteor(pygame.sprite.Sprite): # Class for the meteor images
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) # Call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.bottom = location # The location of the image should be inputted as a tuple (x,y)
# where x is the left side position of the image whereas y is the top side position
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
self.destroyed = False
def storm_fall(self): # Set y and x-axis speed of the meteor
self.rect.x += self.speed_x
self.rect.y += self.speed_y
def meteor_collision(self, lander): # Increment lander damage by 25 % if the meteor collides with the lander
if lander.rect.colliderect(self.rect):
lander.damage += 25
return True
else:
return False
def get_status(self): # Return the status of the meteor
return self.destroyed
def reset_stats(self): # Set the bottom of the sprite to its initial value
self.rect.bottom = 0
class Storm: # Class for the meteor storms
def __init__(self):
self.random_storm = 0 # Holds random storm time
def storm_time(self): # Randomize and return storm time
self.random_storm = randint(int(clock()+3), int(clock() + 12))
return self.random_storm
def resume(): # Resume the game
global pause
pause = False
def paused(): # Pause the game
global game_status
crash_msg = large_text.render('You Have Crashed!', False, (255, 0, 0))
screen.blit(crash_msg, (420, 300)) # Display crash message in the middle of the screen
while pause:
for event in pygame.event.get():
if event.type == pygame.QUIT: # Quit the game if the 'X' button is clicked
sys.exit()
if event.type == pygame.KEYDOWN: # Wait for a key to be pressed and if so resumes the game
resume()
pygame.display.update()
clock_game.tick(FPS)
obstacles = pygame.sprite.Group() # Create obstacle sprite group
meteors = pygame.sprite.Group() # Create meteor sprite group
bckgd = Background('mars_background_instr.png', [0, 0])
lndr = Lander('lander.png', [randint(0, 1123), 0])
pad_1 = LandingPad('pad.png', [randint(858, 1042), 732])
pad_2 = LandingPad('pad_tall.png', [randint(458, 700), 620])
pad_3 = LandingPad('pad.png', [randint(0, 300), 650])
"""
Create 5 obstacles each being placed on a fixed location
on the background image!
"""
obstacle_1 = Obstacle('pipe_ramp_NE.png', [90, 540])
obstacle_2 = Obstacle('building_dome.png', [420, 575])
obstacle_3 = Obstacle('satellite_SW.png', [1150, 435])
obstacle_4 = Obstacle('rocks_ore_SW.png', [1080, 620])
obstacle_5 = Obstacle('building_station_SW.png', [850, 640])
# Add to the sprite group 'obstacles'
obstacles.add(obstacle_1, obstacle_2, obstacle_3, obstacle_4, obstacle_5)
"""
Create 10 meteors using the Meteor class which are placed
at random x-axis locations starting with the bottom of the image rectangle
lying at 0 on the y-axis!
"""
meteor1 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor2 = Meteor('spaceMeteors_2.png', [randint(300, 900), 0])
meteor3 = Meteor('spaceMeteors_3.png', [randint(300, 900), 0])
meteor4 = Meteor('spaceMeteors_4.png', [randint(300, 900), 0])
meteor5 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor6 = Meteor('spaceMeteors_2.png', [randint(300, 900), 0])
meteor7 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor8 = Meteor('spaceMeteors_4.png', [randint(300, 900), 0])
meteor9 = Meteor('spaceMeteors_1.png', [randint(300, 900), 0])
meteor10 = Meteor('spaceMeteors_3.png', [randint(300, 900), 0])
# Add to the sprite group assigned to the 'meteors' variable
meteors.add(meteor1, meteor2, meteor3, meteor4, meteor5, meteor6, meteor7, meteor8, meteor9, meteor10)
storm = Storm() # Storm variable
lives_left = Lives(3) # Each time the player starts with 3 lives
lander_score = GameScore() # Holds the score of the game
alert_signal = SysFailure() # Holds the lander system failure causes
game_status = True # Holds the status of the game
def main(): # The main function which runs the game
global game_status, pause # change name
random_signal = alert_signal.get_alert() # Holds the randomized alert signal time
random_key = alert_signal.get_key() # Carries the randomized key used to decide which control failure will occur
# during the alert signal
random_storm = storm.storm_time() # Random meteor storm time
meteor_storm = False # Set to True whenever a storm should occur
meteor_shower = False # Set to True whenever a storm should occur
print(random_storm)
meteor_number = randint(1, 10) # Determines the number of meteors the storm will contain
print(meteor_number)
while game_status: # main game loop
clock_game.tick(FPS)
screen.fill([255, 255, 255]) # Fill the empty spaces with white color
screen.blit(bckgd.image, bckgd.rect) # Place the background image
screen.blit(pad_1.image, pad_1.rect) # Put the first landing pad on the background
screen.blit(pad_2.image, pad_2.rect) # Put the second landing pad on the background
screen.blit(pad_3.image, pad_3.rect) # Put the last landing pad on the background
for obstacle in obstacles: # draw every one of the obstacles
# if a collision occurs the obstacle gets destroyed and it is no longer shown
if not obstacle.get_status():
screen.blit(obstacle.image, obstacle.rect)
if obstacle.obstacle_collision(lndr):
obstacle.destroyed = True
# Waits for an event
for event in pygame.event.get(): # If the user clicks the 'X' button on the window it quits the program
if event.type == pygame.QUIT:
sys.exit()
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm: # As soon as the clock passes the random storm time it causes meteor rain
if clock() > random_storm:
meteor_storm = True
meteor_shower = True
if meteor_shower:
delay = 0 # Each meteor is drawn with 1 second delay
count = 0 # Counts the meteors number
for meteor in meteors:
if count < meteor_number:
delay += 1
if clock() > random_storm + delay:
if not meteor.get_status(): # Draw every one of the meteors
# if a collision occurs the meteor gets destroyed and it is no longer shown
meteor.storm_fall() # Give x-axis and y-axis velocity to the meteors
screen.blit(meteor.image, meteor.rect)
if meteor.meteor_collision(lndr):
meteor.destroyed = True
count += 1
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
game_status = False
elif lives_left.game_over(): # Terminate program if the player has no lives left
game_status = False
elif lndr.get_fuel() <= 0: # Remove lander controls if it is out of fuel
screen.blit(lndr.rot_image, lndr.rect)
else:
if not random_signal < clock() < random_signal+2: # While the clock is not in the 2 sec alert time
if lndr.get_damage() < 100: # While the lander hasn't sustained 100% damage
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left+31, lndr.rect.bottom-10]) # Create thrust
# sprite
lndr.start_engine() # Call 'start engine' function (Lander)
thrst.rotate_thrust() # Call 'rotate_engine' which rotates the thrust along with the lander
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'left' is pressed
lndr.rotate_right()
if lndr.check_landing(pad_1) or lndr.check_landing(pad_2) or lndr.check_landing(pad_3): # Call
# 'check_landing' method on each pad sprite which checks whether the lander has landed on
# the landing pad
lander_score.successful_land() # Increment score with 50 pts
for meteor in meteors:
meteor.destroyed = False
meteor.reset_stats()
random_signal = alert_signal.get_alert()
random_key = alert_signal.get_key()
random_storm = storm.storm_time()
meteor_number = randint(1, 10)
print(random_storm)
print(meteor_number)
meteor_shower = False
meteor_storm = False
for obstacle in obstacles:
obstacle.destroyed = False
lndr.reset_stats()
else:
lndr.damage = 100 # Stop lander damage at 100 %
else:
alert_msg = alert_large.render('*ALERT*', False, (0, 0, 255))
screen.blit(alert_msg, (190, 80)) # Display alert message
if random_key == 1:
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left + 31, lndr.rect.bottom - 10])
lndr.start_engine()
thrst.rotate_thrust()
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
elif random_key == 2:
if pressed_key[pygame.K_LEFT]: # Rotate lander anticlockwise when 'left' is pressed
lndr.rotate_left()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'right' is pressed
lndr.rotate_right()
else:
if pressed_key[pygame.K_SPACE]: # Show thrust image when 'space' is pressed
thrst = EngineThrust('thrust.png', [lndr.rect.left + 31, lndr.rect.bottom - 10])
lndr.start_engine()
thrst.rotate_thrust()
screen.blit(thrst.rot_image, thrst.rect)
pygame.display.update()
if pressed_key[pygame.K_RIGHT]: # Rotate lander clockwise when 'right' is pressed
lndr.rotate_right()
screen.blit(lndr.rot_image, lndr.rect)
time_passed = myfont.render(':.1f s'.format(clock()), False, (255, 0, 0))
screen.blit(time_passed, (72, 10)) # Display clock in seconds
velocity_y = myfont.render(':.1f m/s'.format(lndr.veloc_y), False, (255, 0, 0))
screen.blit(velocity_y, (280, 56)) # Display y-axis velocity (downward, meters per second)
velocity_x = myfont.render(':.1f m/s'.format(lndr.veloc_x), False, (255, 0, 0))
screen.blit(velocity_x, (280, 33)) # Display x-axis velocity (sideways, meters per second)
fuel_remaining = myfont.render(':d kg'.format(lndr.fuel), False, (255, 0, 0))
screen.blit(fuel_remaining, (72, 33)) # Display remaining fuel in kg
altitude = myfont.render(':.0f m'.format(lndr.to_ground()), False, (255, 0, 0))
screen.blit(altitude, (280, 10)) # Display altitude in meters
lander_damage = myfont.render(' %'.format(lndr.get_damage()), False, (255, 0, 0))
screen.blit(lander_damage, (95, 56)) # Display damage suffered by the mars lander
game_score = myfont.render(':.0f pts'.format(lander_score.get_score()), False, (255, 0, 0))
screen.blit(game_score, (77, 82)) # Display altitude in meters
lndr.free_fall() # Call 'free_fall' method in class 'Lander'
if lndr.check_boundaries(): # Call 'check_boundaries' method located in 'Lander' class
for meteor in meteors:
meteor.destroyed = False
meteor.reset_stats()
random_signal = alert_signal.get_alert() # Get a new random time for the alert
random_key = alert_signal.get_key() # Get a new random key for the lander control failure
random_storm = storm.storm_time() # Get a new random time for the storm
meteor_number = randint(1, 10) # Get a new random number for the meteors
lives_left.crashed() # Reduce lives with 1
print(random_storm)
print(meteor_number)
meteor_shower = False
meteor_storm = False
for obstacle in obstacles: # Reset all obstacles and make them visible
obstacle.destroyed = False
pause = True # Set 'pause' to True so the game pauses when 'paused' method is called
paused() # Call 'paused'
pygame.display.update() # Refresh (update) display
pygame.quit() # quit game if game_status = False
main()
python game pygame
edited Apr 15 at 18:08
Jamalâ¦
30.1k11114225
30.1k11114225
asked Apr 15 at 17:43
Close Enough
263
263
Could you upload the images somewhere, please? We can't run the code without the assets.
â Gareth Rees
Apr 17 at 9:47
Here you go: photos.app.goo.gl/y8mNYY9bLvbv9poM2
â Close Enough
Apr 19 at 12:04
add a comment |Â
Could you upload the images somewhere, please? We can't run the code without the assets.
â Gareth Rees
Apr 17 at 9:47
Here you go: photos.app.goo.gl/y8mNYY9bLvbv9poM2
â Close Enough
Apr 19 at 12:04
Could you upload the images somewhere, please? We can't run the code without the assets.
â Gareth Rees
Apr 17 at 9:47
Could you upload the images somewhere, please? We can't run the code without the assets.
â Gareth Rees
Apr 17 at 9:47
Here you go: photos.app.goo.gl/y8mNYY9bLvbv9poM2
â Close Enough
Apr 19 at 12:04
Here you go: photos.app.goo.gl/y8mNYY9bLvbv9poM2
â Close Enough
Apr 19 at 12:04
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
2
down vote
accepted
You try to extract behaviour into classes, which is a good idea, but you failed at recognising patterns that could be abstracted by a single class and created way too much classes for the same thing: a Sprite
set at certain coordinates; heck, even your LandingPad
and Background
classes are exactly the same, that should have raised a red flag.
The same goes with your Lives
and GameScore
classes which could be a simple integer as they add nothing more.
You should also avoid globals and code at top-level. They, however, are better in an __init__
of some class or other. Thus IâÂÂd create a MarsLanding
class to hold that and the main
function. This would help for refactoring, also.
Lastly, you perform drawing, sprites updates and collision detection manually but pygame
allows to automate it through groups. See for instance the spritecollide
function or the Group.draw
method.
Proposed improvements follows:
import math
from time import clock
from random import uniform, randint, choice
import pygame
def init():
pygame.init()
pygame.font.init()
class MarsLander:
def __init__(self, fps=20, width=1200, height=750):
self.screen = pygame.display.set_mode((width, height))
self.clock = pygame.time.Clock()
self.FPS = fps
self.regular_font = pygame.font.SysFont('Comic Sans MS', 15)
self.alert_font = pygame.font.SysFont('Comic Sans MS', 18)
self.large_font = pygame.font.SysFont('Comic Sans MS', 50)
self.score = 0
self.lives = 3
self.obstacles = pygame.sprite.Group()
self.meteors = pygame.sprite.Group()
self.landing_pads = pygame.sprite.Group()
self.background = Sprite('mars_background_instr.png', 0, 0)
self.lander = Lander(width)
self.height = height
# Create sprites for landing pads and add them to the pads group
# TODO have coordinates dependent on actual width and height
Sprite('pad.png', 732, randint(858, 1042)).add(self.landing_pads)
Sprite('pad_tall.png', 620, randint(458, 700)).add(self.landing_pads)
Sprite('pad.png', 650, randint(0, 300)).add(self.landing_pads)
self.reset_obstacles()
self.create_new_storm()
self.create_new_alert()
@property
def game_over(self):
return self.lives < 1
def reset_obstacles(self):
"""Create obstacles at a fixed location and add the to the obstacles group"""
# TODO have coordinates dependent on actual width and height
self.obstacles.empty()
Sprite('pipe_ramp_NE.png', 540, 90).add(self.obstacles)
Sprite('building_dome.png', 575, 420).add(self.obstacles)
Sprite('satellite_SW.png', 435, 1150).add(self.obstacles)
Sprite('rocks_ore_SW.png', 620, 1080).add(self.obstacles)
Sprite('building_station_SW.png', 640, 850).add(self.obstacles)
def create_new_storm(self, number_of_images=4):
"""Create meteors and add the to the meteors group"""
# TODO have coordinates dependent on actual width and height
now = int(clock())
self.random_storm = randint(now + 3, now + 12)
self.meteors.empty()
for i in range(randint(1, 10)):
image_name = 'spaceMeteors_.png'.format(randint(1, number_of_images))
Meteor(image_name, -2 * i * self.FPS, randint(300, 900)).add(self.meteors)
def create_new_alert(self):
self.random_alert = randint(int(clock() + 5), int(clock() + 15))
self.alert_key = choice((pygame.K_SPACE, pygame.K_LEFT, pygame.K_RIGHT))
def draw_text(self, message, position, color=(255, 0, 0)):
text = self.regular_font.render(message, False, color)
self.screen.blit(text, position)
def run(self):
meteor_storm = False # Set to True whenever a storm should occur
while not self.game_over:
self.clock.tick(self.FPS)
# If the user clicks the 'X' button on the window it quits the program
if any(event.type == pygame.QUIT for event in pygame.event.get()):
return
self.screen.fill([255, 255, 255]) # Fill the empty spaces with white color
self.screen.blit(self.background.image, self.background.rect) # Place the background image
self.landing_pads.draw(self.screen)
self.obstacles.draw(self.screen)
# Check for collisions with obstacles and remove hit ones
obstacles_hit = pygame.sprite.spritecollide(self.lander, self.obstacles, True)
self.lander.damage += 10 * len(obstacles_hit)
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm and clock() > self.random_storm:
# As soon as the clock passes the random storm time it causes meteor rain
meteor_storm = True
if meteor_storm:
self.meteors.update()
self.meteors.draw(self.screen)
# Check for collisions with meteors and remove hit ones
meteors_hit = pygame.sprite.spritecollide(self.lander, self.meteors, True)
self.lander.damage += 25 * len(meteors_hit)
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
return
if self.random_alert < clock() < self.random_alert + 2:
alert_msg = self.large_font.render('*ALERT*', False, (0, 0, 255))
self.screen.blit(alert_msg, (190, 80))
thrust = self.lander.handle_inputs(pressed_key, self.alert_key)
else:
thrust = self.lander.handle_inputs(pressed_key)
if thrust:
self.screen.blit(thrust.rot_image, thrust.rect)
self.screen.blit(self.lander.rot_image, self.lander.rect)
self.draw_text(':1.f s'.format(clock()), (72, 10))
self.draw_text(':.1f m/s'.format(self.lander.veloc_y), (280, 56))
self.draw_text(':.1f m/s'.format(self.lander.veloc_x), (280, 33))
self.draw_text(':d kg'.format(self.lander.fuel), (72, 33))
self.draw_text(':.0f m'.format(self.lander.altitude), (280, 10))
self.draw_text(' %'.format(self.lander.damage), (95, 56))
self.draw_text(':.0f pts'.format(self.score), (77, 82))
self.lander.free_fall()
pygame.display.update()
landing_pad_reached = pygame.sprite.spritecollideany(self.lander, self.landing_pads)
if landing_pad_reached or self.lander.rect.bottom > self.height:
self.create_new_alert()
self.create_new_storm()
self.reset_obstacles()
meteor_storm = False
if landing_pad_reached and self.lander.has_landing_position():
self.score += 50
else:
self.lives -= 1
should_exit = self.show_crash()
if should_exit:
return
self.lander.reset_stats()
def show_crash(self):
"""Display crash message in the middle of the screen and wait for a key press"""
crash_msg = self.large_font.render('You Have Crashed!', False, (255, 0, 0))
self.screen.blit(crash_msg, (420, 300))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
# Quit the game if the 'X' button is clicked
return True
if event.type == pygame.KEYDOWN:
# Wait for a key to be pressed and if so resumes the game
return False
pygame.display.update()
self.clock.tick(self.FPS)
class Sprite(pygame.sprite.Sprite):
def __init__(self, image_file, top, left):
super().__init__()
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.top = top
self.rect.left = left
class EngineThrust(Sprite): # class for the thrust image
def __init__(self, lander_rect, lander_angle):
super().__init__('thrust.png', lander_rect.bottom - 10, lander_rect.left + 31)
self.rot_image = pygame.transform.rotate(self.image, lander_angle)
class Meteor(Sprite):
def __init__(self, image_file, top, left):
super().__init__(image_file, top, left)
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
def update(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
class Lander(Sprite):
def __init__(self, width):
super().__init__('lander.png', 0, 0)
self.width = width
self.reset_stats()
def reset_stats(self):
self.rect.top = 0
self.rect.left = randint(0, self.width - self.rect.width)
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = self.image
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.rigth < 0:
self.rect.left = self.width
if self.rect.left > self.width:
self.rect.right = 0
def start_engine(self):
self.fuel -= 5
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
@property
def altitude(self):
return 1000 - self.rect.top * 1.436
@property
def can_land(self):
return self.fuel > 0 and self.damage < 100
def has_landing_position(self):
return self.can_land and (self.veloc_y < 5) and (-5 < self.veloc_x < 5) and (-7 <= self.angle <= 7)
def handle_inputs(self, pressed_key, alert_key=None):
if not self.can_land:
return
thrust = None
rotated = False
if alert_key != pygame.K_SPACE and pressed_key[pygame.K_SPACE]:
# Show thrust image when 'space' is pressed
thrust = EngineThrust(self.rect, self.angle)
self.start_engine()
if alert_key != pygame.K_LEFT and pressed_key[pygame.K_LEFT]:
# Rotate lander anticlockwise when 'left' is pressed
self.angle += 1
rotated = True
if alert_key != pygame.K_RIGHT and pressed_key[pygame.K_RIGHT]:
# Rotate lander clockwise when 'left' is pressed
self.angle -= 1
rotated = True
if rotated:
self.angle %= 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
return thrust
if __name__ == '__main__':
init()
game = MarsLander()
game.run()
pygame.quit()
You may have seen that I changed some constants into parameters with default values, this will allow you to improve the game customization if you need to by integrating argparse
for instance.
Other changes may include restarting hazards every once in a while (spritecollideany
might be of some help to detect when every meteor have run off the background)
Thank you for taking time out to answer my question! I am looking forward to implementing many of your suggestions.
â Close Enough
Apr 20 at 16:33
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
You try to extract behaviour into classes, which is a good idea, but you failed at recognising patterns that could be abstracted by a single class and created way too much classes for the same thing: a Sprite
set at certain coordinates; heck, even your LandingPad
and Background
classes are exactly the same, that should have raised a red flag.
The same goes with your Lives
and GameScore
classes which could be a simple integer as they add nothing more.
You should also avoid globals and code at top-level. They, however, are better in an __init__
of some class or other. Thus IâÂÂd create a MarsLanding
class to hold that and the main
function. This would help for refactoring, also.
Lastly, you perform drawing, sprites updates and collision detection manually but pygame
allows to automate it through groups. See for instance the spritecollide
function or the Group.draw
method.
Proposed improvements follows:
import math
from time import clock
from random import uniform, randint, choice
import pygame
def init():
pygame.init()
pygame.font.init()
class MarsLander:
def __init__(self, fps=20, width=1200, height=750):
self.screen = pygame.display.set_mode((width, height))
self.clock = pygame.time.Clock()
self.FPS = fps
self.regular_font = pygame.font.SysFont('Comic Sans MS', 15)
self.alert_font = pygame.font.SysFont('Comic Sans MS', 18)
self.large_font = pygame.font.SysFont('Comic Sans MS', 50)
self.score = 0
self.lives = 3
self.obstacles = pygame.sprite.Group()
self.meteors = pygame.sprite.Group()
self.landing_pads = pygame.sprite.Group()
self.background = Sprite('mars_background_instr.png', 0, 0)
self.lander = Lander(width)
self.height = height
# Create sprites for landing pads and add them to the pads group
# TODO have coordinates dependent on actual width and height
Sprite('pad.png', 732, randint(858, 1042)).add(self.landing_pads)
Sprite('pad_tall.png', 620, randint(458, 700)).add(self.landing_pads)
Sprite('pad.png', 650, randint(0, 300)).add(self.landing_pads)
self.reset_obstacles()
self.create_new_storm()
self.create_new_alert()
@property
def game_over(self):
return self.lives < 1
def reset_obstacles(self):
"""Create obstacles at a fixed location and add the to the obstacles group"""
# TODO have coordinates dependent on actual width and height
self.obstacles.empty()
Sprite('pipe_ramp_NE.png', 540, 90).add(self.obstacles)
Sprite('building_dome.png', 575, 420).add(self.obstacles)
Sprite('satellite_SW.png', 435, 1150).add(self.obstacles)
Sprite('rocks_ore_SW.png', 620, 1080).add(self.obstacles)
Sprite('building_station_SW.png', 640, 850).add(self.obstacles)
def create_new_storm(self, number_of_images=4):
"""Create meteors and add the to the meteors group"""
# TODO have coordinates dependent on actual width and height
now = int(clock())
self.random_storm = randint(now + 3, now + 12)
self.meteors.empty()
for i in range(randint(1, 10)):
image_name = 'spaceMeteors_.png'.format(randint(1, number_of_images))
Meteor(image_name, -2 * i * self.FPS, randint(300, 900)).add(self.meteors)
def create_new_alert(self):
self.random_alert = randint(int(clock() + 5), int(clock() + 15))
self.alert_key = choice((pygame.K_SPACE, pygame.K_LEFT, pygame.K_RIGHT))
def draw_text(self, message, position, color=(255, 0, 0)):
text = self.regular_font.render(message, False, color)
self.screen.blit(text, position)
def run(self):
meteor_storm = False # Set to True whenever a storm should occur
while not self.game_over:
self.clock.tick(self.FPS)
# If the user clicks the 'X' button on the window it quits the program
if any(event.type == pygame.QUIT for event in pygame.event.get()):
return
self.screen.fill([255, 255, 255]) # Fill the empty spaces with white color
self.screen.blit(self.background.image, self.background.rect) # Place the background image
self.landing_pads.draw(self.screen)
self.obstacles.draw(self.screen)
# Check for collisions with obstacles and remove hit ones
obstacles_hit = pygame.sprite.spritecollide(self.lander, self.obstacles, True)
self.lander.damage += 10 * len(obstacles_hit)
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm and clock() > self.random_storm:
# As soon as the clock passes the random storm time it causes meteor rain
meteor_storm = True
if meteor_storm:
self.meteors.update()
self.meteors.draw(self.screen)
# Check for collisions with meteors and remove hit ones
meteors_hit = pygame.sprite.spritecollide(self.lander, self.meteors, True)
self.lander.damage += 25 * len(meteors_hit)
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
return
if self.random_alert < clock() < self.random_alert + 2:
alert_msg = self.large_font.render('*ALERT*', False, (0, 0, 255))
self.screen.blit(alert_msg, (190, 80))
thrust = self.lander.handle_inputs(pressed_key, self.alert_key)
else:
thrust = self.lander.handle_inputs(pressed_key)
if thrust:
self.screen.blit(thrust.rot_image, thrust.rect)
self.screen.blit(self.lander.rot_image, self.lander.rect)
self.draw_text(':1.f s'.format(clock()), (72, 10))
self.draw_text(':.1f m/s'.format(self.lander.veloc_y), (280, 56))
self.draw_text(':.1f m/s'.format(self.lander.veloc_x), (280, 33))
self.draw_text(':d kg'.format(self.lander.fuel), (72, 33))
self.draw_text(':.0f m'.format(self.lander.altitude), (280, 10))
self.draw_text(' %'.format(self.lander.damage), (95, 56))
self.draw_text(':.0f pts'.format(self.score), (77, 82))
self.lander.free_fall()
pygame.display.update()
landing_pad_reached = pygame.sprite.spritecollideany(self.lander, self.landing_pads)
if landing_pad_reached or self.lander.rect.bottom > self.height:
self.create_new_alert()
self.create_new_storm()
self.reset_obstacles()
meteor_storm = False
if landing_pad_reached and self.lander.has_landing_position():
self.score += 50
else:
self.lives -= 1
should_exit = self.show_crash()
if should_exit:
return
self.lander.reset_stats()
def show_crash(self):
"""Display crash message in the middle of the screen and wait for a key press"""
crash_msg = self.large_font.render('You Have Crashed!', False, (255, 0, 0))
self.screen.blit(crash_msg, (420, 300))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
# Quit the game if the 'X' button is clicked
return True
if event.type == pygame.KEYDOWN:
# Wait for a key to be pressed and if so resumes the game
return False
pygame.display.update()
self.clock.tick(self.FPS)
class Sprite(pygame.sprite.Sprite):
def __init__(self, image_file, top, left):
super().__init__()
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.top = top
self.rect.left = left
class EngineThrust(Sprite): # class for the thrust image
def __init__(self, lander_rect, lander_angle):
super().__init__('thrust.png', lander_rect.bottom - 10, lander_rect.left + 31)
self.rot_image = pygame.transform.rotate(self.image, lander_angle)
class Meteor(Sprite):
def __init__(self, image_file, top, left):
super().__init__(image_file, top, left)
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
def update(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
class Lander(Sprite):
def __init__(self, width):
super().__init__('lander.png', 0, 0)
self.width = width
self.reset_stats()
def reset_stats(self):
self.rect.top = 0
self.rect.left = randint(0, self.width - self.rect.width)
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = self.image
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.rigth < 0:
self.rect.left = self.width
if self.rect.left > self.width:
self.rect.right = 0
def start_engine(self):
self.fuel -= 5
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
@property
def altitude(self):
return 1000 - self.rect.top * 1.436
@property
def can_land(self):
return self.fuel > 0 and self.damage < 100
def has_landing_position(self):
return self.can_land and (self.veloc_y < 5) and (-5 < self.veloc_x < 5) and (-7 <= self.angle <= 7)
def handle_inputs(self, pressed_key, alert_key=None):
if not self.can_land:
return
thrust = None
rotated = False
if alert_key != pygame.K_SPACE and pressed_key[pygame.K_SPACE]:
# Show thrust image when 'space' is pressed
thrust = EngineThrust(self.rect, self.angle)
self.start_engine()
if alert_key != pygame.K_LEFT and pressed_key[pygame.K_LEFT]:
# Rotate lander anticlockwise when 'left' is pressed
self.angle += 1
rotated = True
if alert_key != pygame.K_RIGHT and pressed_key[pygame.K_RIGHT]:
# Rotate lander clockwise when 'left' is pressed
self.angle -= 1
rotated = True
if rotated:
self.angle %= 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
return thrust
if __name__ == '__main__':
init()
game = MarsLander()
game.run()
pygame.quit()
You may have seen that I changed some constants into parameters with default values, this will allow you to improve the game customization if you need to by integrating argparse
for instance.
Other changes may include restarting hazards every once in a while (spritecollideany
might be of some help to detect when every meteor have run off the background)
Thank you for taking time out to answer my question! I am looking forward to implementing many of your suggestions.
â Close Enough
Apr 20 at 16:33
add a comment |Â
up vote
2
down vote
accepted
You try to extract behaviour into classes, which is a good idea, but you failed at recognising patterns that could be abstracted by a single class and created way too much classes for the same thing: a Sprite
set at certain coordinates; heck, even your LandingPad
and Background
classes are exactly the same, that should have raised a red flag.
The same goes with your Lives
and GameScore
classes which could be a simple integer as they add nothing more.
You should also avoid globals and code at top-level. They, however, are better in an __init__
of some class or other. Thus IâÂÂd create a MarsLanding
class to hold that and the main
function. This would help for refactoring, also.
Lastly, you perform drawing, sprites updates and collision detection manually but pygame
allows to automate it through groups. See for instance the spritecollide
function or the Group.draw
method.
Proposed improvements follows:
import math
from time import clock
from random import uniform, randint, choice
import pygame
def init():
pygame.init()
pygame.font.init()
class MarsLander:
def __init__(self, fps=20, width=1200, height=750):
self.screen = pygame.display.set_mode((width, height))
self.clock = pygame.time.Clock()
self.FPS = fps
self.regular_font = pygame.font.SysFont('Comic Sans MS', 15)
self.alert_font = pygame.font.SysFont('Comic Sans MS', 18)
self.large_font = pygame.font.SysFont('Comic Sans MS', 50)
self.score = 0
self.lives = 3
self.obstacles = pygame.sprite.Group()
self.meteors = pygame.sprite.Group()
self.landing_pads = pygame.sprite.Group()
self.background = Sprite('mars_background_instr.png', 0, 0)
self.lander = Lander(width)
self.height = height
# Create sprites for landing pads and add them to the pads group
# TODO have coordinates dependent on actual width and height
Sprite('pad.png', 732, randint(858, 1042)).add(self.landing_pads)
Sprite('pad_tall.png', 620, randint(458, 700)).add(self.landing_pads)
Sprite('pad.png', 650, randint(0, 300)).add(self.landing_pads)
self.reset_obstacles()
self.create_new_storm()
self.create_new_alert()
@property
def game_over(self):
return self.lives < 1
def reset_obstacles(self):
"""Create obstacles at a fixed location and add the to the obstacles group"""
# TODO have coordinates dependent on actual width and height
self.obstacles.empty()
Sprite('pipe_ramp_NE.png', 540, 90).add(self.obstacles)
Sprite('building_dome.png', 575, 420).add(self.obstacles)
Sprite('satellite_SW.png', 435, 1150).add(self.obstacles)
Sprite('rocks_ore_SW.png', 620, 1080).add(self.obstacles)
Sprite('building_station_SW.png', 640, 850).add(self.obstacles)
def create_new_storm(self, number_of_images=4):
"""Create meteors and add the to the meteors group"""
# TODO have coordinates dependent on actual width and height
now = int(clock())
self.random_storm = randint(now + 3, now + 12)
self.meteors.empty()
for i in range(randint(1, 10)):
image_name = 'spaceMeteors_.png'.format(randint(1, number_of_images))
Meteor(image_name, -2 * i * self.FPS, randint(300, 900)).add(self.meteors)
def create_new_alert(self):
self.random_alert = randint(int(clock() + 5), int(clock() + 15))
self.alert_key = choice((pygame.K_SPACE, pygame.K_LEFT, pygame.K_RIGHT))
def draw_text(self, message, position, color=(255, 0, 0)):
text = self.regular_font.render(message, False, color)
self.screen.blit(text, position)
def run(self):
meteor_storm = False # Set to True whenever a storm should occur
while not self.game_over:
self.clock.tick(self.FPS)
# If the user clicks the 'X' button on the window it quits the program
if any(event.type == pygame.QUIT for event in pygame.event.get()):
return
self.screen.fill([255, 255, 255]) # Fill the empty spaces with white color
self.screen.blit(self.background.image, self.background.rect) # Place the background image
self.landing_pads.draw(self.screen)
self.obstacles.draw(self.screen)
# Check for collisions with obstacles and remove hit ones
obstacles_hit = pygame.sprite.spritecollide(self.lander, self.obstacles, True)
self.lander.damage += 10 * len(obstacles_hit)
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm and clock() > self.random_storm:
# As soon as the clock passes the random storm time it causes meteor rain
meteor_storm = True
if meteor_storm:
self.meteors.update()
self.meteors.draw(self.screen)
# Check for collisions with meteors and remove hit ones
meteors_hit = pygame.sprite.spritecollide(self.lander, self.meteors, True)
self.lander.damage += 25 * len(meteors_hit)
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
return
if self.random_alert < clock() < self.random_alert + 2:
alert_msg = self.large_font.render('*ALERT*', False, (0, 0, 255))
self.screen.blit(alert_msg, (190, 80))
thrust = self.lander.handle_inputs(pressed_key, self.alert_key)
else:
thrust = self.lander.handle_inputs(pressed_key)
if thrust:
self.screen.blit(thrust.rot_image, thrust.rect)
self.screen.blit(self.lander.rot_image, self.lander.rect)
self.draw_text(':1.f s'.format(clock()), (72, 10))
self.draw_text(':.1f m/s'.format(self.lander.veloc_y), (280, 56))
self.draw_text(':.1f m/s'.format(self.lander.veloc_x), (280, 33))
self.draw_text(':d kg'.format(self.lander.fuel), (72, 33))
self.draw_text(':.0f m'.format(self.lander.altitude), (280, 10))
self.draw_text(' %'.format(self.lander.damage), (95, 56))
self.draw_text(':.0f pts'.format(self.score), (77, 82))
self.lander.free_fall()
pygame.display.update()
landing_pad_reached = pygame.sprite.spritecollideany(self.lander, self.landing_pads)
if landing_pad_reached or self.lander.rect.bottom > self.height:
self.create_new_alert()
self.create_new_storm()
self.reset_obstacles()
meteor_storm = False
if landing_pad_reached and self.lander.has_landing_position():
self.score += 50
else:
self.lives -= 1
should_exit = self.show_crash()
if should_exit:
return
self.lander.reset_stats()
def show_crash(self):
"""Display crash message in the middle of the screen and wait for a key press"""
crash_msg = self.large_font.render('You Have Crashed!', False, (255, 0, 0))
self.screen.blit(crash_msg, (420, 300))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
# Quit the game if the 'X' button is clicked
return True
if event.type == pygame.KEYDOWN:
# Wait for a key to be pressed and if so resumes the game
return False
pygame.display.update()
self.clock.tick(self.FPS)
class Sprite(pygame.sprite.Sprite):
def __init__(self, image_file, top, left):
super().__init__()
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.top = top
self.rect.left = left
class EngineThrust(Sprite): # class for the thrust image
def __init__(self, lander_rect, lander_angle):
super().__init__('thrust.png', lander_rect.bottom - 10, lander_rect.left + 31)
self.rot_image = pygame.transform.rotate(self.image, lander_angle)
class Meteor(Sprite):
def __init__(self, image_file, top, left):
super().__init__(image_file, top, left)
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
def update(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
class Lander(Sprite):
def __init__(self, width):
super().__init__('lander.png', 0, 0)
self.width = width
self.reset_stats()
def reset_stats(self):
self.rect.top = 0
self.rect.left = randint(0, self.width - self.rect.width)
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = self.image
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.rigth < 0:
self.rect.left = self.width
if self.rect.left > self.width:
self.rect.right = 0
def start_engine(self):
self.fuel -= 5
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
@property
def altitude(self):
return 1000 - self.rect.top * 1.436
@property
def can_land(self):
return self.fuel > 0 and self.damage < 100
def has_landing_position(self):
return self.can_land and (self.veloc_y < 5) and (-5 < self.veloc_x < 5) and (-7 <= self.angle <= 7)
def handle_inputs(self, pressed_key, alert_key=None):
if not self.can_land:
return
thrust = None
rotated = False
if alert_key != pygame.K_SPACE and pressed_key[pygame.K_SPACE]:
# Show thrust image when 'space' is pressed
thrust = EngineThrust(self.rect, self.angle)
self.start_engine()
if alert_key != pygame.K_LEFT and pressed_key[pygame.K_LEFT]:
# Rotate lander anticlockwise when 'left' is pressed
self.angle += 1
rotated = True
if alert_key != pygame.K_RIGHT and pressed_key[pygame.K_RIGHT]:
# Rotate lander clockwise when 'left' is pressed
self.angle -= 1
rotated = True
if rotated:
self.angle %= 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
return thrust
if __name__ == '__main__':
init()
game = MarsLander()
game.run()
pygame.quit()
You may have seen that I changed some constants into parameters with default values, this will allow you to improve the game customization if you need to by integrating argparse
for instance.
Other changes may include restarting hazards every once in a while (spritecollideany
might be of some help to detect when every meteor have run off the background)
Thank you for taking time out to answer my question! I am looking forward to implementing many of your suggestions.
â Close Enough
Apr 20 at 16:33
add a comment |Â
up vote
2
down vote
accepted
up vote
2
down vote
accepted
You try to extract behaviour into classes, which is a good idea, but you failed at recognising patterns that could be abstracted by a single class and created way too much classes for the same thing: a Sprite
set at certain coordinates; heck, even your LandingPad
and Background
classes are exactly the same, that should have raised a red flag.
The same goes with your Lives
and GameScore
classes which could be a simple integer as they add nothing more.
You should also avoid globals and code at top-level. They, however, are better in an __init__
of some class or other. Thus IâÂÂd create a MarsLanding
class to hold that and the main
function. This would help for refactoring, also.
Lastly, you perform drawing, sprites updates and collision detection manually but pygame
allows to automate it through groups. See for instance the spritecollide
function or the Group.draw
method.
Proposed improvements follows:
import math
from time import clock
from random import uniform, randint, choice
import pygame
def init():
pygame.init()
pygame.font.init()
class MarsLander:
def __init__(self, fps=20, width=1200, height=750):
self.screen = pygame.display.set_mode((width, height))
self.clock = pygame.time.Clock()
self.FPS = fps
self.regular_font = pygame.font.SysFont('Comic Sans MS', 15)
self.alert_font = pygame.font.SysFont('Comic Sans MS', 18)
self.large_font = pygame.font.SysFont('Comic Sans MS', 50)
self.score = 0
self.lives = 3
self.obstacles = pygame.sprite.Group()
self.meteors = pygame.sprite.Group()
self.landing_pads = pygame.sprite.Group()
self.background = Sprite('mars_background_instr.png', 0, 0)
self.lander = Lander(width)
self.height = height
# Create sprites for landing pads and add them to the pads group
# TODO have coordinates dependent on actual width and height
Sprite('pad.png', 732, randint(858, 1042)).add(self.landing_pads)
Sprite('pad_tall.png', 620, randint(458, 700)).add(self.landing_pads)
Sprite('pad.png', 650, randint(0, 300)).add(self.landing_pads)
self.reset_obstacles()
self.create_new_storm()
self.create_new_alert()
@property
def game_over(self):
return self.lives < 1
def reset_obstacles(self):
"""Create obstacles at a fixed location and add the to the obstacles group"""
# TODO have coordinates dependent on actual width and height
self.obstacles.empty()
Sprite('pipe_ramp_NE.png', 540, 90).add(self.obstacles)
Sprite('building_dome.png', 575, 420).add(self.obstacles)
Sprite('satellite_SW.png', 435, 1150).add(self.obstacles)
Sprite('rocks_ore_SW.png', 620, 1080).add(self.obstacles)
Sprite('building_station_SW.png', 640, 850).add(self.obstacles)
def create_new_storm(self, number_of_images=4):
"""Create meteors and add the to the meteors group"""
# TODO have coordinates dependent on actual width and height
now = int(clock())
self.random_storm = randint(now + 3, now + 12)
self.meteors.empty()
for i in range(randint(1, 10)):
image_name = 'spaceMeteors_.png'.format(randint(1, number_of_images))
Meteor(image_name, -2 * i * self.FPS, randint(300, 900)).add(self.meteors)
def create_new_alert(self):
self.random_alert = randint(int(clock() + 5), int(clock() + 15))
self.alert_key = choice((pygame.K_SPACE, pygame.K_LEFT, pygame.K_RIGHT))
def draw_text(self, message, position, color=(255, 0, 0)):
text = self.regular_font.render(message, False, color)
self.screen.blit(text, position)
def run(self):
meteor_storm = False # Set to True whenever a storm should occur
while not self.game_over:
self.clock.tick(self.FPS)
# If the user clicks the 'X' button on the window it quits the program
if any(event.type == pygame.QUIT for event in pygame.event.get()):
return
self.screen.fill([255, 255, 255]) # Fill the empty spaces with white color
self.screen.blit(self.background.image, self.background.rect) # Place the background image
self.landing_pads.draw(self.screen)
self.obstacles.draw(self.screen)
# Check for collisions with obstacles and remove hit ones
obstacles_hit = pygame.sprite.spritecollide(self.lander, self.obstacles, True)
self.lander.damage += 10 * len(obstacles_hit)
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm and clock() > self.random_storm:
# As soon as the clock passes the random storm time it causes meteor rain
meteor_storm = True
if meteor_storm:
self.meteors.update()
self.meteors.draw(self.screen)
# Check for collisions with meteors and remove hit ones
meteors_hit = pygame.sprite.spritecollide(self.lander, self.meteors, True)
self.lander.damage += 25 * len(meteors_hit)
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
return
if self.random_alert < clock() < self.random_alert + 2:
alert_msg = self.large_font.render('*ALERT*', False, (0, 0, 255))
self.screen.blit(alert_msg, (190, 80))
thrust = self.lander.handle_inputs(pressed_key, self.alert_key)
else:
thrust = self.lander.handle_inputs(pressed_key)
if thrust:
self.screen.blit(thrust.rot_image, thrust.rect)
self.screen.blit(self.lander.rot_image, self.lander.rect)
self.draw_text(':1.f s'.format(clock()), (72, 10))
self.draw_text(':.1f m/s'.format(self.lander.veloc_y), (280, 56))
self.draw_text(':.1f m/s'.format(self.lander.veloc_x), (280, 33))
self.draw_text(':d kg'.format(self.lander.fuel), (72, 33))
self.draw_text(':.0f m'.format(self.lander.altitude), (280, 10))
self.draw_text(' %'.format(self.lander.damage), (95, 56))
self.draw_text(':.0f pts'.format(self.score), (77, 82))
self.lander.free_fall()
pygame.display.update()
landing_pad_reached = pygame.sprite.spritecollideany(self.lander, self.landing_pads)
if landing_pad_reached or self.lander.rect.bottom > self.height:
self.create_new_alert()
self.create_new_storm()
self.reset_obstacles()
meteor_storm = False
if landing_pad_reached and self.lander.has_landing_position():
self.score += 50
else:
self.lives -= 1
should_exit = self.show_crash()
if should_exit:
return
self.lander.reset_stats()
def show_crash(self):
"""Display crash message in the middle of the screen and wait for a key press"""
crash_msg = self.large_font.render('You Have Crashed!', False, (255, 0, 0))
self.screen.blit(crash_msg, (420, 300))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
# Quit the game if the 'X' button is clicked
return True
if event.type == pygame.KEYDOWN:
# Wait for a key to be pressed and if so resumes the game
return False
pygame.display.update()
self.clock.tick(self.FPS)
class Sprite(pygame.sprite.Sprite):
def __init__(self, image_file, top, left):
super().__init__()
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.top = top
self.rect.left = left
class EngineThrust(Sprite): # class for the thrust image
def __init__(self, lander_rect, lander_angle):
super().__init__('thrust.png', lander_rect.bottom - 10, lander_rect.left + 31)
self.rot_image = pygame.transform.rotate(self.image, lander_angle)
class Meteor(Sprite):
def __init__(self, image_file, top, left):
super().__init__(image_file, top, left)
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
def update(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
class Lander(Sprite):
def __init__(self, width):
super().__init__('lander.png', 0, 0)
self.width = width
self.reset_stats()
def reset_stats(self):
self.rect.top = 0
self.rect.left = randint(0, self.width - self.rect.width)
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = self.image
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.rigth < 0:
self.rect.left = self.width
if self.rect.left > self.width:
self.rect.right = 0
def start_engine(self):
self.fuel -= 5
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
@property
def altitude(self):
return 1000 - self.rect.top * 1.436
@property
def can_land(self):
return self.fuel > 0 and self.damage < 100
def has_landing_position(self):
return self.can_land and (self.veloc_y < 5) and (-5 < self.veloc_x < 5) and (-7 <= self.angle <= 7)
def handle_inputs(self, pressed_key, alert_key=None):
if not self.can_land:
return
thrust = None
rotated = False
if alert_key != pygame.K_SPACE and pressed_key[pygame.K_SPACE]:
# Show thrust image when 'space' is pressed
thrust = EngineThrust(self.rect, self.angle)
self.start_engine()
if alert_key != pygame.K_LEFT and pressed_key[pygame.K_LEFT]:
# Rotate lander anticlockwise when 'left' is pressed
self.angle += 1
rotated = True
if alert_key != pygame.K_RIGHT and pressed_key[pygame.K_RIGHT]:
# Rotate lander clockwise when 'left' is pressed
self.angle -= 1
rotated = True
if rotated:
self.angle %= 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
return thrust
if __name__ == '__main__':
init()
game = MarsLander()
game.run()
pygame.quit()
You may have seen that I changed some constants into parameters with default values, this will allow you to improve the game customization if you need to by integrating argparse
for instance.
Other changes may include restarting hazards every once in a while (spritecollideany
might be of some help to detect when every meteor have run off the background)
You try to extract behaviour into classes, which is a good idea, but you failed at recognising patterns that could be abstracted by a single class and created way too much classes for the same thing: a Sprite
set at certain coordinates; heck, even your LandingPad
and Background
classes are exactly the same, that should have raised a red flag.
The same goes with your Lives
and GameScore
classes which could be a simple integer as they add nothing more.
You should also avoid globals and code at top-level. They, however, are better in an __init__
of some class or other. Thus IâÂÂd create a MarsLanding
class to hold that and the main
function. This would help for refactoring, also.
Lastly, you perform drawing, sprites updates and collision detection manually but pygame
allows to automate it through groups. See for instance the spritecollide
function or the Group.draw
method.
Proposed improvements follows:
import math
from time import clock
from random import uniform, randint, choice
import pygame
def init():
pygame.init()
pygame.font.init()
class MarsLander:
def __init__(self, fps=20, width=1200, height=750):
self.screen = pygame.display.set_mode((width, height))
self.clock = pygame.time.Clock()
self.FPS = fps
self.regular_font = pygame.font.SysFont('Comic Sans MS', 15)
self.alert_font = pygame.font.SysFont('Comic Sans MS', 18)
self.large_font = pygame.font.SysFont('Comic Sans MS', 50)
self.score = 0
self.lives = 3
self.obstacles = pygame.sprite.Group()
self.meteors = pygame.sprite.Group()
self.landing_pads = pygame.sprite.Group()
self.background = Sprite('mars_background_instr.png', 0, 0)
self.lander = Lander(width)
self.height = height
# Create sprites for landing pads and add them to the pads group
# TODO have coordinates dependent on actual width and height
Sprite('pad.png', 732, randint(858, 1042)).add(self.landing_pads)
Sprite('pad_tall.png', 620, randint(458, 700)).add(self.landing_pads)
Sprite('pad.png', 650, randint(0, 300)).add(self.landing_pads)
self.reset_obstacles()
self.create_new_storm()
self.create_new_alert()
@property
def game_over(self):
return self.lives < 1
def reset_obstacles(self):
"""Create obstacles at a fixed location and add the to the obstacles group"""
# TODO have coordinates dependent on actual width and height
self.obstacles.empty()
Sprite('pipe_ramp_NE.png', 540, 90).add(self.obstacles)
Sprite('building_dome.png', 575, 420).add(self.obstacles)
Sprite('satellite_SW.png', 435, 1150).add(self.obstacles)
Sprite('rocks_ore_SW.png', 620, 1080).add(self.obstacles)
Sprite('building_station_SW.png', 640, 850).add(self.obstacles)
def create_new_storm(self, number_of_images=4):
"""Create meteors and add the to the meteors group"""
# TODO have coordinates dependent on actual width and height
now = int(clock())
self.random_storm = randint(now + 3, now + 12)
self.meteors.empty()
for i in range(randint(1, 10)):
image_name = 'spaceMeteors_.png'.format(randint(1, number_of_images))
Meteor(image_name, -2 * i * self.FPS, randint(300, 900)).add(self.meteors)
def create_new_alert(self):
self.random_alert = randint(int(clock() + 5), int(clock() + 15))
self.alert_key = choice((pygame.K_SPACE, pygame.K_LEFT, pygame.K_RIGHT))
def draw_text(self, message, position, color=(255, 0, 0)):
text = self.regular_font.render(message, False, color)
self.screen.blit(text, position)
def run(self):
meteor_storm = False # Set to True whenever a storm should occur
while not self.game_over:
self.clock.tick(self.FPS)
# If the user clicks the 'X' button on the window it quits the program
if any(event.type == pygame.QUIT for event in pygame.event.get()):
return
self.screen.fill([255, 255, 255]) # Fill the empty spaces with white color
self.screen.blit(self.background.image, self.background.rect) # Place the background image
self.landing_pads.draw(self.screen)
self.obstacles.draw(self.screen)
# Check for collisions with obstacles and remove hit ones
obstacles_hit = pygame.sprite.spritecollide(self.lander, self.obstacles, True)
self.lander.damage += 10 * len(obstacles_hit)
pressed_key = pygame.key.get_pressed() # Take pressed key value
if not meteor_storm and clock() > self.random_storm:
# As soon as the clock passes the random storm time it causes meteor rain
meteor_storm = True
if meteor_storm:
self.meteors.update()
self.meteors.draw(self.screen)
# Check for collisions with meteors and remove hit ones
meteors_hit = pygame.sprite.spritecollide(self.lander, self.meteors, True)
self.lander.damage += 25 * len(meteors_hit)
if pressed_key[pygame.K_ESCAPE]: # Stop game if the 'Esc' button is pressed
return
if self.random_alert < clock() < self.random_alert + 2:
alert_msg = self.large_font.render('*ALERT*', False, (0, 0, 255))
self.screen.blit(alert_msg, (190, 80))
thrust = self.lander.handle_inputs(pressed_key, self.alert_key)
else:
thrust = self.lander.handle_inputs(pressed_key)
if thrust:
self.screen.blit(thrust.rot_image, thrust.rect)
self.screen.blit(self.lander.rot_image, self.lander.rect)
self.draw_text(':1.f s'.format(clock()), (72, 10))
self.draw_text(':.1f m/s'.format(self.lander.veloc_y), (280, 56))
self.draw_text(':.1f m/s'.format(self.lander.veloc_x), (280, 33))
self.draw_text(':d kg'.format(self.lander.fuel), (72, 33))
self.draw_text(':.0f m'.format(self.lander.altitude), (280, 10))
self.draw_text(' %'.format(self.lander.damage), (95, 56))
self.draw_text(':.0f pts'.format(self.score), (77, 82))
self.lander.free_fall()
pygame.display.update()
landing_pad_reached = pygame.sprite.spritecollideany(self.lander, self.landing_pads)
if landing_pad_reached or self.lander.rect.bottom > self.height:
self.create_new_alert()
self.create_new_storm()
self.reset_obstacles()
meteor_storm = False
if landing_pad_reached and self.lander.has_landing_position():
self.score += 50
else:
self.lives -= 1
should_exit = self.show_crash()
if should_exit:
return
self.lander.reset_stats()
def show_crash(self):
"""Display crash message in the middle of the screen and wait for a key press"""
crash_msg = self.large_font.render('You Have Crashed!', False, (255, 0, 0))
self.screen.blit(crash_msg, (420, 300))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
# Quit the game if the 'X' button is clicked
return True
if event.type == pygame.KEYDOWN:
# Wait for a key to be pressed and if so resumes the game
return False
pygame.display.update()
self.clock.tick(self.FPS)
class Sprite(pygame.sprite.Sprite):
def __init__(self, image_file, top, left):
super().__init__()
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.top = top
self.rect.left = left
class EngineThrust(Sprite): # class for the thrust image
def __init__(self, lander_rect, lander_angle):
super().__init__('thrust.png', lander_rect.bottom - 10, lander_rect.left + 31)
self.rot_image = pygame.transform.rotate(self.image, lander_angle)
class Meteor(Sprite):
def __init__(self, image_file, top, left):
super().__init__(image_file, top, left)
self.speed_y = uniform(5, 10)
self.speed_x = uniform(-2, 2)
def update(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
class Lander(Sprite):
def __init__(self, width):
super().__init__('lander.png', 0, 0)
self.width = width
self.reset_stats()
def reset_stats(self):
self.rect.top = 0
self.rect.left = randint(0, self.width - self.rect.width)
self.veloc_y = uniform(0.0, 1.0)
self.veloc_x = uniform(-1.0, 1.0)
self.fuel = 500
self.angle = 0
self.damage = 0
self.rot_image = self.image
def free_fall(self):
self.rect.y += self.veloc_y
self.rect.x += self.veloc_x
self.veloc_y += 0.1
if self.rect.top < 0:
self.rect.top = 0
self.veloc_y = uniform(0.0, 1.0)
if self.rect.rigth < 0:
self.rect.left = self.width
if self.rect.left > self.width:
self.rect.right = 0
def start_engine(self):
self.fuel -= 5
self.veloc_x = self.veloc_x + 0.33 * math.sin(math.radians(-self.angle))
self.veloc_y = self.veloc_y - 0.33 * math.cos(math.radians(self.angle))
@property
def altitude(self):
return 1000 - self.rect.top * 1.436
@property
def can_land(self):
return self.fuel > 0 and self.damage < 100
def has_landing_position(self):
return self.can_land and (self.veloc_y < 5) and (-5 < self.veloc_x < 5) and (-7 <= self.angle <= 7)
def handle_inputs(self, pressed_key, alert_key=None):
if not self.can_land:
return
thrust = None
rotated = False
if alert_key != pygame.K_SPACE and pressed_key[pygame.K_SPACE]:
# Show thrust image when 'space' is pressed
thrust = EngineThrust(self.rect, self.angle)
self.start_engine()
if alert_key != pygame.K_LEFT and pressed_key[pygame.K_LEFT]:
# Rotate lander anticlockwise when 'left' is pressed
self.angle += 1
rotated = True
if alert_key != pygame.K_RIGHT and pressed_key[pygame.K_RIGHT]:
# Rotate lander clockwise when 'left' is pressed
self.angle -= 1
rotated = True
if rotated:
self.angle %= 360
self.rot_image = pygame.transform.rotate(self.image, self.angle)
return thrust
if __name__ == '__main__':
init()
game = MarsLander()
game.run()
pygame.quit()
You may have seen that I changed some constants into parameters with default values, this will allow you to improve the game customization if you need to by integrating argparse
for instance.
Other changes may include restarting hazards every once in a while (spritecollideany
might be of some help to detect when every meteor have run off the background)
answered Apr 19 at 21:33
Mathias Ettinger
21.8k32876
21.8k32876
Thank you for taking time out to answer my question! I am looking forward to implementing many of your suggestions.
â Close Enough
Apr 20 at 16:33
add a comment |Â
Thank you for taking time out to answer my question! I am looking forward to implementing many of your suggestions.
â Close Enough
Apr 20 at 16:33
Thank you for taking time out to answer my question! I am looking forward to implementing many of your suggestions.
â Close Enough
Apr 20 at 16:33
Thank you for taking time out to answer my question! I am looking forward to implementing many of your suggestions.
â Close Enough
Apr 20 at 16:33
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f192128%2fmars-lander-pygame%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Could you upload the images somewhere, please? We can't run the code without the assets.
â Gareth Rees
Apr 17 at 9:47
Here you go: photos.app.goo.gl/y8mNYY9bLvbv9poM2
â Close Enough
Apr 19 at 12:04