pythonpygamecollision-detection

how to solve bug on snake wall teleportation


I'm doing a snake game and I got a bug I can't figure out how to solve, I want to make my snake teleport trough walls, when the snake colllides with a wall it teleports to another with the opposite speed and position, like the classic game, but with my code when the snake gets near the wall it duplicates to the opposite wall, but it was supposed to not even detect collision yetin the way this print shows

important thing: in the grid the snake is on the side of the wall, like this

SSSS
WWWW

and not like this:

SSSS
NNNN
WWWW

when S represents the snake, W represents the wall and N represents nothing.

edit: whole code

import pygame, random, os, sys
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((1020, 585))
pygame.display.set_caption('2snakes!')

#files location
current_path = os.path.dirname(__file__)
data_path = os.path.join(current_path, 'data')
icon = pygame.image.load(os.path.join(data_path, 'icon.png'))
press_any = pygame.image.load(os.path.join(data_path, 'press_any.png'))
pygame.display.set_icon(icon)


#collission
def collision(c1,c2):
    return (c1[0] == c2[0]) and (c1[1] == c2[1])

#game over
def gameOverBlue():
    print("blue")
    main()
def gameOverRed():
    print("red")
    main()
fps = 15
def main():
#variables
    direction = 'RIGHT'
    direction2 = 'RIGHT'
    change_to = direction
    change2_to = direction2
    

    #snake
    size = 15
    s_posx = 60
    s_posy = 60
    snake = [(s_posx + size * 2, s_posy),(s_posx + size, s_posy),(s_posx, s_posy)]
    s_skin = pygame.Surface((size, size))
    s_skin.fill((82,128,208))

    #snake2
    size2 = 15
    s2_posx = 390
    s2_posy = 390
    snake2 = [(s2_posx + size2 * 2, s2_posy),(s2_posx + size2, s2_posy),(s2_posx, s2_posy)]
    s2_skin = pygame.Surface((size2, size2))
    s2_skin.fill((208,128,82))

    #apple
    apple = pygame.Surface((size, size))
    apple_pos = ((random.randint(0, 67)) * 15, (random.randint(0, 38)) * 15)
#endregion
    while True:
        pygame.time.Clock().tick(fps)
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()

        

        #input
            elif event.type == pygame.KEYDOWN:
                #snake
                if event.key == ord('w'):
                    change_to = 'UP'
                if event.key == ord('s'):
                    change_to = 'DOWN'
                if event.key == ord('a'):
                    change_to = 'LEFT'
                if event.key == ord('d'):
                    change_to = 'RIGHT'   
                        
                #snake2
                if event.key == pygame.K_UP:
                    change2_to = 'UP'
                if event.key == pygame.K_DOWN:
                    change2_to = 'DOWN'
                if event.key == pygame.K_LEFT:
                    change2_to = 'LEFT'
                if event.key == pygame.K_RIGHT:
                    change2_to = 'RIGHT'

                #quit game
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.quit()
                    
    #smooth snake movement
            #snake
        if change_to == 'UP' and direction != 'DOWN':
            direction = 'UP'
        if change_to == 'DOWN' and direction != 'UP':
            direction = 'DOWN'
        if change_to == 'LEFT' and direction != 'RIGHT':
            direction = 'LEFT'
        if change_to == 'RIGHT' and direction != 'LEFT':
            direction = 'RIGHT'
            
            #snake2
        if change2_to == 'UP' and direction2 != 'DOWN':
            direction2 = 'UP'
        if change2_to == 'DOWN' and direction2 != 'UP':
            direction2 = 'DOWN'
        if change2_to == 'LEFT' and direction2 != 'RIGHT':
            direction2 = 'LEFT'
        if change2_to == 'RIGHT' and direction2 != 'LEFT':
            direction2 = 'RIGHT'

    #movement
        #snake
        new_pos = None
        if direction == 'DOWN':
            new_pos = (snake[0][0], snake[0][1] + size)
        if direction == 'UP':
            new_pos = (snake[0][0], snake[0][1] - size)
        if direction == 'LEFT':
            new_pos = (snake[0][0] - size, snake[0][1])
        if direction == 'RIGHT':
            new_pos = (snake[0][0] + size, snake[0][1])
        if new_pos:
            snake = [new_pos] + snake
            del snake[-1]
        
        #snake2
        new_pos2 = None
        if direction2 == 'DOWN':
            new_pos2 = (snake2[0][0], snake2[0][1] + size2)
        if direction2 == 'UP':
            new_pos2 = (snake2[0][0], snake2[0][1] - size2)
        if direction2 == 'LEFT':
            new_pos2 = (snake2[0][0] - size2, snake2[0][1])
        if direction2 == 'RIGHT':
            new_pos2 = (snake2[0][0] + size2, snake2[0][1])
        if new_pos2:
            snake2 = [new_pos2] + snake2
            del snake2[-1]

    #apple collision
        #snake
        if collision(snake[0], apple_pos):
            snake.append((-20,-20))
            size = 15
            s_skin = pygame.Surface((size, size))
            s_skin.fill((82,128,208))
            apple_pos = ((random.randint(0, 32)) * 15, (random.randint(0, 19)) * 15)
        #snake2
        if collision(snake2[0], apple_pos):
            snake2.append((-20,-20))
            
            apple_pos = ((random.randint(0, 67)) * 15, (random.randint(0, 38)) * 15)

    #wall collisison
        #snake
        _pos = None
        if snake[0][0] == 15:
            _pos = (990, snake[0][1])
        elif snake[0][1] == 15:
            _pos = (snake[0][0], 555)
        elif snake[0][0] == 990:
            _pos = (15, snake[0][1])
        elif snake[0][1] == 555:
            _pos = (snake[0][0], 15)
        if _pos:
            snake = [_pos] + snake
            del snake[-1]
        #snake2
        _pos2 = None
        if snake2[0][0] == 15:
            _pos2 = (1005, snake2[0][1])
        elif snake2[0][1] == 0:
            _pos2 = (snake2[0][0], 570)
        elif snake2[0][0] == 1005:
            _pos2 = (0, snake2[0][1])
        elif snake2[0][1] == 570:
            _pos2 = (snake2[0][0], 0)
        if _pos2:
            snake2 = [_pos2] + snake2
            del snake2[-1]

    #self collisison
        #snake
        if snake[0] in snake[1:]:
            print("self collision")
            gameOverBlue()
        #snake2
        if snake2[0] in snake2[1:]:
            print("self collision")
            gameOverRed()
        #snakes colliding with each other
        if snake2[0] == snake[0]:
            print("head to head collisions")
        if snake[0] in snake2:
            gameOverRed()
        if snake2[0] in snake:
            gameOverBlue()

    #rendering
        apple.fill((255,0,0))
        screen.fill((10,10,10))
        screen.blit(apple,apple_pos)
        for pos in snake:
            screen.blit(s_skin,pos)
        for pos2 in snake2:
            screen.blit(s2_skin,pos2)
        pygame.display.update()

