Спускаемый аппарат Марс, с которым Pygame


Вот мой код о наших финального проекта в универе называют 'спускаемый аппарат Марса. Дело в том, что мой код, кажется, все на месте (точнее в функции 'main' в конце кода...) поэтому я хотел бы получить некоторую помощь с класть вещи на место! Это мой первый раз делает игру, используя библиотеки, с которым Pygame и этот был, безусловно, стоит усилий!

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()


256
4
задан 15 апреля 2018 в 05:04 Источник Поделиться
Комментарии
1 ответ

Вы попробуйте экстракт поведения на классы, что это хорошая идея, но вы не распознают паттерны, которые могут быть абстрагированы от одного класса и создано слишком много классов для одного: Sprite установить на определенные координаты; черт, даже ваш LandingPad и Background занятия точно так же, что должно быть поднято красное знамя.

То же самое с вашим Lives и GameScore классы, которые могут быть простым числом, так как они добавить больше нечего.

Вам также следует избегать глобальных переменных и код на верхнем уровне. Они, однако, лучше в __init__ какого-то класса или другой. Таким образом я хотел создать MarsLanding класс считают, что и main функция. Это поможет для рефакторинга, также.

И, наконец, выполнить чертеж, спрайты обновления и обнаружение столкновений вручную, но pygame позволяет автоматизировать через группы. См., например, spritecollide функция или Group.draw метод.

Предлагаемые улучшения следующие:

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()

Вы, возможно, видели, что я изменил некоторые константы в параметры со значениями по умолчанию, это позволит вам улучшить игру, игры если вам нужно по интеграции argparse например.

Другие изменения могут включать в себя перезапуск опасности каждый раз в то время (spritecollideany могли бы помочь определить, когда каждый метеор скрылся фоне)

2
ответ дан 19 апреля 2018 в 09:04 Источник Поделиться