João Victor 0 Newbie Poster

Just looking for some pointers on how I could improve the code. This is my first game ever and python is my first language so it's bit messy but I'll be happy to explain stuff if needed. Hope you guys can help me out. Here it is:

objects.py

try:
    import pygame
    import Vec2D
    import random
    from constants import *
    from pygame.locals import *
except ImportError as message:
    print("ImportError:", message)

class Text(object):

    def __init__(self, value, size, color,
                 font=None,
                 x=0, y=0,
                 top=None, bottom=None, left=None, right=None,
                 centerx=None, centery=None):
        self._size = size
        self._color = color
        self._value = value
        self._font = pygame.font.Font(font, self._size)
        self.image = self._create_surface()

        self.rect = self.image.get_rect()
        if x: self.rect.x = x
        if y: self.rect.y = y
        if top: self.rect.top = top
        if bottom: self.rect.bottom = bottom
        if left: self.rect.left = left
        if right: self.rect.right = right
        if centerx: self.rect.centerx = centerx
        if centery: self.rect.centery = centery

    def _create_surface(self):
        return self._font.render(str(self._value), 1, self._color)

    def set_value(self, new_value):
        if new_value != self._value: 
            self._value = new_value
            self.image = self._create_surface()
            self.rect = self.image.get_rect(x       = self.rect.x,
                                            y       = self.rect.y,
                                            top     = self.rect.top,
                                            bottom  = self.rect.bottom,
                                            left    = self.rect.left,
                                            right   = self.rect.right,
                                            centerx = self.rect.centerx,
                                            centery = self.rect.centery)

class Ball(pygame.sprite.Sprite):

    def __init__(self, game, vector=Vec2D.Vec2D()):
        super(Ball, self).__init__()

        self.image = pygame.Surface((BALL_RADIUS*2, BALL_RADIUS*2))
        self.rect = self.image.get_rect()
        self.__draw_ball()

        screen = pygame.display.get_surface()
        self.area = screen.get_rect().inflate(-GAP*2, 0)

        self.vector = vector
        self.game = game
        self.reinit()

    def __draw_ball(self):
        self.image.fill(BLACK)
        self.image.set_colorkey(BLACK, RLEACCEL)
        pygame.draw.circle(self.image, WHITE, (self.rect.centerx, self.rect.centery), BALL_RADIUS)

    def reinit(self):
        self.rect.centerx = self.area.centerx
        self.rect.centery = self.area.centery
        self.vector = Vec2D.Vec2D.from_magn_and_angle(BALL_SPEED, 0)

    def update(self, dt):
        self.rect = self.calcnewpos(dt)
        self.handle_collision()

    def calcnewpos(self, dt):
        (dx, dy) = self.vector.get_xy()
        return self.rect.move(dx, dy)

    def handle_collision(self):
        (dx, dy) = self.vector.get_xy()

        if not self.area.contains(self.rect):
            if self.__hit_topbottom():
                dy = -dy
            elif self.__hit_leftright():
                self.game.increase_score(self.__hit_leftright())
                self.reinit()
                return
        else:
            if self.hit_paddle():
                paddle = self.hit_paddle()
                if paddle.side == 'left':
                    self.rect.left = GAP + PADDLE_WIDTH
                elif paddle.side == 'right': 
                    self.rect.right = SCREEN_WIDTH - (GAP + PADDLE_WIDTH)
                dx = -dx

                dy = (self.rect.centery - paddle.rect.centery)
                if dy <= -32:
                    dy = -32
                elif -32 < dy <= -16:
                    dy = -16
                elif -16 < dy < 16:
                    dy = 0
                elif 16 <= dy < 32:
                    dy = 16
                elif dy >= 32:
                    dy = 32
                dy /= 4
                paddle.collided = True

        self.vector = Vec2D.Vec2D(dx, dy)

    def __hit_topbottom(self):
        return self.rect.top < 0 or self.rect.bottom > SCREEN_HEIGHT

    def __hit_leftright(self):
        if self.rect.left < self.area.left: return 'left'
        elif self.rect.right > self.area.right: return 'right'

    def hit_paddle(self):
        for paddle in self.game.paddle_sprites:
            if self.rect.colliderect(paddle.rect): return paddle

