Python Forum

Full Version: Snake game: how to get an instance for my snake's body multiple times?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
i am totaly begginer in programming and i am learning all by myself,sooo ty in andvance for any help. i am trying to make a snake game using pygame.My problem is that when the game starts i have the whole body of the snake and when the snake starts to move it desappears and i have only the head and the 1st block of the body. i know that i dont need all this complex code for such a simple game, but i want to upgrade it as much as i can adding more and more things so i can train myself to learn better a higher lvl of logic for my future projects. in my new game def i use my BODY_LIST to create my instace multiple times but when i am doint the same thing in the class it doesn't work...

In the main.py def new() i have this " for pos in BODY_LIST: self.body = Body(self,pos[0],pos[1]) " and it gives me the whole snake at the beggining.Then in sprites.py in Head class and Body class i have this print(self.pos_list) checkers and it seems that both work fine and i am getting both list printed as i want. Soooo...why in Body class movement() works fine but only for the first block of the body(that means the lost indexof my list)


this is my main:
from sprites import *
from config import *
import sys

class Game:

    def __init__(self):
        pygame.init()

        self.running = True
        self.screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
        self.clock = pygame.time.Clock()

    def new(self):
        self.playing = True
        self.all_sprites = pygame.sprite.LayeredUpdates()
        self.blocks = pygame.sprite.LayeredUpdates()
        self.target = pygame.sprite.LayeredUpdates()
        self.ground = pygame.sprite.LayeredUpdates()
        self.head = Head(self,32,24,)
        for pos in BODY_LIST:
         self.body = Body(self,pos[0],pos[1])

    def events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.playing = False
                self.running = False

    def update(self):
        self.all_sprites.update()

    def draw(self):
        self.screen.fill(BLACK)
        self.all_sprites.draw(self.screen)
        self.clock.tick(PLAYER_SPEED)
        pygame.display.update()

    def main(self):
        while self.playing:

            self.events()
            self.update()
            self.draw()
        self.running = False
    def game_over(self):
        pass
    def intro(self):
        pass

g = Game()
#g.intro()
g.new()
while g.running:
    g.main()
    #g.game_over()
pygame.quit()
sys.exit()
this my sprites.py :
from config import *
import math
import random

class Head(pygame.sprite.Sprite):
    def __init__(self,game, x, y):
        self.game = game
        self.groups = self.game.all_sprites
        pygame.sprite.Sprite.__init__(self,self.groups)
        self.clock = pygame.time.Clock()
        # Head
        self.x = x * TILESIZE
        self.y = y * TILESIZE
        self.width = TILESIZE
        self.height = TILESIZE
        self.move= [0,0]
        self.pos_list= POS_LIST

        self.image = pygame.Surface([self.width, self.height])
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.x = self.x
        self.rect.y = self.y

    def update(self):
        self.movement()
        self.rect = self.rect.move(self.move)
        if self.game.head.move != [0, 0]:
         self.pos_list.append([int(self.rect.x/10), int(self.rect.y/10)])
         if len(self.pos_list) > SNAKE_SIZE :
          self.pos_list.pop(0)

         print(self.pos_list)

    def movement(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT] and self.move != (TILESIZE, 0) :
            self.move = (-TILESIZE, 0)
        if keys[pygame.K_RIGHT] and self.move != (-TILESIZE, 0):
            self.move = (TILESIZE, 0)
        if keys[pygame.K_UP] and self.move != (0, TILESIZE):
            self.move = (0, -TILESIZE)
        if keys[pygame.K_DOWN] and self.move != (0, -TILESIZE):
            self.move = (0, TILESIZE)

class Body(pygame.sprite.Sprite):
    def __init__(self,game,x,y):
        self.game = game
        self.groups = self.game.all_sprites
        pygame.sprite.Sprite.__init__(self,self.groups)

        self.pos_list =POS_LIST[1:-1]
        self.x = x * TILESIZE
        self.y = y * TILESIZE
        self.width = TILESIZE
        self.height = TILESIZE
        self.move = [0, 0]

        self.image = pygame.Surface([self.width, self.height])
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.x = self.x
        self.rect.y = self.y

    def update(self):

        self.pos_list=POS_LIST[1:-1]
        self.movement()

    def movement(self):
      if self.game.head.move != [0, 0]:
             for pos in self.pos_list:
              self.rect = pos[0]*10,pos[1]*10
             print(self.pos_list)
and this is my config:

WIN_HEIGHT = 480
TILESIZE = 10

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

FPS = 60

PLAYER_LAYER = 1
PLAYER_SPEED = 15
POS_LIST = [[28, 24], [29, 24], [30, 24], [31, 24], [32, 24]]
BODY_LIST = POS_LIST[1:-1]
SNAKE_SIZE=5
self.body can only reference one Body object, so self.body is only the last Body sprite.
        for pos in BODY_LIST:
         self.body = Body(self,pos[0],pos[1])
If you wanted all the Body sprites you could do something like this:
self.body = [Body(self, x, y) for x, y in  POS_LIST[1:-1]]
But I don't think this matters because you don't ever use self.body, you use self.all_sprites.

The real problem is this code in Body.
    def update(self):
 
        self.pos_list=POS_LIST[1:-1]
        self.movement()
 
    def movement(self):
      if self.game.head.move != [0, 0]:  # Indenting should always be 4 spaces, never 1
             for pos in self.pos_list:
              self.rect = pos[0]*10,pos[1]*10
             print(self.pos_list)
If there are 3 positions in self.pos_list this code moves the same Body sprite 3 times. If you have 3 Body sprites all three are moved 3 times TO THE SAME FINAL POSITION (self.pos_list[-1]). That is why it looks like all but one of the Body sprites have disappeared, they are all sitting on top of each other.

To fix the problem you need to change how Body works. If it is only a single sprite it will have to know what index to use from the position list and it only updates itself. No "for pos in self.pos_list".
(Jan-25-2022, 05:53 PM)deanhystad Wrote: [ -> ]self.body can only reference one Body object, so self.body is only the last Body sprite.
        for pos in BODY_LIST:
         self.body = Body(self,pos[0],pos[1])
If you wanted all the Body sprites you could do something like this:
self.body = [Body(self, x, y) for x, y in  POS_LIST[1:-1]]
But I don't think this matters because you don't ever use self.body, you use self.all_sprites.

The real problem is this code in Body.
    def update(self):
 
        self.pos_list=POS_LIST[1:-1]
        self.movement()
 
    def movement(self):
      if self.game.head.move != [0, 0]:  # Indenting should always be 4 spaces, never 1
             for pos in self.pos_list:
              self.rect = pos[0]*10,pos[1]*10
             print(self.pos_list)
If there are 3 positions in self.pos_list this code moves the same Body sprite 3 times. If you have 3 Body sprites all three are moved 3 times TO THE SAME FINAL POSITION (self.pos_list[-1]). That is why it looks like all but one of the Body sprites have disappeared, they are all sitting on top of each other.

To fix the problem you need to change how Body works. If it is only a single sprite it will have to know what index to use from the position list and it only updates itself. No "for pos in self.pos_list".

ty mate...