0

I'm trying to make a simple snake game using images with python and pygame. Now I know that some other people had problem too in other languages but still I can't figure it out. So I create a head list and one body list too include easily collisions too, also i create a position list. Every 2 elements of the position list are the x and y attributes, [0][1] head, [2][3] first body(I create these 2 when the game starts). The problem is that the head is moving but the first body doesn't, the same happens after the head collide with an apple, creating a new body on the middle of the screen that immediately suppose to reposition according to the position list. But it just stays there on middle, I checked using some print statements just to be sure about the rect.x and rect.y values of head and position list [0][1] and the list updates perfectly, but not the body when i give the new values. Any ideas because its the last part that needs to be done and I'm really stressed the last days about this. Thanks in advance and here is some part of the code(some of it needs to be deleted or used as test, it will be done later).

Lists:

 snake_positions_list = []
    #snake = Snake(display_width, display_height,screen.WHITE)
    snake_head_list = pygame.sprite.Group()
    snake_head = snake_Head(display_width, display_height, screen.WHITE)
    snake_head_list.add(snake_head)
    all_sprites_list.add(snake_head)

    snake_body_list = pygame.sprite.Group()
    snake_body = snake_Body(display_width, display_height, screen.WHITE)
    snake_body_list.add(snake_body)
    all_sprites_list.add(snake_body)

Init head and body:

     if init_list == 1:
        for snake_head in snake_head_list:
            snake_head.set_Init_Position()
            snake_positions_list.append(snake_head.rect.x)
            snake_positions_list.append(snake_head.rect.y)
        for snake_body in snake_body_list:
            snake_body.speedx = 30
            snake_body.speedy = 0
            snake_body.set_Init_Position()
            snake_positions_list.append(snake_body.rect.x)
            snake_positions_list.append(snake_body.rect.y)
        init_list = 0

