복붙노트

[PYTHON] 파이 게임 버튼 한 번 클릭

PYTHON

파이 게임 버튼 한 번 클릭

클릭 이벤트에 대한 파이 게임에서 버튼을 만들었지 만 문제가 있습니다. 마우스 버튼을 클릭하고 버튼 경계 사이에서 마우스를 움직이면 클릭 이벤트가 반복됩니다. 마우스 버튼을 놓을 때까지 한 번의 클릭 만하면됩니다. 어떻게해야합니까?

import pygame,time
pygame.init()
x,y = (200,300)
pencere = pygame.display.set_mode((x,y))
pygame.display.set_caption("Click")

white = (255,255,255)
black = (0,0,0)
black2 = (30,30,30)

class Counter:
    count = 0
    def click(self):
        self.count += 1

number = Counter()
def text_objects(text, font, color):
    textSurface = font.render(text, True, color)
    return textSurface, textSurface.get_rect()

def button(msg,x,y,w,h,c,ic,action=None):
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()
    pygame.draw.rect(pencere, c,(x,y,w,h))

    smallText = pygame.font.Font("freesansbold.ttf",20)
    textSurf, textRect = text_objects(msg, smallText, white)
    textRect.center = ( (x+(w/2)), (y+(h/2)) )
    pencere.blit(textSurf, textRect)

    if x+w > mouse[0] > x and y+h > mouse[1] > y:
        pygame.draw.rect(pencere, ic,(x,y,w,h))
        if click[0] == 1 != None:
            action()
        smallText = pygame.font.Font("freesansbold.ttf",20)
        textSurf, textRect = text_objects(msg, smallText, white)
        textRect.center = ( (x+(w/2)), (y+(h/2)) )
        pencere.blit(textSurf, textRect)
def loop():
    cikis = False
    while not cikis:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                cikis = True
                pygame.quit()
                quit()
            pencere.fill(white)
            smallText = pygame.font.Font("freesansbold.ttf",50)
            textSurf, textRect = text_objects(str(number.count), smallText, black)
            textRect.center = ((x/2)), (30)
            pencere.blit(textSurf, textRect)
            button("Click",0,100,200,200,black,black2,number.click)
            pygame.display.update()
loop()
pygame.quit()
quit()

