使用Python Turtle 创建Breakout游戏

使用Python Turtle 创建Breakout游戏

在这篇文章中,我们将建立一个著名游戏的克隆: ‘Breakout’。 我们将使用Python的一个内置库建立这个游戏: Turtle.

要求

  • 一个像PyCharm这样的IDE。
  • Python的基础知识
  • Python-Turtle的基本知识。
  • 愿意学习

项目结构

我们希望保持编程的简单性。最好的方法是将问题分解成更简单的任务,并逐一实现。我们使用面向对象的编程来实现这一目标。这将使我们的组件相互独立,从而使它们的工作变得容易。

使用Python Turtle 创建Breakout游戏

逐步实现

第1步:创建一个黑色背景颜色的窗口:

这里我们简单地导入turtle,并从它的Screen类中创建一个对象来控制程序窗口的发生。我们给它以大小、颜色和标题,以获得一个空白的黑色屏幕。

main.py

import turtle as tr
  
screen = tr.Screen()
screen.setup(width=1200, height=600)
screen.bgcolor('black')
screen.title('Breakout')
  
tr.mainloop()

第二步:创建桨:

首先要做的是导入海龟库。决定桨在每次移动时的距离。我们创建桨的类,继承海龟类,然后赋予我们的桨所需的属性,其中包括它的形状、大小和颜色。我们不希望它作画,因此我们使用penup方法。然后我们让它走到屏幕的最低处和中间。我们的窗口宽度为600。由于每个乌龟屏幕是一个XY平面,原点在中心,所以最低点是-300。默认情况下,乌龟对象的尺寸是20 X 20。因为我们只拉伸了它的长度,宽度没有受到影响。我们把乌龟放在最低点以上20个像素处,这样它就能保持可见,否则它的一半就会被隐藏起来。我们还需要它移动,因此我们定义了两个方法来使它向左和向右移动。由于默认情况下乌龟是朝向右边的,所以它基本上会向前和向后移动。

paddle.py

from turtle import Turtle
  
MOVE_DIST = 70
  
class Paddle(Turtle):
    def __init__(self):
        super().__init__()
        self.color('steel blue')
        self.shape('square')
        self.penup()
        self.shapesize(stretch_wid=1, stretch_len=10)
        self.goto(x=0, y=-280)
  
    def move_left(self):
        self.backward(MOVE_DIST)
  
    def move_right(self):
        self.forward(MOVE_DIST)

现在,导入划桨的类,并从中创建一个对象。我们使用屏幕对象的listen()方法,使我们的程序对按下的键作出反应。然后,使用onkey()方法,我们定义它需要听哪个键,也就是左和右的方向键。为此,我们分别将我们的乌龟向后中心和向前移动。这里我们也使用了屏幕对象的两个方法,tracer()和update()。我们在程序的窗口中控制动画。通过向tracer传递0,我们关闭了它,通过使用update,我们打开了它。因为有了这些方法,我们可以直接看到屏幕底部的桨叶。如果我们不使用这些方法,我们就会看到在窗口中央创建一个桨,然后移动到底部。

main.py

import turtle as tr
from paddle import Paddle
  
screen = tr.Screen()
screen.setup(width=1200, height=600)
screen.bgcolor('black')
screen.title('Breakout')
screen.tracer(0)
  
paddle = Paddle()
  
screen.listen()
screen.onkey(key='Left', fun=paddle.move_left)
screen.onkey(key='Right', fun=paddle.move_right)
  
screen.update()
  
tr.mainloop()

第三步:创建我们的球

与桨类似,我们继承了Turtle类并创建了一个球,它只是一个具有圆形的乌龟。我们也不希望它作画,所以我们再次使用penup。我们定义的第一个方法是让它移动。由于我们的球将在XY平面上移动,我们让它的x和y坐标同时增加相同的量。这个移动方法将在我们的main.py中被循环调用,因为我们看到一个一直在移动的球。然后我们创建一个方法,当它与墙壁、砖头或球拍碰撞时,使其改变方向,从而将其弹开。有时只需要改变侧面的方向,有时需要改变垂直方向,有时则两者都需要。因此我们有两个if语句。要使方向改变,只需使移动距离为负数,为所需的坐标。最后,我们有一个复位方法,它使我们的球回到它开始的地方。我们希望当球与底部的墙相撞时,这意味着用户没有击中它,应该失去一条生命。

