def get_line_segments(self):
        top_left = make_vector(self.rect.left, self.rect.top)
        top_right = make_vector(self.rect.right, self.rect.top)
        bottom_right = make_vector(self.rect.right, self.rect.bottom)
        bottom_left = make_vector(self.rect.left, self.rect.bottom)

        return [(top_left, top_right), (top_right, bottom_right),
                (bottom_right, bottom_left), (bottom_left, top_left)]
Exemple #2
0
    def __init__(self, input_state, state, size, left_player_generator,
                 right_player_generator):
        assert size.width > 0 and size.height > 0

        self.bounds = pygame.Rect(0, 0, size.width, size.height)
        self._background = config.BACKGROUND_COLOR

        net = Board._create_net(self.bounds)

        self._ball = Board._create_ball(
            self.bounds, initial_velocity=Board._create_initial_velocity())

        left_center = self._create_paddle(paddle_type=PaddleType.VERTICAL)
        left_top = self._create_paddle(paddle_type=PaddleType.TOP)
        left_bottom = self._create_paddle(paddle_type=PaddleType.BOTTOM)

        right_center = self._create_paddle(player=Board.RIGHT_PLAYER)
        right_top = self._create_paddle(player=Board.RIGHT_PLAYER,
                                        paddle_type=PaddleType.TOP)
        right_bottom = self._create_paddle(player=Board.RIGHT_PLAYER,
                                           paddle_type=PaddleType.BOTTOM)

        self._paddles = pygame.sprite.Group(left_center, left_top, left_bottom,
                                            right_center, right_top,
                                            right_bottom)
        self._passives = pygame.sprite.Group(net)

        self._status = Board.IN_PROGRESS

        self._left_player = left_player_generator(input_state, left_center,
                                                  left_top, left_bottom)
        self._right_player = right_player_generator(input_state, right_center,
                                                    right_top, right_bottom)

        left_score_pos = make_vector(left_top.rect.centerx,
                                     left_top.rect.bottom)
        right_score_pos = make_vector(right_top.rect.centerx,
                                      right_top.rect.bottom)

        self._left_score = self._create_text(
            left_score_pos,
            self._left_player.name + ": " + str(state.points[0]))
        self._right_score = self._create_text(
            right_score_pos,
            self._right_player.name + ": " + str(state.points[1]))

        # text is centered at given pos, but this means it will overlap the top paddles somewhat
        delta_pos = make_vector(
            0,
            max(0.5 * self._left_score.rect.height,
                0.5 * self._right_score.rect.height))

        self._left_score.set_position(self._left_score.get_position() +
                                      delta_pos)
        self._right_score.set_position(self._right_score.get_position() +
                                       delta_pos)

        self._passives.add(self._left_score, self._right_score)
