Как связать движение тела змеи? - PullRequest
1 голос
/ 25 мая 2020

Я хочу реализовать змейку. Змея блуждает по детской площадке. Каждый раз, когда змея съедает какую-нибудь пищу, длина змеи увеличивается на один элемент. Элементы тела змеи следуют за ее головой, как цепь. 1006 *

1 Ответ

7 голосов
/ 25 мая 2020

В общем, вы должны различать guish 2 разных типа змеи . В первом случае змея движется по сетке и каждый раз, когда она движется, она продвигается вперед на одно поле в сетке. В другом типе положение змей не в растре и не привязано к полям сетки, положение свободное, и змейка плавно скользит по полям.
В первом случае каждый элемент тела привязан к поля сетки, как и голова. Другой вариант более сложный, поскольку положение элемента тела зависит от размера элемента и динамики c, предыдущих положений головы змеи.


Сначала змея , который привязан к сетке.

Элементы змейки могут быть сохранены в списке кортежей. Каждый кортеж содержит столбец и строку элемента змейки в сетке. Изменения элементов в списке следуют непосредственно за движением змеи. Если змея движется, новая позиция добавляется в начало списка, а конец списка удаляется.

Например, у нас есть змея со следующими элементами:

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

Пример:

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

Теперь змейка с полностью свободным позиционированием .

Мы должны отслеживать все позиции, которые посетила голова змеи в списке. Мы должны разместить элементы тела змеи на позициях в списке, как жемчужины цепочки.

Ключ в том, чтобы вычислить евклидово расстояние между последним элементом тела в цепи и следующими позициями на треке. Когда обнаруживается новая точка с достаточно большим расстоянием, к цепочке (телу) добавляется новая жемчужина (элемент).

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 - это количество элементов тела встряхивания, а 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

Пример:

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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...