Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Python Game Programming By Example

You're reading from   Python Game Programming By Example A pragmatic guide for developing your own games with Python

Arrow left icon
Product type Paperback
Published in Sep 2015
Publisher
ISBN-13 9781785281532
Length 230 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Toc

Movement and collisions

Now that we have placed all of our game objects, we can define the methods that will be executed in the game loop. This loop runs indefinitely until the game ends, and each iteration updates the position of the ball and checks the collision that occurs.

With the Canvas widget, we can calculate what the items that overlap with the given coordinates are, so for now, we will implement the methods that are responsible for moving the ball and changing its direction.

Let's start with the movement of the ball and the conditions for recreating the bouncing effect when it reaches the canvas borders:

    def update(self):
        coords = self.get_position()
        width = self.canvas.winfo_width()
        if coords[0] <= 0 or coords[2] >= width:
            self.direction[0] *= -1
        if coords[1] <= 0:
            self.direction[1] *= -1
        x = self.direction[0] * self.speed
        y = self.direction[1] * self.speed
        self.move(x, y)

The update method does the following:

  • It gets the current position and the width of the canvas. It stores the values in the coords and width local variables, respectively.
  • If the position collides with the left or right border of the canvas, the horizontal component of the direction vector changes its sign
  • If the position collides with the upper border of the canvas, the vertical component of the direction vector changes its sign
  • We scale the direction vector by the ball's speed
  • The self.move(x, y) moves the ball

For instance, if the ball hits the left border, the coords[0] <= 0 condition evaluates to true, so the x-axis component of the direction changes its sign, as shown in this diagram:

Movement and collisions

If the ball hits the top-right corner, both coords[2] >= width and coords[1] <= 0 evaluate to true. This changes the sign of both the components of the direction vector, like this:

Movement and collisions

The logic of the collision with a brick is a bit more complex, since the direction of the rebound depends on the side where the collision occurs.

We will calculate the x-axis component of the ball's center and check whether it is between the lowermost and uppermost x-axis coordinates of the colliding brick. To translate this into a quick implementation, the following snippet shows the possible changes in the direction vector as per the ball and brick coordinates:

        coords = self.get_position()
        x = (coords[0] + coords[2]) * 0.5
        brick_coords = brick.get_position()
        if x > brick_coords[2]:
            self.direction[0] = 1
        elif x < brick_coords[0]:
            self.direction[0] = -1
        else:
            self.direction[1] *= -1

For instance, this collision causes a horizontal rebound, since the brick is being hit from above, as shown here:

Movement and collisions

On the other hand, a collision from the right-hand side of the brick would be as follows:

Movement and collisions

This is valid when the ball hits the paddle or a single brick. However, the ball can hit two bricks at the same time. In this situation, we cannot execute the previous statements for each brick; if the y-axis direction is multiplied by -1 twice, the value in the next iteration of the game loop will be the same.

We could check whether the collision occurred from above or behind, but the problem with multiple bricks is that the ball may overlap the lateral of one of the bricks and, therefore, change the x-axis direction as well. This happens because of the ball's speed and the rate at which its position is updated.

We will simplify this by assuming that a collision with multiple bricks at the same time occurs only from above or below. That means that it changes the y-axis component of the direction without calculating the position of the colliding bricks:

        if len(game_objects) > 1:
            self.direction[1] *= -1

With these two conditions, we can define the collide method. As we will see later, another method will be responsible for determining the list of colliding bricks, so this method only handles the outcome of a collision with one or more bricks:

    def collide(self, game_objects):
        coords = self.get_position()
        x = (coords[0] + coords[2]) * 0.5
        if len(game_objects) > 1:
            self.direction[1] *= -1
        elif len(game_objects) == 1:
            game_object = game_objects[0]
            coords = game_object.get_position()
            if x > coords[2]:
                self.direction[0] = 1
            elif x < coords[0]:
                self.direction[0] = -1
            else:
                self.direction[1] *= -1

        for game_object in game_objects:
            if isinstance(game_object, Brick):
                game_object.hit()

Note that this method hits every brick instance that is colliding with the ball, so the hit counters are decreased and the bricks are removed if they reach zero hits.

You have been reading a chapter from
Python Game Programming By Example
Published in: Sep 2015
Publisher:
ISBN-13: 9781785281532
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime