Pygame鼠标点击检测

50
我想知道如何编写代码来检测鼠标点击精灵的操作。例如:
if #Function that checks for mouse clicked on Sprite:
    print ("You have opened a chest!")
4个回答

66
我假设你的游戏有一个主循环,并且所有的精灵都在一个名为“sprites”的列表中。
在你的主循环中,获取所有事件,并检查是否有“MOUSEBUTTONDOWN”或“MOUSEBUTTONUP”事件。
while ... # your main loop
  # get all events
  ev = pygame.event.get()

  # proceed events
  for event in ev:

    # handle MOUSEBUTTONUP
    if event.type == pygame.MOUSEBUTTONUP:
      pos = pygame.mouse.get_pos()

      # get a list of all sprites that are under the mouse cursor
      clicked_sprites = [s for s in sprites if s.rect.collidepoint(pos)]
      # do something with the clicked sprites...

基本上,你需要在每次主循环迭代中自己检查精灵的点击。你需要使用mouse.get_pos()rect.collidepoint()
Pygame不提供事件驱动编程,例如cocos2d
另一种方法是检查鼠标光标的位置和按下按钮的状态,但这种方法存在一些问题。
if pygame.mouse.get_pressed()[0] and mysprite.rect.collidepoint(pygame.mouse.get_pos()):
  print ("You have opened a chest!")

如果您处理了这种情况,您需要引入某种标志,否则该代码将在主循环的每次迭代中打印“您已打开一个宝箱!”。
handled = False

while ... // your loop

  if pygame.mouse.get_pressed()[0] and mysprite.rect.collidepoint(pygame.mouse.get_pos()) and not handled:
    print ("You have opened a chest!")
    handled = pygame.mouse.get_pressed()[0]

当然,您可以继承 Sprite 并添加一个名为 is_clicked 的方法,如下所示:
class MySprite(Sprite):
  ...

  def is_clicked(self):
    return pygame.mouse.get_pressed()[0] and self.rect.collidepoint(pygame.mouse.get_pos())

因此,在我看来,最好使用第一种方法。

8
请注意,鼠标的位置也可以在事件本身的event.pos下获得。 - Nicu Surdu
1
我建议从event.pos获取鼠标位置,而不是mouse.get_pos。一些操作系统允许鼠标在实际点击和事件被应用程序处理之间稍微移动。此外,单元测试中的点击可能不是来自鼠标。 - Героям слава

20
MOUSEBUTTONDOWN事件在单击鼠标按钮时发生,MOUSEBUTTONUP事件在释放鼠标按钮时发生。pygame.event.Event()对象具有两个属性,提供关于鼠标事件的信息。 pos是一个存储被单击位置的元组。 button存储被单击的按钮。每个鼠标按钮都与一个值相关联。例如,左鼠标按钮、中间鼠标按钮、右鼠标按钮、鼠标滚轮向上和向下的属性值分别为1、2、3、4、5。当多个键被按下时,会发生多个鼠标按钮事件。更多解释可以在模块pygame.event的文档中找到。
使用pygame.sprite.Sprite对象的rect属性和collidepoint方法来查看是否单击了Sprite。 将事件列表传递给pygame.sprite.Groupupdate方法,以便您可以在Sprite类中处理事件。
class SpriteObject(pygame.sprite.Sprite):
    # [...]

    def update(self, event_list):

        for event in event_list:
            if event.type == pygame.MOUSEBUTTONDOWN:
                if self.rect.collidepoint(event.pos):
                    # [...]

my_sprite = SpriteObject()
group = pygame.sprite.Group(my_sprite)

# [...]

run = True
while run:
    event_list = pygame.event.get()
    for event in event_list:
        if event.type == pygame.QUIT:
            run = False 

    group.update(event_list)

    # [...]

最简单的例子: repl.it/@Rabbid76/PyGame-MouseClick

import pygame

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y, color):
        super().__init__() 
        self.original_image = pygame.Surface((50, 50), pygame.SRCALPHA)
        pygame.draw.circle(self.original_image, color, (25, 25), 25)
        self.click_image = pygame.Surface((50, 50), pygame.SRCALPHA)
        pygame.draw.circle(self.click_image, color, (25, 25), 25)
        pygame.draw.circle(self.click_image, (255, 255, 255), (25, 25), 25, 4)
        self.image = self.original_image 
        self.rect = self.image.get_rect(center = (x, y))
        self.clicked = False

    def update(self, event_list):
        for event in event_list:
            if event.type == pygame.MOUSEBUTTONDOWN:
                if self.rect.collidepoint(event.pos):
                    self.clicked = not self.clicked

        self.image = self.click_image if self.clicked else self.original_image

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

