【问题标题】:How do I get the snake to grow and chain the movement of the snake's body?如何让蛇生长并锁住蛇身体的运动?
【发布时间】:2022-11-19 20:00:26
【问题描述】:

我想实现一个贪吃蛇游戏。蛇蜿蜒穿过操场。每次当蛇一些食物,蛇的长度增加一个元素。 蛇身的元素像链条一样跟随它的头部。

snake_x, snake_y = WIDTH//2, HEIGHT//2
body = []
move_x, move_y = (1, 0)
food_x, food_y = new_food(body)

run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT: move_x, move_y = (-1, 0)
            elif event.key == pygame.K_RIGHT: move_x, move_y = (1, 0)
            elif event.key == pygame.K_UP: move_x, move_y = (0, -1)
            elif event.key == pygame.K_DOWN: move_x, move_y = (0, 1)

    snake_x = (snake_x + move_x) % WIDTH
    snake_y = (snake_y + move_y) % HEIGHT 
    if snake_x == food_x and snake_y == food_y:
        food_x, food_y = new_food(body)
        body.append((snake_x, snake_x))

    # [...]

当蛇头向前移动时,我如何实现身体部位跟随蛇头的路径?

【问题讨论】:

    标签: python pygame


    【解决方案1】:

    一般来说,你必须区分两种不同类型的.在第一种情况下,蛇在一个网格中移动,蛇每移动一次,它就会在网格中向前迈出一个领域。在另一种类型中,蛇的位置不在光栅中,也没有捕捉到网格的区域,位置是自由的,蛇在区域中顺利滑动。
    在前者中,身体的每个元素都被捕捉到网格的区域,就像头部一样。另一个更诡异,因为身体元素的位置取决于元素的大小和蛇头的动态、先前位置。


    首先是, 捕捉到网格。

    蛇的元素可以存储在元组列表中。每个元组包含网格中 snakes 元素的列和行。列表中项目的变化直接跟随蛇的移动。如果蛇移动,则将新位置添加到列表的头部并删除列表的尾部。

    例如,我们有一条具有以下元素的蛇:

    body = [(3, 3), (3, 4), (4, 4), (5, 4), (6, 4)]
    

    当蛇头从(3, 3)移动到(3, 2)时,新的头部位置被添加到列表的头部(body.insert(0, (3, 2)):

    body = [(3, 2), (3, 3), (3, 4), (4, 4), (5, 4), (6, 4)]
    

    最后去掉了ist的尾巴(del body[-1]):

    body = [(3, 2), (3, 3), (3, 4), (4, 4), (5, 4)]
    

    最小的例子: repl.it/@Rabbid76/PyGame-SnakeMoveInGrid

    import pygame
    import random
    
    pygame.init()
    COLUMNS, ROWS, SIZE = 10, 10, 20
    screen = pygame.display.set_mode((COLUMNS*SIZE, ROWS*SIZE))
    clock  = pygame.time.Clock()
    
    background = pygame.Surface((COLUMNS*SIZE, ROWS*SIZE))
    background.fill((255, 255, 255))
    for i in range(1, COLUMNS):
        pygame.draw.line(background, (128, 128, 128), (i*SIZE-1, 0), (i*SIZE-1, ROWS*SIZE), 2)
    for i in range(1, ROWS):
        pygame.draw.line(background, (128, 128, 128), (0, i*SIZE-1), (COLUMNS*SIZE, i*SIZE-1), 2)
    
    def random_pos(body):
        while True:
            pos = random.randrange(COLUMNS), random.randrange(ROWS)
            if pos not in body:
                break     
        return pos
    
    length = 1
    body = [(COLUMNS//2, ROWS//2)]
    dir = (1, 0)
    food = random_pos(body)
    
    run = True
    while run:
        clock.tick(5)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT: dir = (-1, 0)
                elif event.key == pygame.K_RIGHT: dir = (1, 0)
                elif event.key == pygame.K_UP: dir = (0, -1)
                elif event.key == pygame.K_DOWN: dir = (0, 1)
    
        body.insert(0, body[0][:])    
        body[0] = (body[0][0] + dir[0]) % COLUMNS, (body[0][1] + dir[1]) % ROWS
        if body[0] == food:
            food = random_pos(body)
            length += 1
        while len(body) > length:
            del body[-1] 
        
        screen.blit(background, (0, 0))
        pygame.draw.rect(screen, (255, 0, 255), (food[0]*SIZE, food[1]*SIZE, SIZE, SIZE))
        for i, pos in enumerate(body):
            color = (255, 0, 0) if i==0 else (0, 192, 0) if (i%2)==0 else (255, 128, 0)
            pygame.draw.rect(screen, color, (pos[0]*SIZE, pos[1]*SIZE, SIZE, SIZE))
        pygame.display.flip()
    

    现在完全自由定位。

    我们必须在列表中跟踪蛇头访问过的所有位置。我们必须将蛇体的元素像链中的珍珠一样放置在列表中的位置上。

    关键是计算链中主体的最后一个元素与轨道上后续位置之间的Euclidean distance。 当找到一个距离足够大的新点时,就会将一个新的珍珠(元素)添加到链(主体)中。

    dx, dy = body[-1][0]-pos[0], body[-1][1]-pos[1]
    if math.sqrt(dx*dx + dy*dy) >= distance:
        body.append(pos)
    

    以下函数有 3 个参数。 track 是头部位置列表。 no_pearls 是 shakes 主体的元素数,distance 是元素之间的 Euclidean distance。该函数创建并返回蛇身体位置的列表。

    def create_body(track, no_pearls, distance):
        body = [(track[0])]
        track_i = 1
        for i in range(1, no_pearls):
            while track_i < len(track):
                pos = track[track_i]
                track_i += 1
                dx, dy = body[-1][0]-pos[0], body[-1][1]-pos[1]
                if math.sqrt(dx*dx + dy*dy) >= distance:
                    body.append(pos)
                    break
        while len(body) < no_pearls:
            body.append(track[-1])
        del track[track_i:]
        return body
    

    最小的例子: repl.it/@Rabbid76/PyGame-SnakeMoveFree

    import pygame
    import random
    import math
    
    pygame.init()
    COLUMNS, ROWS, SIZE = 10, 10, 20
    WIDTH, HEIGHT = COLUMNS*SIZE, ROWS*SIZE
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    clock  = pygame.time.Clock()
    
    background = pygame.Surface((WIDTH, HEIGHT))
    background.fill((255, 255, 255))
    for i in range(1, COLUMNS):
        pygame.draw.line(background, (128, 128, 128), (i*SIZE-1, 0), (i*SIZE-1, ROWS*SIZE), 2)
    for i in range(1, ROWS):
        pygame.draw.line(background, (128, 128, 128), (0, i*SIZE-1), (COLUMNS*SIZE, i*SIZE-1), 2)
    
    def hit(pos_a, pos_b, distance):
        dx, dy = pos_a[0]-pos_b[0], pos_a[1]-pos_b[1]
        return math.sqrt(dx*dx + dy*dy) < distance
    
    def random_pos(body):
        pos = None
        while True:
            pos = random.randint(SIZE//2, WIDTH-SIZE//2), random.randint(SIZE//2, HEIGHT-SIZE//2)
            if not any([hit(pos, bpos, 20) for bpos in body]):
                break    
        return pos
    
    def create_body(track, no_pearls, distance):
        body = [(track[0])]
        track_i = 1
        for i in range(1, no_pearls):
            while track_i < len(track):
                pos = track[track_i]
                track_i += 1
                dx, dy = body[-1][0]-pos[0], body[-1][1]-pos[1]
                if math.sqrt(dx*dx + dy*dy) >= distance:
                    body.append(pos)
                    break
        while len(body) < no_pearls:
            body.append(track[-1])
        del track[track_i:]
        return body
    
    length = 1
    track = [(WIDTH//2, HEIGHT//2)]
    dir = (1, 0)
    food = random_pos(track)
    
    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:
                if event.key == pygame.K_LEFT: dir = (-1, 0)
                elif event.key == pygame.K_RIGHT: dir = (1, 0)
                elif event.key == pygame.K_UP: dir = (0, -1)
                elif event.key == pygame.K_DOWN: dir = (0, 1)
    
        track.insert(0, track[0][:])    
        track[0] = (track[0][0] + dir[0]) % WIDTH, (track[0][1] + dir[1]) % HEIGHT
    
        body = create_body(track, length, 20)
    
        if hit(body[0], food, 20):
            food = random_pos(body)
            length += 1 
            
        screen.blit(background, (0, 0))
        pygame.draw.circle(screen, (255, 0, 255), food, SIZE//2)
        for i, pos in enumerate(body):
            color = (255, 0, 0) if i==0 else (0, 192, 0) if (i%2)==0 else (255, 128, 0)
            pygame.draw.circle(screen, color, pos, SIZE//2)
        pygame.display.flip()
    

    【讨论】:

    • 很好的答案!第二个例子给了蛇一个令人毛骨悚然的动作,这反映了我在现实生活中对它们的看法。
    猜你喜欢
    • 1970-01-01
    • 2015-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-30
    相关资源
    最近更新 更多