12

I want to ask about the use of custom events in pygame ..
I experimented a little with pygame, so I know how usual pygame-generated events works ..
My Question is, why would anyone be interested in a user event, does it help simplify
combined pygame-events
? And how would someone implement it, and benefit from
it
in a real world example .. ?

I found an example in a book I have been reading lately ..

# creating the event
CATONKEYBOARD = USEREVENT+1
my_event = pygame.event.Event(CATONKEYBOARD, message="Bad cat!")
pygame.event.post(my_event)

# handling it
for event in pygame.event.get():
    if event.type == CATONKEYBOARD:
        print event.message


I tried it, and I found that the event generates only one time (as soon as it gets posted)
Can anybody explain things to me .. ?

Thanks ..

Amr Ayman
  • 1,129
  • 1
  • 8
  • 24

4 Answers4

22

You can post custom events either manually with pygame.event.post, as shown in your example.

Also, you can use pygame.time.set_timer to post a custom event at specific time intervalls. Here's a little example I wrote for another question, where custom events are used to move objects and to control a reload timeout:

enter image description here

import pygame

# you'll be able to shoot every 450ms
RELOAD_SPEED = 450

# the foes move every 1000ms sideways and every 3500ms down
MOVE_SIDE = 1000
MOVE_DOWN = 3500

screen = pygame.display.set_mode((300, 200))
clock = pygame.time.Clock()

pygame.display.set_caption("Micro Invader")

# create a bunch of events 
move_side_event = pygame.USEREVENT + 1
move_down_event = pygame.USEREVENT + 2
reloaded_event  = pygame.USEREVENT + 3

move_left, reloaded = True, True

invaders, colors, shots = [], [] ,[]
for x in range(15, 300, 15):
    for y in range(10, 100, 15):
        invaders.append(pygame.Rect(x, y, 7, 7))
        colors.append(((x * 0.7) % 256, (y * 2.4) % 256))

# set timer for the movement events
pygame.time.set_timer(move_side_event, MOVE_SIDE)
pygame.time.set_timer(move_down_event, MOVE_DOWN)

player = pygame.Rect(150, 180, 10, 7)

while True:
    clock.tick(40)
    if pygame.event.get(pygame.QUIT): break
    for e in pygame.event.get():
        if e.type == move_side_event:
            for invader in invaders:
                invader.move_ip((-10 if move_left else 10, 0))
            move_left = not move_left
        elif e.type == move_down_event:
            for invader in invaders:
                invader.move_ip(0, 10)
        elif e.type == reloaded_event:
            # when the reload timer runs out, reset it
            reloaded = True
            pygame.time.set_timer(reloaded_event, 0)

    for shot in shots[:]:
        shot.move_ip((0, -4))
        if not screen.get_rect().contains(shot):
            shots.remove(shot)
        else:
            hit = False
            for invader in invaders[:]:
                if invader.colliderect(shot):
                    hit = True
                    i = invaders.index(invader)
                    del colors[i]
                    del invaders[i]
            if hit:
                shots.remove(shot)

    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_LEFT]: player.move_ip((-4, 0))
    if pressed[pygame.K_RIGHT]: player.move_ip((4, 0))

    if pressed[pygame.K_SPACE]: 
        if reloaded:
            shots.append(player.copy())
            reloaded = False
            # when shooting, create a timeout of RELOAD_SPEED
            pygame.time.set_timer(reloaded_event, RELOAD_SPEED)

    player.clamp_ip(screen.get_rect())

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

    for invader, (a, b) in zip(invaders, colors): 
        pygame.draw.rect(screen, (150, a, b), invader)

    for shot in shots: 
        pygame.draw.rect(screen, (255, 180, 0), shot)

    pygame.draw.rect(screen, (180, 180, 180), player)    
    pygame.display.flip()

Can't I just "give in" the suitable conditions for posting the event, so that it can generate it automatically then ? I think that way it'd be more practical ...

Implementing such a function is quite easy. Just create a list of conditions and events and check every condition in your main loop:

