首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >游戏玩家元素小游戏

游戏玩家元素小游戏
EN

Code Review用户
提问于 2016-08-16 03:09:44
回答 1查看 587关注 0票数 3

最近我一直在使用派克模块制作游戏。我注意到Pygame没有内置的GUI元素。为了节省我(希望其他人)的时间,我创建了一些非常简单的GUI元素:按钮和标签。

按钮模块是第一个被创建的,并且可能是最彻底的测试。按钮模块有一个按钮类。类有创建和更新按钮的主要方法。__init__方法、render_button方法和update_button方法。

我在所有方法上都包含了文档字符串,所以我不需要过多地解释代码:

代码语言:javascript
复制
import pygame as pg
pg.init()


class Button:
    def __init__(self, surface, x, y, width, height):
        """
           Creates a Button that can be used in any pygame project.

           Arguments:
           surface -- a pygame.display.set_mode() object.
           x -- the x axis position of the button's top left corner
           y -- the y axis position of the button's top left corner
           width -- the width of the button
           height -- the height og the button

           the syntax of creating a instance of the class:
           btn_obj = Button(surface, x, y, width, height)

           To display the button and update the button, acording the the mouse
           position, two methods must be called:

           btn_obj.render_button() -- this method is used to render the button.
           Call the function after Updating the pygame screen in your
           Pygame window event loop.

           btn_obj.update_button() -- this method is used to change the buttons
           sytle and to run a specifed function, depending on the mouse position.
           Call this function in your Pygame window event loop.
        """
        # getting surface/window/display reference
        self.surface = surface

        # setting button dimensions
        self.x = x
        self.y = y
        self.width = width
        self.height = height

        # button rectangle and button outline
        self.button_obj = pg.Rect(x, y, width, height)
        self.button_inline = (self.button_obj[0] - 1, self.button_obj[1] - 1,
                              self.button_obj[2], self.button_obj[3] + 1)
        self.button_outline = (self.button_obj[0] + 1, self.button_obj[1] + 1,
                               self.button_obj[2] + 1, self.button_obj[3] + 1)

        # variables to check if the button is is non-active, active, or pressed
        self.normal = True
        self.active = False
        self.run_function = False
        self.clicked = False
        # colors for normal button and active button
        self.button_color = (210, 210, 210)
        self.active_color = (230, 230, 230)
        self.pressed_color = (200, 200, 200)

        # function for button
        self.function = None

        # setting default font parameters
        self.fs = 18
        self.fc = (0, 0, 0)
        self.font_name = pg.font.match_font('arial')
        self.caption = ""

        # setting parameters for button outline and inline color
        self.olc = (0, 0, 0)
        self.ilc = (255, 255, 255)

    def decorate(self, caption="", button_color=(210, 210, 210),
                 active_color=(230, 230, 230), pressed_color=(200, 200, 200),
                 font_size=16, font_color=(0, 0, 0),
                 font_name=pg.font.match_font('arial'),
                 function=None):
        '''
           Gives the user the abiltiy to decorate their buttons.
           Takes the keyword values given, and changes the
           default values accordingly.
           Keyword arguments:

           caption -- the text(if any) to display on the button.
           button_color -- the main color of the button.
           actvie_color -- the color of the button when being hovered over.
           pressed_color -- the color of the button when being pressed.
           font_size -- the size of the button text.
           font_color -- the color of the button text.
           font_name -- the type/name of font used for displaying button text.
           function -- the function to run when the buttons is pressed
        '''

        self.button_color = self.button_color
        self.active_color = self.active_color
        self.pressed_color = self.pressed_color

        self.function = function

        self.fs = font_size
        self.fc = font_color
        self.font_name = font_name
        self.caption = caption


    def _set_button_state_pressed(self):
        """
            Sets the Button 'click' effect when pressed,
            by offsetting the outline and inline lines of the button
        """
        self.button_outline = (self.button_obj[0] - 1, self.button_obj[1] - 1,
                               self.button_obj[2] + 2, self.button_obj[3] + 1)

        self.button_inline = (self.button_obj[0] + 1, self.button_obj[1] + 1,
                              self.button_obj[2], self.button_obj[3])

    def _set_button_state_normal(self):
        """
            Sets the Button state to normal when not being clicked
            by resetting the outline and inline lines of the button

        """
        self.button_inline = (self.button_obj[0] - 1, self.button_obj[1] - 1,
                              self.button_obj[2] + 1, self.button_obj[3] + 1)

        self.button_outline = (self.button_obj[0] - 1, self.button_obj[1] - 1,
                               self.button_obj[2] + 3, self.button_obj[3] + 3)

    def _draw_button_text(self):
        """
            Creates the Button text to draw on the button.
            Changes the button text position text, when the
            button is being clicked
        """
        if self.clicked:
            self.font = pg.font.Font(self.font_name, self.fs)
            self.font_surf = self.font.render(self.caption, True, self.fc)
            w, h = self.font.size(self.caption)
            self.font_pos = (self.x + self.width / 2 - w / 2 + 1, self.y + self.height / 2 - h / 2 + 1)  # I'm adding +1
            # to the text position when pressed to move the text with the button
            self.surface.blit(self.font_surf, self.font_pos)
        else:
            self.font = pg.font.Font(self.font_name, self.fs)
            self.font_surf = self.font.render(self.caption, True, self.fc)
            w, h = self.font.size(self.caption)
            self.font_pos = (self.x + self.width / 2 - w / 2 - 1, self.y + self.height / 2 - h / 2 - 1)
            self.surface.blit(self.font_surf, self.font_pos)

    def render_button(self):
        """
            Renders the button to the screen while checking for
            each button state flag(self.normal, self.active, self.pressed)
        """
        if self.clicked:
            self._set_button_state_pressed()
            pg.draw.rect(self.surface, self.olc, self.button_outline)
            pg.draw.rect(self.surface, self.ilc, self.button_inline)
            pg.draw.rect(self.surface, self.button_color, self.button_obj)
        else:
            self._set_button_state_normal()
            pg.draw.rect(self.surface, self.olc, self.button_outline)
            pg.draw.rect(self.surface, self.ilc, self.button_inline)
            pg.draw.rect(self.surface, self.button_color, self.button_obj)

        # change button color based on the event(active, pressed, or normal)
        if self.active and not self.clicked:
            pg.draw.rect(self.surface, self.active_color, self.button_obj)
        elif self.clicked and self.active:
            pg.draw.rect(self.surface, self.pressed_color, self.button_obj)
        elif self.normal:
            pg.draw.rect(self.surface, self.button_color, self.button_obj)
        self._draw_button_text()


    def _update(self, event):
        """
            Sets the self.active flag and the self.pressed flag
            to True at the correct times
        """
        x, y = event.pos
        mouse_buttons = pg.mouse.get_pressed()
        self.active = False
        if self.x < x < self.x + self.width:
            if self.y < y < self.y + self.height:
                self.active = True

        if mouse_buttons[0] == 1:
            self.active = False

    def _mouse_down(self):
        """
            sets the flag self.clicked to True when being pressed
        """
        if self.active:
            self.run_function = True
            self.clicked = True

    def _mouse_up(self):
        """
            checks if the user gives a function for self.function, if they do, then it will be ran
            when the button is clicked.

            self.run_function is used to track if the button was pressed, like self.clicked.
            however self.clicked is used to set the drawing position of the button and thus
            has to be set back to false on the MOUSEBUTTONUP event. so a seconds variable(self.run_function)
            is needed to know when to run the function given. When the function is run, self.run_function
            is set to False until the button is cliked agian which will set it back to True.
        """
        self.clicked = False
        if self.function is not None and self.active and self.run_function:
            self.run_function = False
            self.function()
        elif self.function is None and self.active is True and self.run_function:
            self.run_function = False

    def update_button(self, event_object):
        """
            checks for the actual pygame events, and calls the right function accordingly
        """
        if event_object.type == pg.MOUSEBUTTONDOWN and self.active is True:
            self._mouse_down()
        if event_object.type == pg.MOUSEBUTTONUP:
            self._mouse_up()
        if event_object.type == pg.MOUSEMOTION:
            self._update(event_object)

    def is_clicked(self):
        """
            Checks if the button was pressed for the user.
            and returns True if it was. Use the method in
            a if-statement, to test if the buttons is\was
            pressed:

            if btn_obj.is_clicked:
                .....
        """
        if self.clicked is True:
            return True
        else:
            return False

