pizzaholic81
pizzaholic81

Reputation: 25

Pygame Beat-Em-Up Game // Enemy Attack Timer Never Reaches Zero

Pastebin Link

I am working with Pygame on a beat-em-up game and I'm running into an issue where the enemy will grab the player, forever and not let him go. A timer is supposed to run and when it reaches zero the enemy is supposed to push the player away, but it never reaches zero.

The variable contact is group of enemies who are actually touching the player. It's using the pygame.sprite.Group() function. If the sprites overlap (of the player and any enemy in the list of all enemies), then they are added to the group. If the sprites stop overlapping (enemy walks away, or the player walks away), then the enemies are removed from that group.

contact = pygame.sprite.spritecollide(heroBoonrit, enemy_list, False)

For every tick of the clock, I have it set up to check if there are any enemies touching the player, and if there are, then go through every one of them and see if any of the enemies are in a grabbing state (villan.grabbing_cooldown). It's just an on/off switch that means that the enemy is currently attacking with a grab move. I could probably come up with a more logical variable name.

If those conditions are met, then a few things happen, such as snapping the player's position up (or down) in order to be on the same y coordinate as the enemy. The variable heroBoonrit.held_cooldown is another on/off switch that means that he is currently being held. Both the player and the enemies have their own variable called heroBoonrit.held_countdown and villan.grabbing_countdown respectively. The problem I'm seeing (by running print() for diagnostics is that the enemy countdown decrements by 1 and then it stop decreasing, whereas the countdown timer of my hero decrements until 0. So therefore the enemy never lets go.

I have a feeling that there's probably a much more elegant and clean way to go about handling player and enemy behavior rather than putting on/off switches for the player that relate to whether or not he's being stunned, grabbed, etc (and additionally, corresponding countdown timers for those), but I've only been doing programming for a month and would appreciate any feedback. Maybe after every tick of the clock, the enemy holding timer is reset to 60 again...I have read in other posts that when you are using the Pygame Group() function, you can't easily iterate over the group. Still learning many of the ins and outs...

I ran this command in my main game loop to find out that enemy's grabbing countdown timer only goes from 60 to 59 and then stops counting down:

print("||Shit Clown|| Grabbing = ", enemyShit_Clown.grabbing_cooldown, " Countdown = ", enemyShit_Clown.grabbing_countdown, "||Boonrit|| Grabbed = ", heroBoonrit.held_cooldown, " Countdown = ", heroBoonrit.held_countdown)

Here is the block of code where I'm running into the problem.

    for villan in contact:
        for villan in enemy_list:
            if villan.grabbing_cooldown:
                heroBoonrit.held_cooldown = True
                heroBoonrit.rect.y = villan.rect.y
                heroBoonrit.rect.x = (villan.rect.x + 30)
                heroBoonrit.held_countdown = villan.grabbing_countdown
                villan.grabbing_countdown -= 1
                heroBoonrit.held_countdown -= 1
                if villan.grabbing_countdown <= 0:
                    villan.grabbing_countdown = 0
                    heroBoonrit.held_countdown = 0
                    villan.grabbing_cooldown = False
                    heroBoonrit.held_cooldown = False
                    heroBoonrit.rect.x += 30
            elif villan.attacking_cooldown:
                if heroBoonrit.blocking != True:
                    heroBoonrit.hp -= 100
                else:
                    heroBoonrit.hp -= 10

Here is the Enemy class:

class Enemy(pygame.sprite.Sprite):
def __init__(self, name, level, speed, hp, stamina, fear, blocking_cooldown, jumping_cooldown, attacking_cooldown, held_cooldown, knockdown_cooldown, stun_cooldown, jumping_countdown, attacking_countdown, held_countdown, knockdown_countdown, stun_countdown, grabbing_cooldown, grabbing_countdown, blocking_countdown):
    super().__init__()
    self.image = pygame.image.load(os.path.join('res','img','chars','shit_clown-3.png'))
    #self.image = pygame.Surface([30,20])
    #self.image.fill(color)
    self.rect = self.image.get_rect()
    self.surf = pygame.Surface((30,20))
    self.name = name
    self.speed = speed
    self.level = level
    self.hp = hp
    self.stamina = stamina
    self.fear = fear
    self.blocking_cooldown = blocking_cooldown
    self.jumping_cooldown = jumping_cooldown
    self.jumping_countdown = jumping_countdown
    self.attacking_cooldown = attacking_cooldown
    self.attacking_countdown = attacking_countdown
    self.held_cooldown = held_cooldown
    self.held_countdown = held_countdown
    self.knockdown_cooldown = knockdown_cooldown
    self.knockdown_countdown = knockdown_countdown
    self.stun_cooldown = stun_cooldown
    self.stun_countdown = stun_countdown
    self.grabbing_cooldown = grabbing_cooldown
    self.grabbing_countdown = grabbing_countdown
    self.blocking_countdown = blocking_countdown

    blocking_cooldown = False
    jumping_cooldown = False
    jumping_coutndown = 0
    attacking_cooldown = False
    attacking_countdown = 0
    held_cooldown = False
    held_countdown = 0
    knockdown_cooldown = False
    knockdown_countdown = 0
    stun_cooldown = False
    stun_countdown = 0
    grabbing_cooldown = False
    grabbing_countdown = 0
    blocking_countdown = 0

And to spawn the enemy:

enemyShit_Clown = Enemy("Shit Clown", 1, 4, 1000, 10, 90, False, False, False, False, False, False, 0, 0, 0, 0, 0, True, 60, 0)
enemyShit_Clown.image = pygame.image.load(os.path.join('res','img','chars','shit_clown-3.png')).convert()
enemyShit_Clown.rect.x = 300 #random.randrange(300, 400)
enemyShit_Clown.rect.y =  300 #random.randrange(200, 400)
enemy_list.add(enemyShit_Clown)
all_sprites_list.add(enemyShit_Clown)

Thanks very much for your help

Upvotes: 2

Views: 859

Answers (1)

aghast
aghast

Reputation: 15310

I don't see any obvious reason for the villain to stop counting down. That said, I do see that you are decrementing the countdown both in the hero.update code and in the main loop. I'd expect your hero to count down twice as fast as the villain, but not 60 times as fast.

I'd like to suggest some code changes. First, get rid of most of the parameters in your __init__ code. Just set the default values to 0 or False or whatever.

Next, I see you creating a character, and then assigning an image to it. That should be in the constructor, with maybe a defaulted parameter to select a different starting image.

Next, you have countdown code in the hero's update method, but not in the villains. I think this is a mistake. Move the countdowns into the update routine, and don't worry about searching for it in the main loop.

Finally, there is a rule of thumb for OO programming you are missing: "Tell, don't ask."

Basically, this says, among other things, that you shouldn't be accessing the attributes of another object, and you definitely shouldn't be modifying the attributes of another object.

I suggest you write methods for this stuff, like so:

class Villain: 
    def grab(self, hero):
        """Start a grabbing attack on hero, if not blocked/busy"""

        if self.grabbing:
            # Already grabbing someone. Fail.
            return False

        ticks = 60

        if hero.grabbed(self, ticks):
            self.grabbing = hero
            self.grabbing_countdown = ticks
            hero.moveto(self.rect.y, self.rect.x + 30)
            return True
        else:
            return False

    def update(self, *args):
        :
        blah blah blah
        :
        if self.grabbing:
            self.grabbing_countdown -= 1

            if self.grabbing_countdown == 0:
                self.grabbing.release(self)
                self.grabbing.push_away(self.rect.x, self.rect.y, 30)
                self.grabbing = None

Upvotes: 2

Related Questions