Home > Blockchain >  My pygame timer keeps resetting after I hit a target in my aim game
My pygame timer keeps resetting after I hit a target in my aim game

Time:12-30

I am trying to make an aim game where a target pops up and once the player clicks on it, the target vanishes and a new one appears in a random location, I want it so that there is a 10 second timer but it keeps going back to 10 each time a target is "hit"

import pygame, random as r, time
FPS = 60
WIDTH = 900
HEIGHT = 500
WHITE = 255,255,255
BG = 26,26,26
RANGEXMIN = 20
RANGEXMAX = 840
RANGEYMIN = 20
RANGEYMAX = 440
window = pygame.display.set_mode((WIDTH, HEIGHT))
tick = pygame.USEREVENT
pygame.time.set_timer(tick,1000)
pygame.font.init()
FONT = pygame.font.Font('slkscr.ttf', 50)  

    
def aim_train():
    def new_target(countdown,text):
        clock = pygame.time.Clock()
        x = r.randint(RANGEXMIN, RANGEXMAX)
        y = r.randint(RANGEYMIN, RANGEYMAX)
        hit = False
        while not hit:
            for event in pygame.event.get():            
                if event.type == pygame.QUIT:
                    run = False
                elif event.type == pygame.USEREVENT:
                    if event.type == tick:
                        countdown= countdown - 1
                        text = str(countdown)
            clock.tick(FPS)
            window.fill(BG)
            timer = FONT.render(text, False, WHITE)
            window.blit(timer, (435, 20))
            pygame.mouse.set_visible(False)
            pos = pygame.mouse.get_pos()
            pos_x = pos[0]
            pos_y = pos[1]
            target = pygame.draw.rect(window, WHITE, (x,y,50,50))
            cursor_outline = pygame.draw.circle(window, BG, (pos_x,pos_y), 11)
            cursor = pygame.draw.circle(window, WHITE,(pos_x,pos_y) ,10)
            hit = (pygame.mouse.get_pressed()[0] and target.colliderect(cursor_outline))
            pygame.display.update()
            
    run = True
    countdown = 10
    text = str(countdown)
    while run:
        for event in pygame.event.get():            
            if event.type == pygame.QUIT:
                run = False
        window.fill(BG)
        pygame.mouse.set_visible(False)
        new_target(countdown,text)
    pygame.quit()
        
aim_train()

The variable "counter" somehow gets reset back to 10 after ever successful hit on a target

CodePudding user response:

The problem is that you use a seperate function, new_target, for each target.

The countdown variable is defined inside the scope of the function aim_train. Because the new_target function is defined inside the aim_train function, it is a nested function and it can use all the variables that are defined inside aim_train. However, the new_target function still has its own scope. It can make changes to any variables defined inside aim_train, but those changes remain in its own scope. When a call of new_target is ended, its scope is discarded and all changes to the variables of aim_target are undone. This causes the countdown variable to be reset every in new call of new_target and thus every time a new target is created.

You might also have noticed that you can't close your window. The window doesn't react to clicking the red cross. This is because the same applies to the run variable. When you click the red cross, the run variable is set to True inside new_target, but not in the scope of aim_train. As such, the main loop in aim_train is not quitted and the program continues.

As a solution to this problem, I would recommend to include all the code of new_target into the aim_train function. Then you only have one function, which makes that all changes to variables are in the same scope and no changes are discarded:

import pygame, random as r, time
pygame.init()
FPS = 60
WIDTH = 900
HEIGHT = 500
WHITE = 255,255,255
BG = 26,26,26
RANGEXMIN = 20
RANGEXMAX = 840
RANGEYMIN = 20
RANGEYMAX = 440
window = pygame.display.set_mode((WIDTH, HEIGHT))
tick = pygame.USEREVENT
pygame.time.set_timer(tick,1000)
pygame.font.init()
FONT = pygame.font.SysFont('arial', 50)
    
def aim_train():
    run = True
    hit = False
    
    countdown = 10
    text = str(countdown)
    
    x = r.randint(RANGEXMIN, RANGEXMAX)
    y = r.randint(RANGEYMIN, RANGEYMAX)

    clock = pygame.time.Clock()
    pygame.mouse.set_visible(False)#switched
    while run:
        clock.tick(FPS)
        for event in pygame.event.get():            
            if event.type == pygame.QUIT:
                run = False
            elif event.type == tick: #changed
                countdown= countdown - 1
                text = str(countdown)
        
        window.fill(BG)
        timer = FONT.render(text, False, WHITE)
        window.blit(timer, (435, 20))
        
        
        pos = pygame.mouse.get_pos()
        pos_x = pos[0]
        pos_y = pos[1]

        target = pygame.draw.rect(window, WHITE, (x,y,50,50))
        cursor_outline = pygame.draw.circle(window, BG, (pos_x,pos_y), 11)
        cursor = pygame.draw.circle(window, WHITE,(pos_x,pos_y) ,10)
        
        hit = (pygame.mouse.get_pressed()[0] and target.colliderect(cursor_outline))
        if hit:
            hit = False
            x = r.randint(RANGEXMIN, RANGEXMAX)
            y = r.randint(RANGEYMIN, RANGEYMAX)
        pygame.display.update()
        
    pygame.quit()
        
aim_train()

Apart from fixing the problem, I have also restructured your code a bit and did the following changes:

  • I have changed:

    elif event.type == pygame.USEREVENT:
        if event.type == tick:
    

    into:

    elif event.type == tick:
    

    Outside of the aim_train function, you have stated that tick and pygame.USEREVENT are equal. As such, it is useless to compare to them two times, because if the first check is true, then the second one will certainly be.

  • I've placed pygame.mouse.set_visible(False) outside of the main loop.

    Calling the function sets the mouse invisible untill another call changes sets the mouse back to visible. As such, it is useless to call it multiple times in the loop.

CodePudding user response:

There are actually 2 countdown variables, one in the new_target function and one in the aim_train function. If you change the variable countdown in the new_target function, this will not change the variable countdown in the aim_train function. You must return the new value of countdown from the new_target function:

def aim_train():
    def new_target(countdown):
        # [...]

        while not hit:
            for event in pygame.event.get():            
                
                # [...]
                elif event.type == pygame.USEREVENT:
                    if event.type == tick:
                        countdown= countdown - 1
            
            # [...]

        return countdown
            
    run = True
    countdown = 10
    while run:
        # [...]

        countdown = new_target(countdown)

However, I suggest that you restructure your code. Do not use nested application loops. Also see Pygame mouse clicking detection:

import pygame, random as r

FPS = 60
WIDTH = 900
HEIGHT = 500
WHITE = 255,255,255
BG = 26,26,26
RANGEXMIN = 20
RANGEXMAX = 840
RANGEYMIN = 20
RANGEYMAX = 440
window = pygame.display.set_mode((WIDTH, HEIGHT))
tick = pygame.USEREVENT
pygame.time.set_timer(tick,1000)
pygame.font.init()
FONT = pygame.font.Font('slkscr.ttf', 50)  

def new_target():
    x = r.randint(RANGEXMIN, RANGEXMAX)
    y = r.randint(RANGEYMIN, RANGEYMAX)
    return x, y
    
def aim_train():
    clock = pygame.time.Clock()
    pygame.mouse.set_visible(False)
    run = True
    countdown = 10
    hits = 0
    countdownSurf = FONT.render(f'time {countdown}', False, WHITE)
    hitsSurf = FONT.render(f'hits {hits}', False, WHITE)
    target = pygame.Rect(0, 0, 50, 50)
    target.center = new_target()
    while run:
        clock.tick(FPS)
        for event in pygame.event.get():            
            if event.type == pygame.QUIT:
                run = False
            elif event.type == tick:
                countdown -= 1
                countdownSurf = FONT.render(f'time {countdown}', False, WHITE)
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                if target.collidepoint(event.pos):
                    target.center = new_target()
                    hits  = 1
                    hitsSurf = FONT.render(f'hits {hits}', False, WHITE)

        pos = pygame.mouse.get_pos()
        window.fill(BG)
        window.blit(countdownSurf, countdownSurf.get_rect(center = (300, 45)))
        window.blit(hitsSurf, hitsSurf.get_rect(center = (600, 45)))
        pygame.draw.rect(window, WHITE, target)
        pygame.draw.circle(window, BG, pos, 11)
        pygame.draw.circle(window, WHITE, pos, 10)
        pygame.display.update()

    pygame.quit()
        
aim_train()
  • Related