Search code examples
pythonpygamemousefill

pygame circle not changing


I finally got the collision down so that when my mouse is hovering over the circles and you click the left mouse button, It fills in. It changes going up, but going down it doesn't.

Here's my code:

# Imports a library of functions!
import pygame
import random
# Initializes the game engine
pygame.init()
# Defines the colors
BLACK = (  0,   0,   0)
GREEN = (  3, 255,   3)
# Controls the width of the circle
width_1 = 2
width_2 = 2
width_3 = 2
width_4 = 2
# Un-fills circles
filled_1 = False
filled_2 = False
filled_3 = False
filled_4 = False
# Sets the height and width of the screen
size = [720, 575] 
screen = pygame.display.set_mode(size)
# Loops until the user clicks the close button
done = False
clock = pygame.time.Clock()
# While loop
while not done:
    # Leaves the fps at 30
    clock.tick(30)
    for event in pygame.event.get(): # If user did something
        if event.type == pygame.QUIT: # If user clicked close
             done = True
        elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            # Finds where you clicked            
            x, y = event.pos
            # Check if mouse was over it
            if circle_1.collidepoint(x, y):
                # Lets the circle draw
                filled_1 = True
                width_1 = 0
                if filled_1 == True:
                    circle_1 = pygame.draw.circle(screen, BLACK, [250, 230], 7, width_1)
            elif circle_2.collidepoint(x, y):
                # Lets the circle draw
                filled_2 = True
                width_2 = 0
                if filled_2 == True:
                    circle_2 = pygame.draw.circle(screen, BLACK, [250, 260], 7, width_2)               
            elif circle_3.collidepoint(x, y):
                # Lets the circle draw
                filled_3 = True
                width_3 = 0
                if filled_3 == True:
                    circle_3 = pygame.draw.circle(screen, BLACK, [250, 260], 7, width_3)
            elif circle_4.collidepoint(x, y):
                # Lets the circle draw
                filled_4 = True
                width_4 = 0
                if filled_4 == True:
                    circle_4 = pygame.draw.circle(screen, BLACK, [250, 260], 7, width_4)
    # Cleans the screen and sets the screen background
    screen.fill(GREEN)
    # Circles
    circle_1 = pygame.draw.circle(screen, BLACK, [250, 230], 7, width_1)
    circle_2 = pygame.draw.circle(screen, BLACK, [250, 260], 7, width_2)
    circle_3 = pygame.draw.circle(screen, BLACK, [250, 290], 7, width_3)
    circle_4 = pygame.draw.circle(screen, BLACK, [250, 320], 7, width_4)
    if filled_1 == True:
        filled_2 = False
        filled_3 = False
        filled_4 = False
        width_2 = 2
        width_3 = 2
        width_4 = 2
    elif filled_2 == True:
        filled_1 = False
        filled_3 = False
        filled_4 = False
        width_1 = 2
        width_3 = 2
        width_4 = 2
    elif filled_3 == True:
        filled_1 = False
        filled_2 = False
        filled_4 = False
        width_1 = 2
        width_2 = 2
        width_4 = 2
    elif filled_4 == True:
        filled_1 = False
        filled_2 = False
        filled_3 = False
        width_1 = 2
        width_2 = 2
        width_3 = 2
    # Update the screen
    pygame.display.flip()

Just run this code and see what happens, its pretty hard to explain.


