Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
breakout clone
#30
Dear deanhystad,

I'm sorry for asking again.

I made the changes you mentioned.

You said "Brick.get_image() goes away":

In Brick class in line 139 I found a "self.get_image(self.hits - 1)"...
How should I replace it?

I would prefer to use just the Brick-pngs, the paddle-png and the ball-png, because the error below occurs and
I think that is easier to understand instead of specify a color.

Please be so kind and have a look at the code...

(I'm sorry for annoying again.)
I wish you a pleasant sunday...
Traceback (most recent call last):
  File "C:\Users\...\AppData\Local\Programs\Python\Python310\lib\site-packages\PIL\Image.py", line 2957, in open
    fp.seek(0)
AttributeError: 'tuple' object has no attribute 'seek'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\Daten\Breakout neu 2\main.py", line 338, in <module>
    main()
  File "D:\Daten\Breakout neu 2\main.py", line 305, in main
    Brick(*coord)
  File "D:\Daten\Breakout neu 2\main.py", line 138, in __init__
    self.images = [create_image(file, color) for file in BRICK_FILES]  # Making the images  here
  File "D:\Daten\Breakout neu 2\main.py", line 138, in <listcomp>
    self.images = [create_image(file, color) for file in BRICK_FILES]  # Making the images  here
  File "D:\Daten\Breakout neu 2\main.py", line 41, in create_image
    image = Image.open(file).convert("RGB")
  File "C:\Users\...\AppData\Local\Programs\Python\Python310\lib\site-packages\PIL\Image.py", line 2959, in open
    fp = io.BytesIO(fp.read())
AttributeError: 'tuple' object has no attribute 'read'
import random, math, pygame
from PIL import Image
from itertools import product

# Define colors used by the game.
TEXT_COLOR = (255, 255, 255)
BACKGROUND = (0, 0, 200)
FOREGROUND = (0, 0, 0)  # Recolor image pixels that are this color
TRANSPARENT = (255, 255, 255)  # Make image pixels this color transparent
BALL_COLOR = (220, 220, 220)
PADDLE_COLOR = (255, 255, 0)
BRICK_COLORS = ((255, 0, 0), (255, 50, 0), (255, 100, 0), (255, 150, 0), (255, 200, 0), (255, 255, 0))

BRICK_COORDS = [(32, 32), (64, 32), (96, 32), (160, 32), (288, 32), (320, 32), (352, 32), (416, 32), (448, 32),
                (480, 32), (576, 32), (608, 32), (640, 32), (32, 64), (160, 64), (288, 64), (352, 64), (416, 64),
                (480, 64), (576, 64), (640, 64), (32, 96), (160, 96), (288, 96), (352, 96), (416, 96), (480, 96),
                (576, 96), (640, 96), (32, 128), (64, 128), (96, 128), (160, 128), (288, 128), (352, 128), (416, 128),
                (448, 128), (480, 128), (576, 128), (608, 128), (640, 128), (32, 160), (160, 160), (288, 160), (352, 160),
                (416, 160), (448, 160), (576, 160), (640, 160), (32, 192), (160, 192), (288, 192), (352, 192), (416, 192),
                (480, 192), (576, 192), (640, 192), (32, 224), (160, 224), (192, 224), (224, 224), (288, 224), (320, 224),
                (352, 224), (416, 224), (512, 224), (576, 224), (640, 224)]

# Define some image files
BALL_IMAGE = "ball.png"
PADDLE_IMAGE = "paddle.png"
BRICK_FILES = (("brick0.png", "brick1.png", "brick2.png"), ("bbrick0.png", "bbrick1.png", "bbrick2.png"))

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 800
SCORE_POSITION = (SCREEN_WIDTH - 150, SCREEN_HEIGHT - 30)


def create_image(file, color=None):
    """
    Create image from a file.  If color is specified, replace all FOREGROUND
    pixels with color pixels.  Modify image so TRANSPARENT colored pixels are
    transparent.
    """
    if color:
        # Recolor the image
        image = Image.open(file).convert("RGB")
        for xy in product(range(image.width), range(image.height)):
            if image.getpixel(xy) == FOREGROUND:
                image.putpixel(xy, color)
        image = pygame.image.fromstring(image.tobytes(), image.size, "RGB")
    else:
        image = pygame.image.load(file)
    image.set_colorkey(TRANSPARENT)
    return image.convert()


class EnhancedSprite(pygame.sprite.Sprite):
    """
    Sprite with image and rectangle.  I expose some of my rectangle's
    properties.
    """

    def __init__(self, image, group=None, **kwargs):
        super().__init__(**kwargs)
        self.image = image
        self.rect = image.get_rect()
        if group is not None:
            group.add(self)

    def at(self, x, y):
        """Convenience method for setting my position"""
        self.x = x
        self.y = y
        return self

    # Properties below expose properties of my rectangle so you can use
    # self.x = 10 or self.centery = 30 instead of self.rect.x = 10
    @property
    def x(self):
        return self.rect.x

    @x.setter
    def x(self, value):
        self.rect.x = value

    @property
    def y(self):
        return self.rect.y

    @y.setter
    def y(self, value):
        self.rect.y = value

    @property
    def centerx(self):
        return self.rect.centerx

    @centerx.setter
    def centerx(self, value):
        self.rect.centerx = value

    @property
    def centery(self):
        return self.rect.centery

    @centery.setter
    def centery(self, value):
        self.rect.centery = value

    @property
    def right(self):
        return self.rect.right

    @right.setter
    def right(self, value):
        self.rect.right = value

    @property
    def bottom(self):
        return self.rect.bottom

    @bottom.setter
    def bottom(self, value):
        self.rect.bottom = value


class Brick(EnhancedSprite):
    """
    A target for the ball.  After I take some number of hits I die.
    Number of hits I can take is in range 1 to 3.  Hits is randomly
    selected if not specified.

    Specify brick color using (R, G, B) format.  If color not specified
    a color is selected based on the row.
    """
    group = pygame.sprite.Group()

    def __init__(self, x, y, image_files=None, color=None, hits=None):
        color = color or random.choice(BRICK_COLORS)
        hits = hits or random.choice((1, 1, 1, 2, 2, 3))
        self.value = self.hits = max(1, min(3, hits))
        image_files = image_files or random.choice(BRICK_FILES)  # Randomly picking files
        self.images = [create_image(file, color) for file in BRICK_FILES]  # Making the images  here
        super().__init__(self.get_image(self.hits - 1), self.group).at(x, y)

    #def get_image(self):
        #"""Return an image based on my color and number of hits."""
        #images = [create_image(image_file, self.color) for image_file in BRICK_FILES]

        # Make brick images for this color
        #return images[self.hits - 1]

    def __len__(self):
        """Return how many bricks remaining"""
        return len(self.group)

    def hit(self, score):
        """
        I was hit!  Update my appearance or die based on my hit total.
        Return score based on being hit.
        """
        self.hits -= 1
        if self.hits > 0:
            self.image = self.images[self.hits - 1]  # Images are not shared anymore.  Each brick has their own.
            return 0
        self.kill()
        return self.value  # This was hits, but hits is 0


class Paddle(EnhancedSprite):
    """The sprite the player moves around to redirect the ball"""
    group = pygame.sprite.Group()

    def __init__(self, bottom):
        super().__init__(create_image(PADDLE_IMAGE, PADDLE_COLOR), self.group)
        self.bottom = bottom
        self.xmin = self.rect.width // 2  # Compute paddle x range.
        self.xmax = SCREEN_WIDTH - self.xmin

    def move(self, x):
        """Move to follow the cursor.  Clamp to window bounds"""
        self.centerx = max(self.xmin, min(self.xmax, x))


class LifeCounter():
    """Keep track of lives count.  Display lives remaining using ball image"""

    def __init__(self, x, y, count=5):
        self.x, self.y = x, y
        self.image = create_image(BALL_IMAGE, BALL_COLOR)
        self.spacing = self.image.get_width() + 5
        self.group = pygame.sprite.Group()
        self.reset(count)

    def reset(self, count):
        """Reset number of lives"""
        self.count = count
        for c in range(count - 1):
            EnhancedSprite(self.image, self.group).at(self.x + c * self.spacing, self.y)

    def __len__(self):
        """Return number of lives remaining"""
        return self.count

    def kill(self):
        """Reduce number of lives"""
        if self.count > 1:
            self.group.sprites()[-1].kill()
        self.count = max(0, self.count - 1)


class Ball(EnhancedSprite):
    """Ball bounces around colliding with walls, paddles and bricks"""
    group = pygame.sprite.Group()

    def __init__(self, paddle, lives, speed=10):
        super().__init__(create_image(BALL_IMAGE, BALL_COLOR), self.group)
        self.paddle = paddle
        self.lives = lives
        self.speed = speed
        self.dx = self.dy = 0
        self.xmax = SCREEN_WIDTH - self.rect.width
        self.ymax = self.paddle.bottom - self.rect.height
        self.reset(0)

    def reset(self, score=None):
        """Reset for a new game"""
        self.active = False
        if score is not None:
            self.score = score

    def start(self):
        """Start moving the ball in a random direction"""
        angle = random.random() - 0.5  # Launch angle limited to about +/-60 degrees
        self.dx = int(self.speed * math.sin(angle))
        self.dy = -int(self.speed * math.cos(angle))
        self.active = True

    def move(self):
        """Update the ball position.  Check for collisions with bricks, walls and the paddle"""
        if not self.active:
            # Sit on top of the paddle
            self.centerx = self.paddle.centerx
            self.bottom = self.paddle.y - 2
            return self

        # Did I hit some bricks?  Update the bricks and the score
        x1, y1 = self.x, self.y
        x2, y2 = x1 + self.dx, y1 + self.dy
        if (xhits := pygame.sprite.spritecollide(self.at(x2, y1), Brick.group, False)):
            self.dx = -self.dx
        if (yhits := pygame.sprite.spritecollide(self.at(x1, y2), Brick.group, False)):
            self.dy = -self.dy
        if (hits := set(xhits) or set(yhits)):
            for brick in hits:
                self.score += brick.hit(self.score)

        # Did I hit a wall?
        if x2 <= 0 or x2 >= self.xmax:
            self.dx = -self.dx
            hits = True
        if y2 <= 0:
            self.dy = abs(self.dy)
            hits = True

        # Did I hit or get past the paddle?
        if y2 >= self.paddle.y:
            # Did it get past the paddle?
            if self.x > self.paddle.right or self.right < self.paddle.x:
                self.lives.kill()
                self.active = False
            else:
                # I hit the paddle.  Compute angle of reflection
                bangle = math.atan2(-self.dx, self.dy)  # Ball angle of approach
                pangle = math.atan2(self.centerx - self.paddle.centerx, 30)  # Paddle angle
                rangle = (pangle - bangle) / 2  # Angle of reflection
                self.dx = math.sin(rangle) * self.speed
                self.dy = -math.cos(rangle) * self.speed
                hits = True

        if hits:
            self.at(x1, y1)
        else:
            self.at(x2, y2)


def main():
    """Play game until out of lives or out of bricks"""

    def displayText(text, font, pos=None, color=TEXT_COLOR):
        text = font.render(text, 1, color)
        if pos is None:
            pos = ((SCREEN_WIDTH - text.get_width()) // 2, (SCREEN_HEIGHT - text.get_height()) // 2)
        screen.blit(text, pos)

    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("Breakout")
    clock = pygame.time.Clock()
    allsprites = pygame.sprite.Group()
    score_font = pygame.font.Font(None, 34)

    try:
        level = 1
        lives = LifeCounter(10, SCREEN_HEIGHT - 30)
        paddle = Paddle(bottom=SCREEN_HEIGHT - 40)
        ball = Ball(paddle, lives)
        allsprites.add(paddle.group, lives.group, ball.group)
        for coord in BRICK_COORDS:
            Brick(*coord)
        allsprites.add(Brick.group)

        while len(lives) > 0 and len(Brick.group):
            clock.tick(60)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    raise SystemExit
                elif event.type == pygame.MOUSEMOTION:
                    paddle.move(event.pos[0])
                elif event.type == pygame.MOUSEBUTTONUP:
                    if not ball.active:
                        ball.start()
            ball.move()
            screen.fill(BACKGROUND)
            displayText(f"Score: {ball.score}", font=score_font, pos=SCORE_POSITION)
            allsprites.draw(screen)
            pygame.display.flip()

        if len(lives) == 0:
            displayText("Game over", font=pygame.font.Font(None, 74))
        elif len(Brick.group) == 0:
            level += 1
            displayText(f"Level {level}", font=pygame.font.Font(None, 74))
            ball.speed *= 1.25
            ball.reset(ball.score)
        pygame.display.flip()
        pygame.time.wait(3000)
    finally:
        pygame.quit()


if __name__ == "__main__":
    main()
Reply


Messages In This Thread
breakout clone - by flash77 - Feb-09-2022, 07:27 PM
RE: breakout clone - by deanhystad - Feb-09-2022, 08:44 PM
RE: breakout clone - by flash77 - Feb-10-2022, 06:51 PM
RE: breakout clone - by deanhystad - Feb-13-2022, 04:17 AM
RE: breakout clone - by flash77 - Feb-14-2022, 08:40 PM
RE: breakout clone - by deanhystad - Feb-15-2022, 04:38 AM
RE: breakout clone - by deanhystad - Feb-18-2022, 11:04 PM
RE: breakout clone - by flash77 - Feb-19-2022, 05:55 PM
RE: breakout clone - by deanhystad - Feb-20-2022, 03:10 PM
RE: breakout clone - by flash77 - Feb-21-2022, 05:59 PM
RE: breakout clone - by flash77 - Feb-26-2022, 04:09 PM
RE: breakout clone - by deanhystad - Feb-26-2022, 04:12 PM
RE: breakout clone - by flash77 - Feb-26-2022, 04:31 PM
RE: breakout clone - by deanhystad - Feb-26-2022, 04:35 PM
RE: breakout clone - by flash77 - Feb-26-2022, 05:05 PM
RE: breakout clone - by deanhystad - Feb-26-2022, 06:20 PM
RE: breakout clone - by flash77 - Feb-26-2022, 08:53 PM
RE: breakout clone - by deanhystad - Feb-27-2022, 05:05 AM
RE: breakout clone - by flash77 - Feb-27-2022, 10:19 AM
RE: breakout clone - by deanhystad - Feb-27-2022, 02:41 PM
RE: breakout clone - by flash77 - Feb-27-2022, 06:41 PM
RE: breakout clone - by deanhystad - Feb-28-2022, 04:21 AM
RE: breakout clone - by flash77 - Mar-02-2022, 05:26 PM
RE: breakout clone - by deanhystad - Mar-02-2022, 08:44 PM
RE: breakout clone - by deanhystad - Mar-04-2022, 09:07 PM
RE: breakout clone - by flash77 - Mar-05-2022, 10:41 AM
RE: breakout clone - by deanhystad - Mar-05-2022, 02:50 PM
RE: breakout clone - by flash77 - Mar-05-2022, 07:11 PM
RE: breakout clone - by deanhystad - Mar-05-2022, 07:37 PM
RE: breakout clone - by flash77 - Mar-06-2022, 09:03 AM
RE: breakout clone - by deanhystad - Mar-06-2022, 04:53 PM
RE: breakout clone - by flash77 - Mar-07-2022, 06:13 PM
RE: breakout clone - by deanhystad - Mar-07-2022, 08:12 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyGame] little space invaders / breakout game flash77 0 1,394 Jul-24-2024, 08:56 PM
Last Post: flash77
  breakout clone pygame flash77 2 2,668 Feb-06-2022, 06:36 PM
Last Post: flash77
  [PyGame] arkanoid / breakout clone flash77 2 5,132 Feb-04-2022, 05:42 PM
Last Post: flash77

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020