1

I created a snake-like game in which the user moves the sprite, and the sprite leaves a trail. If the user runs into the trail he's created, I'd like the game to end, and the player to lose.

As the sprite moves, it makes a white trail on a black background. If I can detect that the user-controlled sprite will hit one of the white areas (its 'tail'), I can create an if statement that leads to a game loss.

I use the [Surface.get_at()][1] function to grab the color of the space the sprite will move into, and checking if this color is the same as the background color. If it's not the same as the background color, that means it's the sprite's tail, and the game ends in a loss.

Here's the code:

if screen.get_at(player.peek_next()) != BACKGROUND:
    done=True

However, as soon as I run the game, I can travel in a straight line for about two seconds before that 'if statement' kicks in, and the game ends. I don't know why it thinks that the 'next' position is !=BACKROUND. I tried to slow down the clock so it would register less frames per second, but that doesn't keep it from closing, it only delays it.

Here's the code:

import pygame
import os
import time

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BACKGROUND = (100, 30, 100)

class Player(pygame.sprite.Sprite):

    def __init__(self, x, y):
        super().__init__()

        self.image = pygame.Surface([20, 20])
        self.image.fill(WHITE)

        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

        self.change_x = 0
        self.change_y = 0

    def peek_next(self):
        return (self.rect.x+self.change_x, self.rect.y+self.change_y)

    def changespeed(self, x, y):
        self.change_x += x
        self.change_y += y

    def update(self):
        self.rect.x += self.change_x
        self.rect.y += self.change_y

pygame.init()

screen = pygame.display.set_mode([800, 600])

pygame.display.set_caption('The Etch-a-Sketch Game')

screen.fill(BACKGROUND)

myfont = pygame.font.SysFont('Times', 20)
textsurface = myfont.render('This is the Etch-a-Sketch Game', False, (255, 255, 255))
screen.blit(textsurface,(0,0))

myfont = pygame.font.SysFont('Times', 15)
textsurface = myfont.render('Feel free to draw, but if you cross your own path, you will die.', False, (255, 255, 255))
screen.blit(textsurface,(0,20))

player = Player(400, 300)
all_sprites_list = pygame.sprite.Group()
all_sprites_list.add(player)

clock = pygame.time.Clock()
done = False

while not done:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                player.changespeed(-3, 0)
            elif event.key == pygame.K_RIGHT:
                player.changespeed(3, 0)
            elif event.key == pygame.K_UP:
                player.changespeed(0, -3)
            elif event.key == pygame.K_DOWN:
                player.changespeed(0, 3)

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                player.changespeed(3, 0)
            elif event.key == pygame.K_RIGHT:
                player.changespeed(-3, 0)
            elif event.key == pygame.K_UP:
                player.changespeed(0, 3)
            elif event.key == pygame.K_DOWN:
                player.changespeed(0, -3)

        if screen.get_at(player.peek_next()) != BACKGROUND:
            done=True

    player.update()

    all_sprites_list.draw(screen)

    pygame.display.flip()
    clock.tick(250)

pygame.quit ()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Gal
  • 41
  • 6
  • As a side note here: the way you programmed the controls seems to make no sense - should the snake-direction be _reversed_ whenever a key is realized?? Reversing the motion will just make it go through the path it was coming through, and automatically colide. It probably makes sense just dropping the whole `elif event.type == pygame.KEYUP:` block. – jsbueno Jan 23 '19 at 17:27

1 Answers1

2

There are 2 issues.

First you have to respect the size of the player (white rectangle) when you calculate the position for the collision test. Test the "next", beside the rectangle:

def peek_next(self):
    if self.change_x > 0:
        return (self.rect.right+1, self.rect.centery)
    elif self.change_x < 0:
        return (self.rect.left-1, self.rect.centery)
    elif self.change_y > 0:
        return (self.rect.centerx, self.rect.bottom+1)
    elif self.change_y < 0:
        return (self.rect.centerx, self.rect.top-1)
    return (self.rect.x, self.rect.y)

It makes no sense to test, if there is no change of the position, because then the test position is the current position and this will always lead to a "collision". The collision test is only allowed to be done, if player.change_x != 0 or player.change_y != 0 is fulfilled.
Further the test has to be done outside the event loop, note if key is hold, the "player" still moves and you have to check for a collision too:

while not done:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                player.changespeed(-3, 0)
            elif event.key == pygame.K_RIGHT:
                player.changespeed(3, 0)
            elif event.key == pygame.K_UP:
                player.changespeed(0, -3)
            elif event.key == pygame.K_DOWN:
                player.changespeed(0, 3)

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                player.changespeed(3, 0)
            elif event.key == pygame.K_RIGHT:
                player.changespeed(-3, 0)
            elif event.key == pygame.K_UP:
                player.changespeed(0, 3)
            elif event.key == pygame.K_DOWN:
                player.changespeed(0, -3)

    # <----
    if (player.change_x != 0 or player.change_y != 0) and screen.get_at(player.peek_next()) != BACKGROUND:
        done=True 

Rabbid76
  • 202,892
  • 27
  • 131
  • 174