How can I use different images for my snake's body parts in my snake game? (Python, Pygame, Snake)

拥有回忆 提交于 2020-05-13 10:54:00

问题


Explanation

I am currently working on a snake game with Pygame but I have a problem because my snake currently consists only of squares but I would find it better if the snake contained a drawn 25x25 picture for the snake head, the body, the tail and for a bent body part so that when the snake changes its height and direction, this part still looks connected to the snake.

I have also added a sample image so that you can better understand what I mean by the diffrent body parts.


This is the relevant part of my code so you can see how the growing snake body currently works.

block_size = 25
black = (0, 0, 0)

# This function contains a list with the current coordinates of the snake head (coordinates) 
# and then draws rectangles of size 25x25 (block_size).

def body_segments(block_size, coordinates):
    for XnY in coordinates:
        pygame.draw.rect(screen, black, [XnY[0], XnY[1], block_size, block_size])


coordinates = []
snake_lenght = 0

# Game Loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Background
    screen.blit(BackgroundImg,(0, 0))

    # Check for a collision with the food
    if distance_SF() < 20:
        FoodX = random.randrange(50, 500, 25)
        FoodY = random.randrange(50, 500, 50)

        # Increase the Snake lenght
        snake_lenght += 1

    # I hereby create a list (HeadCorList) with the coordinates of the snake's head as elements
    # and then I attach these elements to the "coordinates" list.

    HeadCorList = []
    HeadCorList.append(headX) # headX contains the X coordinates of the snake's head
    HeadCorList.append(headY) # headY contains the Y coordinates of the snake's head
    coordinates.append(HeadCorList)

    # This makes sure that the growing body does not get too long.
    if len(segments) > snake_lenght:
        del segments[0]

    body_segments(block_size, coordinates)

Problem summary

I don't know how to solve these problems because I don't know how to attach pictures to the moving snake's head instead of rectangles, because I don't know how to attach a tail to the end of the snake's body and because I don't know how to implement the bent body part feature becouse there is only a bent body part to be inserted when the snake changes its height and direction.

I hope that I could explaine everything clearly because English is not my main language, Python 3 is my first programming language and this game is only my second program.


回答1:


First of all, let's split your image into 4 parts, and make them all the same size. That will make our task easier:

head.png

body.png

L.png

tail.png

Let's load them using a basic pygame game:

import pygame

TILESIZE = 24
def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 480))

    load = lambda part: pygame.image.load(part + '.png').convert_alpha()
    parts = ('head', 'body', 'tail', 'L')
    head_img, body_img, tail_img, L_img = [load(p) for p in parts]

    clock = pygame.time.Clock()
    dt = 0
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        screen.fill((30, 30, 30))

        screen.blit(head_img, (100, 100))
        screen.blit(body_img, (100, 100 + TILESIZE))
        screen.blit(L_img,    (100, 100 + TILESIZE*2))
        screen.blit(tail_img, (100, 100 + TILESIZE*3))


        dt = clock.tick(60)
        pygame.display.flip()

main()

But we actually also need these images in a rotated form, so let's create them at the beginning:

def build_images():
    load = lambda part: pygame.image.load(part + '.png').convert_alpha()
    parts = ('head', 'body', 'tail', 'L')
    head_img, body_img, tail_img, L_img = [load(p) for p in parts]

    return {
        'HEAD_N': head_img,
        'HEAD_S': pygame.transform.rotate(head_img, 180),
        'HEAD_E': pygame.transform.rotate(head_img, 90),
        'HEAD_W': pygame.transform.rotate(head_img, -90),
        'BODY_NN': body_img,
        'BODY_SS': body_img,
        'BODY_WW': pygame.transform.rotate(body_img, 90),
        'BODY_EE': pygame.transform.rotate(body_img, 90),
        'BODY_NE': pygame.transform.rotate(L_img, 180),
        'BODY_WS': pygame.transform.rotate(L_img, 180),
        'BODY_WN': pygame.transform.rotate(L_img, 90),
        'BODY_SE': pygame.transform.rotate(L_img, 90),
        'BODY_ES': pygame.transform.rotate(L_img, -90),
        'BODY_NW': pygame.transform.rotate(L_img, -90),
        'BODY_EN': pygame.transform.rotate(L_img, 0),
        'BODY_SW': pygame.transform.rotate(L_img, 0),
        'TAIL_N': tail_img,
        'TAIL_S': pygame.transform.rotate(tail_img, 180),
        'TAIL_E': pygame.transform.rotate(tail_img, 90),
        'TAIL_W': pygame.transform.rotate(tail_img, -90)
    }

Using a dictionary with string keys will make it easy for us to get the correct image depending on the direction of each part of the snake and their parent part.

For example, BODY_SE is the image we use when the part of the snake faces east, but the parent is going to move south.

Now we can start implementing our game. Since we're using pygame I'll use basic pygame features like Sprite and Group. Let's see how we could create some Sprites representing the snake:

import pygame
TILESIZE = 24

class Snake(pygame.sprite.Sprite):
    images = None
    def __init__(self, grp, pos, length, parent=None):
        super().__init__(grp)
        self.parent = parent
        self.child = None
        if not self.parent:
            self.image = Snake.images['HEAD_N']
        elif length == 1:
            self.image = Snake.images['TAIL_N']
        else:
            self.image = Snake.images['BODY_NN']
        self.pos = pos
        self.rect = self.image.get_rect(x=pos[0]*TILESIZE, y=pos[1]*TILESIZE)
        if length > 1:
            self.child = Snake(grp, (pos[0], pos[1]+1), length-1, self)

def build_images():
   ...

def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 480))
    Snake.images = build_images()

    all_sprites = pygame.sprite.Group()
    snake = Snake(all_sprites, (4, 4), 6)
    clock = pygame.time.Clock()
    dt = 0
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return

        screen.fill((30, 30, 30))

        all_sprites.update()
        all_sprites.draw(screen)

        dt = clock.tick(60)
        pygame.display.flip()

main()

As you can see, each part of the snake has a reference to the part before (except for the head part), and a refence to the part behind (except for the tail).

So far, so good. Let's move the snake:

import pygame
TILESIZE = 24

class Snake(pygame.sprite.Sprite):
    images = None
    def __init__(self, grp, pos, length, parent=None):
        ...

    def move(self):
        # if we have a parent, let's look were it moves
        parent_direction = self.parent.direction if self.parent else None

        if self.direction == 'N': self.pos = self.pos[0], self.pos[1] - 1
        elif self.direction == 'S': self.pos = self.pos[0], self.pos[1] + 1
        elif self.direction == 'E': self.pos = self.pos[0] - 1, self.pos[1]
        elif self.direction == 'W': self.pos = self.pos[0] + 1, self.pos[1]

        self.rect = self.image.get_rect(x=self.pos[0]*TILESIZE, y=self.pos[1]*TILESIZE)

        # move the child
        if self.child:
            self.child.move()

        # follow the parent
        if parent_direction:
            self.direction = parent_direction

    def update(self):
        # no parent means we're the head of the snake
        # and we should move we a key is pressed
        if not self.parent:
            pressed = pygame.key.get_pressed()
            if pressed[pygame.K_w]: self.direction = 'N'
            if pressed[pygame.K_s]: self.direction = 'S'
            if pressed[pygame.K_a]: self.direction = 'E'
            if pressed[pygame.K_d]: self.direction = 'W'

def main():
    ...
    # let's trigger the MOVE event every 500ms
    MOVE = pygame.USEREVENT + 1
    pygame.time.set_timer(MOVE, 500)
    ...
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
            if e.type == MOVE:
                snake.move()

Wonderfull. What's left is to actually change the image of each body part if the direction changes.

Here's the full code:

import pygame
TILESIZE = 24

class Snake(pygame.sprite.Sprite):
    images = None
    def __init__(self, grp, pos, length, parent=None):
        super().__init__(grp)
        self.parent = parent
        self.child = None
        self.direction = 'N'

        if not self.parent: self.image = Snake.images['HEAD_N']
        elif length == 1: self.image = Snake.images['TAIL_N']
        else: self.image = Snake.images['BODY_NN']

        self.pos = pos
        self.rect = self.image.get_rect(x=self.pos[0]*TILESIZE, y=self.pos[1]*TILESIZE)
        if length > 1:
            self.child = Snake(grp, (pos[0], pos[1]+1), length-1, self)

    def move(self):
        # if we have a parent, let's look were it moves
        parent_direction = self.parent.direction if self.parent else None

        if self.direction == 'N': self.pos = self.pos[0], self.pos[1] - 1
        elif self.direction == 'S': self.pos = self.pos[0], self.pos[1] + 1
        elif self.direction == 'E': self.pos = self.pos[0] - 1, self.pos[1]
        elif self.direction == 'W': self.pos = self.pos[0] + 1, self.pos[1]

        self.rect = self.image.get_rect(x=self.pos[0]*TILESIZE, y=self.pos[1]*TILESIZE)

        # move the child
        if self.child:
            self.child.move()

        if not self.parent: self.image = Snake.images['HEAD_' + self.direction]
        elif not self.child: self.image = Snake.images['TAIL_' + parent_direction]
        else: self.image = Snake.images['BODY_' + parent_direction + self.direction]

        # follow the parent
        if parent_direction:
            self.direction = parent_direction

    def update(self):
        # no parent means we're the head of the snake
        # and we should move we a key is pressed
        if not self.parent:
            pressed = pygame.key.get_pressed()
            if pressed[pygame.K_w]: self.direction = 'N'
            if pressed[pygame.K_s]: self.direction = 'S'
            if pressed[pygame.K_a]: self.direction = 'E'
            if pressed[pygame.K_d]: self.direction = 'W'


def build_images():
    load = lambda part: pygame.image.load(part + '.png').convert_alpha()
    parts = ('head', 'body', 'tail', 'L')
    head_img, body_img, tail_img, L_img = [load(p) for p in parts]

    return {
        'HEAD_N': head_img,
        'HEAD_S': pygame.transform.rotate(head_img, 180),
        'HEAD_E': pygame.transform.rotate(head_img, 90),
        'HEAD_W': pygame.transform.rotate(head_img, -90),
        'BODY_NN': body_img,
        'BODY_SS': body_img,
        'BODY_WW': pygame.transform.rotate(body_img, 90),
        'BODY_EE': pygame.transform.rotate(body_img, 90),
        'BODY_NE': pygame.transform.rotate(L_img, 180),
        'BODY_WS': pygame.transform.rotate(L_img, 180),
        'BODY_WN': pygame.transform.rotate(L_img, 90),
        'BODY_SE': pygame.transform.rotate(L_img, 90),
        'BODY_ES': pygame.transform.rotate(L_img, -90),
        'BODY_NW': pygame.transform.rotate(L_img, -90),
        'BODY_EN': pygame.transform.rotate(L_img, 0),
        'BODY_SW': pygame.transform.rotate(L_img, 0),
        'TAIL_N': tail_img,
        'TAIL_S': pygame.transform.rotate(tail_img, 180),
        'TAIL_E': pygame.transform.rotate(tail_img, 90),
        'TAIL_W': pygame.transform.rotate(tail_img, -90)
    }

def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 480))
    Snake.images = build_images()

    # let's trigger the MOVE event every 500ms
    MOVE = pygame.USEREVENT + 1
    pygame.time.set_timer(MOVE, 500)

    all_sprites = pygame.sprite.Group()
    snake = Snake(all_sprites, (4, 4), 8)
    clock = pygame.time.Clock()
    dt = 0
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
            if e.type == MOVE:
                snake.move()

        screen.fill((30, 30, 30))

        all_sprites.update()
        all_sprites.draw(screen)

        dt = clock.tick(60)
        pygame.display.flip()

main()



来源:https://stackoverflow.com/questions/60823576/how-can-i-use-different-images-for-my-snakes-body-parts-in-my-snake-game-pyth

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!