Питон: сегменты змеи, которые «текут», а не «привязываются» к положению головы змеи - PullRequest
0 голосов
/ 20 января 2019

Я пытаюсь сделать игру со змеями на python, и я бы хотел, чтобы сегменты змеи текли, когда пользователь нажимает клавиши WASD, а не сегменты, щелкающие в нужном направлении пользователя

import pygame
import random
import time
pygame.init()
win = pygame.display.set_mode((800,600))
pygame.display.set_caption("Pygame")
clock = pygame.time.Clock()
x = 30
y = 30
x2 = x
y2 = random.randrange(1,601-30)
vel = 2
run = True
facing = 0
direction = 0
text = pygame.font.SysFont('Times New Roman',30)
score = 0
segments = []
green = ((0,128,0))
white = ((255,255,255))
counting = 0
segmentTime = time.time()
class segmentClass():
    def __init__(self,x,y,pos,color):
        self.x = x
        self.y = y
        self.pos = pos
        self.color = color
    def draw(self,win):
        pygame.draw.rect(win,(self.color),(self.x,self.y,30,30))
def gameOver():
    global run
    run = False
def segmentGrowth():
    global x2
    global y2
    global score
    global vel
    global ammount
    segments.append(segmentClass(x,y,len(segments)+1,green))
    ammount = 0
    x2 = random.randrange(1,801-30)
    y2 = random.randrange(1,601-30)
    score += 1
    print(vel)
while run:
    currentTime = time.time()
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
    vel += (score*0.0001)
    keys = pygame.key.get_pressed()
    if keys[pygame.K_w]:
        if direction != 1:
            direction = 1
            facing = -1
    if keys[pygame.K_s]:
        if direction != 1:
            direction = 1
            facing = 1
    if keys[pygame.K_a]:
        if direction != 0:
            direction = 0
            facing = -1
    if keys[pygame.K_d]:
        if direction != 0:
            direction = 0
            facing = 1
    if direction == 1:
        y += (vel*facing)
    else:
        x += (vel*facing)
    if x > x2 and x < x2 + 30 or x + 30 > x2 and x + 30 < x2 + 30:
        if y == y2:
            segmentGrowth()
        if y > y2 and y < y2 + 30 or y + 30 > y2 and y + 30 < y2 + 30:
            segmentGrowth()          
    if y > y2 and y < y2 + 30 or y + 30 > y2 and y + 30 < y2 + 30:
        if x == x2:
            segmentGrowth()
        if x > x2 and x < x2 + 30 or x + 30 > x2 and x + 30 < x2 + 30:
            segmentGrowth()
    if x > 800-30 or y > 600-30 or x < 0 or y < 0:
        gameOver()
    win.fill((0,0,0))
    for segment in segments:
        if direction == 0: #X value
            if facing == 1: #Right
                segment.x = x - (35 * segment.pos)
                segment.y = y
            else: #Left
                segment.x = x + (35 * segment.pos)
                segment.y = y
        else: #Y value
            if facing == -1: #Up
                segment.y = y + (35 * segment.pos)
                segment.x = x 
            else:#Down
                segment.y = y - (35 * segment.pos)
                segment.x = x
    for segment in segments:
        segment.draw(win)
    scoreDisplay = text.render(str(score),1,(255,255,255))
    win.blit(scoreDisplay,(760,0))
    pygame.draw.rect(win,(0,128,0),(x,y,30,30))
    pygame.draw.rect(win,(255,0,0),(x2,y2,30,30))
    pygame.display.update()
pygame.quit()

Как это работает, есть список сегментов и класс для информации каждого сегмента (то есть x, y и т. Д.).Я добавляю к этому списку экземпляр класса сегмента всякий раз, когда пользователь сталкивается с красным кубом.У меня есть этот код:

for segment in segments:
        if direction == 0: #X value
            if facing == 1: #Right
                segment.x = x - (35 * segment.pos)
                segment.y = y
            else: #Left
                segment.x = x + (35 * segment.pos)
                segment.y = y
        else: #Y value
            if facing == -1: #Up
                segment.y = y + (35 * segment.pos)
                segment.x = x 
            else:#Down
                segment.y = y - (35 * segment.pos)
                segment.x = x

Он будет перемещать все сегменты змеи одновременно, когда игрок решит, в каком направлении он хочет двигаться.Тем не менее, сегменты сразу переходят в положение x головы, а не перемещаются по одному плавно.Если бы кто-то мог помочь мне с этим, это было бы здорово.Спасибо!

1 Ответ

0 голосов
/ 20 января 2019

Хорошая игра.Я рекомендую создать список точек, который представляет собой список кортежей положений головы змей ((x, y)).Добавьте каждую позицию в список:

pts = []
while run:

    # [...]

    pts.append((x, y))

Создайте функцию, которая вычисляет положение части змеи по ее индексу (i), подсчитанному до головы змеи.Расстояние до головы должно быть lenToI = i * 35.
Расстояние между точками может быть рассчитано по Евклидову дистанцию ​​ (math.sqrt((px-pnx)*(px-pnx) + (py-pny)*(py-pny)), где точки (px, py) и (pnx, pny).Если сумма расстояний между точками (lenAct) превышает длину до точки (lenToI), то определяется положение детали i:

def getPos(i):
    global pts
    lenToI = i * 35
    lenAct = 0
    px, py = pts[-1]
    for j in reversed(range(len(pts)-1)):
        px, py = pts[j]
        pnx, pny = pts[j+1]
        lenAct += math.sqrt((px-pnx)*(px-pnx) + (py-pny)*(py-pny))
        if lenAct >= lenToI:
            break
    return (px, py)

Написать другую функцию cutPts, который удаляет точки из списка, которые больше не требуются:

def cutPts(i):
    global pts
    lenToI = i * 35
    lenAct = 0
    cut_i = 0
    px, py = pts[0]
    for j in reversed(range(len(pts)-1)):
        px, py = pts[j]
        pnx, pny = pts[j+1]
        lenAct += math.sqrt((px-pnx)*(px-pnx) + (py-pny)*(py-pny))
        if lenAct >= lenToI:
            break
        cut_i = j
    del pts[:cut_i]

Обновление позиций сегментов в цикле:

pts.append((x, y))
for i in range(len(segments)):
    segments[i].x, segments[i].y = getPos(len(segments)-i)  
cutPts(len(segments)+1)


По поводу комментария:

как мне поступить с вызовом функции gameOver(), если голова змеи касается какого-либо из ее сегментов? Я пыталсяиспользование оператора if для проверки на столкновение (так же, как я сделал для яблока) с использованием сегмент.х и сегмент.й, но это не сработает, поскольку второй сегмент змеи всегда перекрывает голову при движении змеи.

Обратите внимание, голова никогда не сможет "дотронуться" до первого сегментаза исключением того, что направление изменено на обратное, но этот случай может быть легко обработан дополнительным тестом.
Достаточно проверить, "ударяет" ли головка какой-либо сегмент, кроме первого, который соединяется сhead.
Используйте pygame.Rect.colliderect для проверки пересечения прямоугольных сегментов:

def selfCollide():    
    for i in range(len(segments)-1):
        s = segments[i]
        if pygame.Rect(x, y, 30, 30).colliderect(pygame.Rect(s.x, s.y, 30, 30)):
            return True
    return False
if selfCollide():
    gameOver()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...