1

I am creating a snake clone in pygame, and I am currently working on managing the turning mechanisms of the snake and its movement. I create a point at the time of turning in pygame, then store it in snake.pos_at_turn. After that I adjust each snake segments direction the should move until either their x or y coordinate equals that of snake.pos_at_turn then I change their direction again. The problem is that for some reason the snake segment doesn't move at all. Any help would be appreciated!

Code for snake object and event loop:

import pygame,sys,random
from pygame.locals import *

pygame.init()

WINDOW_HEIGHT = 500
WINDOW_WIDTH = 500

RECTANGLE_HEIGHT = 20
RECTANGLE_WIDTH = 20

GREEN = (0,255,0)
WHITE = (255,255,255)
BLACK = (0,0,0)
RED = (255,0,0)

clock = pygame.time.Clock()
SCREEN = pygame.display.set_mode((WINDOW_WIDTH,WINDOW_HEIGHT))
pygame.display.set_caption("Snake")

class Snake(object):
    def __init__(self, color, width, height):
        self.body = []
        self.image = pygame.Surface((width,height))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.speed = 20 
        self.direction = 'right'
        self.living = True
        self.move = (0,0)
        self.pos_at_turn = [0,0]

    def movement(self):
        if self.direction == 'right':
            self.move = (self.speed,0)
        elif self.direction == 'left':
            self.move = (-self.speed,0)
        elif self.direction == 'up':
            self.move = (0,-self.speed)
        elif self.direction == 'down':
            self.move = (0,self.speed)

        self.rect.move_ip(self.move)
        self.rect.clamp_ip(SCREEN.get_rect())

    def blit(self):
        SCREEN.blit(self.image,self.rect)

    def update(self,food):
        self.movement()
        self.add_segment(food)
        self.draw_snake()
        for snake_segment in self.body:
            print(snake_segment.rect)

    def add_segment(self,food):
        snake_segment = Snake(GREEN,RECTANGLE_WIDTH,RECTANGLE_HEIGHT)

        if len(self.body) == 0:
            self.body.append(snake_segment)

        if self.rect.colliderect(food.rect):
            self.body.append(snake_segment)

    def draw_snake(self):
        for snake_segment in self.body:
            if self.body.index(snake_segment) == 0:
                self.blit()
            else:
                # original movement code:

                # if self.direction == 'right':
                #     snake_segment.rect.x = self.rect.x - RECTANGLE_WIDTH * self.body.index(snake_segment)
                #     snake_segment.rect.y = self.rect.y
                #     snake_segment.blit()
                # elif self.direction == 'left':
                #     snake_segment.rect.x = self.rect.x + RECTANGLE_WIDTH * self.body.index(snake_segment)
                #     snake_segment.rect.y = self.rect.y
                #     snake_segment.blit()
                # elif self.direction == 'up':
                #     snake_segment.rect.x = self.rect.x
                #     snake_segment.rect.y = self.rect.y + RECTANGLE_HEIGHT * self.body.index(snake_segment)
                #     snake_segment.blit()
                # elif self.direction == 'down':
                #     snake_segment.rect.x = self.rect.x
                #     snake_segment.rect.y = self.rect.y - RECTANGLE_HEIGHT * self.body.index(snake_segment)
                #     snake_segment.blit()

                if self.direction == 'right':
                    if snake_segment.rect.y >= self.pos_at_turn[1]:
                        snake_segment.direction = 'down'
                    elif snake_segment.rect.y <= self.pos_at_turn[1]:
                        snake_segment.direction = 'up'

                    if snake_segment.rect.y == self.pos_at_turn[1]:
                        if snake_segment.rect.x >= self.pos_at_turn[0]:
                            snake_segment.direction = 'left'
                        elif snake_segment.rect.x <= self.pos_at_turn[0]:
                            snake_segment.direction = 'right'

                    snake_segment.blit()
                elif self.direction == 'left':
                    if snake_segment.rect.y >= self.pos_at_turn[1]:
                        snake_segment.direction = 'down'
                    elif snake_segment.rect.y <= self.pos_at_turn[1]:
                        snake_segment.direction = 'up'

                    if snake_segment.rect.y == self.pos_at_turn[1]:
                        if snake_segment.rect.x >= self.pos_at_turn[0]:
                            snake_segment.direction = 'left'
                        elif snake_segment.rect.x <= self.pos_at_turn[0]:
                            snake_segment.direction = 'right'
                    snake_segment.blit()

                elif self.direction == 'up':
                    if snake_segment.rect.x >= self.pos_at_turn[0]:
                        snake_segment.direction = 'left'
                    elif snake_segment.rect.x <= self.pos_at_turn[0]:
                        snake_segment.direction = 'right'

                    if snake_segment.rect.x == self.pos_at_turn[0]:
                        if snake_segment.rect.y >= self.pos_at_turn[1]:
                            snake_segment.direction = 'down'
                        elif snake_segment.rect.y <= self.pos_at_turn[1]:
                            snake_segment.direction = 'up'
                    snake_segment.blit()

                elif self.direction == 'down':
                    if snake_segment.rect.x >= self.pos_at_turn[0]:
                        snake_segment.direction = 'left'
                    elif snake_segment.rect.x <= self.pos_at_turn[0]:
                        snake_segment.direction = 'right'

                    if snake_segment.rect.x == self.pos_at_turn[0]:
                        if snake_segment.rect.y >= self.pos_at_turn[1]:
                            snake_segment.direction = 'down'
                        elif snake_segment.rect.y <= self.pos_at_turn[1]:
                            snake_segment.direction = 'up'
                    snake_segment.blit()