sprite_object = SpriteObject(*window.get_rect().center, (128, 128, 0))
group = pygame.sprite.Group([
    SpriteObject(window.get_width() // 3, window.get_height() // 3, (128, 0, 0)),
    SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, (0, 128, 0)),
    SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, (0, 0, 128)),
    SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, (128, 128, 0)),
])

run = True
while run:
    clock.tick(60)
    event_list = pygame.event.get()
    for event in event_list:
        if event.type == pygame.QUIT:
            run = False 

    group.update(event_list)

    window.fill(0)
    group.draw(window)
    pygame.display.flip()

pygame.quit()
exit()

进一步了解:在Pygame中使用同一个精灵类创建具有不同update()的多个精灵


当前鼠标位置可以通过pygame.mouse.get_pos()确定。返回值是一个元组,表示鼠标光标的x和y坐标。pygame.mouse.get_pressed()返回一个布尔值列表,表示所有鼠标按钮的状态(TrueFalse)。只要按下按钮,按钮的状态就是True。当多个按钮被按下时,列表中的多个项是True。列表中的第1、2和3个元素分别表示左、中和右鼠标按钮。
pygame.sprite.Sprite对象的Update方法中检测和评估鼠标状态:
class SpriteObject(pygame.sprite.Sprite):
    # [...]

    def update(self, event_list):

        mouse_pos = pygame.mouse.get_pos()
        mouse_buttons = pygame.mouse.get_pressed()

        if  self.rect.collidepoint(mouse_pos) and any(mouse_buttons):
            # [...]

my_sprite = SpriteObject()
group = pygame.sprite.Group(my_sprite)

# [...]

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

    group.update(event_list)

    # [...]

最简示例: repl.it/@Rabbid76/PyGame-MouseHover

涉及IT技术,建议将“示例”和“鼠标悬停”等词汇翻译成对应的IT术语。

import pygame

class SpriteObject(pygame.sprite.Sprite):
    def __init__(self, x, y, color):
        super().__init__() 
        self.original_image = pygame.Surface((50, 50), pygame.SRCALPHA)
        pygame.draw.circle(self.original_image, color, (25, 25), 25)
        self.hover_image = pygame.Surface((50, 50), pygame.SRCALPHA)
        pygame.draw.circle(self.hover_image, color, (25, 25), 25)
        pygame.draw.circle(self.hover_image, (255, 255, 255), (25, 25), 25, 4)
        self.image = self.original_image 
        self.rect = self.image.get_rect(center = (x, y))
        self.hover = False

    def update(self):
        mouse_pos = pygame.mouse.get_pos()
        mouse_buttons = pygame.mouse.get_pressed()

        #self.hover = self.rect.collidepoint(mouse_pos)
        self.hover = self.rect.collidepoint(mouse_pos) and any(mouse_buttons)

        self.image = self.hover_image if self.hover else self.original_image

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

sprite_object = SpriteObject(*window.get_rect().center, (128, 128, 0))
group = pygame.sprite.Group([
    SpriteObject(window.get_width() // 3, window.get_height() // 3, (128, 0, 0)),
    SpriteObject(window.get_width() * 2 // 3, window.get_height() // 3, (0, 128, 0)),
    SpriteObject(window.get_width() // 3, window.get_height() * 2 // 3, (0, 0, 128)),
    SpriteObject(window.get_width() * 2// 3, window.get_height() * 2 // 3, (128, 128, 0)),
])

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False 

    group.update()

    window.fill(0)
    group.draw(window)
    pygame.display.flip()

pygame.quit()
exit()

10

pygame的鼠标事件文档在这里

你可以使用pygame.mouse.get_pressed方法,与pygame.mouse.get_pos方法一起协作使用(如果需要)。

记得通过主事件循环使用鼠标单击事件。事件循环更好的原因是“短点击”。您可能在普通计算机上注意不到这一点,但是使用触摸板进行轻敲点击的计算机存在过于短的点击时间。使用鼠标事件将防止出现此类问题。

编辑: 要执行像素完美的碰撞,请使用精灵文档中找到的pygame.sprite.collide_rect()方法。


我该如何将其应用于点击精灵上呢? - user1406948
@EliasBenevedes 我在我的答案中加入了像素级别的碰撞检测。 - jakebird451

6

我也在寻找同样的答案,经过反复琢磨,我得出了以下答案:

# Python 3.4.3 with Pygame
from sys import exit
import pygame
pygame.init()

WIDTH = HEIGHT = 300
window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Crash!')

# Draw Once
rectangle = pygame.draw.rect(window, (255, 0, 0), (100, 100, 100, 100))
pygame.display.update()

# Main Loop
while True:
    # Mouse position and button clicking
    pos = pygame.mouse.get_pos()
    pressed1 = pygame.mouse.get_pressed()[0]

    # Check if rectangle collided with pos and if the left mouse button was pressed
    if rectangle.collidepoint(pos) and pressed1:
        print("You have opened a chest!")

    # Quit pygame
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接