python - 如何使用 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()
我希望小蛇在吃饼干时会显示另一张贴在脸上的图像,但它只是吃了饼干,然后立即返回错误......
解决方案
根据我的评论,可以在蛇是身体部位列表/数组的情况下实现这一点。第一个(第 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()
推荐阅读
- rust-actix - create_app 的正确返回类型
- python - 我可以分叉一个张量流检查点吗?
- tensorflow - 非归一化输出时如何解决回归误差
- entity-framework-core - Powershell、VS2019、db 迁移:“Drop-Database”未被识别为 cmdlet 的名称
- sqlite - 文字(原始)值作为 sqlite 中的外键
- mysql - FIELD()的mysql订单不使用值包含空格
- python - 如何在 Gensim 中打印文档主题?
- reactjs - Recharts - 图表不显示线条并在其上显示错误的值
- reactjs - 从网络获取数据时如何隐藏和显示进度条?
- flutter - 一旦在颤动中完成输入,如何保存文本表单字段的输入值