class Food(object):
    def __init__(self,color,width,height):
        self.image = pygame.Surface((width,height))
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.x = random.randrange(20,500,20) 
        self.rect.y = random.randrange(20,500,20) 
        self.state = 'uneaten'

    def check_eaten(self,snake):
        if snake.rect.colliderect(self.rect):
            self.state = 'eaten'

    def blit(self):
        SCREEN.blit(self.image,self.rect)

    def update_state(self,snake):
        self.check_eaten(snake)
        if self.state == 'uneaten':
            self.blit()
        elif self.state == 'eaten':
            self.rect.x = random.randrange(0,500,20) 
            self.rect.y = random.randrange(0,500,20) 

            if self.rect.x == snake.rect.x or self.rect.x == snake.rect.y:
                self.rect.x = random.randrange(0,500,20) 
                self.rect.y = random.randrange(0,500,20)

            self.blit()
            self.state = 'uneaten'

    def update(self,snake):
        self.update_state(snake)

def event_loop(snake,food):
    while True:
        SCREEN.fill(BLACK)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == KEYDOWN:
                snake.pos_at_turn = [snake.rect.x, snake.rect.y]
                if event.key == K_RIGHT:
                    snake.direction = 'right'
                elif event.key == K_LEFT:
                    snake.direction = 'left'
                elif event.key == K_UP:
                    snake.direction = 'up'
                elif event.key == K_DOWN:
                    snake.direction = 'down'

        snake.update(food)
        food.update(snake)

        pygame.display.update()
        clock.tick(5)

SNAKE = Snake(GREEN,RECTANGLE_WIDTH,RECTANGLE_HEIGHT)
FOOD = Food(RED,RECTANGLE_WIDTH,RECTANGLE_HEIGHT)
event_loop(SNAKE,FOOD)
  • What are you trying to achieve by remembering the turning point - a way to "slither" the snake around a corner? – Kingsley Mar 07 '19 at 05:07
  • I store a turning point so I can change the direction adjust for and change the direction at that point. The segment is going down let's say and it passed through that point. Then it turns to the right once it's rect coordinates equal the coordinates of the turning point. –  Mar 07 '19 at 05:17
  • You don't really need to remember the point like this, just move the Nth body part to the position of the (N-1)th part. If this is what you're doing I answered a question explaining this a few days ago. https://stackoverflow.com/questions/54894355/remaking-snake-cant-make-more-segments/54894669#54894669 (But I'm not 100% sure this is what you mean, if not, please disregard this comment). – Kingsley Mar 07 '19 at 05:36
  • I have looked at your comment. I will try that tomorrow. –  Mar 07 '19 at 05:41

1 Answers1

0

You've to traverse the snakes body in reverse order in the method movement. For each part of the body, copy the position of the previous part. The head part gets the current position of the snake:

class Snake(object):
    # [...]

    def movement(self):
        if self.direction == 'right':
            self.move = (self.speed,0)
        elif self.direction == 'left':
            self.move = (-self.speed,0)
        elif self.direction == 'up':
            self.move = (0,-self.speed)
        elif self.direction == 'down':
            self.move = (0,self.speed)

        self.rect.move_ip(self.move)
        self.rect.clamp_ip(SCREEN.get_rect())

        for i in range(len(self.body)-1, 0, -1):
            self.body[i].rect.center = self.body[i-1].rect.center
        if self.body:
            self.body[0].rect.center = self.rect.center

Init the position of a part, when a new part is add to the body in the method add_segment:

class Snake(object):
    # [...]

    def add_segment(self,food):
        if len(self.body) == 0 or self.rect.colliderect(food.rect):
            snake_segment = Snake(GREEN,RECTANGLE_WIDTH,RECTANGLE_HEIGHT)
            snake_segment.rect.center = self.rect.center
            self.body.append(snake_segment)

In the method draw_snake it is sufficient to traverse the parts of the body and to "blit" each part:

class Snake(object):
    # [...]

    def draw_snake(self):
        for snake_segment in self.body:
            snake_segment.blit()

Rabbid76
  • 202,892
  • 27
  • 131
  • 174