Hi I have written the following code in Python. Any improvements on my code will be appreciated as this is my first shot at pygame!
There are some problems in terms of functionality, to state briefly ball sometimes gets stuck within a paddle, which I am continuing to resolve
import pygame, sys
import random
from math import degrees, radians, sin, cos
pygame.init()
# Create a screen
win = pygame.display.set_mode((800,700))
win_info = pygame.display.Info()
win_w, win_h = win_info.current_w, win_info.current_h
pygame.display.set_caption("Pong")
pad_colour = (255,255,255) #White colour
pad_distance = 50
speed = 10
running = True
fps = 30
#############################################################################
class border(pygame.sprite.Sprite):
width = win_w
height = 10
def __init__(self,width, height, colour):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((width,height))
self.image.fill(colour)
self.rect = self.image.get_rect()
def draw(self):
win.blit(self.image, self.rect)
#############################################################################
class paddle(pygame.sprite.Sprite):
"""left arg refer to top left corner coordinate of paddle
"""
def __init__(self,width, height, speed, colour):
pygame.sprite.Sprite.__init__(self)
self.width = width
self.height = height
self.image = pygame.Surface([width, height])
self.image.fill(colour)
self.speed = speed
self.vel_x = 0
self.vel_y = 0
# Size of rectangle
self.rect = self.image.get_rect()
def move(self, x, y):
self.rect.move_ip(x,y)
def moveTo(self, x, y):
self.rect.topleft = (x,y)
def draw(self):
win.blit(self.image, (self.rect.x,self.rect.y))
#############################################################################
class ball(pygame.sprite.Sprite):
""" Facing takes 1 or -1 to determine direction of movement
"""
def __init__(self, radius, factor, colour):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([radius*2, radius*2])
pygame.draw.circle(self.image, colour, (radius,radius), radius)
self.rect = self.image.get_rect()
# Replace facing when finalising implementation of ball
self.facing = -1
self.factor = factor
self.vel_x, self.vel_y = self.randomMovement(factor)
def move(self, x,y):
self.rect.move_ip(x,y)
def moveTo(self, x, y):
self.rect.center = (x,y)
def collidePad(self):
self.vel_x = self.vel_x * -1
def draw(self):
win.blit(self.image, (self.rect))
def randomMovement(self,factor=None):
#unit vector
if factor == None:
# Used for all calls after ball instance is initialised
factor = self.factor
angle = radians(random.randint(30,80))
rand_y = random.randrange(-1,2,2)
y = sin(angle)
x = cos(angle)
""" How to get over the use of int and rounding errors. Using int the line before causes the vector to be 0,0 as the numbers after 0 are truncated"""
self.vel_x, self.vel_y = (int(x*factor),int(y*factor)*rand_y)
#print(x, y)
return self.vel_x, self.vel_y
#############################################################################
# Main Game Loop
def main():
white = (255,255,255)
border_height = 10
pad_width = int(win_w/50)
pad_height = int(win_h/6)
pad_left = paddle(pad_width, pad_height, speed, white)
pad_right = paddle(pad_width, pad_height, speed, white)
# Set X and Y of left pad
pad_left.rect.topleft = (pad_distance, int((win_h-pad_height)/2))
# Set X and Y of right pad
pad_right.rect.topleft = ((win_w-pad_distance-pad_width), int((win_h-pad_height)/2))
top_border = border(win_w, border_height, white)
top_border.rect.topleft = (0, 0)
bottom_border = border(win_w, border_height, white)
bottom_border.rect.topleft = (0, win_h-border_height)
gameBall = ball(10, 10, white)
gameBall.rect.center = (int(win_w/2), int(win_h/2))
to_draw = [pad_left, pad_right, top_border, bottom_border, gameBall]
redrawGameWin(to_draw)
backGround = pygame.Surface((win_w,win_h))
backGround.fill(white)
clock = pygame.time.Clock()
# Game Loop
while running:
clock.tick(fps)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.display.quit()
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
# Left pad moving up with top boundary collision
if keys[pygame.K_s] and pad_left.rect.y > 0:
if pad_left.rect.y - top_border.height - pad_left.speed <= 0 :
pad_left.vel_y = 0
pad_left.moveTo(pad_distance, top_border.height)
else:
pad_left.vel_y = -pad_left.speed
pad_left.move(0, pad_left.vel_y)
# Left pad moving down with bottom boundary collision
if keys[pygame.K_x]:
if pad_left.rect.y + pad_left.height + pad_left.speed + bottom_border.height >= win_h:
pad_left.vel_y = 0
pad_left.moveTo(pad_distance, win_h-pad_left.height-bottom_border.height)
else:
pad_left.vel_y = pad_left.speed
pad_left.move(0, pad_left.speed)
if not keys[pygame.K_s] and not keys[pygame.K_x]:
pad_left.vel_x, pad_left.vel_y = 0,0
# Right pad moving up with top boundary collision
if keys[pygame.K_k]:
if pad_right.rect.y - top_border.height - pad_right.speed <= 0:
pad_right.vel_y = 0
pad_right.moveTo(win_w-pad_distance-pad_right.width, top_border.height)
else:
pad_right.vel_y = -pad_right.speed
pad_right.move(0, pad_right.vel_y)
# Right pad moving down with bottom boundary collision
if keys[pygame.K_m]:
if pad_right.rect.y + pad_right.height + pad_right.speed + bottom_border.height >= win_h:
pad_right.vel_y = 0
pad_right.moveTo(win_w-pad_distance-pad_right.width, win_h-pad_right.height-bottom_border.height)
else:
pad_right.vel_y = pad_right.speed
pad_right.move(0, pad_right.vel_y)
if not keys[pygame.K_k] and not keys[pygame.K_m]:
pad_right.vel_x, pad_right.vel_y = 0,0
# Ball bouncing off top border
if gameBall.rect.colliderect(top_border.rect) or gameBall.rect.colliderect(bottom_border.rect):
gameBall.vel_y = gameBall.vel_y*-1
# Ball bouncing off right pad
if gameBall.rect.colliderect(pad_right.rect):
gameBall.vel_x = -(gameBall.vel_x + pad_right.vel_x)
gameBall.vel_y = gameBall.vel_y + pad_right.vel_y
# Ball bouncing off left pad
if gameBall.rect.colliderect(pad_left.rect):
gameBall.vel_x = -(gameBall.vel_x + pad_left.vel_x)
gameBall.vel_y = gameBall.vel_y + pad_left.vel_y
# Ball moving by velocity vector
gameBall.move(gameBall.vel_x, gameBall.vel_y)
# Ball moves off screem
if gameBall.rect.left >= win_w or gameBall.rect.right <=0:
gameBall.randomMovement()
gameBall.moveTo(int(win_w/2), int(win_h/2))
redrawGameWin(to_draw)
#############################################################################
def redrawGameWin(shapes):
#global dirty_rects
win.fill((0,0,0))
for shape in shapes:
shape.draw()
pygame.display.flip()
#############################################################################
if __name__ == "__main__":
main()
-
3\$\begingroup\$ Welcome to Code Review! Are you interested in adding features to your code or in reviewing the existing code? Please read the help center before answering that. \$\endgroup\$Mast– Mast ♦2020年03月16日 10:21:06 +00:00Commented Mar 16, 2020 at 10:21
-
\$\begingroup\$ @Mast Reviewing the existing code! And trying to address the problems I have outlined \$\endgroup\$KOOTS HOOTS– KOOTS HOOTS2020年03月17日 08:00:56 +00:00Commented Mar 17, 2020 at 8:00
-
\$\begingroup\$ Reviewing the existing code is on topic for here but addressing that list of problems is not. You should likely edit the question to indicate that there are some functional problems but they will be addressed separately on stack overflow. \$\endgroup\$Reinderien– Reinderien2020年03月17日 13:02:33 +00:00Commented Mar 17, 2020 at 13:02
-
1\$\begingroup\$ @Reinderien Will do that now! \$\endgroup\$KOOTS HOOTS– KOOTS HOOTS2020年03月18日 14:09:49 +00:00Commented Mar 18, 2020 at 14:09
1 Answer 1
Global code
This stuff:
pygame.init()
# Create a screen
win = pygame.display.set_mode((800,700))
win_info = pygame.display.Info()
win_w, win_h = win_info.current_w, win_info.current_h
pygame.display.set_caption("Pong")
should be moved into one or more functions. These:
pad_colour = (255,255,255) #White colour
pad_distance = 50
speed = 10
running = True
fps = 30
can probably stay in global scope as ALL_CAPS constants.
Class names
border
should be Border
since it's a class name. The same goes for Paddle
.
Statics
class border(pygame.sprite.Sprite):
width = win_w
height = 10
def __init__(self,width, height, colour):
I don't think that the first width/height variables do what you think they do. Currently they can be deleted. If you want them to take effect as defaults, then you'd actually write
class border(pygame.sprite.Sprite):
def __init__(self, colour, width=win_w, height=10):
Method names
should be snake_case, i.e.
random_movement
, collide_pad
, etc.
Don't repeat yourself
The code to check the left/right pad is very repetitive. Think about what is shared and what differs, and factor out the differing stuff. So far as I can see, the differences include the references to the key constants K_s
, some distance calculations, etc.