Lesson 4 of 4Programming games
Programming games · Lesson 4 · 60 minutes
Project: paddle duel
What you'll learn
- Move a ball with a speed in x and y direction
- Let the ball bounce off walls and paddles
- Program a simple computer opponent
Two paddles, one ball
In the paddle duel there is a paddle on the left and one on the right. A ball flies back and forth between them. Whoever lets it through gives the other one a point. You play on the right, the computer plays on the left.
A ball with speed
So far everything moved in one direction only. Now the ball gets two speeds: dx for left-right and dy for up-down. Both are added in every frame. A negative dy means: upwards.
from codelix import game
game.window(400, 300)
ball = {"x": 50, "y": 250, "dx": 3, "dy": -2}
def update():
ball["x"] = ball["x"] + ball["dx"]
ball["y"] = ball["y"] + ball["dy"]
game.circle(ball["x"], ball["y"], 8, "orange")
game.run(update)The ball flies diagonally up and to the right – and then out of the picture.
Bouncing
When a ball bounces off a wall, one direction flips. At the ceiling, dy = -2 becomes dy = 2. You write it like this: ball["dy"] = -ball["dy"].
from codelix import game
game.window(400, 300)
ball = {"x": 50, "y": 150, "dx": 4, "dy": 3, "r": 10}
def update():
ball["x"] = ball["x"] + ball["dx"]
ball["y"] = ball["y"] + ball["dy"]
if ball["x"] - ball["r"] < 0 or ball["x"] + ball["r"] > 400:
ball["dx"] = -ball["dx"]
if ball["y"] - ball["r"] < 0 or ball["y"] + ball["r"] > 300:
ball["dy"] = -ball["dy"]
game.circle(ball["x"], ball["y"], ball["r"], "orange")
game.run(update)Watch out, sticky trap: If the ball is still a little inside the wall after flipping, it flips again in the next frame. Then it wobbles along the wall. That’s why the task also checks the direction: at the top, it only flips while it is flying upwards.
The computer opponent
How does a computer play? Very simply: if the ball is lower than the middle of its paddle, the paddle moves down. Otherwise up. Because it only manages 3 pixels per frame, you can beat it:
paddle = {"y": 120, "h": 60}
ball_y = 200
for frame in range(5):
middle = paddle["y"] + paddle["h"] / 2
if middle < ball_y:
paddle["y"] = paddle["y"] + 3
elif middle > ball_y:
paddle["y"] = paddle["y"] - 3
print(f"Frame {frame}: paddle at y = {paddle['y']}")In the task, everything around it is already done: the controls, the opponent and the points. Only the functions for the ball are missing. After that you can play – with ↑ and ↓ or with the buttons below the game area.
Keep building: Play with two people: steer the left paddle with
game.key("w")andgame.key("s")instead of the computer. Make the ball a little faster after every hit. Or end the game withgame.stop()as soon as someone has 5 points.
Your task
Your turn
Write the two functions that make the paddle duel work. 1. move_ball(ball, height): Move the ball by dx and dy. If it then hits the top (y - r < 0) or the bottom (y + r > height), flip dy – but only if it is moving towards that wall right now. 2. bounce(ball, paddle): If the ball touches the paddle, flip dx and return True, otherwise False. The ball counts as a square from x - r to x + r and from y - r to y + r.
from codelix import game
game.window(400, 300)
ball = {"x": 200, "y": 150, "dx": 4, "dy": 3, "r": 8}
left = {"x": 10, "y": 120, "w": 10, "h": 60}
right = {"x": 380, "y": 120, "w": 10, "h": 60}
score = {"left": 0, "right": 0}
def move_ball(ball, height):
pass
def bounce(ball, paddle):
return False
def update():
# You steer the right paddle with ↑ and ↓, the computer steers the left one
if game.key("up") and right["y"] > 0:
right["y"] = right["y"] - 6
if game.key("down") and right["y"] < 240:
right["y"] = right["y"] + 6
if left["y"] + 30 < ball["y"]:
left["y"] = left["y"] + 3
elif left["y"] + 30 > ball["y"]:
left["y"] = left["y"] - 3
move_ball(ball, 300)
if ball["dx"] < 0:
bounce(ball, left)
else:
bounce(ball, right)
if ball["x"] < 0 or ball["x"] > 400:
if ball["x"] < 0:
score["right"] = score["right"] + 1
else:
score["left"] = score["left"] + 1
ball["x"], ball["y"] = 200, 150
game.line(200, 0, 200, 300, "#334", 2)
game.rect(left["x"], left["y"], left["w"], left["h"], "white")
game.rect(right["x"], right["y"], right["w"], right["h"], "cyan")
game.circle(ball["x"], ball["y"], ball["r"], "orange")
game.text(160, 10, f"{score['left']} : {score['right']}", "white", 24)
game.run(update)- Something to think about
ball["x"]=ball["x"]+ball["dx"]– and the same for y with dy. - Which command helps?
Flipping means:
ball["dy"]=-ball["dy"]. The ball hits the top whenball["y"]-ball["r"]<0.Only flip dy at the top
ifdy<0 (the ball flies up), and at the bottom onlyifdy>0.It's the same collision as in the catch game – only the ball is a square from x − r to x
+r. - Gap code – fill in the ___
from codelix import game game.window(400, 300) ball = {"x": 200, "y": 150, "dx": 4, "dy": 3, "r": 8} left = {"x": 10, "y": 120, "w": 10, "h": 60} right = {"x": 380, "y": 120, "w": 10, "h": 60} score = {"left": 0, "right": 0} def move_ball(ball, height): ball["x"] = ___ ball["y"] = ___ if ___: ball["dy"] = ___ if ___: ball["dy"] = ___ def bounce(ball, paddle): r = ___ ___ ___ ball["dx"] = ___ return ___ return False def update(): # You steer the right paddle with ↑ and ↓, the computer steers the left one if game.key("up") and right["y"] > 0: right["y"] = right["y"] - 6 if game.key("down") and right["y"] < 240: right["y"] = right["y"] + 6 if left["y"] + 30 < ball["y"]: left["y"] = left["y"] + 3 elif left["y"] + 30 > ball["y"]: left["y"] = left["y"] - 3 move_ball(ball, 300) if ball["dx"] < 0: bounce(ball, left) else: bounce(ball, right) if ball["x"] < 0 or ball["x"] > 400: if ball["x"] < 0: score["right"] = score["right"] + 1 else: score["left"] = score["left"] + 1 ball["x"], ball["y"] = 200, 150 game.line(200, 0, 200, 300, "#334", 2) game.rect(left["x"], left["y"], left["w"], left["h"], "white") game.rect(right["x"], right["y"], right["w"], right["h"], "cyan") game.circle(ball["x"], ball["y"], ball["r"], "orange") game.text(160, 10, f"{score['left']} : {score['right']}", "white", 24) game.run(update)
Solve it without hints to earn a ⭐ on your progress page.
Show solution🔒 Unlocks after all hints or 2 checks
Try it yourself first – mistakes teach you the most. Here is one possible solution:
from codelix import game
game.window(400, 300)
ball = {"x": 200, "y": 150, "dx": 4, "dy": 3, "r": 8}
left = {"x": 10, "y": 120, "w": 10, "h": 60}
right = {"x": 380, "y": 120, "w": 10, "h": 60}
score = {"left": 0, "right": 0}
def move_ball(ball, height):
ball["x"] = ball["x"] + ball["dx"]
ball["y"] = ball["y"] + ball["dy"]
if ball["y"] - ball["r"] < 0 and ball["dy"] < 0:
ball["dy"] = -ball["dy"]
if ball["y"] + ball["r"] > height and ball["dy"] > 0:
ball["dy"] = -ball["dy"]
def bounce(ball, paddle):
r = ball["r"]
if (ball["x"] - r < paddle["x"] + paddle["w"] and ball["x"] + r > paddle["x"]
and ball["y"] - r < paddle["y"] + paddle["h"] and ball["y"] + r > paddle["y"]):
ball["dx"] = -ball["dx"]
return True
return False
def update():
# You steer the right paddle with ↑ and ↓, the computer steers the left one
if game.key("up") and right["y"] > 0:
right["y"] = right["y"] - 6
if game.key("down") and right["y"] < 240:
right["y"] = right["y"] + 6
if left["y"] + 30 < ball["y"]:
left["y"] = left["y"] + 3
elif left["y"] + 30 > ball["y"]:
left["y"] = left["y"] - 3
move_ball(ball, 300)
if ball["dx"] < 0:
bounce(ball, left)
else:
bounce(ball, right)
if ball["x"] < 0 or ball["x"] > 400:
if ball["x"] < 0:
score["right"] = score["right"] + 1
else:
score["left"] = score["left"] + 1
ball["x"], ball["y"] = 200, 150
game.line(200, 0, 200, 300, "#334", 2)
game.rect(left["x"], left["y"], left["w"], left["h"], "white")
game.rect(right["x"], right["y"], right["w"], right["h"], "cyan")
game.circle(ball["x"], ball["y"], ball["r"], "orange")
game.text(160, 10, f"{score['left']} : {score['right']}", "white", 24)
game.run(update)