Как сделать волновой таймер в Pygame - PullRequest
1 голос
/ 24 мая 2019

Итак, я совершенно новичок в программировании (занимаюсь этим пару месяцев) и решил попробовать написать код для игры.На этом замечание, большое спасибо Крису Брэдфилду за серию обучающих программ по пигментному кодированию, они просто великолепны!Однако теперь, когда я закончил с учебными пособиями и должен работать самостоятельно, я столкнулся с проблемой.Я делаю шутер сверху вниз и делаю его на основе волн.Итак, когда зомби в одной волне умирают, я хочу показать таймер, который ведет обратный отсчет до начала следующей волны.Я ДУМАЮ, что я иду по правильному пути, позвольте мне показать вам, с чем я работаю.

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, но оно не уменьшается, поэтому я предполагаю, что проблема заключается где-то в вызове / запуске многопоточной функции обратного отсчета?

Кроме того, я впервые пишу здесь, поэтому, пожалуйста, дайте мне знать, что нужно сделать для лучшего форматирования и т. д., чтобы вы моглипомощь

Ответы [ 2 ]

1 голос
/ 24 мая 2019

Не связывайтесь с темами.Не нужно усложнять жизнь.

Обычно вы все равно используете Clock в своей игре (если нет, вам следует начать ее использовать), чтобы ограничить частоту кадров и обеспечитьчто ваш мир движется с постоянной скоростью (если нет, вы должны начать это делать).

Поэтому, если вы хотите вызвать что-то, скажем, через 5 секунд, просто создайте переменную, которая содержит значение 5000и вычтите время, которое потребовалось для обработки вашего последнего кадра (который возвращается Clock. tick):

clock = pygame.time.Clock()
dt = 0
timer = 5000
while True:
    ...
    timer -= dt
    if timer <= 0:
       do_something()
    dt = clock.tick(60)

Я взломал простой пример ниже.Там я использую простой класс, который также является 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()

enter image description here

0 голосов
/ 24 мая 2019

Мне кажется, вам не хватает механизма, чтобы проверить, сколько мобов осталось на экране.Я предполагаю, что это может быть что-то вроде этого:

class CountdownClock:
    def __init__(self):
        self.start_no = 1
        self.time_between_waves = 5
        self.t = threading.Thread(target=self.check_mobs_left)
        self.t.start()

    def check_mobs_left(self):
        self.mobs = ["mob" for _ in range(randint(2, 7))]  #generate 2-7 mobs per level
        print (f"Wave {self.start_no} : Total {len(self.mobs)} mobs found!")
        while self.mobs:
            print (f"Still {len(self.mobs)} mobs left!")
            time.sleep(1)
            del self.mobs[-1] #simulate mob kill - remove this line from your actual setting
        self.next_wave(self.time_between_waves)
        self.time_between_waves +=2 #increased time for each wave

    def next_wave(self,time_between_waves):
        self.time_left = time_between_waves
        print(f"Wave {self.start_no} cleared!")
        self.start_no += 1
        while self.time_left:
            print (f"Next wave in...{self.time_left}")
            self.time_left -=1
            time.sleep(1)
        self.t = threading.Thread(target=self.check_mobs_left)
        self.t.start()

a = CountdownClock()

Вы будете иметь это постоянно без необходимости вызывать метод каждый раунд и устанавливать флаг и прочее.

...