ball.py

from turtle import Turtle
  
MOVE_DIST = 10
  
class Ball(Turtle):
    def __init__(self):
        super().__init__()
        self.shape('circle')
        self.color('white')
        self.penup()
        self.x_move_dist = MOVE_DIST
        self.y_move_dist = MOVE_DIST
        self.reset()
  
    def move(self):
        # move only 10 steps ahead, both vertically 
        # and horizontally.
        new_y = self.ycor() + self.y_move_dist
        new_x = self.xcor() + self.x_move_dist
        self.goto(x=new_x, y=new_y)
  
    def bounce(self, x_bounce, y_bounce):
        if x_bounce:
            # reverse the horizontal direction
            self.x_move_dist *= -1
  
        if y_bounce:
            # reverse the vertical direction
            self.y_move_dist *= -1
  
    def reset(self):
        # ball should got to an initial position, 
        # always moving up.
        self.goto(x=0, y=-240)
        self.y_move_dist = 10

我们在里面创建一个无限的while循环,我们做的第一件事就是更新我们的屏幕,延迟我们的程序并使球移动。计算机的速度比我们人类所能理解的要快得多,因此我们的程序在每次迭代时都会延迟一点。在每次迭代中,我们也让球移动一次。我们还检查我们的球的当前坐标是什么。我们已经知道了窗口的尺寸,以及在原点位于窗口中心的情况下,边缘的X和Y点将是什么。因此,我们检查我们的球的坐标是否已经超过了边缘,基于此,我们让它弹起。类似地,我们将有方法来检测与球拍的碰撞和与砖块的碰撞。

main.py

import turtle as tr
from paddle import Paddle
from ball import Ball
import time
  
screen = tr.Screen()
screen.setup(width=1200, height=600)
screen.bgcolor('black')
screen.title('Breakout')
screen.tracer(0)
  
paddle = Paddle()
ball = Ball()
  
playing_game = True
  
  
screen.listen()
screen.onkey(key='Left', fun=paddle.move_left)
screen.onkey(key='Right', fun=paddle.move_right)
  
def check_collision_with_walls():
  
    global ball
  
    # detect collision with left and right walls:
    if ball.xcor() < -580 or ball.xcor() > 570:
        ball.bounce(x_bounce=True, y_bounce=False)
        return
  
    # detect collision with upper wall
    if ball.ycor() > 270:
        ball.bounce(x_bounce=False, y_bounce=True)
        return
  
    # detect collision with bottom wall
    # In this case, user failed to hit the ball 
    # thus he loses. The game resets.
    if ball.ycor() < -280:
        ball.reset()
        return
  
def check_collision_with_paddle():
    pass
  
def check_collision_with_bricks():
    pass
  
while playing_game:
    screen.update()
    time.sleep(0.01)
    ball.move()
  
    check_collision_with_walls()
  
  
tr.mainloop()

第四步:让桨能够弹开球。

桨的长度是200,乌龟的距离法测量两只乌龟中心的距离。因此,桨的左边和右边都有100。同样地,球(20 X 20)在其中心点周围有10。因此,当它们的中心点之间的距离为100+10<=110时,球就接触到了划子。同时,我们也要检查球的y坐标是否已经达到了桨的上端y坐标。当这两个条件都满足时,球就会被球拍击中,并且应该会弹起来。在原来的游戏中,我们可以注意到,当球打到球拍的极端边缘时,它的水平方向也会改变。我们检查球拍是否在窗口的左边或右边,在这里面,我们检查球是否在球拍的左边或右边,然后如果都在左边或右边,就使它改变水平方向。

main.py

import turtle as tr
from paddle import Paddle
from ball import Ball
import time
  
screen = tr.Screen()
screen.setup(width=1200, height=600)
screen.bgcolor('black')
screen.title('Breakout')
screen.tracer(0)
  
paddle = Paddle()
ball = Ball()
  
playing_game = True
  
  
screen.listen()
screen.onkey(key='Left', fun=paddle.move_left)
screen.onkey(key='Right', fun=paddle.move_right)
  
