首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在while循环中使用pygame.time.set_timer()

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

Stack Overflow用户
提问于 2018-07-21 01:45:56
回答 1查看 418关注 0票数 1

我目前正在尝试制作一个基于游戏植物对抗僵尸的游戏动画。当僵尸在屏幕上移动并联系植物时,植物精灵将保存在一个名为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下载

下面是我的代码:

代码语言:javascript
复制
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()
EN

回答 1

Stack Overflow用户

发布于 2018-07-21 15:17:49

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

这是因为僵尸的rect在下一帧中仍然与植物的rect发生碰撞,spritecollide将返回包含碰撞植物的列表,并且调用set_timerfor plant in plants_eaten_list:循环将在每一帧中再次执行。

您需要做的第一件事是在第一次碰撞后将zombie.rect.left位置设置为plant.rect.right,以便精灵在下一帧中不再发生碰撞:

代码语言:javascript
复制
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()

但是,一旦开始添加更多僵尸,代码将中断,因为您在事件循环中使用了zombie1plant变量(取决于for plant in plants_eaten_list:循环),因此它将仅适用于此僵尸和最后一个碰撞的植物。不幸的是,使用pygame.time.set_timer不可能发送比事件类型更多的信息,否则您可以将特定的僵尸和植物附加到事件,以便在事件循环中处理它们。

另一种解决方案是给每个僵尸一个自己的计时器属性(只是一个数字),你可以在他们的update方法中更新它。

因此,当僵尸接触植物时,我通过将其设置为1(秒)来启动计时器,然后在每一帧中将其递减增量时间(最后一帧所需的时间)。我还停止了僵尸self.change_x = 0,将rect移回self.rect.left = plant.rect.right并存储对植物的引用,因为我们需要它在计时器结束时减少其生命值。

一旦计时器被<= 0,僵尸开始再次移动,咬了一口植物,精灵再次接触,计时器重新启动。

代码语言:javascript
复制
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()
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51447879

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档