Search code examples
pythonkeyboardpygamecollisiongame-development

Python Snake Game Finalization Issues


I am coding a game of snake using the pygame module in python and am very near the end yet running into some unforeseen issues. My code runs yet my snake won't turn left and when I collide with an apple I do not grow in size and the apple does not disappear. Additionally, when my character hits the wall the end screen also does not come up. I am positive this is due to some small issue in my code and would appreciate for anyone to take a look and see if they can root it out since I have been unable to do so. Also, any tips for improving the code would be greatly appreciated.

import sys, time, random, pygame
from random import randrange
pygame.init()
fps_controller = pygame.time.Clock()
#Screen Dimensions
screen_width = 800
screen_height = 800

#Screen Set Up
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Snake by Bela Zumbansen")
pygame.mouse.set_visible(0)

#Colours
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLACK  = (0, 0, 0)
GREY = (200, 200, 200)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
LIGHTBLUE = (0, 155, 155)

#Directions
RIGHT = 1
LEFT = 2
UP = 3
DOWN = 4

#Game Set Up
snake_pos = [100, 100]
snake_body = [[100, 100], [190, 100], [180, 400]]
snake_speed = 10

apple_pos = [random.randrange(1,80)*10, random.randrange(1,80)*10]
apple_spawn = True

direction = RIGHT
update = direction

score = 0

def over():
    run = False
    return run
def game_over():
    screen.fill(LIGHTBLUE)
    draw_text("GAME OVER", 48, WHITE, screen_width/2, screen_height/4)
    draw_text("Score: " + str(score), 22, WHITE,  screen_width / 2, screen_height / 3)
    draw_text("Press SPACE to play again or ESC to exit", 22, WHITE, screen_width/2, screen_height / 4)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                run = True
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
        return run
def draw_text(text, size, color, x, y):
    font = pygame.font.Font('freesansbold.ttf', size)
    TextSurf, TextRect = text_objects(text, font, color)
    TextRect.center = (x, y)
    screen.blit(TextSurf, TextRect)

def text_objects(text, font, color):
    textSurface = font.render(text, True, color)
    return textSurface, textSurface.get_rect()

def eating_apple():
    global score, apple_spawn
    score += 1
    apple_spawn = False

def spawnApple():
    global apple_pos
    apple_pos = [random.randrange(1,80)*10, random.randrange(1,80)*10]

def score(score):
    font = pygame.font.SysFont(None, 25)
    text = font.render("Score: "+str(score), True, WHITE)
    screen.blit(text,(10,10))

def main():
    global update, direction, run, snake_pos, snake_speed, apple_spawn, apple_pos
    pygame.time.delay(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        update = 'LEFT'
    elif keys[pygame.K_RIGHT]:
        update = RIGHT
    elif keys[pygame.K_UP]:
        update = UP
    elif keys[pygame.K_DOWN]:
        update = DOWN

    if update == RIGHT and direction != LEFT:
        direction = RIGHT
    if update == LEFT and direction != RIGHT:
        direction = LEFT
    if update == UP and direction != DOWN:
        direction = UP
    if update == DOWN and direction != UP:
        direction = DOWN

    if direction == RIGHT:
        snake_pos[0] += snake_speed
    if direction == LEFT:
        snake_pos[0] -= snake_speed
    if direction == UP:
        snake_pos[1] -= snake_speed
    if direction == DOWN:
        snake_pos[1] += snake_speed

    snake_body.insert(0, list(snake_pos))
    if snake_pos[0] == apple_pos[0] and snake_pos[1] == apple_pos[1]:
        eating_apple()
    else:
        snake_body.pop()
    if not apple_spawn:
        spawnApple()

    screen.fill(BLACK)
    for pos in snake_body:
        # Snake body
        # .draw.rect(play_surface, color, xy-coordinate)
        # xy-coordinate -> .Rect(x, y, size_x, size_y)
        pygame.draw.rect(screen, GREEN, pygame.Rect(pos[0], pos[1], 20, 20))

    pygame.draw.rect(screen, RED, pygame.Rect(apple_pos[0], apple_pos[1], 20, 20))

    if snake_pos[0] < 0 or snake_pos[0] > screen_width-20:
        over()
    if snake_pos[1] < 0 or snake_pos[1] > screen_height-20:
        over()

    for block in snake_body[1:]:
        if snake_pos[0] == block[0] and snake_pos[1] == block[1]:
            over()

 #   score(score)
    pygame.display.update()
    fps_controller.tick(25)

#main loop
run = True
while run:
    main()
while not run:
    game_over()

Solution

  • [...] my snake won't turn left [...]

    It has to be update = LEFT rather than update = 'LEFT'


    [...] when I collide with an apple I do not grow in size and the apple does not disappear

    There is a variable and a function with the name score. Rename the variable e.g. scoreval. See also [Python: function and variable with same name](Python: function and variable with same name):

    scoreval = 0 
    
    def eating_apple():
        global scoreval, apple_spawn
        scoreval += 1
        apple_spawn = False
    

    Use pygame.Rect.colliderect(), to check if the head of the snake eats the apple:

    if pygame.Rect(*snake_pos, 20, 20).colliderect(*apple_pos, 20, 20):
        eating_apple()
    else:
        snake_body.pop()
    

    When the apple has been spawned, the the state apple_spawn has to be reset:

    def spawnApple():
        global apple_pos, apple_spawn
        apple_pos = [random.randrange(1,80)*10, random.randrange(1,80)*10]
        apple_spawn = True
    

    Referring to the comment:

    [...] I'm still running into are that the game doesn't end if the snake collides with itself or runs into the wall. I have if statements for those so I'm a little lost as to why nothing is happening [...]

    You've to use the global statement to change the value of the variable run in global scope in the function over:

    def over():
        global run
        run = False