class Paddle(pygame.sprite.Sprite):

    def __init__(self):
        super(Paddle, self).__init__()

        self.image = pygame.Surface(PADDLE_SIZE)
        self.rect = self.image.get_rect()
        self.__draw_paddle()

        screen = pygame.display.get_surface()
        self.area = screen.get_rect()

        self.collided = False

    def __draw_paddle(self):
        self.image.fill(WHITE)

    def reinit(self):
        self.state = 'still'
        self.movepos = [0, 0]
        self.rect.centery = self.area.centery

    def update(self):
        new_rect = self.rect.move(self.movepos)
        if self.area.contains(new_rect):
            self.rect = new_rect
        pygame.event.pump()

class Player(Paddle):

    def __init__(self, ball, side):
        super(Player, self).__init__()
        self.ball = ball
        self.side = side
        self.speed = PLAYER_SPEED
        #self.hitpos = 0
        self.score = 0
        self.reinit()

    def update(self, dt):
        self.hitpos = -(self.rect.centery - self.ball.rect.centery)

        keys = pygame.key.get_pressed()
        if keys[K_UP]:
            self.movepos[1] = -self.speed * dt
        if keys[K_DOWN]:
            self.movepos[1] = self.speed * dt
        super(Player, self).update()

    def reinit(self):
        super(Player, self).reinit()
        if self.side == 'left': self.rect.left = GAP
        elif self.side == 'right': self.rect.right = SCREEN_WIDTH - GAP

class Enemy(Paddle):

    def __init__(self, ball):
        super(Enemy, self).__init__()
        self.ball = ball
        self.side = 'right' if PLAYER_SIDE == 'left' else 'left' 
        self.speed = ENEMY_SPEED
        self.hitpos = 0
        self.score = 0
        self.reinit()

    def update(self, dt):
        spot = self.rect.centery + self.hitpos      
        if (spot - self.ball.rect.centery) not in range(-5, 5):
            if spot > self.ball.rect.centery:
                self.movepos[1] = -self.speed * dt
            if spot < self.ball.rect.centery:
                self.movepos[1] = self.speed * dt
        else:
            self.movepos[1] = 0
        super(Enemy, self).update()

        if self.collided:
            self.hitpos = random.randrange(-40, 40)
            self.collided = False

    def reinit(self):
        super(Enemy, self).reinit()
        if self.side == 'left': self.rect.left = GAP
        elif self.side == 'right': self.rect.right = SCREEN_WIDTH - GAP

game.py

try:
    import pygame
    import sys
    import Vec2D
    import math
    from constants import *
    from pygame.locals import *
    from objects import Text, Ball, Player, Enemy
except ImportError as message:
    print("ImportError:", message)

