Weird bug in program that “turns off” certain obje

2019-08-05 08:42发布

问题:

I'm trying to create a "text based game" in a window, using fonts and makeshift buttons. Everything works fine, but when there are multiple buttons on the screen and only one is pressed, the others' collisions carry over to the next screen and mess everything up. While doing debugging, I noticed that pressing the button on the next screen briefly works, but then goes to the 2nd button from the first screen, ex. I press the first button, button on second screen works, but bugs out by triggering the second button on the first screen. Here's the code:

import pygame, sys
from pygame.locals import *

pygame.init()

window = pygame.display.set_mode((640, 480))

starting = 0
choiceNum = 0
isReady = False
isReady2 = False

#old debug tool - hasBeenClicked = False

fList = []

FONTtitle = pygame.font.SysFont('arial black', 20)
FONT = pygame.font.SysFont('arial black', 15)

YELLOW = (255, 255, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 40)
BLUE = (0, 0, 255)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)

class StoryFont():
    def __init__(self, FONT, string, x, y, window):
        self.FONT = FONT
        self.string = string
        self.x = x
        self.y = y
        self.window = window
        self.fSFont = 0

    def create(self):
        self.fSFont = self.FONT.render(self.string, False, WHITE)
        self.fSFontr = self.fSFont.get_rect()
        self.fSFontr.center = (self.x,self.y)

    def blit(self):
        self.window.blit(self.fSFont, self.fSFontr)

class Button():
    def __init__(self, FONT, string, x1, y1, x2, y2, fontx, fonty, choiceNum, window):
        self.FONT = FONT
        self.string = string
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.fontx = fontx
        self.fonty = fonty
        self.choiceNum = choiceNum
        self.hasBeenClicked = False
        self.window = window
        self.fSFont = 0

    def create(self):
        self.fSFont = self.FONT.render(self.string, False, WHITE)
        self.fSFontr = self.fSFont.get_rect()
        self.fSFontr.center = (self.fontx, self.fonty)
        self.window.blit(self.fSFont, self.fSFontr)


        if self.hasBeenClicked == False:
            if pos[1] >= self.fonty - 17.5 and pos[1] <= self.fonty + 17.5 and pos[0] >= self.fontx - 47.5 and pos[0] <= self.fontx + 47.5:
                pygame.draw.rect(window, BLUE, ((self.x1, self.y1), (self.x2, self.y2)), 0)
                window.blit(self.fSFont, self.fSFontr)

                if pygame.mouse.get_pressed()[0] == True:
                    global starting
                    self.hasBeenClicked = True
                    starting = self.choiceNum
                    isReady2 = False
                    window.fill(BLACK)
                    return starting


            else:
                global isReady2
                isReady2 = False






def blitMenu(FONTtitle):
    pygame.display.set_caption("Text Game")
    fTITLE = StoryFont(FONTtitle, "Text Game", 320, 100, window)
    fTITLE.create()

    fBUTTON = Button(FONTtitle, "Start", 285, 285, 70, 30, 320, 300, 1, window)

    def blitMenuDraw():

        window.fill(BLACK)
        fTITLE.blit()
        fBUTTON.create()
    blitMenuDraw()


def choice1(FONT):
    fSTORY = StoryFont(FONT, "You are Mr. Stetkiewicz, a seventh grade science teacher.", 320, 50, window)
    fSTORY.create()

    fSTORY2 = StoryFont(FONT, "One day, when coming into your class room to teach about the", 320, 70, window)
    fSTORY2.create()

    fSTORY3 = StoryFont(FONT, "behavior of convection, you notice that no one is there.", 320, 90, window)
    fSTORY3.create()

    fSTORY4 = StoryFont(FONT, "Everything seems cold and somewhat dark.", 320, 140, window)
    fSTORY4.create()

    fSTORY5 = StoryFont(FONT, "Eventually you realize that there is a lack of thermal energy,", 320, 190, window)
    fSTORY5.create()

    fSTORY6 = StoryFont(FONT, "as if it has been stolen. Infuriated, you grab your trusty frying pan", 320, 210, window)
    fSTORY6.create()

    fSTORY7 = StoryFont(FONT, "engraved with '212 degree boiling point', and set out on your journey", 320, 230, window)
    fSTORY7.create()

    fSTORY8 = StoryFont(FONT, "to restore thermal energy to the school.", 320, 250, window)
    fSTORY8.create()

    fSTORY9 = StoryFont(FONT, "Good Luck!", 320, 300, window)
    fSTORY9.create()

    fBUTTON2 = Button(FONT, "Continue", 270, 385, 100, 30, 320, 400, 2, window)


    def choice1Draw():

        window.fill(BLACK)
        fSTORY.blit()
        fSTORY2.blit()
        fSTORY3.blit()
        fSTORY4.blit()
        fSTORY5.blit()
        fSTORY6.blit()
        fSTORY7.blit()
        fSTORY8.blit()
        fSTORY9.blit()
        fBUTTON2.create()

    choice1Draw()