conditions = [ # blink if player is outside screen
              (lambda: not s_r.contains(player), pygame.event.Event(E_OUTSIDE)),
               # if mouse if over player then grow and shrink player  
              (lambda: player.collidepoint(pygame.mouse.get_pos()), pygame.event.Event(E_MOUSE))]

while True:
    # generate events from conditions
    map(pygame.event.post, [e for (c, e) in conditions if c()])

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

Here's the full example:

enter image description here

import pygame

pygame.init() 

screen = pygame.display.set_mode((300, 300)) 
s_r = screen.get_rect()
player = pygame.Rect((100, 100, 50, 50))
timer = pygame.time.Clock()
flash = 0
grow = True
color = pygame.color.Color('Black')

E_OUTSIDE = pygame.USEREVENT  + 1
E_MOUSE   = pygame.USEREVENT  + 2

conditions = [ # blink if player is outside screen
              (lambda: not s_r.contains(player), pygame.event.Event(E_OUTSIDE)),
               # if mouse if over player then grow and shrink player  
              (lambda: player.collidepoint(pygame.mouse.get_pos()), pygame.event.Event(E_MOUSE))]

while True:
    # generate events from conditions
    map(pygame.event.post, [e for (c, e) in conditions if c()])

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            raise
        elif event.type == E_OUTSIDE and not flash:
            flash = 5
        elif event.type == E_MOUSE:
            if grow: 
                player.inflate_ip(4, 4)
                grow = player.width < 75
            else: 
                player.inflate_ip(-4, -4)
                grow = player.width < 50

    flash = max(flash - 1, 0)
    if flash % 2:
        color = pygame.color.Color('White')                

    pressed = pygame.key.get_pressed()
    l, r, u, d = map(lambda x: x*4, [pressed[k] for k in pygame.K_a, pygame.K_d, pygame.K_w, pygame.K_s])
    player.move_ip((-l + r, -u + d))

    screen.fill(color)
    color = pygame.color.Color('Black')

    pygame.draw.rect(screen, pygame.color.Color('Grey'), player)

    pygame.display.flip()
    timer.tick(25)
Community
  • 1
  • 1
sloth
  • 99,095
  • 21
  • 171
  • 219
2

You can compare events with "==". The compared events must be exactly the same with their attirubutes to be equal

Event1=pygame.event.Event(pygame.USEREVENT, attr1='Event1')
Event2=pygame.event.Event(pygame.USEREVENT, attr1='Event2')

Throw events

pygame.event.post(Event1)
pygame.event.post(Event2)

Main loop

while True:

    for event in pygame.event.get():

        if event.type == pygame.QUIT:
            break
        if event== Event1:
            print("event1")
        elif event == Event2:
            print("event2")
samet kaya
  • 105
  • 3
1

Event is send only when you use pgame.event.post(my_event)
If you post it only once then you get it only once.

Class Player could post event I'm dead and mainloop would end the game.

furas
  • 134,197
  • 12
  • 106
  • 148
  • I think that explains why the message gets printed when the game is launched, so does that mean that user events are posted manually every time I see circumstances appropriate ? Can't I just "give in" the suitable conditions for posting the event, so that it can generate it automatically then ? I think that way it'd be more practical ... – Amr Ayman Jun 29 '14 at 14:49
  • You will have to manually create code which will post events automatically :) PyGame is `"low-level"` modul/library/framework - you even have to create mainloop on your own. Other GUIs have built-in mainloop. – furas Jun 29 '14 at 15:13
0
MY_EVENT_ID = 1
pg.event.post(pg.event.Event(pg.USEREVENT, user_event=MY_EVENT_ID)
...
for event in pg.event.get():
    if event.type = pg.QUIT:
        pg.quit()
    if event.type = pg.USEREVENT:
        if event.user_type = MY_EVENT_ID:
            ...
Anikei
  • 1
  • 2
  • 2
    Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Mark Rotteveel May 04 '22 at 10:21
  • I speak Russian, it's hard for me, sorry – Anikei Jul 04 '22 at 19:41