我在上面的代码中讨论的唯一问题是,我是否应该有一个单独的方法(decorate)来设置按钮的样式,还是应该让用户在__init__方法中这样做。

标签模块要简单得多,代码也少得多。label模块有一个名为Label的类。与按钮类一样,label类具有__init__、render和update方法:

代码语言:javascript
复制
import pygame as pg
pg.init()


class Label:
    def __init__(self, surface, x, y, w, h,
                 color=(230, 230, 230),
                 label_highlight=(False, (255, 255, 255)), text="",
                 text_pos=(0, 0), font_size=18,
                 font_type=pg.font.match_font('arial'),
                 font_color=(0, 0, 0)):
        '''
        A multipurpose label object for pygame games.

        Arguments:
        surface -- your pygame.display.set_mode() object.
        x -- the x axis position of the labels's top left corner
        y -- the y axis position of the labels's top left corner
        width -- the width of the button
        height -- the height of the button

        Keyword arguments:
        color -- main color of the label(default: white)
        label_highlight -- takes a boolean and a color. If you entered
        True, the label will b highlighted the color you
        specify(default: False (255, 255, 255))
        text -- text to display on label(default: "")
        text_pos -- the x and y coordinates of the text
        realative to your label(default: (0, 0))
        font_size -- size of label font(default: 18)
        font_type -- type of font to use for label font(default: arial)
        font_color -- color of font displayed on label(default: (0, 0, 0))

        the syntax of creating a instance of the class:
        lbl_obj = Label(surface, x, y, width, height)

        To display the label and update the label, acording the the mouse
        position, two methods must be called:

        lbl_obj.render_label() -- this method is used to render the label.
        Call the function after Updating the pygame screen in your
        Pygame window event loop.

        lbl_obj.update_label() -- this method is used to change the labels
        color depending on the mouse position.
        Call this function in your Pygame window event loop.
        '''

        self.surface = surface
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.tp = text_pos
        self.fs = font_size
        self.ft = font_type
        self.fc = font_color
        self.hovering = False
        ######--------------------#######
        self.label = pg.Surface((self.w, self.h))
        self.label_color = color
        self.label_pos = (self.x, self.y)
        self.label.fill(self.label_color)
        self.label_highlight = label_highlight[0]
        self.label_highlight_color = label_highlight[1]
        self.text = text

    def _render_text(self):
        ''' Used to render the label text'''
        self.font = pg.font.Font(self.ft, self.fs)
        w, h = self.font.size(self.text)
        self.font_surf = self.font.render(self.text, True, self.fc)
        self.label.blit(self.font_surf, (self.tp[0], self.tp[1]))


    def render_label(self):
        ''' Used to render the label'''
        if self.hovering:
            self.label.fill(self.label_highlight_color)
            self._render_text()
            self.surface.blit(self.label, self.label_pos)
        elif not self.hovering:
            self.label.fill(self.label_color)
            self._render_text()
            self.surface.blit(self.label, self.label_pos)

    def _update(self, event_obj):
        ''' This function is only called if the user sets
            label_hightlight equal to True and gives a color.
        '''
        x, y = event_obj.pos
        self.hovering = False
        if x > self.x and x < self.x + self.w:
            if y > self.y and y < self.y + self.h:
                self.hovering = True


    def update_label(self, event_obj):
        ''' Calls the acutal Pygame events to update the label'''
        if event_obj.type == pg.MOUSEMOTION:
             if self.label_highlight:
                 self._update(event_obj)
        print(self.hovering)