def check_collision_with_walls():
  
    global ball
  
    # detect collision with left and right walls:
    if ball.xcor() < -580 or ball.xcor() > 570:
        ball.bounce(x_bounce=True, y_bounce=False)
        return
  
    # detect collision with upper wall
    if ball.ycor() > 270:
        ball.bounce(x_bounce=False, y_bounce=True)
        return
  
    # detect collision with bottom wall
    # In this case, user failed to hit the 
    # ball thus he loses. The game resets.
    if ball.ycor() < -280:
        ball.reset()
        return
  
def check_collision_with_paddle():
  
    global ball, paddle
    # record x-axis coordinates of ball and paddle
    paddle_x = paddle.xcor()
    ball_x = ball.xcor()
  
    # check if ball's distance(from its middle) 
    # from paddle(from its middle) is less than
    # width of paddle and ball is belo a certain 
    # coordinate to detect their collision
    if ball.distance(paddle) < 110 and ball.ycor() < -250:
  
        # If Paddle is on Right of Screen
        if paddle_x > 0:
            if ball_x > paddle_x:
                # If ball hits paddles left side it 
                # should go back to left
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
        # If Paddle is left of Screen
        elif paddle_x < 0:
            if ball_x < paddle_x:
                # If ball hits paddles left side it 
                # should go back to left
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
        # Else Paddle is in the Middle horizontally
        else:
            if ball_x > paddle_x:
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            elif ball_x < paddle_x:
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
def check_collision_with_bricks():
    pass
  
while playing_game:
    screen.update()
    time.sleep(0.01)
    ball.move()
  
    check_collision_with_walls()
  
    check_collision_with_paddle()
  
tr.mainloop()

第五步:设置砖块

我们创建两个类。一个是单独的砖块,一个是出现在窗口上的那些砖块的集合。一个单独的砖块是一个简单的乌龟,它被横向拉长,使其成为矩形。它也有一个 “数量 “属性,即它的重量,也就是在它消失之前需要被球击中的次数。这个数字是随机赋予砖头的,我们不希望我们的游戏太难,因此我们把1作为最指定的重量。为了检查球与砖头的碰撞情况,我们需要砖头四条边的坐标。X坐标代表左右,Y坐标代表上下。砖块的列表只是一个砖块的数组。我们知道,砖块的起始x坐标和结束坐标保持不变,对于每一层或每一列砖块,唯一变化的是y轴。因此,一个单一的车道创建方法被调用到另一个方法里面,该方法传递其车道的y坐标。

bricks.py

from turtle import Turtle
import random
  
COLOR_LIST = ['light blue', 'royal blue', 
              'light steel blue', 'steel blue',
              'light cyan', 'light sky blue',
              'violet', 'salmon', 'tomato',
              'sandy brown', 'purple', 'deep pink',
              'medium sea green', 'khaki']
  
weights = [1, 2, 1, 1, 3, 2, 1, 4, 1, 3, 
           1, 1, 1, 4, 1, 3, 2, 2, 1, 2, 
           1, 2, 1, 2, 1]
  
  
class Brick(Turtle):
    def __init__(self, x_cor, y_cor):
        super().__init__()
        self.penup()
        self.shape('square')
        self.shapesize(stretch_wid=1.5, stretch_len=3)
        self.color(random.choice(COLOR_LIST))
        self.goto(x=x_cor, y=y_cor)
  
        self.quantity = random.choice(weights)
  
        # Defining borders of the brick
        self.left_wall = self.xcor() - 30
        self.right_wall = self.xcor() + 30
        self.upper_wall = self.ycor() + 15
        self.bottom_wall = self.ycor() - 15
  
  
class Bricks:
    def __init__(self):
        self.y_start = 0
        self.y_end = 240
        self.bricks = []
        self.create_all_lanes()
  
    def create_lane(self, y_cor):
        for i in range(-570, 650, 62):
            brick = Brick(i, y_cor)
            self.bricks.append(brick)
  
    def create_all_lanes(self):
        for i in range(self.y_start, self.y_end, 32):
            self.create_lane(i)

