首页 > 解决方案 > 如何使用 blits 使蛇在 pygame 中生长?

问题描述

所以,我目前正在使用 pygame 创建一个蛇游戏,我想让它在吃饼干后让蛇长大。我希望它简单地将我用于头部的图像添加到蛇的末端,但我似乎无法弄清楚如何。

在调用我的函数 checkForConsumption() 后,我尝试对两条蛇形矩形进行 .union。它返回一个错误说“TypeError:参数必须是矩形样式对象”。

import pygame, sys, random, time
screenWidth = 500
gameDisplay = pygame.display.set_mode((screenWidth,screenWidth))
#this begins the game
pygame.init()
#This sets a caption
pygame.display.set_caption("Picnic")
#Variables:
snakeFace = pygame.image.load('morrison.png')
screen = pygame.display.set_mode((screenWidth,screenWidth))
x= 50
y= 50
snakewidth = 30
snakeheight = 30
food1width = 40
food1height = 17
food2width = 25
food2height = 26
food3width = 27
food3height = 23
vel = 12
run = True
lastKey = None
food1 = pygame.image.load("thinMint.png")
food2 = pygame.image.load("cookie.png")
food3 = pygame.image.load("triscuit.png")
randFoodX1 = 0
randFoodY1 = 0
randFoodX2 = 0
randFoodY2 = 0

topWall=pygame.draw.rect(screen, (255,0,0), ( 0,0,490,10))
bottomWall=pygame.draw.rect(screen, (255,0,0), (0,490,490,10))
leftWall=pygame.draw.rect(screen, (255,0,0), ( 0,0,10,490))
rightWall=pygame.draw.rect(screen, (255,0,0), ( 490,0,10,490))
def generateFoodPosition():
    randFoodX1 = random.randrange(1, 40, 1) * 10
    randFoodY1 = random.randrange(1, 40, 1) * 10
    randFoodX2 = random.randrange(1, 40, 1) * 10
    randFoodY2 = random.randrange(1, 40, 1) * 10
    randFoodX3 = random.randrange(1, 40, 1) * 10
    randFoodY3 = random.randrange(1, 40, 1) * 10
    if randFoodX1 == randFoodX2 or randFoodY1 == randFoodY2 or 
randFoodY2==randFoodY3 or randFoodY1== randFoodY3 or randFoodX2 == 
randFoodX3 or randFoodX3 == randFoodX1:
        generateFoodPosition()
    else:
        return [randFoodX1, randFoodY1, randFoodX2, 
randFoodY2, randFoodX3, randFoodY3]
def checkForConsumption():
    if snakeRect.colliderect(food1Rect):
            foodPositions[0] = random.randrange(1, 40, 1) * 10
        foodPositions[1] = random.randrange(1, 40, 1) * 10
    if snakeRect.colliderect(food2Rect):
        foodPositions[2] = random.randrange(1, 40, 1) * 10
        foodPositions[3] = random.randrange(1, 40, 1) * 10
    if snakeRect.colliderect(food3Rect):
        foodPositions[4] = random.randrange(1, 40, 1) * 10
        foodPositions[5] = random.randrange(1, 40, 1) * 10
foodPositions = generateFoodPosition()
def checkForWallCollision():
    if snakeRect.colliderect(topWall):
        print("Poop")
        pygame.quit()
    if snakeRect.colliderect(bottomWall):
        print("Poop")
        pygame.quit()
    if snakeRect.colliderect(leftWall):
        print("Poop")
        pygame.quit()
    if snakeRect.colliderect(rightWall):
        print("Poop")
        pygame.quit()
def growSnake():
    if snakeRect.colliderect(food1Rect) or 
snakeRect.colliderect(food2Rect) or snakeRect.colliderect(food3Rect):
        pygame.Rect.union(snakeRect)
        print("yah")


while run:
    snakeRect = gameDisplay.blit(snakeFace,(x, y))
    food1Rect = gameDisplay.blit(food1, (foodPositions[0], 
foodPositions[1]))
    food2Rect = gameDisplay.blit(food2, (foodPositions[2], 
foodPositions[3]))
    food3Rect = gameDisplay.blit(food3, (foodPositions[4], 
foodPositions[5]))
    pygame.time.delay(10) #1/2 milisecond delay
     #this controls the "X" button
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        if event.type == pygame.KEYDOWN:
            lastKey = event.key
#this controls the movement of the snake
    if lastKey == pygame.K_LEFT:# and x > vel:
        x-=vel
    if lastKey == pygame.K_RIGHT and x< screenWidth:# - 