while True:
        pygame.time.Clock().tick(fps)
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()

        #input
            elif event.type == pygame.KEYDOWN:
                main()
        screen.blit(press_any, (0,0))
        pygame.display.update()

edit: the red dot is the food/apple


Solution

  • You want to implement a teleporter. Therefore, once the snake is over the edge of the window, you will have to teleport to the other side. The size of your window is 1020x585. The snake is out of the window if x == -15, y == -15, x == 1020 or y == 585 Hence you have to do the following teleportations:

    _pos = None
    if snake[0][0] == -15:
        _pos = (1005, snake[0][1])
    elif snake[0][1] == -15:
        _pos = (snake[0][0], 570)
    elif snake[0][0] == 1020:
        _pos = (0, snake[0][1])
    elif snake[0][1] == 585:
        _pos = (snake[0][0], 0)
    if _pos:
        snake = [_pos] + snake
        del snake[-1]
    

    Another simplest solution is to use the %(modulo) operator:

    x1 = (x1 + x1_change) % dis_width
    y1 = (y1 + y1_change) % dis_height
    
    >>> width = 100
    >>> 101 % width
    1
    >>> -1 % width
    99
    >>>
    

    Minimal example: repl.it/@Rabbid76/PyGame-ContinuousMovement

    import pygame
    
    pygame.init()
    window = pygame.display.set_mode((300, 300))
    clock = pygame.time.Clock()
    
    rect = pygame.Rect(0, 0, 20, 20)
    rect.center = window.get_rect().center
    vel = 5
    
    run = True
    while run:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            if event.type == pygame.KEYDOWN:
                print(pygame.key.name(event.key))
    
        keys = pygame.key.get_pressed()
        
        dx = (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel
        dy = (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * vel
            
        rect.centerx = (rect.centerx + dx) % window.get_width()
        rect.centery = (rect.centery + dy) % window.get_height()
    
        window.fill(0)
        pygame.draw.rect(window, (255, 0, 0), rect)
        pygame.display.flip()
    
    pygame.quit()
    exit()
    

    Note:

    >>> width = 100
    >>> 101 % width
    1
    >>> -1 % width
    99
    >>>