Adding the Breakout items
Now that the organization of our items is separated into these top-level classes, we can extend the __init__
method of our Game
class:
class Game(tk.Frame): def __init__(self, master): super(Game, self).__init__(master) self.lives = 3 self.width = 610 self.height = 400 self.canvas = tk.Canvas(self, bg='#aaaaff', width=self.width, height=self.height) self.canvas.pack() self.pack() self.items = {} self.ball = None self.paddle = Paddle(self.canvas, self.width/2, 326) self.items[self.paddle.item] = self.paddle for x in range(5, self.width - 5, 75): self.add_brick(x + 37.5, 50, 2) self.add_brick(x + 37.5, 70, 1) self.add_brick(x + 37.5, 90, 1) self.hud = None self.setup_game() self.canvas.focus_set() self.canvas.bind('<Left>', lambda _: self.paddle.move(-10)) self.canvas.bind('<Right>', lambda _: self.paddle.move(10)) def setup_game(self): self.add_ball() self.update_lives_text() self.text = self.draw_text(300, 200, 'Press Space to start') self.canvas.bind('<space>', lambda _: self.start_game())
This initialization is more complex that what we had at the beginning of the chapter. We can divide it into two sections:
- Game object instantiation, and their insertion into the
self.items
dictionary. This attribute contains all the canvas items that can collide with the ball, so we add only the bricks and the player's paddle to it. The keys are the references to the canvas items, and the values are the corresponding game objects. We will use this attribute later in the collision check, when we will have the colliding items and will need to fetch thegame
object. - Key input binding, via the Canvas widget. The
canvas.focus_set()
call sets the focus on the canvas, so the input events are directly bound to this widget. Then we bind the left and right keys to the paddle'smove()
method and the spacebar to trigger the game start. Thanks to thelambda
construct, we can define anonymous functions as event handlers. Since the callback argument of thebind
method is a function that receives a Tkinter event as an argument, we define a lambda that ignores the first parameter—lambda _: <expression>
.
Our new add_ball
and add_brick
methods are used to create game objects and perform a basic initialization. While the first one creates a new ball on top of the player's paddle, the second one is a shorthand way of adding a Brick
instance:
def add_ball(self): if self.ball is not None: self.ball.delete() paddle_coords = self.paddle.get_position() x = (paddle_coords[0] + paddle_coords[2]) * 0.5 self.ball = Ball(self.canvas, x, 310) self.paddle.set_ball(self.ball) def add_brick(self, x, y, hits): brick = Brick(self.canvas, x, y, hits) self.items[brick.item] = brick
The draw_text
method will be used to display text messages in the canvas. The underlying item created with canvas.create_text()
is returned, and it can be used to modify the information:
def draw_text(self, x, y, text, size='40'): font = ('Helvetica', size) return self.canvas.create_text(x, y, text=text, font=font)
The update_lives_text
method displays the number of lives left and changes its text if the message is already displayed. It is called when the game is initialized—this is when the text is drawn for the first time—and it is also invoked when the player misses a ball rebound:
def update_lives_text(self): text = 'Lives: %s' % self.lives if self.hud is None: self.hud = self.draw_text(50, 20, text, 15) else: self.canvas.itemconfig(self.hud, text=text)
We leave start_game
unimplemented for now, since it triggers the game loop, and this logic will be added in the next section. Since Python requires a code block for each method, we use the pass
statement. This does not execute any operation, and it can be used as a placeholder when a statement is required syntactically:
def start_game(self): pass
See the chapter1_02.py
module, a script with the sample code we have so far. If you execute this script, it will display a Tkinter window like the one shown in the following figure. At this point, we can move the paddle horizontally, so we are ready to start the game and hit some bricks: