How to implement a timeout/cooldown after collision

前端 未结 1 1406
醉梦人生
醉梦人生 2021-01-21 22:14

I have been making a pygame game where two players try and hit a ball into a net. I have a boost feature in my game, however, I want some boost pads where when they

相关标签:
1条回答
  • 2021-01-21 22:47

    Not bad, but let's use some object oriented programming to tidy up the code a bit. That means grouping data structures and behviour and making use of polymorphism. So since we use pygame, let's use its Sprite class, but let's ignore the boosting stuff for now.

    I added some comments for explanation.

    import pygame
    from pygame.math import Vector2
    import time, datetime
    import sys
    
    pygame.font.init()
    pygame.init()
    
    WIDTH = 1150
    HEIGHT = 800
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    clock = pygame.time.Clock()
    
    REDCAR_ORIGINAL = pygame.Surface((50, 30), pygame.SRCALPHA)
    pygame.draw.polygon(REDCAR_ORIGINAL, (255, 0, 0), [(0, 30), (50, 20), (50, 10), (0, 0)])
    
    # Everything car related goes into this class
    class Car(pygame.sprite.Sprite):
        def __init__(self):
            super().__init__()
            self.speed = 5
            # since we want to reset the car on a press of a button, 
            # let's have a reset function so we don't have duplicate code
            self.reset()
    
        def reset(self):
            self.angle = 180
            self.vel = Vector2(-self.speed, 0)
            self.update_image((800, 500))
    
        def update_image(self, center):
            # since we update the image, rect and mask a lot, 
            # let's do this in a function, also
            self.image = pygame.transform.rotate(REDCAR_ORIGINAL, self.angle)
            self.rect = self.image.get_rect(center=center)
            self.mask = pygame.mask.from_surface(self.image)
    
        def update(self):
            # basic input handling
            keys = pygame.key.get_pressed()
            if keys[pygame.K_LEFT]:
                  self.angle += 5
                  self.vel.rotate_ip(-5)
            elif keys[pygame.K_RIGHT]:
                  self.angle -= 5
                  self.vel.rotate_ip(5)
    
            self.update_image(self.rect.center)
    
            # ensure the car does not go out of screen
            self.rect.move_ip(self.vel)
            self.rect.clamp_ip(pygame.display.get_surface().get_rect())
    
    red_car = Car()
    all_sprites = pygame.sprite.Group(red_car)
    
    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            elif event.type == pygame.KEYDOWN:
              if event.key == pygame.K_r:
                  red_car.reset()
    
        # clean little main loop, yeah
        all_sprites.update()
    
        screen.fill((50,200,50))
    
        all_sprites.draw(screen)
    
        pygame.display.flip()
        dt = clock.tick(120)/1000
    
    pygame.quit()
    

    Now that this works, let's at the Boost class. Since we use classes, it's easy to add multiple ones. Each one has its own state; we use simple substraction to implement the timeout:

    import pygame
    from pygame.math import Vector2
    import time, datetime
    import sys
    import pygame.freetype
    
    pygame.font.init()
    pygame.init()
    
    WIDTH = 1150
    HEIGHT = 800
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    clock = pygame.time.Clock()
    
    REDCAR_ORIGINAL = pygame.Surface((50, 30), pygame.SRCALPHA)
    pygame.draw.polygon(REDCAR_ORIGINAL, (255, 0, 0), [(0, 30), (50, 20), (50, 10), (0, 0)])
    
    FONT = pygame.freetype.SysFont(None, 34)
    
    # Everything car related goes into this class
    class Car(pygame.sprite.Sprite):
        def __init__(self):
            super().__init__()
            self.speed = 3
            # since we want to reset the car on a press of a button, 
            # let's have a reset function so we don't have duplicate code
            self.reset()
    
        def reset(self):
            # if boost is > 0, we can drive faster
            self.boost = 0
            self.angle = 180
            self.vel = Vector2(-self.speed, 0)
            self.update_image((800, 500))
    
        def update_image(self, center):
            # since we update the image, rect and mask a lot, 
            # let's do this in a function, also
            self.image = pygame.transform.rotate(REDCAR_ORIGINAL, self.angle)
            self.rect = self.image.get_rect(center=center)
            self.mask = pygame.mask.from_surface(self.image)
    
        def update(self, dt):
    
            if self.boost > 0:
                self.boost -= dt
            if self.boost < 0:
                self.boost = 0
    
            # basic input handling
            keys = pygame.key.get_pressed()
            if keys[pygame.K_LEFT]:
                  self.angle += 5
                  self.vel.rotate_ip(-5)
            elif keys[pygame.K_RIGHT]:
                  self.angle -= 5
                  self.vel.rotate_ip(5)
    
            self.update_image(self.rect.center)
    
            # double the speed if we have boost
            self.rect.move_ip(self.vel * (2 if self.boost else 1))
    
            # ensure the car does not go out of screen
            self.rect.clamp_ip(pygame.display.get_surface().get_rect())
    
    class Booster(pygame.sprite.Sprite):
        def __init__(self, rect, cars):
            super().__init__()
            rect = pygame.rect.Rect(rect)
            # a simple timeout. We do nothing if timeout > 0
            self.timeout = 0
            self.image = pygame.Surface(rect.size, pygame.SRCALPHA)
            self.image.fill(pygame.color.Color('yellow'))
            self.rect = rect
            self.mask = pygame.mask.from_surface(self.image)
            self.cars = cars
    
        def update(self, dt):
            disabled = self.timeout > 0
    
            if disabled:
                self.timeout -= dt
                self.image.fill(pygame.color.Color('grey'))
                FONT.render_to(self.image, (10, 10), str((self.timeout // 1000) + 1), pygame.color.Color('white'))
    
            if self.timeout < 0:
                self.timeout = 0
    
            if disabled and self.timeout == 0:
                # let's reactive
                self.image.fill(pygame.color.Color('yellow'))
            if not disabled:
                for car in pygame.sprite.spritecollide(self, self.cars, False, pygame.sprite.collide_mask):
                    # let's boost the car
                    car.boost += 1000
                    # let's deactivate
                    self.timeout = 3000
                    break
    
    red_car = Car()
    cars = pygame.sprite.Group(red_car)
    all_sprites = pygame.sprite.OrderedUpdates()
    
    # see how easy it is now to create multiple Booster
    for r in [(0, 340, 50, 150), (200, 200, 150, 50), (600, 600, 100, 100)]:
        all_sprites.add(Booster(r, cars))
    
    all_sprites.add(red_car)
    
    dt = 0
    run = True
    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            elif event.type == pygame.KEYDOWN:
              if event.key == pygame.K_r:
                  red_car.reset()
    
        # clean little main loop, yeah
        all_sprites.update(dt)
    
        screen.fill((50,200,50))
    
        all_sprites.draw(screen)
    
        pygame.display.flip()
        dt = clock.tick(120)
    
    pygame.quit()
    

    Each Booster has a timeout of 3 seconds, and it boosts for 1 second.

    We make use of OrderedUpdates to draw our sprites in the right order, and spritecollide and collide_mask for the collision detection.

    Here's a shitty gif of our little game in action:

    0 讨论(0)
提交回复
热议问题