def choice2(FONT):

    fSTORY = StoryFont(FONT, "You have just exited the doorway to your classroom.", 320, 170, window)
    fSTORY.create()

    fSTORY2 = StoryFont(FONT, "What would you like to do?", 320, 230, window)
    fSTORY2.create()

    fBUTTON3 = Button(FONT, "1.Check Inventory", 240, 300, 160, 25, 320, 310, 3, window)

    fBUTTON4 = Button(FONT, "2.Look left down the hallway.", 190, 330, 260, 25, 320, 345, 4, window)

    fBUTTON5 = Button(FONT, "3.Look right down the hallway.", 185, 365, 265, 25, 320, 380, 5, window)

    global fList
    fList = [fSTORY, fSTORY2, fBUTTON3, fBUTTON4, fBUTTON5]

    if fBUTTON3.hasBeenClicked == True:
        fBUTTON4.hasBeenClicked = True
        fBUTTON5.hasBeenClicked = True

    if fBUTTON4.hasBeenClicked == True:
        fBUTTON3.hasBeenClicked = True
        fBUTTON5.hasBeenClicked = True

    if fBUTTON5.hasBeenClicked == True:
        fBUTTON3.hasBeenClicked = True
        fBUTTON4.hasBeenClicked = True

    print(str(fBUTTON3.hasBeenClicked) + "button 3")
    print(str(fBUTTON4.hasBeenClicked) + "button 4")
    print(str(fBUTTON5.hasBeenClicked) + "button 5")


    choice2Draw(fList, window)

def choice2Draw(fList, window):
    window.fill(BLACK)
    fList[0].blit()
    fList[1].blit()
    fList[2].create()
    fList[3].create()
    fList[4].create()

    global isReady2


def choice3(FONT):
    #print("HI")

    fSTORY = StoryFont(FONT, "You are currently holding a frying pan and a pencil in your hands.", 320, 200, window)
    fSTORY.create()

    fSTORY2 = StoryFont(FONT, "You are also wearing glasses, and your work clothes.", 320, 220, window)
    fSTORY2.create()

    fBUTTON = Button(FONT, "Back", 190, 330, 260, 25, 320, 345, 2, window)

    def choice3Draw():
        window.fill(BLACK)
        fSTORY.blit()
        fSTORY2.blit()
        fBUTTON.create()

        global isReady2

    choice3Draw()

def choice4(FONT):
    print("in choice 4")

def choice5(FONT):
    print("in choice 5")

while True:
    pos = pygame.mouse.get_pos()
    if starting == 0:
        blitMenu(FONTtitle)
        print(str(starting))

    if starting == 1:
        choice1(FONT)
        print(str(starting))

    if starting == 2:
        choice2(FONT)
        print(str(starting))

    if starting == 2.5:
        choice2Draw(fList, window)
        print(str(starting))

    if starting == 3:
        choice3(FONT)
        print(str(starting))

    if starting == 4:
        choice4(FONT)
        print(str(starting))

    if starting == 5:
        choice5(FONT)
        print(str(starting))



    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    pygame.display.update()

The problem stems from the stuff in choice 2:

def choice2(FONT):

    fSTORY = StoryFont(FONT, "You have just exited the doorway to your classroom.", 320, 170, window)
    fSTORY.create()

    fSTORY2 = StoryFont(FONT, "What would you like to do?", 320, 230, window)
    fSTORY2.create()

    fBUTTON3 = Button(FONT, "1.Check Inventory", 240, 300, 160, 25, 320, 310, 3, window)

    fBUTTON4 = Button(FONT, "2.Look left down the hallway.", 190, 330, 260, 25, 320, 345, 4, window)

    fBUTTON5 = Button(FONT, "3.Look right down the hallway.", 185, 365, 265, 25, 320, 380, 5, window)

    global fList
    fList = [fSTORY, fSTORY2, fBUTTON3, fBUTTON4, fBUTTON5]

    if fBUTTON3.hasBeenClicked == True:
        fBUTTON4.hasBeenClicked = True
        fBUTTON5.hasBeenClicked = True

    if fBUTTON4.hasBeenClicked == True:
        fBUTTON3.hasBeenClicked = True
        fBUTTON5.hasBeenClicked = True

    if fBUTTON5.hasBeenClicked == True:
        fBUTTON3.hasBeenClicked = True
        fBUTTON4.hasBeenClicked = True

    print(str(fBUTTON3.hasBeenClicked) + "button 3")
    print(str(fBUTTON4.hasBeenClicked) + "button 4")
    print(str(fBUTTON5.hasBeenClicked) + "button 5")


    choice2Draw(fList, window)

def choice2Draw(fList, window):
    window.fill(BLACK)
    fList[0].blit()
    fList[1].blit()
    fList[2].create()
    fList[3].create()
    fList[4].create()

    global isReady2

And while debugging, I found this: (solitary numbers are the choice number)

3
3
3
3
3
True inside button class
2
False button 3
False button 4
False button 5
True inside button class
4
in choice 4
4
in choice 4
4
in choice 4
4
in choice 4
4
in choice 4
4
in choice 4

I'm not really sure why it's behaving this way, or why in this IDLE shell the program went back to choice 2 like expected, but then reverted to the second button declaration in choice 2. It's like everything before fBUTTON4 is disabled. Please, try to run the program yourself and see if you encounter the same error. Help is appreciated!

回答1:

I have reviewed your code and I understand what is happening. The main issue is actually happening in the create function of the Button class.

You have some buttons that are is roughly the same position when they get rendered on the screen. An example of this is the continue button in choice1 and the 3.Look right down the hallway in choice2.

When I click the top of the continue button in choice1, the screen goes to choice2 and thinks I am clicking the 3.Look right down the hallway button in choic2. That will immediately take me to choice5.

What we need to do is make sure that a user releases the mouse button in between clicks.

To do this I have added 6 lines to your program. At the top of the file I added a new variable under the isReady2 variable:

isReady2 = False
hasBeenReleased = not pygame.mouse.get_pressed()[0]

That will check if the player has the mouse button held down or it is released when they start the game.

Next, I added code so they have to release the mouse button before they can click again. I am pasting the code that you already have and then pasting my solution.

if self.hasBeenClicked == False:
        if pos[1] >= self.fonty - 17.5 and pos[1] <= self.fonty + 17.5 and pos[0] >= self.fontx - 47.5 and pos[0] <= self.fontx + 47.5:
            pygame.draw.rect(window, BLUE, ((self.x1, self.y1), (self.x2, self.y2)), 0)
            window.blit(self.fSFont, self.fSFontr)

            if pygame.mouse.get_pressed()[0] == True:
                global starting
                self.hasBeenClicked = True
                starting = self.choiceNum
                isReady2 = False
                window.fill(BLACK)
                return starting

Here is my fix:

if self.hasBeenClicked == False:
    if pos[1] >= self.fonty - 17.5 and pos[1] <= self.fonty + 17.5 and pos[0] >= self.fontx - 47.5 and pos[0] <= self.fontx + 47.5:
        pygame.draw.rect(window, BLUE, ((self.x1, self.y1), (self.x2, self.y2)), 0)
        window.blit(self.fSFont, self.fSFontr)

        global hasBeenReleased
        if hasBeenReleased and pygame.mouse.get_pressed()[0] == True:
            hasBeenReleased = False
            global starting
            self.hasBeenClicked = True
            starting = self.choiceNum
            isReady2 = False
            window.fill(BLACK)
            return starting
        elif not hasBeenReleased:
            hasBeenReleased = not pygame.mouse.get_pressed()[0]

The code checks if the button has been released previously. If it has been released then we can check if it is currently being clicked. If it is being clicked then we hasBeenReleased = False to let us know they did actually click.

The section that lets them click again is:

elif not hasBeenReleased:
    hasBeenReleased = not pygame.mouse.get_pressed()[0]

If we check if they recently clicked and then set the value equal to if they have released the button or if they are still holding it down.

Using this code I was able to go back and forth between choice 2 and 3.



标签: python pygame