snakewidth -vel:
        x+=vel
    if lastKey == pygame.K_DOWN and y < screenWidth:# - 
snakeheight - vel:
        y+= vel
    if lastKey == pygame.K_UP:# and y > vel:
        y-=vel

#if x == (foodPositions[0] or foodPositions[2] or foodPositions[4]) 
or y== (foodPositions[1] or foodPositions[3] or foodPositions[5]):
    checkForWallCollision()
    checkForConsumption()
    growSnake()

    gameDisplay.fill((0,0,0))
    gameDisplay.blit(snakeFace,(x, y))
    gameDisplay.blit(food1, (foodPositions[0], 
foodPositions[1]))
    gameDisplay.blit(food2, (foodPositions[2], 
foodPositions[3]))
    gameDisplay.blit(food3, (foodPositions[4], 
foodPositions[5]))
    pygame.draw.rect(screen, (255,0,0), ( 0,0,490,10))
    pygame.draw.rect(screen, (255,0,0), ( 0,490,490,15))
    pygame.draw.rect(screen, (255,0,0), ( 0,0,10,490))
    pygame.draw.rect(screen, (255,0,0), ( 490,0,15,490))
    pygame.display.update()

我希望小蛇在吃饼干时会显示另一张贴在脸上的图像,但它只是吃了饼干,然后立即返回错误......

标签: pythonpygame

解决方案


根据我的评论,可以在蛇是身体部位列表/数组的情况下实现这一点。第一个(第 0 个)元素是头部,身体的其余部分按顺序存储。body_parts[0]头如此,body_parts[-1]尾亦如此。

这使得蛇相对容易移动。首先,对于body中的每一个元素,它从position-of-part[N]to移动position-of-part[N-1]——即每个body part都移动到前一个body part的位置。例如,第二部分(紧接在头部之后)移动到头部位置,依此类推,沿着身体起伏。

将每个部分存储在列表/数组中,可以使用简单的列表遍历对其进行编码:

def slither( self ):
    # Move each body part to the location of the previous part
    # So we iterate over the tail-parts in reverse
    for i in range( len( self.body )-1, 0, -1 ):
        self.body[i].moveTo( self.body[i-1] )
    # Move the head (the only part that moves independently)
    self.head.move( self.direction )

这允许代码为每个身体部位或简单的矩形使用精灵。

那么......当蛇长大时,它是如何工作的?如果蛇向左移动(为了讨论而直线移动),那么尾巴向右“生长”是有意义的。同样,如果蛇向上移动,它应该向下生长。

这为我们提供了新尾部元素定位的描述:

def getGrowPosition( self ):
    # we grow against self.direction 
    # so if we're moving up, the tail grows down
    x,y = self.body[ -1 ].getRect().center # location of the last body piece

    if ( self.direction == 'up' ):
        y += self.body_size
    elif ( self.direction == 'down' ):
        y -= self.body_size
    elif ( self.direction == 'left' ):
        x += self.body_size
    elif ( self.direction == 'right' ):
        x -= self.body_size
    return (x,y)

def grow( self, by_size=1 ):
    for i in range( by_size ):
        # new body part needs to be added at the tail-position
        new_body_position = self.getGrowPosition()
        self.body.append( BodyPart( new_body_position ) )

我实现了一个蛇体,其中每个段都是一个圆形图像精灵。在这个例子中,蛇在几秒钟后开始移动和生长。没有碰撞检测,屏幕换行。

import pygame
import random
import time
import sys

# Window size
WINDOW_WIDTH=400
WINDOW_HEIGHT=400

pygame.init()
WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )
SPRITES = pygame.sprite.Group()

pygame.display.set_caption("Snake Test")
HEAD_IMAGE = pygame.image.load('snake_head_32.png').convert_alpha()
BODY_IMAGE = pygame.image.load('snake_body_32.png').convert_alpha()

clock = pygame.time.Clock()

BLACK = (0,0,0)

STARTUP_MS = int(time.time() * 1000.0)  # Epoch time programme started
NOW_MS     = 0  # ms since we started

