我目前正在尝试制作一个基于游戏植物对抗僵尸的游戏动画。当僵尸在屏幕上移动并联系植物时,植物精灵将保存在一个名为plants_eaten_list的列表中。僵尸也会停止向左移动。在1秒的延迟之后(当plants_eaten_event被触发时),植物失去10个健康点。当植物的健康状态达到0时,植物子画面将通过plant.kill()命令终止。
设置定时器(pygame.time.set_timer(plants_eaten_event,1000)的代码需要在主while循环中,以便不断检查plants_eaten_list中的工厂。但是,我的问题是,对于while循环的每次刷新,plants_eaten_event的计时器都会重新设置为1秒,因此plants_eaten_event永远不会发生。
我有没有办法构建这个程序的逻辑来缓解这个问题?
我不拥有任何这些图像的版权,这些图像仅供私人使用。僵尸图像从http://plantsvszombies.wikia.com/wiki/Zombie/Gallery?file=Regular_Zombie.png下载植物图像从http://plantsvszombies.wikia.com/wiki/File:Peashooter.png下载
下面是我的代码:
import pygame
import random
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
BLUE = (0,0,255)
GREEN = (0,255,0)
pygame.init()
borders_sound = pygame.mixer.Sound("bump.wav")
class Zombie(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Regular_Zombie.png").convert()
self.image.set_colorkey(WHITE)
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
# -- Attributes
# Set speed vector
self.change_x = -1
self.change_y = 0
def changespeed(self, x, y):
""" Change the speed of the player"""
self.change_x += x
self.change_y += y
def move(self):
""" Find a new position for the player"""
#self.change_x = -1
self.rect.x += self.change_x
#print(self.rect.x)
#times = 0
if self.rect.x < 0:
self.rect.x = 0
def eatplant(self,plant):
pygame.time.set_timer(plants_eaten_event,6000)
self.change_x = 0
plant.health = plant.health - 10
class Plant(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Peashooter.png").convert()
self.image.set_colorkey(BLUE)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.alive = True
self.health = 60
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])
all_sprites_list = pygame.sprite.Group()
plants_list = pygame.sprite.Group()
zombies_list = pygame.sprite.Group()
zombie1 = Zombie(690, 15)
plant1 = Plant(300,15)
all_sprites_list.add(zombie1)
all_sprites_list.add(plant1)
plants_list.add(plant1)
zombies_list.add(zombie1)
done = False
clock = pygame.time.Clock()
plants_eaten_event = pygame.USEREVENT + 1
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# If plant.health = 0 at the end of the timer, the plant is eaten.
# If plant.health != 0 at the end of the timer, the entire event
# must happen again in the while loop until the health = 0
elif event.type == plants_eaten_event:
print("Zombie 1 ate plant 1")
zombie1.eatplant(plant)
# Set timer equal to 0 until another plant gets eaten or zombie continues eating current plant
pygame.time.set_timer(plants_eaten_event, 0)
if plant.health == 0:
plant.alive = False
plant.kill()
zombie1.change_x = -1
zombie1.move()
elif event.type == zombie_eaten_plant_event:
zombie1.change_x = -1
zombie1.move()
print("Zombie 1 is eating plant")
pygame.time.set_timer(zombie_eaten_plant_event, 0)
screen.fill(WHITE)
zombie1.move()
if times == 0 and zombie1.rect.x <= 0:
times = times + 1
borders_sound.play()
all_sprites_list.draw(screen)
all_sprites_list.update()
# I set the do kill command in spritecollide command to False because I
# needed to delay the plants that were eaten before they were removed. They
# will be removed after six seconds(symbolize zombies eating plants). Aftera
# a period of time, then I will create a loop that will loop through all plants
# in the plants_eaten_list and use .kill() . I will need to create a separate
# collision function that will stall the zombies until the plants are killed.
plants_eaten_list = pygame.sprite.spritecollide(zombie1,plants_list,False)
# I changed the delay to 6 seconds, and plant 1 still died instantly. The zombie
# stayed frozen in place for 6 seconds before teh 2nd plant was instantly destroyed
for plant in plants_eaten_list:
# If I remove this, plant1 is not killed, even though
# there is the same code within the plants_eaten_event function.
if plant1.health == 0:
plant1.kill()
# The while loop loops so fast that the timer doesnot have a chance to
# finish its countdown before it restarts again. Will I need some kind of a threading
# module to prevent the timers from restarting until they end?
pygame.time.set_timer(plants_eaten_event, 1000)
for plant_eaten in plants_eaten_list:
borders_sound.play()
clock.tick(60)
pygame.display.flip()
pygame.quit()发布于 2018-07-21 15:17:49
我的问题是,对于while循环的每次刷新,plants_eaten_event的计时器都会重新设置为1秒,因此plants_eaten_event永远不会发生。
这是因为僵尸的rect在下一帧中仍然与植物的rect发生碰撞,spritecollide将返回包含碰撞植物的列表,并且调用set_timer的for plant in plants_eaten_list:循环将在每一帧中再次执行。
您需要做的第一件事是在第一次碰撞后将zombie.rect.left位置设置为plant.rect.right,以便精灵在下一帧中不再发生碰撞:
for plant in plants_eaten_list:
zombie1.change_x = 0 # Stop the zombie.
# Move the rect so that it doesn't touch the plant's rect anymore.
zombie1.rect.left = plant.rect.right
if plant1.health <= 0:
plant1.kill()
pygame.time.set_timer(plants_eaten_event, 1000)
borders_sound.play()但是,一旦开始添加更多僵尸,代码将中断,因为您在事件循环中使用了zombie1和plant变量(取决于for plant in plants_eaten_list:循环),因此它将仅适用于此僵尸和最后一个碰撞的植物。不幸的是,使用pygame.time.set_timer不可能发送比事件类型更多的信息,否则您可以将特定的僵尸和植物附加到事件,以便在事件循环中处理它们。
另一种解决方案是给每个僵尸一个自己的计时器属性(只是一个数字),你可以在他们的update方法中更新它。
因此,当僵尸接触植物时,我通过将其设置为1(秒)来启动计时器,然后在每一帧中将其递减增量时间(最后一帧所需的时间)。我还停止了僵尸self.change_x = 0,将rect移回self.rect.left = plant.rect.right并存储对植物的引用,因为我们需要它在计时器结束时减少其生命值。
一旦计时器被<= 0,僵尸开始再次移动,咬了一口植物,精灵再次接触,计时器重新启动。
import pygame
WHITE = (255,255,255)
BLUE = (0,0,255)
GREEN = (0,255,0)
class Zombie(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 50))
self.image.fill(BLUE)
self.rect = self.image.get_rect(topleft=(x, y))
self.change_x = -1
self.timer = 0 # Timer for the attacks.
self.plant = None # Currently attacked target.
def update(self, dt):
"""Update the zombie."""
if self.timer != 0:
self.timer -= dt
if self.timer <= 0: # Move if the timer is inactive.
self.change_x = -1
# Check if a plant is touching and eat it.
if self.plant is not None:
self.plant.health -= 10
print('Health', self.plant.health)
if self.plant.health <= 0:
print('Eaten')
self.plant.kill()
self.plant = None
self.timer = 0
self.rect.x += self.change_x
if self.rect.x < 0:
self.rect.x = 0
def eatplant(self, plants):
print('Play sound')
self.change_x = 0 # Stop the zombie.
self.timer = 1 # Start the timer.
for plant in plants:
self.plant = plant # Store a reference to the plant.
# Move the zombie back, so that the sprites
# don't collide anymore.
self.rect.left = plant.rect.right
class Plant(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 50))
self.image.fill(GREEN)
self.rect = self.image.get_rect(topleft=(x, y))
self.health = 30
pygame.init()
screen = pygame.display.set_mode([700, 400])
# No need to assign the sprites to variables.
plants = pygame.sprite.Group(Plant(500, 15), Plant(350, 115))
zombies = pygame.sprite.Group(Zombie(690, 15), Zombie(690, 115))
all_sprites = pygame.sprite.Group(plants, zombies)
clock = pygame.time.Clock()
dt = 0
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Pass the delta time to the update methods of the sprites.
all_sprites.update(dt)
# groupcollide returns a dictionary of the collided zombies and plants.
plants_eaten = pygame.sprite.groupcollide(zombies, plants, False, False)
for zombie, collided_plants in plants_eaten.items():
zombie.eatplant(collided_plants)
screen.fill(WHITE)
all_sprites.draw(screen)
dt = clock.tick(60) / 1000 # Time in seconds since last tick.
pygame.display.flip()
pygame.quit()https://stackoverflow.com/questions/51447879
复制相似问题