Solution

  • Let's assume that the second one is clicked, so

    filled_1 = False
    filled_2 = True
    filled_3 = False
    filled_4 = False
    

    Then you click the third. Thus,

    filled_1 = False
    filled_2 = True
    filled_3 = True
    filled_4 = False
    

    You then have:

    screen.fill(GREEN)
    
    circle_1 = pygame.draw.circle(screen, BLACK, [250, 230], 7, width_1)
    circle_2 = pygame.draw.circle(screen, BLACK, [250, 260], 7, width_2)
    circle_3 = pygame.draw.circle(screen, BLACK, [250, 290], 7, width_3)
    circle_4 = pygame.draw.circle(screen, BLACK, [250, 320], 7, width_4)
    

    which will (tempoarily) draw two black circles.

    You then do:

    if filled_1 == True:
        # Not run
    

    and then

    elif filled_2 == True:
        filled_1 = False
        filled_3 = False
        filled_4 = False
        width_1 = 2
        width_3 = 2
        width_4 = 2
    

    so now

    filled_1 = False
    filled_2 = True
    filled_3 = False
    filled_4 = False
    

    This isn't wanted! Now

    elif filled_3 == True:
        # Not run
    elif filled_4 == True:
        # Not run
    

    So clicking the third didn't work! This is because you don't store the order, which is needed.

    I suggest moving this setting-to-false into the part inside the click handler:

    if circle_1.collidepoint(x, y):
        # Lets the circle draw
        filled_1 = True
        width_1 = 0
    
        filled_2 = False
        filled_3 = False
        filled_4 = False
        width_2 = 2
        width_3 = 2
        width_4 = 2
    
        if filled_1 == True:
            circle_1 = pygame.draw.circle(screen, BLACK, [250, 230], 7, width_1)
    
    # ... etc ...
    

    That works beautifully.

    Now, there is still a problem. If on the first iteration there was an event, circle_1 would not be defined. This would crash the program. You should define the circles first.


    Some code advice.

    Instead of

    BLACK = (  0,   0,   0)
    GREEN = (  3, 255,   3)
    

    use

    pygame.Color("black")
    pygame.Color(3, 255, 3)
    

    They exist for you!

    Instead of having

    thing_1 = ...
    thing_2 = ...
    ...
    
    other_thing_1 = ...
    other_thing_2 = ...
    ...
    

    have a list of dictionaries:

    circles = [
        {
            "thing": ...,
            "other_thing": ...
        }, {
            "thing": ...,
            "other_thing": ...
        },
        ...
    ]
    

    where you use circles[1]["thing"] instead of thing_1.

    This allows you to "simplify" to:

    import pygame
    import random
    pygame.init()
    
    circles = [
        {
            "width": 2,
            "filled": False,
            "position": [250, 230]
        }, {
            "width": 2,
            "filled": False,
            "position": [250, 260]
        }, {
            "width": 2,
            "filled": False,
            "position": [250, 290]
        }, {
            "width": 2,
            "filled": False,
            "position": [250, 320]
        },
    ]
    
    size = [720, 575] 
    screen = pygame.display.set_mode(size)
    
        done = False
        clock = pygame.time.Clock()
        while not done:
            clock.tick(30)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                     done = True
    
                elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                    x, y = event.pos
    
                    # Check if mouse was over it
                    if circles[1]["bounding box"].collidepoint(x, y):
                        # Lets the circle draw
                        circles[1]["filled"] = True
                        circles[1]["width"] = 0
    
                        circles[2]["filled"] = False
                        circles[3]["filled"] = False
                        circles[4]["filled"] = False
                        circles[2]["width"] = 2
                        circles[3]["width"] = 2
                        circles[4]["width"] = 2
    
                        if circles[1]["filled"] == True:
                            circles[1]["bounding box"] = pygame.draw.circle(screen, pygame.Color("black"), [250, 230], 7, circles[1]["width"])
    
                    ...
        # Cleans the screen and sets the screen background
        screen.fill(pygame.Color(3, 255, 3))
        # Circles
        circles[1]["bounding box"] = pygame.draw.circle(screen, pygame.Color("black"), circles[1]["position"], 7, circles[1]["width"])
        circles[2]["bounding box"] = pygame.draw.circle(screen, pygame.Color("black"), circles[2]["position"], 7, circles[2]["width"])
        circles[3]["bounding box"] = pygame.draw.circle(screen, pygame.Color("black"), circles[3]["position"], 7, circles[3]["width"])
        circles[4]["bounding box"] = pygame.draw.circle(screen, pygame.Color("black"), circles[4]["position"], 7, circles[4]["width"])
        # Update the screen
        pygame.display.flip()
    

    That might not look simpler, but it allows you to use loops instead of specifying things over and over:

    import pygame
    import random
    pygame.init()
    
    circles = [
        {
            "width": 2,
            "filled": False,
            "position": [250, 230]
        }, {
            "width": 2,
            "filled": False,
            "position": [250, 260]
        }, {
            "width": 2,
            "filled": False,
            "position": [250, 290]
        }, {
            "width": 2,
            "filled": False,
            "position": [250, 320]
        },
    ]
    
    size = [720, 575] 
    screen = pygame.display.set_mode(size)
    
    done = False
    clock = pygame.time.Clock()
    while not done:
        clock.tick(30)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                 done = True
    
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                x, y = event.pos
    
                for circle in circles:
                    # Check if mouse was over it
                    if circle["bounding box"].collidepoint(x, y):
                        # Lets the circle draw
                        circle["filled"] = True
                        circle["width"] = 0
    
                        for othercircle in circles:
                            if othercircle is not circle:
                                othercircle["filled"] = False
                                othercircle["width"] = 2
    
                        if circle["filled"] == True:
                            circle["bounding box"] = pygame.draw.circle(screen, pygame.Color("black"), circle["position"], 7, circle["width"])
    
        # Cleans the screen and sets the screen background
        screen.fill(pygame.Color(3, 255, 3))
        # Circles
        for circle in circles:
            circle["bounding box"] = pygame.draw.circle(screen, pygame.Color("black"), circle["position"], 7, circle["width"])
        # Update the screen
        pygame.display.flip()
    

    With this design:

    Q: What do you do to add another circle?
    A: You add it to circles.

    Q: What do you do to make a circle larger?
    A: You increase its size property.

    Q: What do you do to change the way collisions are checked for?
    A: You change the one place collisions are checked.


    See the benefit? :)