Exemple #3
0
    def _create_initial_velocity(cls):
        # select a random angle to move at
        angle = random.uniform(0, 3.14159 * 2.0)

        # select a random speed
        speed = random.uniform(0, config.BALL_MAX_SPEED -
                               config.BALL_MIN_SPEED) + config.BALL_MIN_SPEED

        # calculate velocity
        return make_vector(math.cos(angle), math.sin(angle)) * speed
    def __init__(self, bounds, radius, velocity_vector):
        super().__init__()

        self.velocity = velocity_vector
        self.bounds = bounds
        self.position = make_vector(self.bounds.centerx, self.bounds.centery)

        self.image = config.BALL_SURFACE

        self.rect = pygame.Rect(0, 0, radius * 2, radius * 2)
        self.rect.center = self.position
    def __init__(self, input_state, previous_state=None):
        super().__init__(input_state, previous_state)

        # rather than clone this time, take a snapshot of the board's current status
        # this can be used to smoothly move board off-screen and results on-screen
        self._snapshot = pygame.Surface(config.WINDOW_SIZE)
        self._snapshot_rect = pygame.Rect(0, 0, self._snapshot.get_width(),
                                          self._snapshot.get_height())
        self._snapshot_y_position = 0.0

        previous_state.board.draw(self._snapshot, draw_ball=False)

        # create "X won game" message
        winner, score = (previous_state.board.left_player, previous_state.games_won[0]) \
            if previous_state.board.get_status() == Board.LEFT_PLAYER \
            else (previous_state.board.right_player, self.games_won[1])

        self._game_won = entities.TextSprite(text=winner.name + " wins! ",
                                             color=config.SCORE_COLOR,
                                             size=26)

        self._play_again = entities.TextSprite(text="Play again? Y/N",
                                               color=config.SCORE_COLOR,
                                               size=18)

        self._game_won.set_center(previous_state.board.bounds.center)

        below_won_prompt = make_vector(
            self._game_won.rect.centerx,
            self._game_won.rect.bottom + self._play_again.rect.height)
        self._play_again.set_center(below_won_prompt)

        # track display time
        self._finished = False
        self._next_state = None

        # play long victory (if appropriate)
        sound = config.VICTORY_LONG if winner is previous_state.board.right_player else config.FAILURE_LONG

        if sound is not None:
            sound.play()
    def __init__(self, input_state, previous_state=None):
        super().__init__(input_state, previous_state)
        self._game = PlayGame(input_state, previous_state)
        self._display_time = 0

        str_messages = \
            ["Game starts in " + str(x) for x in reversed(range(1, config.COUNTDOWN_BEGIN + 1))]
        str_messages.append("Begin!")

        self._countdown_messages = [
            entities.TextSprite(text=x, color=config.SCORE_COLOR)
            for x in str_messages
        ]

        board_center = make_vector(config.WINDOW_SIZE.width * 0.5,
                                   config.WINDOW_SIZE.height * 0.5)

        for msg in self._countdown_messages:
            center = board_center - make_vector(msg.rect.width * 0.5,
                                                -msg.rect.height * 1.5)
            msg.set_position(center)

        game_text = entities.TextSprite(text="Game " +
                                        str(sum(self.games_won) + 1),
                                        color=config.SCORE_COLOR)
        game_text.set_center(board_center -
                             make_vector(0, game_text.rect.height * 2))

        # determine how many more points each player needs to win
        left_points_needed = BeginGame.calc_points_needed(
            self.points[0], self.points[1])
        right_points_needed = BeginGame.calc_points_needed(
            self.points[1], self.points[0])

        left_points_text = entities.TextSprite(text=str(left_points_needed) +
                                               " points to go!",
                                               color=config.SCORE_COLOR)

        right_points_text = entities.TextSprite(text=str(right_points_needed) +
                                                " points to go!",
                                                color=config.SCORE_COLOR)

        # display how many games each player has won

        left_player_wins_text = entities.TextSprite(
            text=str(self.games_won[0]) + " wins", color=config.SCORE_COLOR)
        right_player_wins_text = entities.TextSprite(
            text=str(self.games_won[1]) + " wins", color=config.SCORE_COLOR)

        quarter_offset = make_vector(config.WINDOW_SIZE.width * 0.25, 0)

        left_points_text.set_center(board_center - quarter_offset)
        right_points_text.set_center(board_center + quarter_offset)

        quarter_offset.y = -(left_points_text.rect.height +
                             left_player_wins_text.rect.height)

        left_player_wins_text.set_center(board_center - quarter_offset)
        quarter_offset.y *= -1

        right_player_wins_text.set_center(board_center + quarter_offset)

        self._text_group = pygame.sprite.Group(left_player_wins_text,
                                               left_points_text,
                                               right_player_wins_text,
                                               right_points_text, game_text)
    def update(self, elapsed_seconds, paddles):
        delta_position = self.velocity * elapsed_seconds

        ball_start = self.position
        ball_end = ball_start + delta_position

        # ball is on field, determine whether it has collided with any paddles
        old_rect = self.rect.copy()

        # temporarily update rect to make use of spritecollide
        self.rect.center = ball_end
        colliding_paddles = pygame.sprite.spritecollide(self,
                                                        paddles,
                                                        dokill=False)
        self.rect = old_rect

        if any([
                Ball._intersects_paddle(ball_end, paddle)
                for paddle in colliding_paddles
        ]):
            # at least one collision has occurred; determine closest intersection point
            all_intersections = []

            for paddle in colliding_paddles:
                for segment in paddle.get_line_segments():
                    closest_point = helper.closest_point_on_line(
                        segment[0], segment[1], self.position)

                    intersections = helper.line_line_intersection(
                        ball_start, closest_point, segment[0], segment[1])

                    if len(intersections) > 0:
                        all_intersections.extend([
                            (x, segment[0], segment[1], paddle, closest_point)
                            for x in intersections if x is not None
                        ])

            if len(all_intersections) > 0:
                all_intersections.sort(
                    key=self._sort_by_least_distance_squared)

                # get the segment that we were closest to
                nearest_segment = all_intersections[0]
                segment_start, segment_end = nearest_segment[
                    1], nearest_segment[2]

                # find closest point on this segment to the ball
                closest_point = helper.closest_point_on_line(
                    segment_start, segment_end, ball_end)

                # adjust position of ball such that this distance is the ball's radius
                segment_dir = (segment_end - segment_start).normalize()
                segment_normal = make_vector(segment_dir.y, -segment_dir.x)

                projected_dir = (self.velocity.dot(segment_normal) /
                                 (segment_normal.dot(segment_normal)) *
                                 segment_normal)

                # projected_dir now tells us how to move towards the nearest segment; we can use this to
                # ensure we're at least {radius} units away
                if self.velocity.magnitude() > 0:
                    self.position = closest_point + -projected_dir.normalize(
                    ) * config.BALL_RADIUS
                    delta_position = pygame.Vector2(
                    )  # overwrote delta for this frame
                    Ball.play_sound()

                # adjust velocity based on the segment that was hit
                paddle = all_intersections[0][3]
                up = make_vector(0, 1)

                is_vertical = True if abs(
                    up.dot(segment_dir)) > 0.98 else False

                if is_vertical:
                    self.velocity.x = -self.velocity.x
                    self.velocity.y += paddle.velocity.y * elapsed_seconds * config.PADDLE_BALL_VELOCITY_MODIFIER
                else:
                    self.velocity.x += paddle.velocity.x * elapsed_seconds * config.PADDLE_BALL_VELOCITY_MODIFIER
                    self.velocity.y = -self.velocity.y

        self.position += delta_position
        self.rect.center = self.position
 def get_position(self):
     return make_vector(self.rect.left, self.rect.top)
class MovementDirection:
    LEFT = make_vector(-1, 0)
    RIGHT = make_vector(1, 0)
    UP = make_vector(0, -1)
    DOWN = make_vector(0, 1)
    STOP = make_vector()