2

I dont know how I can use my button function to either overlay the background.jpg back over the buttons or wipe the current screen and put the background back in place after the scene has been cleared.

import pygame


pygame.init()


screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()



BLACK = (0, 0, 0)
BACKGROUND = (200, 230, 234)
WHITE = (255, 255, 255)
HOVER_COLOUR = (50, 70, 90)
# Text Variables 
FONT = pygame.font.SysFont ("Times New Norman", 60)
TEXT = FONT.render ("", True, WHITE)
background_images = pygame.image.load("background.jpg").convert()
screen.blit(background_images, [0,0])
screen.blit(TEXT, (150, 50))
# Text & Rectangles construction
text1 = FONT.render("PlAY", True, WHITE)
text2 = FONT.render("CONTROLS", True, WHITE)
text3 = FONT.render("DIFFICULTY", True, WHITE)
text4 = FONT.render("SCOREBOARD", True, WHITE)

rect1 = pygame.Rect(250,200,300,80)
rect2 = pygame.Rect(250,300,300,80)
rect3 = pygame.Rect(250,400,300,80)
rect4 = pygame.Rect(250,500,300,80)
# The button construction arry. Text and Rectangle 
buttons = [
        [text1, rect1, BACKGROUND, 1],
        [text2, rect2, BACKGROUND, 2],
        [text3, rect3, BACKGROUND, 3],
        [text4, rect4, BACKGROUND, 4],
        ]

# Function for button printing (testing)
def on_button(buttons):
        print(buttons[3])


def game_intro():
        while True:
                for event in pygame.event.get():
                        if event.type == pygame.QUIT:
                                return
                        elif event.type == pygame.MOUSEMOTION:
                                for button in buttons:
                                        # Uses collisionpoint to detect mouse position collisions
                                        if button[1].collidepoint(event.pos):
                                                # Set the button's colour to the hover colour.
                                                button[2] = HOVER_COLOUR
                                        else:
                                                # resets the colour to normal.
                                                button[2] = BACKGROUND
                        # Button Controls 
                        elif event.type == pygame.MOUSEBUTTONDOWN:
                                for button in buttons:
                                        # Uses collisionpoint to detect mouse position collisions
                                        if button[1].collidepoint(event.pos):
                                                on_button(button)
                                        if button == buttons[0]:
                                                screen.fill(0,0,0)


                # Draws the buttons with their current colours (normal & collisions)
                for text, rect, colour, button_id in buttons:
                        pygame.draw.rect(screen, colour, rect)
                        screen.blit(text, rect)

                pygame.display.flip()
                clock.tick(15)


#Run Game
game_intro()
pygame.quit()

As you can see the operation:

if button == buttons[0]:
screen.fill(0,0,0)

Is what im currently working with. The if statement works fine and iv tested its feedback with print operations but i cannot work it with Pygame functions.