我想知道的主要批评是:

  • 我应该做一个全新的方法来装饰我的GUI元素吗?
  • 我是否提供了足够的信息与文件字符串?
  • 该模块易于使用和合并吗?

下面是一个使用每个模块的示例。只需确保按钮和标签模块位于同一个目录中。然后复制粘贴:

代码语言:javascript
复制
import pygame as pg
import button
import label
pg.init()


WIDTH = 800
HEIGHT = 600
display = pg.display.set_mode((WIDTH, HEIGHT))
pg.display.set_caption("Test of GUI elemnts")
clock = pg.time.Clock()

btn = button.Button(display, 300, 300, 100, 20)
lbl = label.Label(display, 300, 270, 100, 20, text="Btn 1")
btn.decorate(caption="btn1")

running = True
while running:
    clock.tick(60)
    for e in pg.event.get():
        if e.type == pg.QUIT:
            running = False
            pg.quit()
            quit()
        btn.update_button(e)
        lbl.update_label(e)

    display.fill((200, 200, 200))
    btn.render_button()
    lbl.render_label()
    pg.display.update()
EN

回答 1

Code Review用户

发布于 2016-08-21 06:45:20

您意识到,当调用.decorate方法时,会重置任何未设置的参数?所以如果我打电话:

代码语言:javascript
复制
button.decorate(caption='mycaption')
button.decorate(font_size=32)

那么第二个调用将标题重置为""

另外,还有其他的游戏GUI图书馆,所以在重新发明轮子之前,您可能会检查那里。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/138810

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档