4
\$\begingroup\$

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 = random.randint(100, WIN_HEIGHT-150)
 return WIN_WIDTH, WIN_HEIGHT
def texts(score):
 font=pygame.font.Font(None,30)
 scoretext=font.render("Score:"+' ' + str(score), 1,(255,255,255))
 screen.blit(scoretext, (500, 15))
eaten = True
pressed_right = True
pressed_left = False
pressed_up = False
pressed_down = False
pygame.key.set_repeat(10,10)
level = [
 "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", 
 "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP", 
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "P P",
 "PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP",
]
def display_menu():
 while True:
 screen.fill(BLACK)
 for event in pygame.event.get():
 if event.type == QUIT:
 pygame.quit()
 sys.exit()
 elif event.type == pygame.MOUSEBUTTONUP:
 pos = pygame.mouse.get_pos()
 if 385 > pos[0] > 275:
 if 202 > pos[1] > 185:
 return
 elif 293 > pos[1] > 275:
 pygame.quit()
 sys.exit() 
 else:
 pass
 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) 
display_menu()
while True:
 screen.fill(BLACK)
 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:
 if pressed_right:
 pressed_right = True# left arrow turns left
 else:
 pressed_left = True
 pressed_right = False
 pressed_up = False
 pressed_down = False
 elif event.key == pygame.K_RIGHT:
 if pressed_left:
 pressed_left = True# right arrow turns right
 else: 
 pressed_right = True
 pressed_left = False
 pressed_up = False
 pressed_down = False
 elif event.key == pygame.K_UP:
 if pressed_down:# up arrow goes up
 pressed_down = True
 else:
 pressed_up = True
 pressed_right = False
 pressed_left = False
 pressed_down = False
 elif event.key == pygame.K_DOWN:
 if pressed_up:
 break
 else:
 pressed_down = True
 pressed_right = False
 pressed_up = False
 pressed_left = False
 x = snakex
 y = snakey
 collision_coords = [1]
 if pressed_left:
 snakex -= speed
 elif pressed_right:
 snakex += speed
 elif pressed_up:
 snakey -= speed
 elif pressed_down:
 snakey += speed
 snake_parts[0] = Snake(snakex, snakey, screen, int(size), RED)
 collision_coords[0] = snake_parts[0].coordinates()
 snake_parts[0].draw(screen)
 if eaten:
 foodx, foody = get_food_pos(WIN_WIDTH, WIN_HEIGHT)
 eaten = False
 my_food = Food(foodx, foody, screen)
 my_food.draw(screen)
 if snake_parts[0].colliderect(my_food):
 eaten = True
 screen.fill(BLACK)
 a_snake = Snake(snakex, snakey, screen, int(size), RED)
 snake_parts.append(a_snake)
 Score += 1
 for i in range(1, len(snake_parts)):
 tempx, tempy = snake_parts[i].coordinates()
 snake_parts[i] = Snake(x, y, screen, int(size), RED)
 collision_coords.append(snake_parts[i].coordinates())
 snake_parts[i].draw(screen)
 x, y = tempx, tempy
 platform_x = 0
 platform_y = 0
 for row in level:
 for col in row:
 if col == "P":
 col = Barrier(platform_x, platform_y, screen)
 col.draw(screen)
 if snake_parts[0].colliderect(col):
 pygame.quit()
 sys.exit()
 platform_x += 15
 platform_y += 20
 platform_x = 0
 for i in range(2, len(collision_coords)):
 if int(collision_coords[0][1]) == int(collision_coords[i][1]) and int(collision_coords[0][0]) == int(collision_coords[i][0]):
 pygame.quit()
 sys.exit() 
 texts(Score)
 pygame.display.update()
 fpsClock.tick(FPS) 
Raystafarian
7,2991 gold badge23 silver badges60 bronze badges
asked Feb 12, 2017 at 14:10
\$\endgroup\$

1 Answer 1

6
\$\begingroup\$

There is quite a lot of code, so I'll point out the first few things that I notice.

  • 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)
  • x and y don't really say much, but you're using them as the old position of the snake, so maybe rename them to orig_x and orig_y
  • pygame.key.set_repeat(10, 10) is useless here, you can remove it.
  • get_food_pos gets 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 simply width and height and return food_x and food_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 while loop.
  • 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()
answered Feb 12, 2017 at 19:00
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.