Змеиные тела не присоединяются к змеиным правильно - PullRequest
1 голос
/ 30 марта 2020

Я наконец-то понял, как добавить части тела в мою змею, но они добавляются необычным образом. Я боролся с этим некоторое время и наконец сделал так, чтобы они добавились. Но они не делают это правильно. Кажется, что они добавляют 1 пиксель сзади вместо полной длины тела. Кто-нибудь знает почему?

# Constants
WIN_WIDTH = 500
WIN_HEIGHT = 600
HALF_WIN_WIDTH = WIN_WIDTH / 2
HALF_WIN_HEIGHT = WIN_HEIGHT / 2
FPS = 10

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
DARK_GREEN = (0, 100, 0)
YELLOW = (255, 255, 0)

# Variables
screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
pygame.display.set_caption("Snake")
clock = pygame.time.Clock()
running = True


class Text:

    def __init__(self, x, y, size, font, color, text):
        self.x = x
        self.y = y
        self.size = size
        self.font = font
        self.color = color
        self.text = text

    def draw(self):
        self.my_font = pygame.font.SysFont(self.font, self.size)
        self.text_surface = self.my_font.render(self.text, True, self.color)
        screen.blit(self.text_surface, (self.x, self.y))


class Food:

    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.width = 25
        self.height = 25

    def draw(self):
        self.rect = (self.x, self.y, self.width, self.height)
        pygame.draw.rect(screen, BLUE, self.rect)

    def events(self):
        pass

    def update(self):
        pass


class Body:

    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.width = 25
        self.height = 25

    def draw(self):
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
        pygame.draw.rect(screen, YELLOW, self.rect)


# Snake class
class Snake:

    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.width = 25
        self.height = 25
        self.direction = 1
        self.kill = False
        self.collide = False
        self.speed = 3
        self.score = 0
        self.bodies = []

    def draw(self):
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
        pygame.draw.rect(screen, BLACK, self.rect)

    def events(self):
        # change direction on key press
        self.keys = pygame.key.get_pressed()

        if self.keys[pygame.K_UP] and self.direction != 3:
            self.direction = 1
        if self.keys[pygame.K_DOWN] and self.direction != 1:
            self.direction = 3
        if self.keys[pygame.K_LEFT] and self.direction != 2:
            self.direction = 4
        if self.keys[pygame.K_RIGHT] and self.direction != 4:
            self.direction = 2

        if self.rect.colliderect(food.rect):
            self.speed += 0.5
            food.x = random.randint(0, WIN_WIDTH)
            food.y = random.randint(0, WIN_HEIGHT)
            self.score += 5
            self.colliide = False
            self.bodies.append(Body(0, 0))

        # Move the end bodies first in reverse order
        for i in range(len(self.bodies)-1, 0, -1):
            x = snake.bodies[i-1].x
            y = snake.bodies[i-1].y
            snake.bodies[i].x = x
            snake.bodies[i].y = y
            snake.bodies[i].draw()

        # Move body 0 to where the head is
        if len(snake.bodies) > 0:
            x = snake.x
            y = snake.y
            snake.bodies[0].x = x
            snake.bodies[0].y = y
            snake.bodies[0].draw()


    def update(self):
        # move
        if self.direction == 1:
            self.y -= self.speed
        if self.direction == 2:
            self.x += self.speed
        if self.direction == 3:
            self.y += self.speed
        if self.direction == 4:
            self.x -= self.speed

        # if on edge of screen
        if self.rect.right > WIN_WIDTH:
            self.kill = True
        if self.x < 0:
            self.kill = True
        if self.y < 0:
            self.kill = True
        if self.rect.bottom > WIN_HEIGHT:
            self.kill = True


# Create the snake object
snake = Snake(HALF_WIN_WIDTH, HALF_WIN_HEIGHT)
food = Food(random.randint(0, WIN_WIDTH), random.randint(0, WIN_HEIGHT))

# Main Loop
while running:
    score_text = Text(220, 5, 40, 'arial', WHITE, f'Score: {snake.score}')
    # Draw
    screen.fill(DARK_GREEN)
    snake.draw()
    food.draw()
    score_text.draw()

    # Event handling
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    if snake.kill:
        running = False

    snake.events()

    # Update
    snake.update()
    food.update()
    clock.tick(60)
    pygame.display.update()

Большое спасибо!

1 Ответ

1 голос
/ 30 марта 2020

Вы должны отслеживать позиции, которые были встречены змеей. Добавьте атрибут списка self.position в класс Snake:

class Snake:

    def __init__(self, x, y):
        # [...]

        self.positions = [(self.x, self.y)]

    # [...]

Добавьте новую позицию в список при перемещении змеи:

class Snake:
    # [...]

    def update(self):

        # move
        if self.direction == 1:
            self.y -= self.speed
        if self.direction == 2:
            self.x += self.speed
        if self.direction == 3:
            self.y += self.speed
        if self.direction == 4:
            self.x -= self.speed

        # add ne position
        if self.x != self.positions[0][0] or self.y != self.positions[0][1]:
            self.positions.insert(0, (self.x, self.y))

Обновите x и y координата тела вдоль сохраненных позиций в events. Определите расстояние между частями тела (например, 35). И используйте метод getPos, чтобы получить позицию детали по ее индексу:

class Snake:
    # [...]

    def events(self):
        # change direction on key press
        self.keys = pygame.key.get_pressed()

        if self.keys[pygame.K_UP] and self.direction != 3:
            self.direction = 1
        if self.keys[pygame.K_DOWN] and self.direction != 1:
            self.direction = 3
        if self.keys[pygame.K_LEFT] and self.direction != 2:
            self.direction = 4
        if self.keys[pygame.K_RIGHT] and self.direction != 4:
            self.direction = 2

        if self.rect.colliderect(food.rect):
            self.speed += 0.5
            food.x = random.randint(100, WIN_WIDTH - 125)
            food.y = random.randint(150, WIN_HEIGHT - 175)
            self.score += 5
            self.colliide = False
            self.bodies.append(Body(0, 0))

        # Move the end bodies first in reverse order
        for i in range(len(self.bodies)):
            pos = self.getPos(i+1, 35, i == len(self.bodies)-1)
            snake.bodies[i].x = pos[0]
            snake.bodies[i].y = pos[1]
            snake.bodies[i].draw()

Аргументы метода getPos - это индекс части тела, расстояние между частями и delToEnd. delToEnd становится истинным, когда последняя часть тела получает, и указывает, что позиции в конце списка, которые находятся «позади» последней части змеи, могут быть удалены:

class Snake:
    # [...]

    def getPos(self, i, dist, delToEnd):
        lenToI = i * dist
        lenAct = 0
        px, py = self.positions[-1]
        for j in range(len(self.positions)-1):
            px, py = self.positions[j]
            pnx, pny = self.positions[j+1]
            delta = math.sqrt((px-pnx)*(px-pnx) + (py-pny)*(py-pny))
            lenAct += delta
            if lenAct >= lenToI:
                w = (lenAct - lenToI) / delta
                px = pnx - (pnx-px) * w
                py = pny - (pny-py) * w
                if delToEnd:
                    del self.positions[j:]
                break
        return (round(px), round(py))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...