Arnar Freyr
Arnar Freyr

Reputation: 373

Can't click on image again, what's wrong with my pygame code?

Okay, I'am trying to create a Tom and Jerry game with the pygame library.

The game focuses on catching mice by clicking on them as they appear in their holes. The problem is that sometimes a cat appears instead of a mouse and should the player erroneously click on the cat (s)he looses all earned points, but the game continues. The mouse is an image of a mouse and the cat is an image of an cat. If you click on the mouse, you get mouse, otherwise the cat gets the points. The code is a mess, that's because I don't know what I'am doing and just set an another event loop because then it works, because it runs after I create the mouse. It works to click on the mouse but then you click somewhere else and after that it's like you did not clicked on the mouse.

The mouse is created in a loop and is supposed to wait for 5 seconds and if you click on the mouse within these seconds then an appropriate message prints out in the console ,,Jerry clicked!" else "1 click". If you don't click on the mouse within 5 seconds a image covers the mouse so she disappears.

Now, what I'am trying to do right now is to print the message 1 click when the player does not click on anything but print 1 click jerry clicked when the player clicks on the mouse. I have a image of the mousehole and then I put the mouse on the mousehole, that is, on an another image.

This code works with one image at least:

pygame.init()
width=350;
height=400
screen = pygame.display.set_mode( (width, height ) )
pygame.display.set_caption('clicked on image')
redSquare = pygame.image.load("images/red-square.png").convert()

x = 20; # x coordnate of image
y = 30; # y coordinate of image
screen.blit(redSquare ,  ( x,y)) # paint to screen
pygame.display.flip() # paint screen one time

running = True
while (running):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            # Set the x, y postions of the mouse click
            x, y = event.pos
            if redSquare.get_rect().collidepoint(x, y):
                print('clicked on image')
#loop over, quite pygame
pygame.quit()

My problem is that, when I click on the mouse and then I don't click on the mouse I can't click on the mouse again at another position.

So what's wrong? What I'am doing wrong here?

Here is my code:

import pygame
from pygame import *
from random import *

init()

run = True
screen = (800,800)
screen = display.set_mode(screen)
xpos = 0
ypos = 0
mouseorcatxpos = 5
mouseorcatypos = 0

mousehole = image.load("mousehole.png").convert()

cat = image.load("tom.png")
jerry = image.load("jerry.png")

def makeholes():
    global ypos
    global xpos

    for holey in range(1,9):

        for holex in range(1,9):
            screen.blit(mousehole,(xpos,ypos))
            display.flip()

            xpos += 100

        ypos += 100
        xpos = 0

def mouseorcat():
    global xpos
    mouseorcatxpos = 5
    ypos = 0

    for mousecaty in range(1,9):

            pygame.event.pump()

            for mousecatx in range(1,9):

                randommouse = randint(1, 3)
                randomcat = randint(1, 10)

                if(randommouse == 2):

                    screen.blit(jerry, (mouseorcatxpos, ypos))
                    display.flip()

                    for event in pygame.event.get():

                        if (event.type == MOUSEBUTTONDOWN):

                            if jerry.get_rect().collidepoint(xpos, ypos) == False:

                               print("l clicked!")

                            x, y = event.pos

                            if jerry.get_rect().collidepoint(xpos, y):
                                print("JERRY CLICKED!!")
                                x, y = event.pos
                                print(x, y)


                    time.wait(5000)
                    #screen.blit(mousehole, (mouseorcatxpos - 5, ypos))
                    display.flip()

                elif(randomcat == 2):
                    screen.blit(cat, (mouseorcatxpos, ypos))
                    display.flip()
                    time.wait(1500)
                    screen.blit(mousehole, (mouseorcatxpos-5, ypos))
                    display.flip()

                mouseorcatxpos += 100
            mouseorcatxpos = 0
            ypos += 100


makeholes()


while run == True:


    for event in pygame.event.get():
        mouseorcat()

        if event.type == QUIT:
            run = False

Upvotes: 0

Views: 232

Answers (1)

skrx
skrx

Reputation: 20438

I rewrote your game to show you how I would do it.