在我们的main.py中导入砖块,使它们对用户可见。现在是时候定义检查球是否与砖块碰撞的函数了。我们使用距离法检查球和砖头是否在接触范围内。在检查这个条件时要记住球和砖头的尺寸,因为接触距离是每个乌龟尺寸的一半之和。有时,需要进行撞击和试验来得出一个适当的数字。一旦我们检查了球和砖头是否接触,我们就检查它的坐标是否在砖头的左边、右边、上面或下面,然后让它相应地弹起。如果它与砖头侧面相撞,球的水平方向会改变,其他垂直方向也会改变。当碰撞被确认后,我们减少该砖头的数量,当变成等于零时,相应的砖头首先被移出可见窗口范围,然后从列表中删除。

main.py

import turtle as tr
from paddle import Paddle
from ball import Ball
from bricks import Bricks()
import time
  
screen = tr.Screen()
screen.setup(width=1200, height=600)
screen.bgcolor('black')
screen.title('Breakout')
screen.tracer(0)
  
paddle = Paddle()
bricks = Bricks()
ball = Ball()
  
playing_game = True
  
  
screen.listen()
screen.onkey(key='Left', fun=paddle.move_left)
screen.onkey(key='Right', fun=paddle.move_right)
  
def check_collision_with_walls():
  
    global ball, score, playing_game, ui
  
    # detect collision with left and right walls:
    if ball.xcor() < -580 or ball.xcor() > 570:
        ball.bounce(x_bounce=True, y_bounce=False)
        return
  
    # detect collision with upper wall
    if ball.ycor() > 270:
        ball.bounce(x_bounce=False, y_bounce=True)
        return
  
    # detect collision with bottom wall
    # In this case, user failed to hit 
    # the ball thus he loses. The game resets.
    if ball.ycor() < -280:
        ball.reset()
        return
  
def check_collision_with_paddle():
  
    global ball, paddle
    # record x-axis coordinates of ball and paddle
    paddle_x = paddle.xcor()
    ball_x = ball.xcor()
  
    # check if ball's distance(from its middle) 
    # from paddle(from its middle) is less than
    # width of paddle and ball is belo a certain
    # coordinate to detect their collision
    if ball.distance(paddle) < 110 and ball.ycor() < -250:
  
        # If Paddle is on Right of Screen
        if paddle_x > 0:
            if ball_x > paddle_x:
                # If ball hits paddles left side it
                # should go back to left
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
        # If Paddle is left of Screen
        elif paddle_x < 0:
            if ball_x < paddle_x:
                # If ball hits paddles left side it
                # should go back to left
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
        # Else Paddle is in the Middle horizontally
        else:
            if ball_x > paddle_x:
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            elif ball_x < paddle_x:
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
def check_collision_with_bricks():
    global ball, bricks
  
    for brick in bricks.bricks:
        if ball.distance(brick) < 40:
            brick.quantity -= 1
            if brick.quantity == 0:
                brick.clear()
                brick.goto(3000, 3000)
                bricks.bricks.remove(brick)
  
            # detect collision from left
            if ball.xcor() < brick.left_wall:
                ball.bounce(x_bounce=True, y_bounce=False)
  
            # detect collision from right
            elif ball.xcor() > brick.right_wall:
                ball.bounce(x_bounce=True, y_bounce=False)
  
            # detect collision from bottom
            elif ball.ycor() < brick.bottom_wall:
                ball.bounce(x_bounce=False, y_bounce=True)
  
            # detect collision from top
            elif ball.ycor() > brick.upper_wall:
                ball.bounce(x_bounce=False, y_bounce=True)
  
while playing_game:
    screen.update()
    time.sleep(0.01)
    ball.move()
  
    check_collision_with_walls()
  
    check_collision_with_paddle()
  
    check_collision_with_bricks()
  
tr.mainloop()

第六步:额外的用户界面、分数和暂停游戏

乌龟只需要写,因此我们把它隐藏起来。我们定义了一个最大的生命数,在decrease_lives方法中,每当用户没有击中球时,该方法将在main.py中被调用,该方法将减少1的值。每次更新分数时,都需要清除之前的文本。我们还维护一个高分,它依赖于从一个文本文件中读取的数据。如果该文本文件不存在,我们就创建一个,并向其写入一个数字,即0。这就是一个高分。否则,如果它存在,我们看它是否有一些数据。如果它没有,那么分数又是空的。如果没有遇到错误,分数就被简单地读取和显示。

scoreboard.py

