PyGame 自定义事件

12 投票
4 回答
20621 浏览
提问于 2025-04-18 11:29

我想问问在pygame中使用自定义事件的事情。
我对pygame做了一些实验,所以我知道pygame生成的普通事件是怎么回事。
我的问题是,为什么会有人对用户事件感兴趣,它能帮助简化
组合的pygame事件吗
?还有人们是怎么实现它的,以及在实际例子中有什么好处呢?

我在一本我最近在读的书里找到了一个例子 ..

# 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


我试了一下,发现这个事件只会生成一次 (一发布就生成)
有人能给我解释一下吗 ..?

谢谢 ..

4 个回答

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:
            ...

这段代码是用来做某些操作的,但具体的功能需要根据上下文来理解。代码块通常包含了一些编程指令,可能是用来处理数据、控制程序流程或者实现某种功能。

如果你看到类似的代码,首先要了解它的结构和每一部分的作用。比如,代码可能会有变量、函数、循环等元素。每个元素都有自己的功能,理解这些基本概念会帮助你更好地掌握代码的整体意思。

总之,代码块是程序的核心部分,理解它们是学习编程的重要一步。

1

事件只有在你使用 pygame.event.post(my_event) 的时候才会被发送。
如果你只发送一次事件,那你就只会收到一次。

比如,Player 类可以发送一个事件,内容是 我死了,然后主循环就会结束游戏。

2

你可以用 "==" 来比较事件。要想两个事件被认为是相等的,它们的属性必须完全一样。

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

抛出事件

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

主循环

while True:

    for event in pygame.event.get():

        if event.type == pygame.QUIT:
            break
        if event== Event1:
            print("event1")
        elif event == Event2:
            print("event2")
22

你可以手动使用 pygame.event.post 来发布自定义事件,就像你在例子中展示的那样。

另外,你还可以使用 pygame.time.set_timer 在特定的时间间隔内发布自定义事件。这里有一个我为 另一个问题 写的小例子,展示了如何使用自定义事件来移动物体和控制重载的时间:

在这里输入图片描述

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()

我不能只“给出”合适的条件来发布事件,让它自动生成吗?我觉得这样会更实用……

实现这样的功能其实很简单。只需要创建一个条件和事件的列表,然后在你的主循环中检查每一个条件:

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():
       ...

这是完整的例子:

在这里输入图片描述

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)

撰写回答