Body and head move - position part(It supposed to work for more than one body after new body spawn and increase on position list but it doesn't even work for the first :

i = 0
        for snake_body in snake_body_list:
            #snake_body.Reposition(snake_positions_list[i], snake_positions_list[i + 1])
            snake_body.rect.x = snake_positions_list[i]
            snake_body.rect.y = snake_positions_list[i + 1]
            snake_body.set_Image()
            print("Snake Body rect x : " +str(snake_body.rect.x))
            print("Snake Body x : "+str(snake_body.x))
            #snake_body.move()
            i += 1
        print("Head x position: "+str(snake_positions_list[0]))
        print("Head y position: "+str(snake_positions_list[1]))

        for snake_head in snake_head_list:
            if snake_head.alive == 1:
                snake_head.move()
                print("SNake rect x : "+ str(snake_head.rect.x))
                print("Snake x : "+ str(snake_head.x))
        i = 0

Snake- fruit collision and new body spawn:

add_score = 0
        add_score = fruit_collision(snake_head_list, objective_list, all_sprites_list)
        game_score = game_score + add_score
        if add_score == 1:
            snake_body = snake_Body(display_width, display_height, screen.WHITE) 
            new_bodyx = snake_length * 2 - 2
            new_bodyy = snake_length * 2 - 1
            snake_positions_list .append(snake_positions_list[new_bodyx - 2])
            snake_positions_list.append(snake_positions_list[new_bodyy - 1])
            snake_body.rect.x = snake_positions_list[new_bodyx]
            snake_body.rect.y = snake_positions_list[new_bodyy]
            snake_body_list.add(snake_body)
            all_sprites_list.add(snake_body)
            snake_length += 1
            add_score = 0

Collision method:

def fruit_collision(snake_head_List, objective_list, all_sprites_list):
k = 0
for snake_head in snake_head_List:
    objective_snake_collide_list = pygame.sprite.spritecollide(snake_head, objective_list, False)
    if objective_snake_collide_list:
        print("Body list length before fruit: " + str(len(snake_head_List)))
        for fruit in objective_list:
            fruit.respawn()
            k = fruit.points
return k

Updating head position on position list [0][1] almost at the end of game while loop to be sure:

snake_positions_list[0] = snake_head.rect.x
snake_positions_list[1] = snake_head.rect.y

Body class:

class snake_Body(pygame.sprite.Sprite):
def __init__(self, display_width, display_height, colour):
    super().__init__()

    self.width = 30
    self.height = 30
    self.original_length = 3
    self.display_width = display_width
    self.display_height = display_height
    self.colour = colour
    self.rotation = 1      
    self.x = (self.display_width / 2) - self.width
    self.y = (self.display_height / 2)
    self.image = pygame.image.load(path + 'Snake_Body_30x30.png').convert()
    self.rect = self.image.get_rect(topleft = (self.x, self.y))
    self.image.set_colorkey(self.colour)
    self.body_type = 2
    self.set_Init_Position()  
    self.set_Image()

def set_Direction(self, x, y, r):
    self.speedx = x
    self.speedy = y
    self.rotation = r

    #Check window boundaries
    if (-self.width > self.rect.x)  or (self.rect.x > self.display_width +5) or (self.rect.y < -self.height) or (self.rect.y > self.display_height +5):
        self.alive = 0

def Reposition(self, listx, listy):
    self.rect.x = listx
    self.rect.y = listy

def set_Rotation(self, r):
    self.rotation = r

def set_Init_Position(self):
    self.x = (self.display_width / 2) - self.width
    self.y = (self.display_height / 2)
    self.image = pygame.image.load(path + 'Snake_Body_30x30.png').convert()
    self.rect = self.image.get_rect(topleft = (self.x, self.y))
    self.image.set_colorkey(self.colour)

def set_Image(self):
    self.image = pygame.image.load(path + 'Snake_Body_30x30.png').convert()
    self.rect = self.image.get_rect(topleft = (self.x, self.y))
    self.image.set_colorkey(self.colour)

Head class:

class snake_Head(pygame.sprite.Sprite):
    def __init__(self, display_width, display_height, colour):
        super().__init__()

        self.speedx = 30
        self.speedy = 0
        self.width = 30
        self.height = 30
        self.original_length = 3
        self.display_width = display_width
        self.display_height = display_height
        self.colour = colour
        self.rotation = 1
        self.alive = 1
        self.points = 0
        self.swap = 1
        self.swapTimer = 0.0       
        self.x = (self.display_width / 2)
        self.y = (self.display_height / 2)
        self.image = pygame.image.load(path + 'Snake_Head_New_Right_30x30.png').convert()
        self.rect = self.image.get_rect(topleft = (self.x, self.y))
        self.image.set_colorkey(self.colour)
        self.body_type = 1
        self.set_Init_Position()  
        self.set_Image()

    def set_body_type(self, t):
        self.body_type = t

    def set_Direction(self, x, y, r):
        self.speedx = x
        self.speedy = y
        self.rotation = r

    def move(self):
        if self.speedx > 0 or self.speedx < 0:
            self.rect.x += self.speedx
            self.x = self.rect.x
        else:
            self.rect.y += self.speedy
            self.y = self.rect.y

        #Check window boundaries
        if (-self.width > self.rect.x)  or (self.rect.x > self.display_width +5) or (self.rect.y < -self.height) or (self.rect.y > self.display_height +5):
            self.alive = 0

    def Reposition(self, listx, listy):
        self.rect.x = listx
        self.rect.y = listy

    def set_Rotation(self, r):
        self.rotation = r

    def set_Init_Position(self):
        self.x = (self.display_width / 2)
        self.y = (self.display_height / 2)
        self.image = pygame.image.load(path + 'Snake_Head_New_Right_30x30.png').convert()
        self.rect = self.image.get_rect(topleft = (self.x, self.y))
        self.image.set_colorkey(self.colour)

    def set_Image(self):
        if self.rotation == 1:
            self.image = pygame.image.load(path + 'Snake_Head_New_Right_30x30.png').convert()
            self.rect = self.image.get_rect(topleft = (self.x, self.y))
            self.image.set_colorkey(self.colour)
        elif self.rotation == 2:
            self.image = pygame.image.load(path + 'Snake_Head_New_Left_30x30.png').convert()
            self.rect = self.image.get_rect(topleft = (self.x, self.y))
            self.image.set_colorkey(self.colour)
        elif self.rotation == 3:
            self.image = pygame.image.load(path + 'Snake_Head_New_Up_30x30.png').convert()
            self.rect = self.image.get_rect(topleft = (self.x, self.y))
            self.image.set_colorkey(self.colour)
        else:
            self.image = pygame.image.load(path + 'Snake_Head_New_Down_30x30.png').convert()
            self.rect = self.image.get_rect(topleft = (self.x, self.y))
            self.image.set_colorkey(self.colour)

The classes don't need some functions, I reformed the project after i stacked on this issue, as I mentioned the cleaning will be done after the project will be ready.

Again thanks for your time!

furas
  • 134,197
  • 12
  • 106
  • 148
M2kk
  • 79
  • 2
  • 10
  • It doesn't make sense to me to have 2 separate objects/lists for the head and body. Just 1 list that you prepend the next position to and pop the last member of every tick would be easier to implement and make sense. A dequeue would be perfect https://docs.python.org/3/library/collections.html#collections.deque – Iain Shelvington Dec 23 '19 at 00:01
  • standard method is to keep all on one list. First element is head and rest is body. When you add new position at the beginning of list then old position of head automatically become position of first element in body. And you have to only remove last element to keep length. If you eat apple then you don't remove last element and it will be longer. – furas Dec 23 '19 at 00:02
  • instead of list `[x1, y1, x2, y2]` use list with tuples or lists `[(x1, y1), (x2, y2)]` and it will be easier to get or add one position. – furas Dec 23 '19 at 00:07
  • it is good rule to use `CamelCaseNames` for classes - ie. `SnakeBody` - and `lower_case_names` for functions and variable - i.e. `set_direction()`, `set_rotation()`, `set_image()`, `reposition()`. More: [PEP 8 -- Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/) – furas Dec 23 '19 at 00:11
  • @IainShelvington .At first I used only one body list containing a head and multiple bodies , e.g. snake_head = snake_Body(), and snake_body = snake_Body(). But for some reason when I was trying to to access head or body in snake_body_list() the interpreter thought that the snake and the body are two identical instances, e.g I tried to print the head's ID and rect.x using a for loop, I should get one print with these two attributes, instead I got two, same thing happened when I printed the first and only body in list, that's why I jumped to two different lists for head and body. – M2kk Dec 23 '19 at 05:01
  • @furas I wanted to do this, use list of lists but after I would be able to make it work for one head and one tail, so to be sure that it's ok with this way. I don't understand though why this way doesn't make the body to attach on the head, did you find any reason why? – M2kk Dec 23 '19 at 05:06
  • your code is very long so I didn't even try to read it and I don't know why it doesn't work. But I think your problem is that you assing new values to `[0],[1]` - so head old position of head is lost. You should `insert()` new head before `[0],[1]` and then old head automatically moves from `[0],[1]` to `[2],[3]` – furas Dec 23 '19 at 10:59

0 Answers0