示例#1
0
class GameEngine(object):
    # object containing Game's data

    screen_w = 1100
    screen_h = int(screen_w / 1.57)

    pitch_w = int(screen_w * 0.8)
    pitch_h = int(pitch_w / 1.57)

    back_color = (164, 143, 91)
    pitch_color_1 = (113, 152, 63)
    pitch_color_2 = (134, 185, 80)

    border_width = 2
    border_color = (174, 202, 137)

    sector_size = 50

    fps = 60
    bots_timer = 0
    test_mode = False
    wall_bounce = 1.0

    goal_delay = 2000  # in miliseconds
    start_delay = 2000  # in miliseconds
    delay_counter = 0
    play_mode = 1
    # play_mode flags states:
    # play_mode = 0 => game running
    # play_mode = -2 => game freezed, players and ball not set on the right positions, waiting time not initialized (set after goal score)
    # play_mode = -1 => game freezed, players and ball not set on the right positions, waiting time initialized (set after goal score and -2 state)
    # play_mode = 1 => game freezed, players and ball set on the right positions, waiting time not initialized (set at the beginning of the game and after -1 state (after goal score cooldown))
    # play_mode = 2 => game freezed, players and ball set on the right positions, waiting time initialized (set after 1 state; after time counter drops to 0, game starts)

    balls = []  # list containing balls
    players = []  # list containing players

    team1_color = (0, 0, 255)
    team2_color = (255, 0, 0)

    def __init__(self):
        pygame.init()

        self.screen = pygame.display.set_mode((self.screen_w, self.screen_h))
        self.fps_clock = pygame.time.Clock()

        # 2D array containing arrays, to store object in the secotrs and optimise collisions
        self.sectors = [[[]
                         for j in range(ceil(self.screen_h / self.sector_size))
                         ]
                        for i in range(ceil(self.screen_w / self.sector_size))]

        # create goals
        screen_margin = (self.screen_h - self.pitch_h) / 2
        self.goal_left = Goal(self, self.pitch_color_1,
                              (self.screen_w - self.pitch_w) / 2,
                              screen_margin + self.pitch_h * 6 / 16,
                              screen_margin + self.pitch_h * 10 / 16, 50, -1)
        self.goal_right = Goal(
            self, self.pitch_color_2,
            self.pitch_w + (self.screen_w - self.pitch_w) / 2,
            screen_margin + self.pitch_h * 6 / 16,
            screen_margin + self.pitch_h * 10 / 16, 50, 0)

        self.team_right = Team(self, self.team1_color, self.goal_right, 1)
        self.team_left = Team(self, self.team2_color, self.goal_left, -1)

        self.start_time = pygame.time.get_ticks()

    def reset(self):
        pygame.quit()

        if self.team_left.score > self.team_right.score:
            print('Team red won!')
        elif self.team_left.score < self.team_right.score:
            print('Team blue won!')
        else:
            print('It is a tie!')

        del self.sectors[:]

        del self.goal_left
        del self.goal_right

        del self.team_right
        del self.team_left

        del self.balls[:]
        del self.players[:]

    def draw_background(self):
        # draw backgroud
        self.screen.fill(self.back_color)

        # draw score
        font = pygame.font.Font(pygame.font.get_default_font(), 30)
        score_left = font.render(str(self.team_left.score), True,
                                 self.team_left.color)
        score_right = font.render(str(self.team_right.score), True,
                                  self.team_right.color)

        self.screen.blit(score_left, ((self.screen_w - self.pitch_w) / 4,
                                      (self.screen_h - self.pitch_h) / 4))
        self.screen.blit(score_right, (self.pitch_w +
                                       (self.screen_w - self.pitch_w) * 3 / 4,
                                       (self.screen_h - self.pitch_h) / 4))

        # draw status message
        if self.play_mode == -1:
            status_message = font.render('GOAL!', True, (0, 0, 0))
            status_message_rect = status_message.get_rect(
                center=(self.screen_w / 2, (self.screen_h - self.pitch_h) / 4))
            self.screen.blit(status_message, status_message_rect)
        elif self.play_mode == 2 and self.delay_counter < 500:
            status_message = font.render('PLAY!', True, (0, 0, 0))
            status_message_rect = status_message.get_rect(
                center=(self.screen_w / 2, (self.screen_h - self.pitch_h) / 4))
            self.screen.blit(status_message, status_message_rect)

        # measure time
        time = pygame.time.get_ticks() - self.start_time

        # draw time
        time_message = font.render(
            str(datetime.timedelta(seconds=int(time / 1000)))[-5:], True,
            (0, 0, 0))
        time_message_rect = time_message.get_rect(
            center=(self.screen_w / 2,
                    self.pitch_h + (self.screen_h - self.pitch_h) * 3 / 4))
        self.screen.blit(time_message, time_message_rect)

        # draw pitch border
        pygame.draw.rect(self.screen, self.border_color, \
            ((self.screen_w - self.pitch_w) / 2 - self.border_width, \
            (self.screen_h - self.pitch_h) / 2 - self.border_width, \
            self.pitch_w + self.border_width * 2 - 1, \
            self.pitch_h + self.border_width * 2 - 1), \
            self.border_width)

        # draw pitch stripes
        for i in range(0, 10):
            if i % 2:
                pygame.draw.rect(self.screen, self.pitch_color_1, \
                   ((self.screen_w - self.pitch_w) / 2 + i * self.pitch_w / 10, \
                   (self.screen_h - self.pitch_h) / 2, self.pitch_w / 10, \
                   self.pitch_h))
            else:
                pygame.draw.rect(self.screen, self.pitch_color_2, \
                    ((self.screen_w - self.pitch_w) / 2 + i * self.pitch_w / 10, \
                    (self.screen_h - self.pitch_h) / 2, self.pitch_w / 10, \
                    self.pitch_h))

        ### draw pitch lines

        # middle line
        pygame.draw.rect(self.screen, self.border_color, \
            ((self.screen_w / 2) - self.border_width + 1, \
            (self.screen_h - self.pitch_h) / 2, \
            self.border_width * 2, self.pitch_h))

        # circle
        pygame.gfxdraw.aacircle(self.screen, \
           int(self.screen_w / 2), \
           int(self.screen_h / 2), \
           int(self.pitch_w / 8), self.border_color)

        pygame.gfxdraw.aacircle(self.screen, \
           int(self.screen_w / 2), \
           int(self.screen_h / 2), \
           int(self.pitch_w / 8 - 1), self.border_color)

        pygame.gfxdraw.aacircle(self.screen, \
           int(self.screen_w / 2), \
           int(self.screen_h / 2), \
           int(self.pitch_w / 8 - 2), self.border_color)

        # dot
        pygame.gfxdraw.filled_circle(self.screen, \
           int(self.screen_w / 2), \
           int(self.screen_h / 2), \
           self.border_width * 3, self.border_color)

        # draw goals and borders

        pygame.draw.rect(
            self.screen, self.border_color,
            (self.goal_left.get_px() - self.border_width,
             self.goal_left.get_py() - self.border_width,
             self.goal_left.get_width() + self.border_width - 1,
             self.goal_left.get_height() + self.border_width * 2 - 1),
            self.border_width)
        pygame.draw.rect(
            self.screen, self.goal_left.color,
            (self.goal_left.get_px(), self.goal_left.get_py(),
             self.goal_left.get_width(), self.goal_left.get_height()))

        pygame.draw.rect(
            self.screen, self.border_color,
            (self.goal_right.get_px(),
             self.goal_right.get_py() - self.border_width,
             self.goal_right.get_width() + self.border_width - 1,
             self.goal_right.get_height() + self.border_width * 2 - 1),
            self.border_width)
        pygame.draw.rect(
            self.screen, self.goal_right.color,
            (self.goal_right.get_px(), self.goal_right.get_py(),
             self.goal_right.get_width(), self.goal_right.get_height()))

        # draw posts
        pygame.gfxdraw.filled_circle(self.screen,
                                     int(self.goal_left.post_up.p.x),
                                     int(self.goal_left.post_up.p.y),
                                     self.goal_left.post_up.size,
                                     self.goal_left.post_up.color)
        pygame.gfxdraw.aacircle(self.screen, int(self.goal_left.post_up.p.x),
                                int(self.goal_left.post_up.p.y),
                                self.goal_left.post_up.size,
                                self.goal_left.post_up.color)
        pygame.gfxdraw.filled_circle(self.screen,
                                     int(self.goal_left.post_down.p.x),
                                     int(self.goal_left.post_down.p.y),
                                     self.goal_left.post_down.size,
                                     self.goal_left.post_down.color)
        pygame.gfxdraw.aacircle(self.screen, int(self.goal_left.post_down.p.x),
                                int(self.goal_left.post_down.p.y),
                                self.goal_left.post_down.size,
                                self.goal_left.post_down.color)

        pygame.gfxdraw.filled_circle(self.screen,
                                     int(self.goal_right.post_up.p.x),
                                     int(self.goal_right.post_up.p.y),
                                     self.goal_right.post_up.size,
                                     self.goal_right.post_up.color)
        pygame.gfxdraw.aacircle(self.screen, int(self.goal_right.post_up.p.x),
                                int(self.goal_right.post_up.p.y),
                                self.goal_right.post_up.size,
                                self.goal_right.post_up.color)
        pygame.gfxdraw.filled_circle(self.screen,
                                     int(self.goal_right.post_down.p.x),
                                     int(self.goal_right.post_down.p.y),
                                     self.goal_right.post_down.size,
                                     self.goal_right.post_down.color)
        pygame.gfxdraw.aacircle(self.screen,
                                int(self.goal_right.post_down.p.x),
                                int(self.goal_right.post_down.p.y),
                                self.goal_right.post_down.size,
                                self.goal_right.post_down.color)

    def draw_graphics(self):
        #ball
        for obj in self.balls:
            self.screen.blit(
                obj.ballImage,
                pygame.rect.Rect(obj.p.x - obj.size, obj.p.y - obj.size,
                                 obj.size, obj.size))
        # goal left
        self.screen.blit(
            self.goal_left.goal,
            pygame.rect.Rect(self.goal_left.get_px(),
                             self.goal_left.get_py() - 3,
                             self.goal_left.get_width(),
                             self.goal_left.get_height()))
        #goal right
        self.screen.blit(
            self.goal_right.goal,
            pygame.rect.Rect(self.goal_right.get_px(),
                             self.goal_right.get_py() - 3,
                             self.goal_right.get_width(),
                             self.goal_right.get_height()))

    def clock_tick(self):
        return self.fps_clock.tick(self.fps)

    def new_player(self, player, team_number=None):
        # add new player to the game and team
        self.players.append(player)

        if team_number == None:
            if self.team_left.size() >= self.team_right.size():
                self.team_right.add_player(player)
            else:
                self.team_left.add_player(player)
        else:
            if team_number == 1:
                self.team_left.add_player(player)
            elif team_number == 2:
                self.team_right.add_player(player)
            else:
                print('ERROR: Wrong team number!')

    def new_ball(self, ball):
        self.balls.append(ball)

    def redraw(self):

        # control game states
        self.game_state_manager()

        # dt is time since last tick
        dt = self.clock_tick()

        self.bots_timer += dt

        if self.delay_counter != 0:
            self.delay_counter -= dt
        if self.delay_counter < 0:
            self.delay_counter = 0

        if self.play_mode == 0:
            # update objects positions and redraw players
            self.update()

        # redraw whole board
        self.display_redraw()

        # update the screen
        pygame.display.update()

    def update(self):
        # update object's positions
        for obj in self.players:
            obj.update()
        for obj in self.balls:
            obj.update()

    def display_redraw(self):
        # redraw board and players

        # draw static background
        self.draw_background()

        ### if test mode is on, draw additional markers on screen
        if self.test_mode:

            # draw sectors around ball and players
            for obj in self.players:
                sector_num = int(obj.size * 4 / self.sector_size)
                for i in range(
                        int(obj.p.x / self.sector_size) - sector_num,
                        int(obj.p.x / self.sector_size) + sector_num + 1):
                    for j in range(
                            int(obj.p.y / self.sector_size) - sector_num,
                            int(obj.p.y / self.sector_size) + sector_num + 1):
                        pygame.draw.rect(
                            self.screen, (0, 255 - ((i + j) % 2) * 50, 0),
                            (i * self.sector_size, j * self.sector_size,
                             int(self.sector_size), int(self.sector_size)))
            for obj in self.balls:
                sector_num = int(obj.size * 4 / self.sector_size)
                for i in range(
                        int(obj.p.x / self.sector_size) - sector_num,
                        int(obj.p.x / self.sector_size) + sector_num + 1):
                    for j in range(
                            int(obj.p.y / self.sector_size) - sector_num,
                            int(obj.p.y / self.sector_size) + sector_num + 1):
                        pygame.draw.rect(
                            self.screen, (0, 255 - ((i + j) % 2) * 50, 0),
                            (int(obj.p.x / self.sector_size) *
                             self.sector_size,
                             int(obj.p.y / self.sector_size) *
                             self.sector_size, int(
                                 self.sector_size), int(self.sector_size)))

            # draw hitboxes
            for obj in self.players:
                pygame.gfxdraw.aacircle(self.screen, int(obj.p.x),
                                        int(obj.p.y), obj.hitbox, (0, 0, 255))
            for obj in self.balls:
                pygame.gfxdraw.aacircle(self.screen, int(obj.p.x),
                                        int(obj.p.y), obj.hitbox, (0, 0, 255))

        # check collisions and redraw all players
        for obj in self.players:
            Collision.collide(obj)
            pygame.gfxdraw.filled_circle(self.screen, int(obj.p.x),
                                         int(obj.p.y), obj.size, obj.color)
            pygame.gfxdraw.aacircle(self.screen, int(obj.p.x), int(obj.p.y),
                                    obj.size, obj.border_color)
            pygame.gfxdraw.aacircle(self.screen, int(obj.p.x), int(obj.p.y),
                                    obj.size - 1, obj.border_color)

        # check collisions with posts
        self.goal_left.goal_collide()
        self.goal_right.goal_collide()

        # draw graphics on top of the screen
        self.draw_graphics()

    # fix objects position, to prevent walls collisions
    def walls_collision(self, obj):
        Collision.walls_collision(obj, self)

    # set game state to -2
    # add point to team which scored
    def goal_scored(self, goal):
        if goal == self.goal_left:
            self.team_right.add_point()
        else:
            self.team_left.add_point()

        self.play_mode = -2

    def game_state_manager(self):
        if self.play_mode == -2:
            # after goal score, prepare delay
            self.play_mode = -1
            self.delay_counter = self.goal_delay
        elif self.play_mode == -1 and self.delay_counter == 0:
            # if delay passed, reset positions and set mode to 1
            self.play_mode = 1
        elif self.play_mode == 1:
            # if game started (or is after goal), prepare delay
            self.positions_reset()
            self.team_left.reset_positions()
            self.team_right.reset_positions()

            self.play_mode = 2
            self.delay_counter = self.start_delay
        elif self.play_mode == 2 and self.delay_counter == 0:
            # if delay passed, start the game
            self.play_mode = 0

    def positions_reset(self):
        for obj in self.balls:
            obj.set_move((0, 0), (self.screen_w / 2, self.screen_h / 2))

    def deserialize(self, data):
        data = self.balls[0].deserialize(data)
        data = self.team_left.deserialize(data)
        data = self.team_right.deserialize(data)