我如何链接蛇身体的运动? [英] How do I chain the movement of a snake's body?

查看:40
本文介绍了我如何链接蛇身体的运动?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个蛇游戏.蛇蜿蜒穿过操场.每当蛇某种食物,蛇的长度就会增加一个元素.蛇身的元素像锁链一样跟随头部.

snake_x,snake_y = WIDTH//2,HEIGHT//2身体 = []move_x, move_y = (1, 0)food_x, food_y = new_food(body)运行 = 真运行时:对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:运行 = 错误如果 event.type == pygame.KEYDOWN:如果 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)蛇_x = (蛇_x + 移动_x) % 宽度蛇_y = (snake_y + move_y) % 高度如果snake_x == food_x 和snake_y == food_y:food_x, food_y = new_food(body)body.append((snake_x,snake_x))# [...]

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

解决方案

通常,您必须区分 2 种不同类型的 snake.在第一种情况下,蛇在一个网格中移动,每次当蛇移动时,它都会向前跨越网格中的一个领域.在另一种类型中,蛇的位置不在栅格中,也没有在网格的字段上对齐,位置是自由的,蛇在字段中平滑滑动.
在以前,身体的每个元素都被捕捉到网格的领域,就像头部一样.另一个是更技巧,因为 body 元素的位置取决于元素的大小和蛇头的动态、先前位置.


首先是 snake,它被捕捉到一个网格.

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

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

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)]

最小示例:

导入pygame随机导入pygame.init()列、行、大小 = 10、10、20屏幕 = pygame.display.set_mode((COLUMNS*SIZE, ROWS*SIZE))时钟 = pygame.time.Clock()背景 = pygame.Surface((COLUMNS*SIZE, ROWS*SIZE))background.fill((255, 255, 255))对于范围内的 i (1, COLUMNS):pygame.draw.line(背景, (128, 128, 128), (i*SIZE-1, 0), (i*SIZE-1, ROWS*SIZE), 2)对于范围内的 i (1, ROWS):pygame.draw.line(背景, (128, 128, 128), (0, i*SIZE-1), (COLUMNS*SIZE, i*SIZE-1), 2)def random_pos(body):为真:pos = random.randrange(COLUMNS), random.randrange(ROWS)如果 pos 不在正文中:休息返回位置长度 = 1body = [(COLUMNS//2, ROWS//2)]目录 = (1, 0)食物 = random_pos(body)运行 = 真运行时:时钟滴答(5)对于 pygame.event.get() 中的事件:如果 event.type == pygame.QUIT:运行 = 错误如果 event.type == pygame.KEYDOWN:如果 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如果身体[0] == 食物:食物 = random_pos(body)长度 += 1而 len(body) >长度:删除身体[-1]screen.blit(背景, (0, 0))pygame.draw.rect(screen, (255, 0, 255), (food[0]*SIZE, food[1]*SIZE, SIZE, SIZE))对于 i, pos in enumerate(body):颜色 = (255, 0, 0) if i==0 else (0, 192, 0) if (i%2)==0 else (255, 128, 0)pygame.draw.rect(屏幕,颜色,(pos[0]*SIZE,pos[1]*SIZE,SIZE,SIZE))pygame.display.flip()


现在snake完全自由定位.

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

关键是,计算身体的最后一个元素之间的

导入pygame随机导入导入数学pygame.init()列、行、大小 = 10、10、20宽度、高度 = 列*大小、行*大小屏幕 = pygame.display.set_mode((WIDTH, HEIGHT))时钟 = pygame.time.Clock()背景 = pygame.Surface((WIDTH, HEIGHT))background.fill((255, 255, 255))对于范围内的 i (1, COLUMNS):pygame.draw.line(背景, (128, 128, 128), (i*SIZE-1, 0), (i*SIZE-1, ROWS*SIZE), 2)对于范围内的 i (1, ROWS):pygame.draw.line(背景, (128, 128, 128), (0, i*SIZE-1), (COLUMNS*SIZE, i*SIZE-1), 2)定义命中(pos_a,pos_b,距离):dx, dy = pos_a[0]-pos_b[0], pos_a[1]-pos_b[1]返回 math.sqrt(dx*dx + dy*dy) <距离def random_pos(body):pos = 无为真:pos = random.randint(SIZE//2, WIDTH-SIZE//2), random.randint(SIZE//2, HEIGHT-SIZE//2)如果没有([hit(pos, bpos, 20) for bpos in body]):休息返回位置def create_body(track, no_pearls, distance):身体 = [(轨道 [0])]轨道_i = 1对于范围内的 i (1, no_pearls):而 track_i <镜头(轨道):pos = track[track_i]轨道_i += 1dx, dy = body[-1][0]-pos[0], body[-1][1]-pos[1]如果 math.sqrt(dx*dx + dy*dy) >= 距离:body.append(pos)休息而 len(body) 

I want to implement a snake game. The snake meanders through the playground. Every time when the snake eats some food, the length of the snake increase by one element. The elements of the snakes body follow its head like a chain.

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))

    # [...]

How do I accomplish, that the body parts follow the snake's head on its path, when the snake's head moves ahead?

解决方案

In general you have to distinguish between 2 different types of snake. In the first case, the snake moves in a grid and every time when the snake moves, it strides ahead one field in the grid. In the other type, the snakes position is not in a raster and not snapped on the fields of the grid, the position is free and the snake slides smoothly through the fields.
In former each element of the body is snapped to the fields of the grid, as the head is. The other is more trick, because the position of a body element depends on the size of the element and the dynamic, previous positions of the snakes head.


First the snake, which is snapped to a grid.

The elements of the snake can be stored in a list of tuples. Each tuple contains the column and row of the snakes element in the grid. The changes to the items in the list directly follow the movement of the snake. If the snake moves, a the new position is add to the head of the list and the tail of the list is removed.

For instance we have a snake with the following elements:

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

When the snakes head moves form (3, 3) to (3, 2), then the new head position is add to the head of the list (body.insert(0, (3, 2)):

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

Finally the tail of the ist is removed (del body[-1]):

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

Minimal example:

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()


Now the snake with completely free positioning.

We have to track all the positions which the snake's head has visited in a list. We have to place the elements of the snakes body on the positions in the list like the pearls of a chain.

The key is, to compute the Euclidean distance between the last element of the body in the chain and the following positions on the track. When an new point with a distance that is large enough is found, then an new pearl (element) is add to the chain (body).

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

The following function has 3 arguments. track is the list of the head positions. no_pearls is then number of elements of the shakes body and distance is the Euclidean distance between the elements. The function creates and returns a list of the snakes body positions.

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

Minimal example:

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()

这篇关于我如何链接蛇身体的运动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