patternpythonMinor
Snake game in Pygame
Viewed 0 times
pygamegamesnake
Problem
This is my first game, and looking for some help to improve current code because I've identified a lot that I think could be written more efficiently, particularly the segment that checks which key has been pressed but I'm not sure how to improve it.
```
import pygame
from pygame.locals import *
import random
import sys
pygame.init()
FPS = 30
fpsClock = pygame.time.Clock()
WIN_WIDTH = 680 #width of window
WIN_HEIGHT = 500 #height of window
DISPLAY = (WIN_WIDTH, WIN_HEIGHT) #variable for screen display
DEPTH = 32 #standard
FLAGS = 0 #standard
BLACK = (0, 0, 0) #black
RED = (255, 0, 0) #red
GOLD = (255, 215, 0)
LOL = (14, 18, 194)
YOLO = (155, 98, 245)
WHITE = (255, 255, 255)
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption('Snaek')
collision_coords = [1]
snake_parts = [1]
Score = 0
speed = 12
snakex = 125
snakey = 70
size = 20
# --- classes ---
class Snake(pygame.Rect):
def __init__(self, x, y, screen, size, colour):
pygame.Rect.__init__(self, x, y, size, 20)
self.screen = screen
self.colour = colour
self.x = x
self.y = y
def draw(self, screen):
pygame.draw.rect(self.screen, self.colour, self)
def coordinates(self):
return self.x, self.y
class Food(pygame.Rect):
def __init__(self, x, y, screen):
pygame.Rect.__init__(self, x, y, 20, 20)
self.screen = screen
def draw(self, screen):
pygame.draw.rect(self.screen, GOLD, self)
class Barrier(pygame.Rect):
def __init__(self, x, y, screen):
pygame.Rect.__init__(self, x, y, 40, 20)
self.screen = screen
def draw(self, screen):
pygame.draw.rect(self.screen, LOL, self)
class GameMenu():
def __init__(self, screen, options):
self.screen = screen
self.options = options
# --- functions ---
def get_food_pos(WIN_WIDTH, WIN_HEIGHT):
WIN_WIDTH = random.randint(100, WIN_WIDTH-150)
WIN_HEIGHT =
```
import pygame
from pygame.locals import *
import random
import sys
pygame.init()
FPS = 30
fpsClock = pygame.time.Clock()
WIN_WIDTH = 680 #width of window
WIN_HEIGHT = 500 #height of window
DISPLAY = (WIN_WIDTH, WIN_HEIGHT) #variable for screen display
DEPTH = 32 #standard
FLAGS = 0 #standard
BLACK = (0, 0, 0) #black
RED = (255, 0, 0) #red
GOLD = (255, 215, 0)
LOL = (14, 18, 194)
YOLO = (155, 98, 245)
WHITE = (255, 255, 255)
screen = pygame.display.set_mode(DISPLAY, FLAGS, DEPTH)
pygame.display.set_caption('Snaek')
collision_coords = [1]
snake_parts = [1]
Score = 0
speed = 12
snakex = 125
snakey = 70
size = 20
# --- classes ---
class Snake(pygame.Rect):
def __init__(self, x, y, screen, size, colour):
pygame.Rect.__init__(self, x, y, size, 20)
self.screen = screen
self.colour = colour
self.x = x
self.y = y
def draw(self, screen):
pygame.draw.rect(self.screen, self.colour, self)
def coordinates(self):
return self.x, self.y
class Food(pygame.Rect):
def __init__(self, x, y, screen):
pygame.Rect.__init__(self, x, y, 20, 20)
self.screen = screen
def draw(self, screen):
pygame.draw.rect(self.screen, GOLD, self)
class Barrier(pygame.Rect):
def __init__(self, x, y, screen):
pygame.Rect.__init__(self, x, y, 40, 20)
self.screen = screen
def draw(self, screen):
pygame.draw.rect(self.screen, LOL, self)
class GameMenu():
def __init__(self, screen, options):
self.screen = screen
self.options = options
# --- functions ---
def get_food_pos(WIN_WIDTH, WIN_HEIGHT):
WIN_WIDTH = random.randint(100, WIN_WIDTH-150)
WIN_HEIGHT =
Solution
There is quite a lot of code, so I'll point out the first few things that I notice.
Now for the implementation.
You can initialize it like this:
And in the main loop use it like this:
What happens here is that if one of the keys is pressed, the array will contain all zero values, except for the pressed key. The value there will be negative or positive depending on the direction, so you can simply sum and multiply the result by your speed.
The display menu can also be written differently.
To sum up, something like this:
- Naming: you have some constants all upper case which is good, but you also have constants that are lower case and one (
Score) which is neither. I'd say stick to all upper case for constants.
- A small typo (
Snaek)
xandydon't really say much, but you're using them as the old position of the snake, so maybe rename them toorig_xandorig_y
pygame.key.set_repeat(10, 10)is useless here, you can remove it.
get_food_posgets as arguments the width and height of the screen, but no need to name them the same way, which is actually quite misleading. Name them simplywidthandheightand returnfood_xandfood_y, not the same variables.
Now for the implementation.
- Yes, the key press handler can be written with less code. You can have an array of key presses and use the values to determine where you're supposed to go.
You can initialize it like this:
(LEFT, RIGHT, UP, DOWN) = (0, 1, 2, 3)
pressed = [0, 1, 0, 0]And in the main loop use it like this:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN: # check for key presses
if event.key == pygame.K_LEFT and not pressed[RIGHT]:
pressed = [-1, 0, 0, 0]
elif event.key == pygame.K_RIGHT and not pressed[LEFT]:
pressed = [0, 1, 0, 0]
elif event.key == pygame.K_UP and not pressed[DOWN]:
pressed = [0, 0, -1, 0]
elif event.key == pygame.K_DOWN and not pressed[UP]:
pressed = [0, 0, 0, 1]
snakex += speed * (pressed[LEFT] + pressed[RIGHT])
snakey += speed * (pressed[UP] + pressed[DOWN])What happens here is that if one of the keys is pressed, the array will contain all zero values, except for the pressed key. The value there will be negative or positive depending on the direction, so you can simply sum and multiply the result by your speed.
The display menu can also be written differently.
- First of all I'd rather have that return a value and use that to determine if the users want to quit or not. Then I'd rename it to something like
get_menu_choice.
- There's no need to repaint continuously if you're not changing anything, so your drawing code can be outside of that
whileloop.
- If you want to detect collisions between anything and a rectangle there's a specific method for that. You get your mouse position and check if it's collided with a rectangle.
To sum up, something like this:
def get_menu_choice():
screen.fill(BLACK)
font = pygame.font.Font(None, 30)
play_game = font.render("Play Game", 1, WHITE)
quit_game = font.render("Quit Game", 1, WHITE)
screen.blit(play_game, (275, 185))
screen.blit(quit_game, (275, 275))
pygame.display.update()
fpsClock.tick(FPS)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pos = pygame.mouse.get_pos()
(mouse_clicked, _, __) = pygame.mouse.get_pressed()
start_game_rect = pygame.Rect(275, 185, 110, 27)
quit_game_rect = pygame.Rect(275, 275, 110, 27)
if mouse_clicked:
if start_game_rect.collidepoint(pos):
return 1
if quit_game_rect.collidepoint(pos):
return 0
if get_menu_choice() == 0:
pygame.quit()
sys.exit()Code Snippets
(LEFT, RIGHT, UP, DOWN) = (0, 1, 2, 3)
pressed = [0, 1, 0, 0]for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN: # check for key presses
if event.key == pygame.K_LEFT and not pressed[RIGHT]:
pressed = [-1, 0, 0, 0]
elif event.key == pygame.K_RIGHT and not pressed[LEFT]:
pressed = [0, 1, 0, 0]
elif event.key == pygame.K_UP and not pressed[DOWN]:
pressed = [0, 0, -1, 0]
elif event.key == pygame.K_DOWN and not pressed[UP]:
pressed = [0, 0, 0, 1]
snakex += speed * (pressed[LEFT] + pressed[RIGHT])
snakey += speed * (pressed[UP] + pressed[DOWN])def get_menu_choice():
screen.fill(BLACK)
font = pygame.font.Font(None, 30)
play_game = font.render("Play Game", 1, WHITE)
quit_game = font.render("Quit Game", 1, WHITE)
screen.blit(play_game, (275, 185))
screen.blit(quit_game, (275, 275))
pygame.display.update()
fpsClock.tick(FPS)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pos = pygame.mouse.get_pos()
(mouse_clicked, _, __) = pygame.mouse.get_pressed()
start_game_rect = pygame.Rect(275, 185, 110, 27)
quit_game_rect = pygame.Rect(275, 275, 110, 27)
if mouse_clicked:
if start_game_rect.collidepoint(pos):
return 1
if quit_game_rect.collidepoint(pos):
return 0
if get_menu_choice() == 0:
pygame.quit()
sys.exit()Context
StackExchange Code Review Q#155162, answer score: 6
Revisions (0)
No revisions yet.