Search code examples
pythonanimationtimerpygamesleep

How to check if keyboard button is getting spammed and add a cool down on before it can be pressed again


I want to know how to add a timer/cool-down to a specific key-button that is pressed. For example, the user is spamming the attack key (in my case p) and I want to add a cooldown to where the person cannot spam this button (in air and on ground). Here is the problem: https://drive.google.com/file/d/1XH-u-1qm2I0ftlCLPSsPMjTJjJGQdsUp/view?usp=sharing

As you can see in the video the person can spam the attack button indefinitely and removes the purpose of the game.

I have tried setting a sleep timer such as def sleeper(): if input() == "p": s(20) but I dont think that is how you do it. Other than that I dont know any other methods to try.

Link to my entire code: https://hastebin.com/zujanifisu.py

More specific code:

def movement(self, window):
        pygame.time.delay(20)
        if self.runcount >= 3:
            self.runcount = 0

        if self.running == True:
            window.blit(run[self.runcount],(int(self.player_x),int(self.player_y)))
            self.runcount +=1
            self.hitbox_running = pygame.Rect(self.player_x+30,self.player_y+20,48,70)
            pygame.draw.rect(window,(255,0,0),self.hitbox_running, 2)

        if (keys[pygame.K_DOWN]) or ((keys[pygame.K_DOWN]) and keys[pygame.K_p]):
            if self.player_y == 378:
                self.running = False
                if self.slidecount >= 4:
                    self.slidecount = 0

                if self.sliding:  
                    window.blit(slide[self.slidecount],(int(self.player_x),int(self.player_y)))
                    self.slidecount +=1
                    pygame.draw.rect(window,(255,0,0),self.hitbox_sliding, 2)

        if event.type == pygame.KEYDOWN:
            if (event.key == pygame.K_DOWN )and self.player_y < self.width:

                self.running = False
                self.jumping = False
                self.fallspeed += 0.2

                if self.fallingcount >= 1:
                    self.fallingcount = 0
                if self.fall:
                    window.blit(falling[self.fallingcount], (int(self.player_x),int(self.player_y)))
                    self.hitbox_falling = pygame.Rect(self.player_x+30,self.player_y,35,80)
                    pygame.draw.rect(window,(255,0,0),self.hitbox_falling, 2)                    
                    self.fallingcount +=1
        if keys[pygame.K_UP] and keys[pygame.K_p] :
            self.fallspeed = 0.3
            self.running = False
            self.jumping = False
            self.sliding = False
            if self.attackcount >= 16:
                self.attackcount = 0
            if self.attacking: 
                window.blit(attack[self.attackcount],(int(self.player_x),int(self.player_y)))
                self.attackcount += 1
                self.hitbox_attacking = pygame.Rect(self.player_x+30,self.player_y+20,38,70)
                self.hitbox_sword = pygame.Rect(self.player_x+72, self.player_y+20, 20, 50)
                pygame.draw.rect(window,(255,0,0),self.hitbox_attacking, 2)
                pygame.draw.rect(window,(255,0,0),self.hitbox_sword, 2)
            if self.jumpingcount >= 20:
                self.jumpingcount = 0
            if self.jumping and self.player_y < self.width:  
                window.blit(jump[self.jumpingcount],(int(self.player_x),int(self.player_y)))
                self.hitbox_jumping = pygame.Rect((self.player_x+20),(self.player_y+20),52,55)
                pygame.draw.rect(window,(255,0,0),self.hitbox_jumping, 2)
                self.jumpingcount +=1
                self.fallspeed = 0.3

        if keys[pygame.K_UP]:
            self.fallspeed = 0.3
            self.running = False
            if self.jumpingcount >= 20:
                self.jumpingcount = 0

            if self.jumping and self.player_y < self.width:  
                window.blit(jump[self.jumpingcount],(int(self.player_x),int(self.player_y)))
                self.hitbox_jumping = pygame.Rect((self.player_x+20),(self.player_y+20),52,55)
                pygame.draw.rect(window,(255,0,0),self.hitbox_jumping, 2)
                self.jumpingcount +=1
                self.fallspeed = 0.3

        if keys[pygame.K_p] and not keys[pygame.K_UP]:
            self.running = False
            self.jumping = False
            self.sliding = False
            if self.attackcount >= 16:
                self.attackcount = 0

            if self.attacking:
                self.hitbox_attacking = pygame.Rect(self.player_x+30,self.player_y+20,38,70)
                self.hitbox_sword = pygame.Rect(self.player_x+72, self.player_y+20, 20, 50)
                window.blit(attack[self.attackcount],(int(self.player_x),int(self.player_y)))
                self.attackcount += 1
                pygame.draw.rect(window,(255,0,0),self.hitbox_attacking, 2)
                pygame.draw.rect(window,(255,0,0),self.hitbox_sword, 2)
        if keys[pygame.K_DOWN] and keys[pygame.K_UP]:
            self.running = False


        if event.type == pygame.KEYUP:
            if event.key == pygame.K_DOWN:
                self.running = True
                self.jumping = True
                self.fallspeed = 0.3

            if event.key == pygame.K_UP:
                self.running=True
            if event.key == pygame.K_p:
                self.running = True
                self.jumping = True
                self.sliding = True

Most of the problem is with the keys[pygame.k_p]

I want to add a timer to the overall button p, so when pressed like 2-3 times it cannot be pressed for another 2-3 seconds.


Solution

  • When a key was pressed, then you've to store the time when the key is allowed to be "pressed" again.
    Use Dictionary for that:

    key_timeout = {}
    

    Write a function in global scope which checks if a key is pressed and allowed to be pressed. The parameters to the function are the state of the keys (pygame.key.get_pressed()), the key and the time out time in milliseconds:

    def getPressed(keys, key, timeout):
    

    If the key is not pressed the function returns False:

    if keys[key] == False:
        return False
    

    Use pygame.time.get_ticks() to get the time in get the time in milliseconds:

    current_time = pygame.time.get_ticks()
    

    If a timeout time is stored in the dictionary and and the time is less than the time when the key is allowed to be pressed again, then the function returns False:

    if key in key_timeout and key_timeout[key] > current_time:
       return Fase
    

    Add the timeout time to the current time, this is the time when the key is allowed to be pressed again and store it to the dictionary.

     key_timeout[key] = current_time + timeout
    

    Full code of the function:

    key_timeout = {}
    def getPressed(keys, key, timeout):
        global key_timeout
    
        if keys[key] == False:
            return False
    
        current_time = pygame.time.get_ticks()
    
        if key in key_timeout and key_timeout[key] > current_time:
            return False
    
        key_timeout[key] = current_time + timeout
        return True
    

    Instead of if keys[pygame.k_p]: you can call:

    if getPressed(keys, pygame.k_p, 2000): # 2000 milliseconds == 2 seconds
        # [...]
    

    If you ask for the state of a key (e.g. keys[pygame.k_p]) multiple times in a function, then you've to store the result of getPressed() to a variable and use the variable instead. e.g.:

    key_p_pressed = getPressed(keys, pygame.k_p, 2000)
    
    if (keys[pygame.K_DOWN]) or ((keys[pygame.K_DOWN]) and key_p_pressed ):
        # [...]
    
    if keys[pygame.K_UP] and key_p_pressed:
        # [...]
    
    if key_p_pressed and not keys[pygame.K_UP]:
        # [...]