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