Pthyon
  • 152
  • 10
  • Should you write `if button == buttons[0]: screen.fill((0,0,0))`? – Haru Apr 02 '19 at 13:18
  • "The if statement works fine" - have you also tried clicking outside any of the buttons? That condition will always be true the first time through the loop, because it loops through all the `button`s in order, so it will first look at `buttons[0]` which of course is `== buttons[0]`. – Karl Knechtel Apr 02 '19 at 13:23
  • Maybe take a look at [this question/answer](https://stackoverflow.com/questions/55408277/pygame-best-way-to-implement-buttons/55414264#55414264) – sloth Apr 02 '19 at 13:45
  • Also, maybe [this question/answer](https://stackoverflow.com/questions/14700889/pygame-level-menu-states) is also helpful – sloth Apr 02 '19 at 13:46

2 Answers2

1

The issue is caused by

screen.fill(0,0,0)

because the 2nd parameter to pygame.Surface.fill() is assumed to be a rectangle (e.g. pygame.Rect), which limits the fill to a specific area.

The 1st parameter to pygame.Surface.fill() has to be a RGB sequence, RGBA sequence or a color index.

So it has to be

screen.fill( (0,0,0) )

or

screen.fill(0)

The buttons are still they, because they are drawn continuously in every frame:

for text, rect, colour, button_id in buttons:
    pygame.draw.rect(screen, colour, rect)
    screen.blit(text, rect)

Add a global state variable (play) which is set when the play button is pressed. Change the state in the function on_button, use the global statement to change the value of the globale variable play. Draw the scene dependent on the state:

play = False
def on_button(buttons):
    global play 
    play = buttons[3] == 1
    print(buttons[3], play)
def game_intro():

    # [...]

    if play:
        screen.fill(0)

        # [...]            

    else: 
        for text, rect, colour, button_id in buttons:
            pygame.draw.rect(screen, colour, rect)
            screen.blit(text, rect)

    pygame.display.flip()
    clock.tick(15)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • That works to make the screen colour black but doesnt remove the buttons? any ideas for getting rid of them? – Pthyon Apr 02 '19 at 13:17
  • The reason the buttons don't disappear is because `start_play` won't be correctly set with this code because again, your `button == buttons[0]` condition doesn't actually make the sense that you want it to make. I assume what you want is to set it if the `button[0]` is the one that was clicked. To make that work, you want the check *inside* the `collidepoint` check. – Karl Knechtel Apr 02 '19 at 13:28
  • @KarlKnechtel Yes, you're right. I missed that. Anyway, I changed the answer. Since there is a function `on_button` the state has to be changed there. – Rabbid76 Apr 02 '19 at 13:33
  • @KarlKnechtel I have just noticed that, thank you so much for calling that out, – Pthyon Apr 02 '19 at 15:26
  • @Rabbid76 When integrating your suggestions Iv ran into an issue, the actual changing of the "play" variable works fine by when applying it to "if play = True THEN..." it's not actually playing that if statement within the function game_intro – Pthyon Apr 02 '19 at 21:01
  • @KallumHancox There was an mistake in my code. It has to be `global play` instead of `global `start_play` in the function `on_button`. Note `play` has to be a variable in global scope. – Rabbid76 Apr 02 '19 at 21:07
  • @Rabbid76 I found it and fixed it, I have now added this to all the buttons listed and im making my game screens. I cannot thank you enough I was super stuck – Pthyon Apr 02 '19 at 21:40
0

To directly answer the question:

if button[1].collidepoint(event.pos):
    on_button(button)
if button == buttons[0]:
    screen.fill(0,0,0)

Check your indentation. For each button, the code does the .collidepoint check and possibly calls on_button, and then it also checks which button is being examined - regardless of the .collidepoint result.

if button[1].collidepoint(event.pos):
    on_button(button)
    if button == buttons[0]:
        screen.fill(0,0,0)

Now the screen.fill only happens if both conditions are true - i.e. the button being examined is buttons[0], and the event.pos (i.e., the place where the user clicked) is inside that button.


But to deal with the problem - you really should use something more sophisticated to represent your buttons. Basically, what we would like to happen is for the on_button code to make the decision of what is done when the button is clicked, according to which button it is. To make that work smoothly, the buttons info needs to include something that tells on_button what to do.

Python allows us to do a neat trick here: names of things are just names, even if the thing being named is a function - and that means, for example, that we can put those names in a list, and then pull them out and use them to call the function. For example, if we had a function that explains what the Play button should do:

def do_play():
    # up to you ;)

And then set up the button to store that name, instead of a button ID:

play_button = [text1, rect1, BACKGROUND, do_play]

Now we can have on_button figure it out for us:

def on_button(button):
    button[3]()

When the .collidepoint test passes for that button, it gets passed to on_button, which looks up the function name do_play and calls that function. Simple :)

(The next level of sophistication is to use a class to represent the Button information instead of a plain list.)

You might also find some useful ideas here:

How to make buttons in python/pygame?

https://www.pygame.org/tags/button

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • Ok so when intergrating this I would have to create a new function right? WOuld this work for all the buttons seperatly so i can use '''def do_controls''' etc? – Pthyon Apr 02 '19 at 16:27