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)
allSprites = pygame.sprite.LayeredUpdates()
# Collision groups
ballGroup = pygame.sprite.Group()
paddleGroup = 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)
# Add to sprite group(s)
allSprites.add(self)
class Paddle(Block):
'''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
# Keep paddle within boundaries.
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
# Add to ballgroup for updates
ballGroup.add(self)
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
spritesCollided = pygame.sprite.spritecollide(self, paddleGroup, False)
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
# fly through paddles.
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
# Add paddles to group for collisions
paddleGroup.add(p1, p2)
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()
paddleGroup.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.