# code
print("GFG")
from turtle import Turtle
  
try:
    score = int(open('highestScore.txt', 'r').read())
except FileNotFoundError:
    score = open('highestScore.txt', 'w').write(str(0))
except ValueError:
    score = 0
FONT = ('arial', 18, 'normal')
  
  
class Scoreboard(Turtle):
    def __init__(self, lives):
        super().__init__()
        self.color('white')
        self.penup()
        self.hideturtle()
        self.highScore = score
        self.goto(x=-580, y=260)
        self.lives = lives
        self.score = 0
        self.update_score()
  
    def update_score(self):
        self.clear()
        self.write(f"Score: {self.score} | Highest Score: {self.highScore} \
        | Lives: {self.lives}", align='left', font=FONT)
  
    def increase_score(self):
        self.score += 1
        if self.score > self.highScore:
            self.highScore += 1
        self.update_score()
  
    def decrease_lives(self):
        self.lives -= 1
        self.update_score()
  
    def reset(self):
        self.clear()
        self.score = 0
        self.update_score()
        open('highestScore.txt', 'w').write(str(self.highScore))

我们只需要在屏幕上写一些东西,所以我们隐藏了乌龟,没有给它任何形状。我们确实给它一个从颜色列表中随机选择的颜色。我们用一个方法来写我们需要写的东西,这个方法在对象第一次被创建时被调用。当游戏暂停时,我们简单地改变颜色并延迟程序,这样它就不会太快,而且用户在再次点击空格键时,在游戏恢复之前会有一些时间。每次,我们在写东西或改变颜色时,都需要清除之前的文本,这里我们也继承了Turtle库中的Turtle类。

ui.py

import time
from turtle import Turtle
import random
  
FONT = ("Courier", 52, "normal")
FONT2 = ("Courier", 32, "normal")
ALIGNMENT = 'center'
COLOR = "white"
COLOR_LIST = ['light blue', 'royal blue', 
              'light steel blue', 'steel blue',
              'light cyan', 'light sky blue',
              'violet', 'salmon', 'tomato',
              'sandy brown', 'purple', 'deep pink', 
              'medium sea green', 'khaki']
  
  
class UI(Turtle):
    def __init__(self):
        super().__init__()
        self.hideturtle()
        self.penup()
        self.color(random.choice(COLOR_LIST))
        self.header()
  
    def header(self):
        self.clear()
        self.goto(x=0, y=-150)
        self.write('Breakout', align=ALIGNMENT, font=FONT)
        self.goto(x=0, y=-180)
        self.write('Press Space to PAUSE or RESUME the Game', 
                   align=ALIGNMENT, font=('Calibri', 14, 'normal'))
  
    def change_color(self):
        self.clear()
        self.color(random.choice(COLOR_LIST))
        self.header()
  
    def paused_status(self):
        self.clear()
        self.change_color()
        time.sleep(0.5)
  
    def game_over(self):
        self.clear()
        self.write("Game is Over", align='center', font=FONT)

我们听从附加键,听从空格键来暂停游戏。按这个键只是调用一个函数,这个函数会反转一个布尔值,如果这个布尔值为真,就用来在无限的while循环中运行一切。如果这个布尔值是假的,我们只是不断改变我们的用户界面的颜色。由于球只有在循环内调用移动方法时才会移动,现在是在布尔值的检查下,整个游戏就会暂停,因为球没有移动。每当球接触到底部墙壁时,我们也会减少生命值。同样地,如果球碰到砖头,我们需要增加分数。我们还需要在生命耗尽时显示游戏结束,并结束while循环。

main.py

import turtle as tr
from paddle import Paddle
from ball import Ball
from scoreboard import Scoreboard
from ui import UI
from bricks import Bricks
import time
  
  
screen = tr.Screen()
screen.setup(width=1200, height=600)
screen.bgcolor('black')
screen.title('Breakout')
screen.tracer(0)
  
ui = UI()
ui.header()
  
score = Scoreboard(lives=5)
paddle = Paddle()
bricks = Bricks()
  
  
ball = Ball()
  
game_paused = False
playing_game = True
  
  
def pause_game():
    global game_paused
    if game_paused:
        game_paused = False
    else:
        game_paused = True
  
  
