在while循环中使用pygame.time.set_timer()

时间:2018-07-20 17:45:56

标签: python-2.7 pygame

我目前正在尝试根据游戏工厂与僵尸之间的关系制作游戏动画。当僵尸在屏幕上移动并接触植物时,植物精灵会保存在一个名为plant_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()

1 个答案:

答案 0 :(得分:1)

  

我的问题是,对于while循环的每次刷新,plants_eaten_event的计时器都会重置为1秒,因此没发生plants_eaten_event。

之所以发生这种情况,是因为僵尸的rect在下一帧中仍然与植物的rect发生冲突,spritecollide返回带有冲突植物的列表和for plant in plants_eaten_list:循环,您在其中调用{{1 }}每帧再次执行。

您需要做的第一件事是在第一次碰撞后将set_timer的位置设置为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方法中对其进行更新。

因此,当僵尸接触植物时,我将计时器设置为1(秒),然后将其递减每帧的增量时间(最后一帧所需的时间),以启动计时器。我也停止了僵尸update,将矩形移回self.change_x = 0并存储对植物的引用,因为我们需要它来减少计时器结束时的运行状况。

一旦计时器为self.rect.left = plant.rect.right,僵尸就会再次移动,咬住植物,精灵再次接触,计时器重新开始。

<= 0