해결법

  1. ==============================

    1.변경해야 할 몇 가지 사항이 있습니다.

    변경해야 할 몇 가지 사항이 있습니다.

    드로잉 및 단추 코드는 이벤트 루프에 있지 않아야하지만 외부 while 루프에 있어야합니다. 이벤트가 발생할 때마다 (예 : 마우스가 움직이는 경우) 버튼 기능을 호출합니다.

    버튼 기능이 너무 많이하고 있습니다. 텍스트 서페이스를 만들고 블리트하고, rect를 그리고, 충돌을 검사하고, click 메서드를 호출합니다.

    pygame.mouse.get_pressed ()를 사용하지 말고 대신 이벤트 루프에서 MOUSEBUTTONDOWN 이벤트를 처리해야합니다. mouse.get_pressed는 마우스 버튼이 눌려 졌는지 확인하고 단 한번의 클릭이 발생하지 않았는지 확인합니다.

    여기서는 단추가없는 직사각형과 기능이없는 쉬운 솔루션을 보여 드리겠습니다. 충돌을 처리하고 이벤트 루프에서 번호를 업데이트합니다. 여러 단추를 만들려면 개체 지향 방식으로 다시 작성하는 것이 좋습니다 (원하는 경우 예제를 보여줄 수 있음).

    import pygame
    
    
    pygame.init()
    width, height = (200,300)
    screen = pygame.display.set_mode((width, height))
    
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
    GRAY = (30, 30, 30)
    FONT = pygame.font.Font("freesansbold.ttf", 50)
    
    
    def loop():
        clock = pygame.time.Clock()
        number = 0
        # The button is just a rect.
        button = pygame.Rect(0, 100, 200, 200)
        done = False
        while not done:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    done = True
                # This block is executed once for each MOUSEBUTTONDOWN event.
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    # 1 is the left mouse button, 2 is middle, 3 is right.
                    if event.button == 1:
                        # `event.pos` is the mouse position.
                        if button.collidepoint(event.pos):
                            # Increment the number.
                            number += 1
    
            screen.fill(WHITE)
            pygame.draw.rect(screen, GRAY, button)
            text_surf = FONT.render(str(number), True, BLACK)
            # You can pass the center directly to the `get_rect` method.
            text_rect = text_surf.get_rect(center=(width/2, 30))
            screen.blit(text_surf, text_rect)
            pygame.display.update()
    
            clock.tick(30)
    
    
    loop()
    pygame.quit()
    

    부록 : pygame.sprite.Sprite의 하위 클래스이며 Sprite 그룹에 추가 될 수있는 Button 클래스를 사용하는 객체 지향 솔루션을 사용하는 것이 좋습니다. Button 클래스에 자신의 이미지를 전달하거나 기본 이미지를 사용할 수 있습니다. handle 클래스의 특정 속성을 업데이트하기 위해 handle_event 메소드에서 호출되는 각 버튼 인스턴스에 콜백 함수 또는 메소드를 전달해야합니다. 여기에는 게임을 종료하는 카운터와 다른 메소드를 증가시키는 메소드가 있습니다.

    import pygame as pg
    
    
    pg.init()
    screen = pg.display.set_mode((800, 600))
    FONT = pg.font.SysFont('Comic Sans MS', 32)
    # Default button images/pygame.Surfaces.
    IMAGE_NORMAL = pg.Surface((100, 32))
    IMAGE_NORMAL.fill(pg.Color('dodgerblue1'))
    IMAGE_HOVER = pg.Surface((100, 32))
    IMAGE_HOVER.fill(pg.Color('lightskyblue'))
    IMAGE_DOWN = pg.Surface((100, 32))
    IMAGE_DOWN.fill(pg.Color('aquamarine1'))
    
    
    # Button is a sprite subclass, that means it can be added to a sprite group.
    # You can draw and update all sprites in a group by
    # calling `group.update()` and `group.draw(screen)`.
    class Button(pg.sprite.Sprite):
    
        def __init__(self, x, y, width, height, callback,
                     font=FONT, text='', text_color=(0, 0, 0),
                     image_normal=IMAGE_NORMAL, image_hover=IMAGE_HOVER,
                     image_down=IMAGE_DOWN):
            super().__init__()
            # Scale the images to the desired size (doesn't modify the originals).
            self.image_normal = pg.transform.scale(image_normal, (width, height))
            self.image_hover = pg.transform.scale(image_hover, (width, height))
            self.image_down = pg.transform.scale(image_down, (width, height))
    
            self.image = self.image_normal  # The currently active image.
            self.rect = self.image.get_rect(topleft=(x, y))
            # To center the text rect.
            image_center = self.image.get_rect().center
            text_surf = font.render(text, True, text_color)
            text_rect = text_surf.get_rect(center=image_center)
            # Blit the text onto the images.
            for image in (self.image_normal, self.image_hover, self.image_down):
                image.blit(text_surf, text_rect)
    
            # This function will be called when the button gets pressed.
            self.callback = callback
            self.button_down = False
    
        def handle_event(self, event):
            if event.type == pg.MOUSEBUTTONDOWN:
                if self.rect.collidepoint(event.pos):
                    self.image = self.image_down
                    self.button_down = True
            elif event.type == pg.MOUSEBUTTONUP:
                # If the rect collides with the mouse pos.
                if self.rect.collidepoint(event.pos) and self.button_down:
                    self.callback()  # Call the function.
                    self.image = self.image_hover
                self.button_down = False
            elif event.type == pg.MOUSEMOTION:
                collided = self.rect.collidepoint(event.pos)
                if collided and not self.button_down:
                    self.image = self.image_hover
                elif not collided:
                    self.image = self.image_normal
    
    
    class Game:
    
        def __init__(self, screen):
            self.done = False
            self.clock = pg.time.Clock()
            self.screen = screen
            # Contains all sprites. Also put the button sprites into a
            # separate group in your own game.
            self.all_sprites = pg.sprite.Group()
            self.number = 0
            # Create the button instances. You can pass your own images here.
            self.start_button = Button(
                320, 70, 170, 65, self.increment_number,
                FONT, 'Increment', (255, 255, 255),
                IMAGE_NORMAL, IMAGE_HOVER, IMAGE_DOWN)
            # If you don't pass images, the default images will be used.
            self.quit_button = Button(
                320, 240, 170, 65, self.quit_game,
                FONT, 'Quit', (255, 255, 255))
            # Add the button sprites to the sprite group.
            self.all_sprites.add(self.start_button, self.quit_button)
    
        def quit_game(self):
            """Callback method to quit the game."""
            self.done = True
    
        def increment_number(self):
            """Callback method to increment the number."""
            self.number += 1
            print(self.number)
    
        def run(self):
            while not self.done:
                self.dt = self.clock.tick(30) / 1000
                self.handle_events()
                self.run_logic()
                self.draw()
    
        def handle_events(self):
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    self.done = True
                for button in self.all_sprites:
                    button.handle_event(event)
    
        def run_logic(self):
            self.all_sprites.update(self.dt)
    
        def draw(self):
            self.screen.fill((30, 30, 30))
            self.all_sprites.draw(self.screen)
            pg.display.flip()
    
    
    if __name__ == '__main__':
        pg.init()
        Game(screen).run()
        pg.quit()
    

    부록 2 : 사전과 같은 단추가있는 중간 솔루션. 또한 목록을 사용할 수도 있지만 사전은 읽기 쉽습니다.

    import pygame
    
    
    pygame.init()
    
    WHITE = (255, 255, 255)
    ACTIVE_COLOR = pygame.Color('dodgerblue1')
    INACTIVE_COLOR = pygame.Color('dodgerblue4')
    FONT = pygame.font.Font(None, 50)
    
    
    def draw_button(button, screen):
        """Draw the button rect and the text surface."""
        pygame.draw.rect(screen, button['color'], button['rect'])
        screen.blit(button['text'], button['text rect'])
    
    
    def create_button(x, y, w, h, text, callback):
        """A button is a dictionary that contains the relevant data.
    
        Consists of a rect, text surface and text rect, color and a
        callback function.
        """
        # The button is a dictionary consisting of the rect, text,
        # text rect, color and the callback function.
        text_surf = FONT.render(text, True, WHITE)
        button_rect = pygame.Rect(x, y, w, h)
        text_rect = text_surf.get_rect(center=button_rect.center)
        button = {
            'rect': button_rect,
            'text': text_surf,
            'text rect': text_rect,
            'color': INACTIVE_COLOR,
            'callback': callback,
            }
        return button
    
    
    def main():
        screen = pygame.display.set_mode((640, 480))
        clock = pygame.time.Clock()
        done = False
    
        number = 0
    
        def increment_number():  # A callback function for the button.
            """Increment the `number` in the enclosing scope."""
            nonlocal number
            number += 1
            print(number)
    
        def quit_game():  # A callback function for the button.
            nonlocal done
            done = True
    
        button1 = create_button(100, 100, 250, 80, 'Click me!', increment_number)
        button2 = create_button(100, 200, 250, 80, 'Me too!', quit_game)
        # A list that contains all buttons.
        button_list = [button1, button2]
    
        while not done:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    done = True
                # This block is executed once for each MOUSEBUTTONDOWN event.
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    # 1 is the left mouse button, 2 is middle, 3 is right.
                    if event.button == 1:
                        for button in button_list:
                            # `event.pos` is the mouse position.
                            if button['rect'].collidepoint(event.pos):
                                # Increment the number by calling the callback
                                # function in the button list.
                                button['callback']()
                elif event.type == pygame.MOUSEMOTION:
                    # When the mouse gets moved, change the color of the
                    # buttons if they collide with the mouse.
                    for button in button_list:
                        if button['rect'].collidepoint(event.pos):
                            button['color'] = ACTIVE_COLOR
                        else:
                            button['color'] = INACTIVE_COLOR
    
            screen.fill(WHITE)
            for button in button_list:
                draw_button(button, screen)
            pygame.display.update()
            clock.tick(30)
    
    
    main()
    pygame.quit()
    
  2. from https://stackoverflow.com/questions/47639826/pygame-button-single-click by cc-by-sa and MIT license