screen.listen()
screen.onkey(key='Left', fun=paddle.move_left)
screen.onkey(key='Right', fun=paddle.move_right)
screen.onkey(key='space', fun=pause_game)
  
  
def check_collision_with_walls():
  
    global ball, score, playing_game, ui
  
    # detect collision with left and right walls:
    if ball.xcor() < -580 or ball.xcor() > 570:
        ball.bounce(x_bounce=True, y_bounce=False)
        return
  
    # detect collision with upper wall
    if ball.ycor() > 270:
        ball.bounce(x_bounce=False, y_bounce=True)
        return
  
    # detect collision with bottom wall
    # In this case, user failed to hit the
    # ball thus he loses. The game resets.
    if ball.ycor() < -280:
        ball.reset()
        score.decrease_lives()
        if score.lives == 0:
            score.reset()
            playing_game = False
            ui.game_over()
            return
        ui.change_color()
        return
  
  
def check_collision_with_paddle():
  
    global ball, paddle
    # record x-axis coordinates of ball and paddle
    paddle_x = paddle.xcor()
    ball_x = ball.xcor()
  
    # check if ball's distance(from its middle) 
    # from paddle(from its middle) is less than
    # width of paddle and ball is belo a certain
    # coordinate to detect their collision
    if ball.distance(paddle) < 110 and ball.ycor() < -250:
  
        # If Paddle is on Right of Screen
        if paddle_x > 0:
            if ball_x > paddle_x:
                # If ball hits paddles left side
                # it should go back to left
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
        # If Paddle is left of Screen
        elif paddle_x < 0:
            if ball_x < paddle_x:
                # If ball hits paddles left side it
                # should go back to left
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
        # Else Paddle is in the Middle horizontally
        else:
            if ball_x > paddle_x:
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            elif ball_x < paddle_x:
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
  
def check_collision_with_bricks():
    global ball, score, bricks
  
    for brick in bricks.bricks:
        if ball.distance(brick) < 40:
            score.increase_score()
            brick.quantity -= 1
            if brick.quantity == 0:
                brick.clear()
                brick.goto(3000, 3000)
                bricks.bricks.remove(brick)
  
            # detect collision from left
            if ball.xcor() < brick.left_wall:
                ball.bounce(x_bounce=True, y_bounce=False)
  
            # detect collision from right
            elif ball.xcor() > brick.right_wall:
                ball.bounce(x_bounce=True, y_bounce=False)
  
            # detect collision from bottom
            elif ball.ycor() < brick.bottom_wall:
                ball.bounce(x_bounce=False, y_bounce=True)
  
            # detect collision from top
            elif ball.ycor() > brick.upper_wall:
                ball.bounce(x_bounce=False, y_bounce=True)
  
  
while playing_game:
  
    if not game_paused:
  
        # UPDATE SCREEN WITH ALL THE MOTION
        # THAT HAS HAPPENED
        screen.update()
        time.sleep(0.01)
        ball.move()
  
        # DETECTING COLLISION WITH WALLS
        check_collision_with_walls()
  
        # DETECTING COLLISION WITH THE PADDLE
        check_collision_with_paddle()
  
        # DETECTING COLLISION WITH A BRICK
        check_collision_with_bricks()
  
    else:
        ui.paused_status()
  
  
tr.mainloop()

最终代码

main.py

import turtle as tr
from paddle import Paddle
from ball import Ball
from scoreboard import Scoreboard
from ui import UI
from bricks import Bricks
import time
  
  
screen = tr.Screen()
screen.setup(width=1200, height=600)
screen.bgcolor('black')
screen.title('Breakout')
screen.tracer(0)
  
ui = UI()
ui.header()
  
score = Scoreboard(lives=5)
paddle = Paddle()
bricks = Bricks()
  
  
ball = Ball()
  
game_paused = False
playing_game = True
  
  
def pause_game():
    global game_paused
    if game_paused:
        game_paused = False
    else:
        game_paused = True
  
  
