所以,我对编程完全陌生(已经做了几个月了),并决定尝试编写游戏代码。在此,非常感谢 Chris Bradfield 提供的 pygame 编码系列教程,它们非常棒!但是,现在我已经完成了教程并需要自己动手了,我遇到了一个问题。我正在制作一个自上而下的射击游戏,并使其基于波次。因此,当一波僵尸死亡时,我想显示一个计时器,倒计时直到下一波开始。我认为我现在走在正确的道路上,让我向你展示我正在做的事情。
def new(self) ''' self.timer_flag = False self.x = threading.Thread(target=self.countdown, args=(TIME_BETWEEN_WAVES,)) ''' def countdown(self, time_between_waves): self.wave_timer = time_between_waves for i in range(TIME_BETWEEN_WAVES): while self.timer_flag: self.wave_timer -= time.sleep(1) def update(self) ''' self.countdown_has_run = False if len(self.mobs) == 0: self.timer_flag = True if not self.countdown_has_run: self.countdown_has_run = True self.x.start() '''
现在,当 timer_flag 为 True 时,我也绘制我的计时器,但它不会减少,所以我认为问题出在调用/启动线程倒计时函数的某个地方?
另外,这是我第一次在这里发帖,所以请告诉我该怎么做才能更好地格式化等等,以便你能够提供帮助
不用为线程烦恼。没必要让你的生活变得复杂。
通常,您会在游戏中使用某种Clock方式(如果没有,您应该开始使用它)来限制帧速率,并确保您的世界以恒定的速度移动(如果没有,您应该开始这样做)。
Clock
因此,如果您想在 5 秒内触发某件事,只需创建一个保存该值的变量5000,然后减去处理最后一帧所花费的时间(由 Clock 返回tick):
5000
tick
clock = pygame.time.Clock() dt = 0 timer = 5000 while True: ... timer -= dt if timer <= 0: do_something() dt = clock.tick(60)
下面我拼凑了一个简单的示例。在那里,我使用一个简单的类,它也是一个Sprite将剩余时间绘制到屏幕上的类。当计时器用完时,它会调用一个函数来创建新的一波僵尸。
Sprite
在主循环中,我检查是否没有计时器正在运行并且没有僵尸,如果是的话,则创建一个新的计时器。
代码如下:
import pygame import pygame.freetype import random # a dict that defines the controls # w moves up, s moves down etc CONTROLS = { pygame.K_w: ( 0, -1), pygame.K_s: ( 0, 1), pygame.K_a: (-1, 0), pygame.K_d: ( 1, 0) } # a function that handles the behaviour a sprite that # should be controled with the keys defined in CONTROLS def keyboard_controlled_b(player, events, dt): # let's see which keys are pressed, and create a # movement vector from all pressed keys. move = pygame.Vector2() pressed = pygame.key.get_pressed() for vec in (CONTROLS[k] for k in CONTROLS if pressed[k]): move += vec if move.length(): move.normalize_ip() move *= (player.speed * dt/10) # apply the movement vector to the position of the player sprite player.pos += move player.rect.center = player.pos # a function that let's a sprite follow another one # and kill it if they touch each other def zombie_runs_to_target_b(target): def zombie_b(zombie, events, dt): if target.rect.colliderect(zombie.rect): zombie.kill() return move = target.pos - zombie.pos if move.length(): move.normalize_ip() move *= (zombie.speed * dt/10) zombie.pos += move zombie.rect.center = zombie.pos return zombie_b # a simple generic sprite class that displays a simple, colored rect # and invokes the given behaviour class Actor(pygame.sprite.Sprite): def __init__(self, color, pos, size, behavior, speed, *grps): super().__init__(*grps) self.image = pygame.Surface(size) self.image.fill(color) self.rect = self.image.get_rect(center=pos) self.pos = pygame.Vector2(pos) self.behavior = behavior self.speed = speed def update(self, events, dt): self.behavior(self, events, dt) # a sprite class that displays a timer # when the timer runs out, a function is invoked # and this sprite is killed class WaveCounter(pygame.sprite.Sprite): font = None def __init__(self, time_until, action, *grps): super().__init__(grps) self.image = pygame.Surface((300, 50)) self.image.fill((3,2,1)) self.image.set_colorkey((3, 2, 1)) self.rect = self.image.get_rect(topleft=(10, 10)) if not WaveCounter.font: WaveCounter.font = pygame.freetype.SysFont(None, 32) WaveCounter.font.render_to(self.image, (0, 0), f'new wave in {time_until}', (255, 255, 255)) self.timer = time_until * 1000 self.action = action def update(self, events, dt): self.timer -= dt self.image.fill((3,2,1)) WaveCounter.font.render_to(self.image, (0, 0), f'new wave in {int(self.timer / 1000) + 1}', (255, 255, 255)) if self.timer <= 0: self.action() self.kill() def main(): pygame.init() screen = pygame.display.set_mode((600, 480)) screen_rect = screen.get_rect() clock = pygame.time.Clock() dt = 0 sprites_grp = pygame.sprite.Group() zombies_grp = pygame.sprite.Group() wave_tm_grp = pygame.sprite.GroupSingle() # the player is controlled with the keyboard player = Actor(pygame.Color('dodgerblue'), screen_rect.center, (32, 32), keyboard_controlled_b, 5, sprites_grp) # this function should be invoked once the timer runs out def create_new_wave_func(): # let's create a bunch of zombies that follow the player for _ in range(15): x = random.randint(0, screen_rect.width) y = random.randint(-100, 0) Actor((random.randint(180, 255), 0, 0), (x, y), (26, 26), zombie_runs_to_target_b(player), random.randint(2, 4), sprites_grp, zombies_grp) while True: events = pygame.event.get() for e in events: if e.type == pygame.QUIT: return # no timer, no zombies => create new timer if len(wave_tm_grp) == 0 and len(zombies_grp) == 0: WaveCounter(5, create_new_wave_func, sprites_grp, wave_tm_grp) sprites_grp.update(events, dt) screen.fill((80, 80, 80)) sprites_grp.draw(screen) pygame.display.flip() dt = clock.tick(60) if __name__ == '__main__': main()