To keep track of the time and to limit the framerate I used a pygame.time.Clock and a timer variable. The clock returns the time in milliseconds since clock.tick was called the last time, which is used to increase the timer variable. The cat just replaces the mouse after two seconds and the mouse is set to a new position. I use pygame.Rects to store the positions, but you could also use lists or tuples.

import sys
import random
import pygame


pygame.init()

size = (800, 800)
screen = pygame.display.set_mode(size)

# Images replaced by pygame.Surface. Do that too
# in the future before you post your code.
mousehole = pygame.Surface((40, 40)).convert()
mousehole.fill(pygame.Color(30, 30, 30))
cat = pygame.Surface((40, 40)).convert()
cat.fill(pygame.Color(110, 110, 130))
jerry = pygame.Surface((40, 40)).convert()
jerry.fill(pygame.Color(190, 130, 0))
# Create the background image and blit the holes.
background = pygame.Surface(size).convert()
for holey in range(8):
    for holex in range(8):
        background.blit(mousehole, (holex*100, holey*100))


def new_position():
    """Return a random position between 0-700 in steps of 100."""
    return (random.randrange(0, 701, 100), random.randrange(0, 701, 100))


def main():
    fps = 30
    clock = pygame.time.Clock()

    jerry_rect = jerry.get_rect()  # Stores jerry's position and size.
    jerry_rect.topleft = new_position() # New random position.
    # The cat is outside of the screen first.
    cat_rect = cat.get_rect(topleft=(-100, -100))
    points = 0
    timer = 0

    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                if jerry_rect.collidepoint(event.pos):
                    points += 1
                    print('Jerry caught! Points:', points)
                    timer = 0
                    jerry_rect.topleft = new_position()
                else:
                    print('Missed. Points:', points)

        # Run logic.
        timer += clock.tick(fps) / 1000  # timer + seconds since last tick.
        if timer > 2:  # Cat catches mouse after 2 seconds.
            cat_rect.topleft = jerry_rect.topleft
            jerry_rect.topleft = new_position()
            timer = 0
            points = 0
            print('Tom caught Jerry.')
        # Draw.
        # Clear the screen by blitting the bg.
        screen.blit(background, (0, 0))  
        screen.blit(jerry, jerry_rect)
        screen.blit(cat, cat_rect)
        pygame.display.flip()

if __name__ == '__main__':
    main()
    pygame.quit()
    sys.exit()

Side notes:

Don't use star imports (from module import *), because that can make code harder to read. If you want you can use from pygame.locals import *, if it's the only star import.

Don't use global variables, because they can make code harder to read, understand and maintain. Pass variables to functions as arguments and then return the result.


Update: Some notes about your program:

The first big problem is that your game has two event loops and the important one is deeply nested inside of two other for loops and a if. The event loop should be directly under the main while loop (one indentation level (when you have more experience you can put it into a function or class method)).


The two for loops seem to have the purpose to let the code run until randommouse or randomcat are 2. To run code until a condition is met is the purpose of a while loop. But in this case you should better just pick a random number and write the if/elif conditions so that they always apply. For example, you want a 2/3 chance for mouse and 1/3 for a cat,

random_number = random.randint(1, 3)
if random_number < 3:
    print("2/3 probability. It's a mouse")
else:
    print("1/3 probability. It's a cat")

Or use random.choice with a list:

>>> random.choice(['mouse', 'mouse', 'cat'])
'mouse'

time.wait(5000) shouldn't be used because the game just hangs in this time. You can't even close the window. Limit the framerate and get the time since the last tick with a pygame.time.Clock.


pygame.event.pump() is not needed.


If you call get_rect() without an argument, the rect is positioned at (0, 0).

if jerry.get_rect().collidepoint(xpos, y):

That's the reason why clicking on jerry only works in the top row, and because you use the global xpos here. Since xpos is 0, the whole top row counts as Jerry.

You can pass coordinates to get_rect like so (you can also use center or other args instead of topleft):

jerry_rect = jerry.get_rect(topleft=(50, 100))

I'm sorry but I don't think I can simply fix your code. I've tried it several times, but I always end up re-writing it completely.

I begin by extracting the event loop out of the two nested for loops, then remove these loops, create rects for the mouse and cat, fix the collision detection, add a timer and so on. Take a close look at my example and try to rewrite your game in a similar way, and keep asking questions if you don't understand something.

Upvotes: 1

Related Questions