screen.listen()
screen.onkey(key='Left', fun=paddle.move_left)
screen.onkey(key='Right', fun=paddle.move_right)
screen.onkey(key='space', fun=pause_game)
  
  
def check_collision_with_walls():
  
    global ball, score, playing_game, ui
  
    # detect collision with left and right walls:
    if ball.xcor() < -580 or ball.xcor() > 570:
        ball.bounce(x_bounce=True, y_bounce=False)
        return
  
    # detect collision with upper wall
    if ball.ycor() > 270:
        ball.bounce(x_bounce=False, y_bounce=True)
        return
  
    # detect collision with bottom wall
    # In this case, user failed to hit the ball 
    # thus he loses. The game resets.
    if ball.ycor() < -280:
        ball.reset()
        score.decrease_lives()
        if score.lives == 0:
            score.reset()
            playing_game = False
            ui.game_over()
            return
        ui.change_color()
        return
  
  
def check_collision_with_paddle():
  
    global ball, paddle
    # record x-axis coordinates of ball and paddle
    paddle_x = paddle.xcor()
    ball_x = ball.xcor()
  
    # check if ball's distance(from its middle) 
    # from paddle(from its middle) is less than
    # width of paddle and ball is belo a certain 
    #coordinate to detect their collision
    if ball.distance(paddle) < 110 and ball.ycor() < -250:
  
        # If Paddle is on Right of Screen
        if paddle_x > 0:
            if ball_x > paddle_x:
                # If ball hits paddles left side it
                # should go back to left
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
        # If Paddle is left of Screen
        elif paddle_x < 0:
            if ball_x < paddle_x:
                # If ball hits paddles left side it 
                # should go back to left
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
        # Else Paddle is in the Middle horizontally
        else:
            if ball_x > paddle_x:
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            elif ball_x < paddle_x:
                ball.bounce(x_bounce=True, y_bounce=True)
                return
            else:
                ball.bounce(x_bounce=False, y_bounce=True)
                return
  
  
def check_collision_with_bricks():
    global ball, score, bricks
  
    for brick in bricks.bricks:
        if ball.distance(brick) < 40:
            score.increase_score()
            brick.quantity -= 1
            if brick.quantity == 0:
                brick.clear()
                brick.goto(3000, 3000)
                bricks.bricks.remove(brick)
  
            # detect collision from left
            if ball.xcor() < brick.left_wall:
                ball.bounce(x_bounce=True, y_bounce=False)
  
            # detect collision from right
            elif ball.xcor() > brick.right_wall:
                ball.bounce(x_bounce=True, y_bounce=False)
  
            # detect collision from bottom
            elif ball.ycor() < brick.bottom_wall:
                ball.bounce(x_bounce=False, y_bounce=True)
  
            # detect collision from top
            elif ball.ycor() > brick.upper_wall:
                ball.bounce(x_bounce=False, y_bounce=True)
  
  
while playing_game:
  
    if not game_paused:
  
        # UPDATE SCREEN WITH ALL THE MOTION THAT HAS HAPPENED
        screen.update()
        time.sleep(0.01)
        ball.move()
  
        # DETECTING COLLISION WITH WALLS
        check_collision_with_walls()
  
        # DETECTING COLLISION WITH THE PADDLE
        check_collision_with_paddle()
  
        # DETECTING COLLISION WITH A BRICK
        check_collision_with_bricks()
  
    else:
        ui.paused_status()
  
  
tr.mainloop()

ball.py

from turtle import Turtle
  
MOVE_DIST = 10
  
  
class Ball(Turtle):
    def __init__(self):
        super().__init__()
        self.shape('circle')
        self.color('white')
        self.penup()
        self.x_move_dist = MOVE_DIST
        self.y_move_dist = MOVE_DIST
        self.reset()
  
    def move(self):
        new_y = self.ycor() + self.y_move_dist
        new_x = self.xcor() + self.x_move_dist
        self.goto(x=new_x, y=new_y)
  
    def bounce(self, x_bounce, y_bounce):
        if x_bounce:
            self.x_move_dist *= -1
  
        if y_bounce:
            self.y_move_dist *= -1
  
    def reset(self):
        self.goto(x=0, y=-240)
        self.y_move_dist = 10

bricks.py

from turtle import Turtle
import random
  
COLOR_LIST = ['light blue', 'royal blue', 
              'light steel blue', 'steel blue',
              'light cyan', 'light sky blue', 
              'violet', 'salmon', 'tomato',
              'sandy brown', 'purple', 'deep pink', 
              'medium sea green', 'khaki']
  
