Book Image

Python Game Programming By Example

Book Image

Python Game Programming By Example

Overview of this book

Table of Contents (14 chapters)
Python Game Programming By Example
Credits
About the Authors
About the Reviewers
www.PacktPub.com
Preface
Index

Starting the game


Finally, we have built the functionality needed to run the game loop—the logic required to update the ball's position according to the rebounds, and restart the game if the player loses one life.

Now we can add the following methods to our Game class to complete the development of our game:

    def start_game(self):
        self.canvas.unbind('<space>')
        self.canvas.delete(self.text)
        self.paddle.ball = None
        self.game_loop()

    def game_loop(self):
        self.check_collisions()
        num_bricks = len(self.canvas.find_withtag('brick'))
        if num_bricks == 0:
            self.ball.speed = None
            self.draw_text(300, 200, 'You win!')
        elif self.ball.get_position()[3] >= self.height:
            self.ball.speed = None
            self.lives -= 1
            if self.lives < 0:
                self.draw_text(300, 200, 'Game Over')
            else:
                self.after(1000, self.setup_game)
        else:
            self.ball.update()
            self.after(50, self.game_loop)

The start_game method, which we left unimplemented in a previous section, is responsible for unbinding the Spacebar input key so that the player cannot start the game twice, detaching the ball from the paddle, and starting the game loop.

Step by step, the game_loop method does the following:

  • It calls self.check_collisions() to process the ball's collisions. We will see its implementation in the next code snippet.

  • If the number of bricks left is zero, it means that the player has won, and a congratulations text is displayed.

  • Suppose the ball has reached the bottom of the canvas:

    • Then, the player loses one life. If the number of lives left is zero, it means that the player has lost, and the Game Over text is shown. Otherwise, the game is reset

  • Otherwise, this is what happens:

    • The position of the ball is updated according to its speed and direction, and the game loop is called again. The .after(delay, callback) method on a Tkinter widget sets a timeout to invoke a function after a delay in milliseconds. Since this statement will be executed when the game is not over yet, this creates the loop necessary to execute this logic continuously:

          def check_collisions(self):
              ball_coords = self.ball.get_position()
              items = self.canvas.find_overlapping(*ball_coords)
              objects = [self.items[x] for x in items \
                     if x in self.items]
              self.ball.collide(objects)

The check_collisions method links the game loop with the ball collision method. Since Ball.collide receives a list of game objects and canvas.find_overlapping returns a list of colliding items with a given position, we use the dictionary of items to transform each canvas item into its corresponding game object.

Remember that the items attribute of the Game class contains only those canvas items that can collide with the ball. Therefore, we need to pass only the items contained in this dictionary. Once we have filtered the canvas items that cannot collide with the ball, such as the text displayed in the top-left corner, we retrieve each game object by its key.

With list comprehensions, we can create the required list in one simple statement:

objects = [self.items[x] for x in items if x in self.items]

The basic syntax of list comprehensions is the following:

new_list = [expr(elem) for elem in collection]

This means that the new_list variable will be a list whose elements are the result of applying the expr function to each elem in the list collection.

We can filter the elements to which the expression will be applied by adding an if clause:

new_list = [expr(elem) for elem in collection if elem is not None]

This syntax is equivalent to the following loop:

new_list = []
for elem in collection:
    if elem is not None:
        new_list.append(elem)

In our case, the initial list is the list of colliding items, the if clause filters the items that are not contained in the dictionary, and the expression applied to each element retrieves the game object associated with the canvas item. The collide method is called with this list as a parameter, and the logic for the game loop is completed.