Public Group

# Code Review - Pong

This topic is 1267 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

So I made Pong. It was a good experience. I learned a lot, but questions were raised I had trouble finding answers to.

Code (Python):

#   --- Imports-Start ---

import pygame; import random

pygame.init() # Initiate pygame

#   --- Imports-End ---
#   --- Globals-Start ---

# Display settings
displayDim = [640, 480]
display = pygame.display.set_mode((displayDim))
pygame.display.set_caption('pong.py')

# Color(s)
WHITE = (255,   255,    255)
BLACK = (0,     0,      0)
RED =   (255,   0,      0)
GREEN = (0,     155,    0)
BLUE =  (110,   170,    255)
GRAY =  (230,   230,    240)

# Time
FPS = 30
clock = pygame.time.Clock()

# Fonts
fontBlocky = "C:\\Users\\rabbitrabbit\\CodeBS\\Pong\\blocky.ttf"
fontSmooth = "C:\\Users\\rabbitrabbit\\CodeBS\\Pong\\smooth.ttf"

# Sprite group(s)

# Collision groups
ballGroup = pygame.sprite.Group()

#   --- Globals-End ---
#   --- Classes-Start ---

class Block(pygame.sprite.Sprite):

def __init__(self, width, height, color, x, y):
# Makes a surface, fills it, and adds to allSprites group
super().__init__()

# Set up image
self.image = pygame.Surface((width, height))
self.image.fill(color)

# Set up surface
self.rect = self.image.get_rect()

# Position and center
self.rect.x = x
self.rect.y = y

self.rect.center = (self.rect.x, self.rect.y)

'''Class representing a player. Also contains the score attribute(s),
but that's just for convenience, score code isn't actually handled within
this class.'''

# Determines whether paddles is moving or not, and how fast
yChange = 0

def update(self):
# Takes the instance's yChange variable and uses it to move a paddle.
self.rect.y += self.yChange

if self.rect.y < 175:
pass
elif self.rect.y > 325:
pass

class Ball(Block):
'''Class representing the ball. All the game's collisions are
handled here'''

# Variables that determine the ball's speed
xChange = 0
yChange = 0

def __init__(self, width, height, color, x, y):

# Call's base class' constructor
super().__init__(width, height, color, x, y)

# Chooses the ball's direction at start and sends it off

self.xChange = random.getrandbits(1)
self.yChange = random.getrandbits(1)

# Sets x/yChange to 2 or -2
self.xChange = (self.xChange * 2 - 1) * 2
self.yChange = (self.yChange * 2 - 1) * 2

def update(self):
# Makes the ball move according to direction, and checks for collisons

self.rect.x += self.xChange
self.rect.y += self.yChange

# Check for walls
if self.rect.y <= 176 or self.rect.y >= 409:
self.yChange = self.yChange * -1

# Check for paddles - spritecollide returns a list of all the sprites
# in paddleGroup that collide with the ball

if len(spritesCollided) != 0:
self.xChange = self.xChange * -1 # Change the ball's direction

# Increases ball's speed a little bit. Checks whether ball
# x&yChange values are positive or negative to prevent accidentally
# lowering the ball's speed instead of raising it.
# Ball speed caps at 15. If it's any higher than 16 the ball would

if self.xChange != 15 or self.xChange != -15:

if self.xChange > 0:
self.xChange += 1
elif self.xChange < 0:
self.xChange -= 1

if self.yChange > 0:
self.yChange += 1
elif self.yChange < 0:
self.yChange -= 1

spritesCollided = [] # Clear the list

#   --- Classes-End ---
#   --- Functions-Start ---

def text(font, message, size, color, x, y):
# Set up font
myFont = pygame.font.Font(font, size)
text = myFont.render(message, 1, color)
textRect = text.get_rect()
textRect.x = x
textRect.y = y

# Render text
display.blit(text, [textRect.x, textRect.y])

def main(): # Main game loop

# Variable to keep loop running
done = False

# Determines whether or not things are happening
gameStarted = False

# Player scores
p1Score = 0
p2Score = 0

# Call instances of classes
# Background elements
topStrip = Block(640, 175, BLUE, 320, 88)
botStrip = Block(640, 55, BLUE, 320, 452)
#strip = Block(640, 250, WHITE, 320, 300) # Background element

ball = Ball(16, 16, BLUE, 320, 300) # The bouncy thing

# They move up and down
p1 = Paddle(16, 64, BLUE, 580, 305) # Right side
p2 = Paddle(16, 60, BLUE, 60, 305) # Left side

while not done:

# Events-Start
for event in pygame.event.get():
# Quits if you press the little red x
if event.type == pygame.QUIT:
done = True

# Keyboard inputs
if event.type == pygame.KEYDOWN: # While key is held
# p1 inputs
if event.key == pygame.K_UP:
p1.yChange = -10
elif event.key == pygame.K_DOWN:
p1.yChange = 10
# p2 inputs
if event.key == pygame.K_w:
p2.yChange = -10
elif event.key == pygame.K_s:
p2.yChange = 10
if event.type == pygame.KEYUP: # When the key's released
# p1 un-inputs(?)
if event.key == pygame.K_UP:
p1.yChange = 0
elif event.key == pygame.K_DOWN:
p1.yChange = 0
# p2 un-inputs(?)
if event.key == pygame.K_w:
p2.yChange = 0
elif event.key == pygame.K_s:
p2.yChange = 0