class SnakeSprite(pygame.sprite.Sprite):
    """ A SnakeSprite holds the image and location of a snake body-segment.
        It also implements the screen wrapping, so moving off-screen moves
        the sprite back-on to the opposite side"""
    def __init__( self, part_image, position ):
        pygame.sprite.Sprite.__init__(self)
        self.image = part_image
        self.rect = self.image.get_rect()
        self.rect.center = position 
        self.move_step = self.image.get_rect().width # pixels per move step

    def wrapAroundScreen(self):
        # Stay on the screen, and wrap around
        if (self.rect.left >= WINDOW_WIDTH ):
            self.rect.right = 0
        elif (self.rect.right <= 0 ):
            self.rect.left = WINDOW_WIDTH
        if (self.rect.top >= WINDOW_HEIGHT ):
            self.rect.bottom = 0
        elif (self.rect.bottom <= 0):
            self.rect.top = WINDOW_HEIGHT

    def move( self, direction ):
        if ( direction == 'up' ):
            self.rect.y -= self.move_step
        elif ( direction == 'down' ):
            self.rect.y += self.move_step
        elif ( direction == 'left' ):
            self.rect.x -= self.move_step
        elif ( direction == 'right' ):
            self.rect.x += self.move_step
        else:
            print(" MOVE ERROR - "+direction)
        self.wrapAroundScreen()

    def moveTo( self, body_part ):
        self.rect.center = body_part.rect.center

    def getRect( self ):
        return self.rect

    def update(self):
        pass


class Snake():
    """ A Snake object is basically a list of SnakeSprites.
        The 0th / first element is the snake head, the other
        elements are "tail" parts"""
    def __init__( self, size=3 ):
        global SPRITES
        # Create the head sprite positioned 64px left of the middle-screen
        self.head = SnakeSprite( HEAD_IMAGE, ( (WINDOW_WIDTH//2)-64, WINDOW_HEIGHT//2 ) )
        # The sprites are positioned by the width of the sprite image
        self.body_size = self.head.image.get_rect().width # pixels per move step
        self.direction = 'left';
        self.body = [ self.head ]
        SPRITES.add( self.head )
        self.grow( size )

    def changeDirection( self, new_direction ):
        self.direction = new_direction
        self.slither()

    def slither( self ):
        # Move each body part to the location of the previous part
        # So we iterate over the tail-parts in reverse
        for i in range( len( self.body )-1, 0, -1 ):
            self.body[i].moveTo( self.body[i-1] ) 
        # Move the head
        self.head.move( self.direction )

    def getGrowPosition( self ):
        # we grow against self.direction 
        # so if we're moving up, the tail grows down
        x,y = self.body[ -1 ].getRect().center
        if ( self.direction == 'up' ):
            y += self.body_size
        elif ( self.direction == 'down' ):
            y -= self.body_size
        elif ( self.direction == 'left' ):
            x += self.body_size
        elif ( self.direction == 'right' ):
            x -= self.body_size
        return (x,y)

    def grow( self, by_size=1 ):
        global SPRITES
        for i in range( by_size ):
            # new body part needs to be added at the tail-position
            new_body_part = SnakeSprite( BODY_IMAGE, self.getGrowPosition() )
            self.body.append( new_body_part )
            SPRITES.add( new_body_part )



def drawGameWindow(screen, sprites):
    global WINDOW_WIDTH, WINDOW_HEIGHT, SPRITES
    screen.fill(BLACK)
    SPRITES.draw(screen)
    pygame.display.update()
    pygame.display.flip()

### MAIN
my_snake = Snake()
last_move = STARTUP_MS
last_grow = STARTUP_MS

clock = pygame.time.Clock()
done = False
while not done:
    NOW_MS = int(time.time() * 1000.0) 

    SPRITES.update()

    # Move every 500ms
    if ( NOW_MS - STARTUP_MS > 3000 and NOW_MS - last_move >= 500 ):
        print("Moving...")
        my_snake.slither()
        last_move = NOW_MS

    # Grow every 2 seonds
    if ( NOW_MS - STARTUP_MS > 3000 and NOW_MS - last_grow >= 2000 ):
        print("Growing...")
        my_snake.grow( 1 )
        last_grow = NOW_MS

    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True
        elif ( event.type == pygame.VIDEORESIZE ):
            # resize-window
            WINDOW_WIDTH  = event.w
            WINDOW_HEIGHT = event.h
            WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )
        elif (event.type == pygame.KEYDOWN):
            # Movement keys
            keys = pygame.key.get_pressed()
            if ( keys[pygame.K_UP] ):
                my_snake.changeDirection("up")
            elif ( keys[pygame.K_DOWN] ):
                my_snake.changeDirection("down")
            elif ( keys[pygame.K_LEFT] ):
                my_snake.changeDirection("left")
            elif ( keys[pygame.K_RIGHT] ):
                my_snake.changeDirection("right")

    drawGameWindow(WINDOW, SPRITES)
    clock.tick_busy_loop(60)

pygame.quit()

蛇头_32.png snake_body_32.png


推荐阅读