class Game(object):

    def main(self):

        # initialize ball
        vector = Vec2D.Vec2D().from_magn_and_angle(BALL_SPEED, math.radians(0))
        self.ball = Ball(self, vector)
        # initialize enemy
        self.enemy = Enemy(self.ball)
        # initialize player
        self.player = Player(self.ball, PLAYER_SIDE)

        # initialize groups
        self.ballsprite = pygame.sprite.Group(self.ball)
        self.paddle_sprites = pygame.sprite.Group(self.enemy, self.player)

        # initialize background
        screen = pygame.display.get_surface()
        self.background = pygame.Surface(screen.get_size())
        self._draw_background()
        screen.blit(self.background, (0,0))

        # initialize text
        self.left_score = Text('', 32, WHITE, top = 10, right = SCREEN_WIDTH/2 - 20)
        self.right_score = Text('', 32, WHITE, top = 10, left = SCREEN_WIDTH/2 + 20)
        self.pause_text = Text('PAUSE', 64, RED,
                          centerx = SCREEN_WIDTH/2, centery = SCREEN_HEIGHT/2)

        # initialize music
        self.theme_music = pygame.mixer.music.load('theme.mp3')
        pygame.mixer.music.play(-1)

        # initialize clock
        clock = pygame.time.Clock()

        pause = False
        while 1:
            dt = clock.tick(FPS) / 1000

            for event in pygame.event.get():
                if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                    pygame.quit()
                    sys.exit()
                elif event.type == KEYUP:
                    if event.key == K_UP or event.key == K_DOWN:
                        self.player.movepos = [0, 0]
                        self.player.state = 'still'
                elif event.type == KEYDOWN:
                    if event.key == K_p:
                        pygame.mixer.music.pause()
                        pygame.time.delay(150)
                        screen.blit(self.pause_text.image, self.pause_text.rect)
                        pause = True

            # erase sprites
            screen.blit(self.background, self.ball.rect, self.ball.rect)
            screen.blit(self.background, self.player.rect, self.player.rect)
            screen.blit(self.background, self.enemy.rect, self.enemy.rect)

            # erase scores
            screen.blit(self.background, self.left_score.rect, self.left_score.rect)
            screen.blit(self.background, self.right_score.rect, self.right_score.rect)

            # update sprites
            self.ballsprite.update(dt)
            self.paddle_sprites.update(dt)

            # update scores
            a, b = self.player.score, self.enemy.score
            if self.player.side != 'left':
                a = self.enemy.score
                b = self.player.score
            self.left_score.set_value(a)
            self.right_score.set_value(b)
            self.left_score.rect.right = SCREEN_WIDTH/2 - 20
            self.right_score.rect.left = SCREEN_WIDTH/2 + 20

            # draw sprites
            self.ballsprite.draw(screen)
            self.paddle_sprites.draw(screen)

            # draw scores
            screen.blit(self.left_score.image, self.left_score.rect)
            screen.blit(self.right_score.image, self.right_score.rect)

            # flip display
            pygame.display.flip()

            # pause game
            while pause:
                dt = clock.tick(FPS)

                for event in pygame.event.get():
                    if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
                        pygame.quit()
                        sys.exit()
                    if event.type == KEYDOWN and event.key == K_p:
                        pygame.mixer.music.unpause()
                        pygame.time.delay(150)
                        screen.blit(self.background, self.pause_text.rect, self.pause_text.rect)
                        pause = False

    def increase_score(self, side):
        if self.player.side == side:
            self.enemy.score += 1
        else:
            self.player.score += 1

    def _draw_background(self):
        self.background.fill(BGCOLOR)

        # draw middle line
        pygame.draw.line(self.background, WHITE, 
                         (SCREEN_WIDTH/2, 0),
                         (SCREEN_WIDTH/2, SCREEN_HEIGHT), 2)

        leftcolor = BLUE
        rightcolor = RED
        if self.player.side != 'left':
            leftcolor = RED
            rightcolor = BLUE

        # draw left line
        pygame.draw.line(self.background, leftcolor,
                         (GAP, 0),
                         (GAP, SCREEN_HEIGHT), 2)

        # draw right line
        pygame.draw.line(self.background, rightcolor,
                         (SCREEN_WIDTH - GAP, 0),
                         (SCREEN_WIDTH - GAP, SCREEN_HEIGHT), 2)

if __name__ == '__main__':
    pygame.init()
    screen = pygame.display.set_mode(SCREEN_SIZE)
    pygame.display.set_caption('Pong!')
    pong = Game()
    pong.main()     

constants.py

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
PADDLE_WIDTH = 20
PADDLE_HEIGHT = 80
BALL_SPEED = 5
BALL_RADIUS = 10
PLAYER_SPEED = 200
ENEMY_SPEED = 200
GAP = 40
FPS = 30

#         R    G    B
BLACK = (  0,   0,   0)
WHITE = (255, 255, 255)
RED   = (255,   0,   0)
BLUE  = (  0,   0, 255)

SCREEN_SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT) 
PADDLE_SIZE = (PADDLE_WIDTH, PADDLE_HEIGHT)
PLAYER_SIDE = 'left'
BGCOLOR = BLACK
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.