2
\$\begingroup\$

Here's the code for a Breakout style game I made in Python. I was hoping to see if someone can give me some advice on improving anything.

I have a bug with the collision between the ball and the top bricks/blocks, and I'm still working on that. Since it doesn't follow "code review" I have removed what I said earlier and want to focus on the code in general and not fixing bugs.

The following is from several different files. I wasn't sure if there was a better way of formatting it here.

import time
from turtle import Screen
from paddle import Paddle
from ball import Ball
from brick import Brick
from scoreboard import Scoreboard
import random

screen = Screen()
screen.setup(width=800, height=600)
screen.bgcolor("black")
screen.title("Breakout Remake")
screen.tracer(0)

paddle = Paddle((0, -275))
ball = Ball((random.randint(-300, 300), 0))
scoreboard = Scoreboard()

brick_list = []
x, y  = 350, 285
for i in range(0, 4):
    for i in range(0, 9):
        brick_list.append(Brick((x, y)))
        x -= 85
    x = 350
    y -= 25

screen.listen()
screen.onkeypress(paddle.go_left, "Left")
screen.onkeypress(paddle.go_right, "Right")

game_is_on = True
while game_is_on:
    time.sleep(ball.move_speed)
    screen.update()
    ball.move()

    # Detect collision with side wall
    if ball.xcor() > 370 or ball.xcor() < -360:
        ball.bounce_side()

    # Detect collision against top
    if ball.ycor() > 280:
        ball.bounce_top()

    # Detect collision with paddle
    if ball.distance(paddle) < 50 and ball.ycor() <= -260:
        ball.bounce_paddle()

    # Detect paddle miss
    if ball.ycor() < -300:
        scoreboard.display()
        game_is_on = False

    # Detect brick hit
    for brick in brick_list:
        if ball.distance(brick) < 25:
            print(ball.position())
            ball.bounce_brick()
            brick.hideturtle()
            brick_list.remove(brick)
            scoreboard.score += 1

screen.exitonclick()    


from turtle import Turtle
import random
class Ball(Turtle):
    def __init__(self, position):
        super().__init__()
        self.shape("circle")
        self.color("white")
        self.penup()
        self.goto(position)
        self.x_move = -10 * random.choice([-1, 1])
        self.y_move = -10
        self.move_speed = 0.1
        self.speed = 6

    def move(self):
        new_x = self.xcor() + self.x_move
        new_y = self.ycor() + self.y_move
        self.goto(new_x, new_y)

    def bounce_side(self):
        self.x_move *= -1

    def bounce_top(self):
        self.y_move = -abs(self.y_move)

    def bounce_brick(self):
        self.y_move *= -1
        self.x_move *= 1.05
        self.y_move *= 1.05

    def bounce_paddle(self):
        self.y_move = abs(self.y_move)

    # for later use if game restart is implemented
    # def reset_position(self):
    #     self.goto((random.randint(-300, 300), 0))
    #     self.x_move = -10 * random.choice([-1, 1])

from turtle import Turtle
class Scoreboard(Turtle):

    def __init__(self):
        super().__init__()
        self.score = 0
        with open("data.txt", mode="r") as file:
            self.high_score = int(file.read())
        self.penup()
        self.hideturtle()
        self.color("white")
        self.goto(-200, 0)
    
    def display(self):
        self.write(f"Game Over\nScore: {self.score} | High Score: {self.high_score}", font=("Calibri", 40, "bold"))

from turtle import Turtle
class Brick(Turtle):
    def __init__(self, position):
        super().__init__()
        self.shape("square")
        self.color("white")
        self.shapesize(stretch_wid=1, stretch_len=4)
        self.penup()
        self.goto(position)

from turtle import Turtle
class Paddle(Turtle):
    def __init__(self, position):
        #inherit superclass (Turtle)
        super().__init__()
        self.shape("square")
        self.color("white")
        self.shapesize(stretch_wid=1, stretch_len=6)
        self.penup()
        self.goto(position)

    def go_left(self):
        if self.xcor() >= -320:
            new_x = self.xcor() - 40
            self.goto(new_x, self.ycor())

    def go_right(self):
        if self.xcor() <= 320:
            new_x = self.xcor() + 40
            self.goto(new_x, self.ycor())
\$\endgroup\$
2
  • 1
    \$\begingroup\$ General review of your code is on topic but fixing your collision bug is not \$\endgroup\$
    – Reinderien
    Commented Jul 12, 2022 at 2:24
  • \$\begingroup\$ Okay, sorry. I will remove the collision bug part. I still would like to know about any improvements to be made other than that as the program still works even with the bug. \$\endgroup\$
    – jp207
    Commented Jul 12, 2022 at 2:26

1 Answer 1

1
\$\begingroup\$

This nested loop:

for i in range(0, 4):
    for i in range(0, 9):

really shouldn't use the same iteration variable name for both levels - unless, of course, it's _ indicating that they're both unused (which they are). However, this disguises what you're actually iterating over, which is x and y. So it should look something like

for y in range(285, 185, -25):
    for x in range(350, -415, -85):

From your screen = Screen() through your exitonclick(), all of that code should be moved into a main() function. Luckily this is quite easy as your classes hold no references to those variables.

Add type hints such as

def __init__(self, position: tuple[int, int]) -> None:

Rather than -10 * random.choice([-1, 1]) why not just write

random.choice((-10, 10))

data.txt is not a good name for that file; consider instead high_score.txt. Don't open it in r - it will always fail the first time around; instead create it if it doesn't exist:

    with open("high_score.txt", 'a+') as file:
        score_str = file.read()
    if score_str == '':
        self.high_score = 0
    else:
        self.high_score = int(score_str)

And yes, your collision code needs some love. It's not enough to just xcor() on your object to get its centre; you need to calculate the edge of your objects and do the math to see whether they collide.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.