Catriona Bennett
Catriona Bennett

Reputation: 155

My button is not doing what I expected it to do

I have created 2 screens and a button to allow you to play again should you either get caught by the monster or win. However, despite the button (on both screens) showing and changing colour when the mouse is hovering over it, the click/link isn't working properly. When on the 'caught' screen, if I click the button several times it will eventually return to the game but at the exact point where you were caught by the monster, rather than go back to the beginning as I intended. And when on the win screen, the click/link isn't working at all. I copied the code from another game where it works fine, you click the button and the game restarts, thus I'm really not sure what the issue it. I'm including all of the code as I think it might help. Thank you in advance!

import pygame
import random
import os

os.environ['SDL_VIDEO_CENTERED'] = '1'
#Initalise pygame
pygame.init()

#Colours required
black = (0,0,0)
blue = (0,0,255)
green = (0,200,0)
red = (255,0,0)
orange = (255,200,0)
bright_green = (0,255,0)

#Set up the display
width = 640
height = 480
screen = pygame.display.set_mode((width, height))
background = pygame.image.load('path.png')
caption = pygame.display.set_caption('Escape from the mazes!')
clock = pygame.time.Clock()

#Class for the player rect
class Player(object):
    def __init__(self, pos):
        self.rect = pygame.Rect(pos[0], pos[1], 32, 32) #x-axis, y-axis, width, height
        self.image = pygame.image.load('player.png')

    def move(self, dx, dy):
        #Move each axis separately. NB this checks for collisions both times
        if dx != 0:
            self.move_single_axis(dx, 0)
        if dy != 0:
            self.move_single_axis(0, dy)

    def move_single_axis(self, dx, dy):
        #Move the rect
        self.rect.x += dx
        self.rect.y += dy

        #If you collide with a wall, move out based on velocity
        for wall in walls:
            if self.rect.colliderect(wall.rect):
                if dx > 0: #Moving right, hit the left side of wall
                    self.rect.right = wall.rect.left
                if dx < 0: #Moving left, hit the right side of wall
                    self.rect.left = wall.rect.right
                if dy > 0: #Moving down, hit the top side of wall
                    self.rect.bottom = wall.rect.top
                if dy < 0: #Moving up, hit the bottom side of wall
                    self.rect.top = wall.rect.bottom

#Class for the monster rect
class Monster(object):
    def __init__(self, pos):
        self.rect = pygame.Rect(pos[0], pos[1], 32, 32)
        self.image = pygame.image.load('monster.png')
        self.dist = 3
        self.direction = random.randint(0, 3)
        self.steps = random.randint(3, 9) * 32

    def move(self):
        direction_list = ((-1,0), (1,0), (0,-1), (0,1))
        dx, dy = direction_list[self.direction]
        self.rect.x += dx
        self.rect.y += dy

        collide = False
        for wall in walls:
            if self.rect.colliderect(wall.rect):
                collide = True
                if dx > 0:
                    self.rect.right = wall.rect.left
                if dx < 0:
                    self.rect.left = wall.rect.right
                if dy > 0:
                    self.rect.bottom = wall.rect.top
                if dy < 0:
                    self.rect.top = wall.rect.bottom

        self.steps -= 1
        if collide or self.steps == 0:
            #New random direction
            self.direction = random.randint(0, 3)
            self.steps = random.randint(3, 9) * 32

#Class for the wall rect
class Wall(object):
    def __init__(self, pos):
        self.rect = pygame.Rect(pos[0], pos[1], 32, 32)
        self.image = pygame.image.load('hedge.png')

#Class for end rect
class Finish(object):
    def __init__(self, pos):
        self.rect = pygame.Rect(pos[0], pos[1], 32, 32)
        self.image = pygame.image.load('gate.png')

#Variables
currentLevel = 0