weights = [1, 2, 1, 1, 3, 2, 1, 4, 1, 
           3, 1, 1, 1, 4, 1, 3, 2, 2, 
           1, 2, 1, 2, 1, 2, 1]
  
  
class Brick(Turtle):
    def __init__(self, x_cor, y_cor):
        super().__init__()
        self.penup()
        self.shape('square')
        self.shapesize(stretch_wid=1.5, stretch_len=3)
        self.color(random.choice(COLOR_LIST))
        self.goto(x=x_cor, y=y_cor)
  
        self.quantity = random.choice(weights)
  
        # Defining borders of the brick
        self.left_wall = self.xcor() - 30
        self.right_wall = self.xcor() + 30
        self.upper_wall = self.ycor() + 15
        self.bottom_wall = self.ycor() - 15
  
  
class Bricks:
    def __init__(self):
        self.y_start = 0
        self.y_end = 240
        self.bricks = []
        self.create_all_lanes()
  
    def create_lane(self, y_cor):
        for i in range(-570, 650, 62):
            brick = Brick(i, y_cor)
            self.bricks.append(brick)
  
    def create_all_lanes(self):
        for i in range(self.y_start, self.y_end, 32):
            self.create_lane(i)

paddle.py

from turtle import Turtle
  
  
MOVE_DIST = 70
  
  
class Paddle(Turtle):
    def __init__(self):
        super().__init__()
        self.color('steel blue')
        self.shape('square')
        self.penup()
        self.shapesize(stretch_wid=1, stretch_len=10)
        self.goto(x=0, y=-280)
  
    def move_left(self):
        self.backward(MOVE_DIST)
  
    def move_right(self):
        self.forward(MOVE_DIST)

scoreboard.py

from turtle import Turtle
  
try:
    score = int(open('highestScore.txt', 'r').read())
except FileNotFoundError:
    score = open('highestScore.txt', 'w').write(str(0))
except ValueError:
    score = 0
FONT = ('arial', 18, 'normal')
  
  
class Scoreboard(Turtle):
    def __init__(self, lives):
        super().__init__()
        self.color('white')
        self.penup()
        self.hideturtle()
        self.highScore = score
        self.goto(x=-580, y=260)
        self.lives = lives
        self.score = 0
        self.update_score()
  
    def update_score(self):
        self.clear()
        self.write(f"Score: {self.score} | Highest Score: \
        {self.highScore} | Lives: {self.lives}", align='left', 
                   font=FONT)
  
    def increase_score(self):
        self.score += 1
        if self.score > self.highScore:
            self.highScore += 1
        self.update_score()
  
    def decrease_lives(self):
        self.lives -= 1
        self.update_score()
  
    def reset(self):
        self.clear()
        self.score = 0
        self.update_score()
        open('highestScore.txt', 'w').write(str(self.highScore))

ui.py

import time
from turtle import Turtle
import random
  
FONT = ("Courier", 52, "normal")
FONT2 = ("Courier", 32, "normal")
ALIGNMENT = 'center'
COLOR = "white"
COLOR_LIST = ['light blue', 'royal blue', 
              'light steel blue', 'steel blue',
              'light cyan', 'light sky blue', 
              'violet', 'salmon', 'tomato',
              'sandy brown', 'purple', 'deep pink', 
              'medium sea green', 'khaki']
  
  
class UI(Turtle):
    def __init__(self):
        super().__init__()
        self.hideturtle()
        self.penup()
        self.color(random.choice(COLOR_LIST))
        self.header()
  
    def header(self):
        self.clear()
        self.goto(x=0, y=-150)
        self.write('Breakout', align=ALIGNMENT, font=FONT)
        self.goto(x=0, y=-180)
        self.write('Press Space to PAUSE or RESUME the Game',
                   align=ALIGNMENT, font=('Calibri', 14, 'normal'))
  
    def change_color(self):
        self.clear()
        self.color(random.choice(COLOR_LIST))
        self.header()
  
    def paused_status(self):
        self.clear()
        self.change_color()
        time.sleep(0.5)
  
    def game_over(self):
        self.clear()
        self.write("Game is Over", align='center', font=FONT)

Output:

使用Python Turtle 创建Breakout游戏

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程