0
votes

In a pygame program I am creating, I have need for an interactive object on the screen that will call a function when the player character moves onto it and presses the enter key. Here is the code I have so far:

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
done = False
x = 30
y = 30

clock = pygame.time.Clock()

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

    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP] and y > 0: y -= 5
    if pressed[pygame.K_DOWN] and y < 600 - 60: y += 5
    if pressed[pygame.K_LEFT] and x > 0: x -= 5
    if pressed[pygame.K_RIGHT] and x < 800 - 60: x += 5

    screen.fill((0, 0, 0))
    color = (0, 128, 255)
    pygame.draw.rect(screen, color, pygame.Rect(x, y, 60, 60))

    myfont = pygame.font.SysFont("monospace", 15)

    label = myfont.render("Start the experiment simulator", 1, (255,255,255))
    screen.blit(label, (100, 100))

    label2 = myfont.render("Start the quiz", 1, (255,255,255))
    screen.blit(label2, (550, 100))

    label3 = myfont.render("Quit game", 1, (255,255,255))
    screen.blit(label3, (350, 400))

    pygame.draw.rect(screen, red, pygame.Rect(600, 125, 30, 30))
    pygame.draw.rect(screen, red, pygame.Rect(225, 125, 30, 30))
    pygame.draw.rect(screen, red, pygame.Rect(375, 425, 30, 30))

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

At the moment, the object is just a test so would be best to be a small red rectangle, about half the size of the player, that I can replace with an icon later on. This rectangle should be placed below the 'quit game' label on the pygame window and quit the game when interacted with. This is one method that I have tried so far:

if pressed[pygame.K_RETURN] and x >= 375 or x <= 405 and y >=425 or y <= 455:
            pygame.display.quit()
            pygame.quit()
            sys.exit()

Where theoretically the system checks if the user is in a specific area and has pressed the enter key before performing the command.

3
You should first show what you have tried, and though process about the problem. Though I would recommend that you check the x and y of your character and run it against a list of objects to check if they collide. - Paradizigmania

3 Answers

1
votes

You can check for collision, using pygame's colliderect

First, create three rects that will represent your three option rects :

simulator_rect = pygame.Rect(600, 125, 30, 30)
quiz_rect = pygame.Rect(225, 125, 30, 30)
quit_rect = pygame.Rect(375, 425, 30, 30)

Next, we'll create a rect that will represent the blue selector rect :

selector_rect = pygame.Rect(50, 50, 60, 60)

So now you've got rects that are created only once, instead of unnamed rects that are created every time

Now, for the actual collision detection :

# Check to see if the user presses the enter key
    if pressed[pygame.K_RETURN]:
        # Check to see if the selection rect 
        # collides with any other rect
        for rect in option_rects:
            if selector_rect.colliderect(rect):
                if rect == simulator_rect:
                    # Do simulations stuff!
                    print('Simulating!')
                elif rect == quiz_rect:
                    # Do quizzing stuff!
                    print('Quizzing!')
                elif rect == quit_rect:
                    # Quit!
                    done = True

Final code :

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
done = False
x = 30
y = 30

clock = pygame.time.Clock()

# RGB values for red
red = (255, 0 ,0)

# Your three button rects :
simulator_rect = pygame.Rect(225, 125, 30, 30)
quiz_rect = pygame.Rect(600, 125, 30, 30)
quit_rect = pygame.Rect(375, 425, 30, 30)
# These represent your three option rects
option_rects = [simulator_rect, quiz_rect, quit_rect]

# Your blue selector rect
selector_rect = pygame.Rect(50, 50, 60, 60)
# The 50, 50 xy coords are temporary

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

    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP] and y > 0: y -= 5
    if pressed[pygame.K_DOWN] and y < 600 - 60: y += 5
    if pressed[pygame.K_LEFT] and x > 0: x -= 5
    if pressed[pygame.K_RIGHT] and x < 800 - 60: x += 5

    # Set the slector rect's coords to x/y
    selector_rect.x, selector_rect.y = x, y

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

    color = (0, 128, 255)
    pygame.draw.rect(screen, color, selector_rect)

    myfont = pygame.font.SysFont("monospace", 15)

    label = myfont.render("Start the experiment simulator", 1, (255,255,255))
    screen.blit(label, (100, 100))

    label2 = myfont.render("Start the quiz", 1, (255,255,255))
    screen.blit(label2, (550, 100))

    label3 = myfont.render("Quit game", 1, (255,255,255))
    screen.blit(label3, (350, 400))

    # Use our created rects
    pygame.draw.rect(screen, red, simulator_rect)
    pygame.draw.rect(screen, red, quiz_rect)
    pygame.draw.rect(screen, red, quit_rect)

    # Check to see if the user presses the enter key
    if pressed[pygame.K_RETURN]:
        # Check to see if the selection rect 
        # collides with any other rect
        for rect in option_rects:
        # Add rects as needed
            if selector_rect.colliderect(rect):
                if rect == simulator_rect:
                    # Do simulations stuff!
                    print('Simulating!')
                elif rect == quiz_rect:
                    # Do quizzing stuff!
                    print('Quizzing!')
                elif rect == quit_rect:
                    # Quit!
                    done = True




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

This does add some complication to your program, but at least it's a rock solid method that you can add features too, and that will remain robust.

1
votes

Answer to my own question, I managed to make my first attempt work by adding brackets around the x and y checks in this section:

and x >= 375 or x <= 405 and y >=425 or y <= 455:

So that it now reads:

and (x >= 375 and x <= 405) and (y >= 425 and y <= 455):
0
votes

I just create a system for interactable object and it easy to scale.

listBox = [] # this list used to store all object inside

listBox.append(("text", pos, size, bgColor, textColor, InteractAction)) 
# add dumy object to the list, "text" can be empty if you dont want.

def InteractAction(mousePos):  # sample action used to tie to object
    print("do somehthing")    

def newDrawBox(IableO):
      pygame.draw.rect(gameDisplay, IableO[3],(IableO[1][0], IableO[1][1], IableO[2][0], IableO[2][1]))
      text = basicfont.render(str(IableO[0]), True,IableO[4], None)
      textrect = text.get_rect()
      textrect.centerx = IableO[1][0] + IableO[2][0] / 2
      textrect.centery = IableO[1][1] + IableO[2][1] / 2
      gameDisplay.blit(text, textrect)

while not gameExit:
  for event in pygame.event.get():
        if event.type == pygame.QUIT:
            gameExit = True
        elif event.type == pygame.MOUSEBUTTONDOWN:
            Mouse[event.button] = 1
            Mouse[0] = (event.pos[0], event.pos[1])
        elif event.type == pygame.MOUSEBUTTONUP:
            Mouse[event.button] = 0
            Mouse[0] = (event.pos[0], event.pos[1])
#-------- check if mouse is click on any object in the list of Interatable object
  if Mouse[1] == 1:
        for x in listBox:
            if x[1][0] < Mouse[0][0] < x[1][0] + x[2][0] and x[1][1] < Mouse[0][1] < x[1][1] + x[2][1]:
                x[5](Mouse[0])
#---- draw all object -----
  for x in listBox:
        newDrawBox(x)