#Holds the level layout in a list of strings
levels = [[
'WWWWWWWWWWWWWWWWWWWW',
'WP          W      W',
'W         WWWWWW   W',
'W   WWWW       W   W',
'W   W        WWWW  W',
'W WWW  WWWW        W',
'W   W    MW W      W',
'W   W     W   WWW WW',
'W   WWW WWW   W W  W',
'W     W   W   W W  W',
'WWW   W   WWWWW W  W',
'W W      WW        W',
'W W   WWWW   WWWWWWW',
'W     W           FW',
'WWWWWWWWWWWWWWWWWWWW',
],
[
'WWWWWWWWWWWWWWWWWWWW',
'W W     W     W   FW',
'W W     W     W    W',
'W W  W  W  W  W  WWW',
'W W  W  W  W  W    W',
'W W  W  W  W  W    W',
'W W  W  W  W  W  W W',
'W    W  M  W  W  W W',
'W    W     W  W  WWW',
'WWW  WWWWWWW  W    W',
'W      W      W    W',
'W      W  WWWWWWW  W',
'WWWWW  W           W',
'WP     W           W',
'WWWWWWWWWWWWWWWWWWWW',
],
[
'WWWWWWWWWWWWWWWWWWWW',
'WP W           W   W',
'W  W  WWWW  WWWW W W',
'W  W     W       W W',
'W  WWWW  W    WWWW W',
'W     W  W  WW     W',
'W     W  W  W  WWWWW',
'WWWW  W  W MW     FW',
'W  W  W  W  WWWWWWWW',
'W  W     W         W',
'W  W     W         W',
'W  W  WWWWWWWWWWW  W',
'W               W  W',
'W               W  W',
'WWWWWWWWWWWWWWWWWWWW',
],
[
'WWWWWWWWWWWWWWWWWWWW',
'W     W            W',
'W     W   M        W',
'W  W  W  WWWWWWWW  W',
'W  W  W  W         W',
'W  W  W  W         W',
'W  W  W  W  WWWWWWWW',
'WP W  W  W         W',
'WWWW  W  W         W',
'W     W  WWWWWWWW  W',
'W     W  W         W',
'W  WWWW  W         W',
'W        W  WWWWWWWW',
'W        W        FW',
'WWWWWWWWWWWWWWWWWWWW',
],
[
'WWWWWWWWWWWWWWWWWWWW',
'W  W        W     FW',
'W  W        WWW W  W',
'W  W  WWWW    W WWWW',
'W  W     W    W    W',
'W  WWWW  WWWW W  W W',
'W    MW     W WWWW W',
'W     W     W      W',
'W  W  WWWW  WWWWWWWW',
'W  W     W         W',
'W  W     W         W',
'WWWW  W  WWWWWWWW  W',
'W     W            W',
'WP    W            W',
'WWWWWWWWWWWWWWWWWWWW',
],
[
'WWWWWWWWWWWWWWWWWWWW',
'WP     W     W   W W',
'WWWWW  W     W W W W',
'W  W  W  WWWWWWW W W',
'W W  W M W         W',
'WW  W  W W WWWWW   W',
'W  W   W W     W WWW',
'W  WWWWW WWWWW W WFW',
'W      W       W W W',
'WWWWWW W WWWWWWW W W',
'W      W W       W W',
'W W  WWW W       W W',
'W W      W WWWWWWW W',
'W W      W         W',
'WWWWWWWWWWWWWWWWWWWW',
]]

def load_level(level):
    walls = []
    players = []
    monsters = []
    finishes = []

    #Parse the level string above. W = wall, F = exit, P = player, M = monster
    x = y = 0
    for row in levels[level]:
        for col in row:
            if col == 'W':
                walls.append(Wall((x, y)))
            if col == 'P':
                players.append(Player((x, y)))
            if col == 'M':
                monsters.append(Monster((x, y)))
            if col == 'F':
                finishes.append(Finish((x, y)))
            x += 32
        y += 32
        x = 0
    return walls, players, monsters, finishes

walls, players, monsters, finishes = load_level(currentLevel)
Highest_level = len(levels)-1 #index of last level

def button(msg,x,y,w,h,ic,ac,action=None):
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    if x+w > mouse[0] > x and y+h > mouse[1] > y:
        pygame.draw.rect(screen, ac, (x,y,w,h))
        if click[0] == 1 and action != None:
            action()
    else:
        pygame.draw.rect(screen, ic, (x,y,w,h))

    smallText = pygame.font.Font("freesansbold.ttf", 20)
    textSurf, textRect = text_objects(msg, smallText)
    textRect.center = ((x+(w/2)), (y+(h/2)))
    screen.blit(textSurf, textRect)

def text_objects(text, font):
    textSurface = font.render(text, True, black)
    return textSurface, textSurface.get_rect()

def win():
    screen.fill(blue)
    largeText = pygame.font.Font('freesansbold.ttf', 90)
    TextSurf, TextRect = text_objects('You Escaped!', largeText)
    TextRect.center = (int((width/2)), int((height/2)))
    screen.blit(TextSurf, TextRect)

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        button('Play Again',280,350,110,50,green,bright_green,main)

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

def caught():
    screen.fill(red)
    largeText = pygame.font.Font('freesansbold.ttf', 75)
    TextSurf, TextRect = text_objects('You Got Caught!', largeText)
    TextRect.center = (int((width/2)), int((height/2)))
    screen.blit(TextSurf, TextRect)

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        button('Try Again',280,350,100,50,green,bright_green,main)

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