if ball.rect.x <= -16:
p1Score += 1 # Increases player one's score
ball.kill() # Gets rid of the ball
ball = Ball(16, 16, BLUE, 320, 300) # Makes a new one

elif ball.rect.x >= 640:
p2Score += 1 # Increases player two's score
ball.kill() # Gets rid of the ball
ball = Ball(16, 16, BLUE, 320, 300) # Makes a new one

ballGroup.update()

# Events-End

# Draw
display.fill(WHITE)

# Display the players' scores
text(fontSmooth, str(p1Score), 100, GRAY, 440, 250)
text(fontSmooth, str(p2Score), 100, GRAY, 160, 250)

# Draw objects
allSprites.draw(display)

# Display the game's name and stuff
text(fontBlocky, 'PONG', 105, WHITE, 180, -15)
text(fontSmooth, 'Made by m(O)x. He is great.', 20, WHITE, 185, 440)

# Time and update
clock.tick(FPS)
pygame.display.update()

pygame.quit()

#   --- Functions-End ---

# Run

if __name__ == '__main__':
main()



In case you were wondering about the rectangles, here's what it looks like.

Font was made by Kenney, on OGA.

Questions:

1. I tend to see people calling the update method of sprite groups that contain all of a game's sprites. If I have sprites that don't move (like the instances of the Block class), and I want to do this, would I just make an update method that passes whenever it's called?

2. Is there a way to not have all the input stuff in my main loop?

3. Is it considered bad form to have a lot of stuff in my main loop?

4. How can I add text to the allSprites group, so I don't have to render it strategically before or after calling the group's draw method?

5.How can I tell how fast my code is running aside from whether it lags or not?

6. How can I have multiple hitboxes on a single object, so the game doesn't glitch out when the ball hits the top/bottom of the paddle?

7. Is it just me, or is the ball throbbing?

Any input is appreciated.

Edited by m(O)x

##### Share on other sites

1. I tend to see people calling the update method of sprite groups that contain all of a game's sprites. If I have sprites that don't move (like the instances of the Block class), and I want to do this, would I just make an update method that passes whenever it's called?

So long as you don't have thousands of static objects, it's perfectly fine to have empty update methods that don't do anything when they are called.  The performance hit you take doing that is negligible until you start doing it thousands of times per frame.

2. Is there a way to not have all the input stuff in my main loop?

Sure.  First step is have some type of 'GetInput' method.  Second step is to separate game state changes from input processing.  All those yChange lines should be in your Paddle update method.  Input processing should only set an associated object variable indicating the action to be performed.

3. Is it considered bad form to have a lot of stuff in my main loop?

It's considered bad form to have a lot of stuff in any single method/function.  For main, your init code, Input code, Update Code, and Draw code should all be factored into separate method calls.  Timing code can remain in main.  Some people even go a step further and factor all of that code into a RunGame method so that main just has the one line calling RunGame.

5.How can I tell how fast my code is running aside from whether it lags or not?

pygame.time.get_ticks() returns the number of milliseconds since pygame.init() was called.  So call get_ticks before executing the code you want to time, call get_ticks after executing that code, subtract the first time from the second time, and you have how many milliseconds it took to execute that code.

There's just one caveat.  The resolution of get_ticks() isn't guaranteed to be 1 millisecond.  It may be 10.  It may be more.  TIMER_RESOLUTION is a constant that tells you the resolution.  If it's in the 10+ range, it isn't going to be very good for timing small portions of the game loop that are likely to only take a few milliseconds to run, or less.

https://www.pygame.org/docs/ref/time.html

http://stackoverflow.com/questions/1938048/high-precision-clock-in-python

6. How can I have multiple hitboxes on a single object, so the game doesn't glitch out when the ball hits the top/bottom of the paddle?

Not really sure what you mean by the ball glitching out when it hits the top/bottom of the paddle, but it shouldn't require multipel hitboxes, just additional processing to determine where the ball hit the paddle, and then conditional processing based on where the hit occurred.

7. Is it just me, or is the ball throbbing?

Doesn't appear to be throbbing in the picture you posted. ;)

##### Share on other sites

m(O)x, on 27 Jul 2015 - 2:42 PM, said:

6. How can I have multiple hitboxes on a single object, so the game doesn't glitch out when the ball hits the top/bottom of the paddle?

Not really sure what you mean by the ball glitching out when it hits the top/bottom of the paddle, but it shouldn't require multipel hitboxes, just additional processing to determine where the ball hit the paddle, and then conditional processing based on where the hit occurred.

Since whenever the ball touches the paddle, only its horizontal direction is reversed, when you move the paddle up/down onto the ball, it'll just sort of flicker as long as the paddle's on top of it. I guess the game doesn't actually glitch out, it just looks really weird.

How would I determine where the ball hit the paddle? Do I just write the numbers in, like for the top of the paddle: paddle.rect.y + 30?

##### Share on other sites

Since whenever the ball touches the paddle, only its horizontal direction is reversed, when you move the paddle up/down onto the ball, it'll just sort of flicker as long as the paddle's on top of it.

The simplest resolution is to add a second check for the direction the ball is moving, and if it's moving towards the paddle, then reverse the direction.  If it's already moving away from the paddle, you shouldn't reverse the direction again.

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 15
• 14
• 46
• 22
• 27
• ### Forum Statistics

• Total Topics
634044
• Total Posts
3015216
×