def main():
    global walls, players, monsters, currentLevel, finishes
    running = True
    while running:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                pygame.quit()
                quit()

        #Move the player if an arrow key is pressed
        key = pygame.key.get_pressed()
        if key[pygame.K_LEFT]:
            player.move(-2, 0)
        if key[pygame.K_RIGHT]:
            player.move(2, 0)
        if key[pygame.K_UP]:
            player.move(0, -2)
        if key[pygame.K_DOWN]:
            player.move(0, 2)

        #Move monster
        for monster in monsters:
            monster.move()

        #Moving to next level/win
        for player in players:
            for finish in finishes:
                if player.rect.colliderect(finish.rect):
                    if currentLevel < Highest_level:
                        currentLevel +=1
                        walls, players, monsters, finishes = load_level(currentLevel)
                    else:
                        win()

        #Getting caught by the monster
        for player in players:
            for monster in monsters:
                if player.rect.colliderect(monster.rect):
                    caught()

        #Draw the scene
        screen.fill(blue)
        screen.blit(background, (0,0))
        for wall in walls:
            #pygame.draw.rect(screen, green, wall.rect)
            screen.blit(wall.image, wall.rect)
        for player in players:
            #pygame.draw.rect(screen, orange, player.rect)
            screen.blit(player.image, player.rect)
        for monster in monsters:
            #pygame.draw.rect(screen, bright_green, monster.rect)
            screen.blit(monster.image, monster.rect)
        for finish in finishes:
            #pygame.draw.rect(screen, red, finish.rect)
            screen.blit(finish.image, finish.rect)
        pygame.display.update()

main()
pygame.quit()
quit()

Upvotes: 2

Views: 66

Answers (1)

Rabbid76
Rabbid76

Reputation: 211277

Sorry, but that does not work like that. The action of the button can not be the main application loop (function main).
What you actually do is to execute the main application loop. If the player is caught, then the function caught is called.
In caught is another loop, which is executed endless. If the button is pressed, then the main is called. At this point you've the following situation:

main
  |
  +--caught
       |
       +--main

Since the game state has not changed, the player is caught immediately in the inner (main) application loop and caught is called again:

main
  |
  +--caught
       |
       +--main
            |
            +-caught

That continues infinitely.


I'll show you haw to fix this for caught.

Create an function, which resets the current game state. The function resets the current level to its begin:

def load_current_level():
    global walls, players, monsters, finishes
    walls, players, monsters, finishes = load_level(currentLevel)

Change the function button in that way, that it returns True when the button was clicked and else False:

def button(msg,x,y,w,h,ic,ac,action=None):

    clicked = False
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    if x+w > mouse[0] > x and y+h > mouse[1] > y:
        pygame.draw.rect(screen, ac, (x,y,w,h))
        if click[0] == 1 and action != None:
            action()
            clicked = True
    else:
        pygame.draw.rect(screen, ic, (x,y,w,h))

    smallText = pygame.font.Font("freesansbold.ttf", 20)
    textSurf, textRect = text_objects(msg, smallText)
    textRect.center = ((x+(w/2)), (y+(h/2)))
    screen.blit(textSurf, textRect)

    return clicked

Terminate the loop in caught, when the button was pressed. The action which is associated to the button is load_current_level:

def caught():
    screen.fill(red)
    largeText = pygame.font.Font('freesansbold.ttf', 75)
    TextSurf, TextRect = text_objects('You Got Caught!', largeText)
    TextRect.center = (int((width/2)), int((height/2)))
    screen.blit(TextSurf, TextRect)

    try_again = False
    while not try_again:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        try_again = button('Try Again',280,350,100,50,green,bright_green,load_current_level)

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

When the button is pressed, then the level is reset and the loop in caught is terminated. caught returns to the main application loop and you can retry the level.


Do something similar in win:

def load_first_level():
    global walls, players, monsters, finishes, currentLevel
    currentLevel = 0
    walls, players, monsters, finishes = load_level(currentLevel)
def win():
    screen.fill(blue)
    largeText = pygame.font.Font('freesansbold.ttf', 90)
    TextSurf, TextRect = text_objects('You Escaped!', largeText)
    TextRect.center = (int((width/2)), int((height/2)))
    screen.blit(TextSurf, TextRect)

    play_again = False
    while not play_again:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()

        play_again = button('Play Again',280,350,110,50,green,bright_green,load_first_level)

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

Upvotes: 1

Related Questions