【问题标题】:Z Ordering Controls, collision detection on top and bottom of controlZ 排序控件,控件顶部和底部的碰撞检测
【发布时间】:2017-09-27 17:24:11
【问题描述】:

我正在 pygame 中创建控件(按钮和标签是唯一完成的,以及为控件设置工具提示)。我正在以正确的 Z 顺序绘制控件,但我试图检测鼠标不在另一个控件上,因此它只会激活可见的那个。

这在某种程度上,如果你删除 test.set_on_bottom(btnButton7),它只会触发 button7,即使鼠标悬停在它下面的按钮之一上,但是如果你点击 button7 下面的按钮之一并移动将鼠标悬停在 button7 上它仍然认为原始按钮正在被点击(当它不应该被点击时)

几天来我一直在思考这个问题,但我就是想不通。

(另外,我今天早上才想到,但我应该把每个控件变成一个类而不是一个 id,所以稍后会重新设计)

代码太长,不能发,这里是pastebin link

设置状态和触发消息的所有处理都在 process_events 方法中完成

def process_events(self):
    button = pygame.mouse.get_pressed()
    mouse_pos = pygame.mouse.get_pos()

    for control_id in reversed(self.__z_order):
        state = self.__control_list[control_id]['state']

        if state in ('deleted', 'disabled') or not self.__control_list[control_id]['draw']:
            continue

        if self.__control_list[control_id]['rect'].collidepoint(mouse_pos):
            # left mouse is pressed
            if button[0]:
                # current state of this control is hot and left mouse is pressed on it
                if state == 'hot':
                    for x in self.__z_order[0:control_id - 1]:
                        if self.__control_list[x]['state'] == 'pressed':
                            self.__control_list[x]['state'] = 'normal'
                    can_change = True
                    for x in self.__z_order[control_id + 1:]:
                        if self.__control_list[x]['state'] == 'pressed':
                            can_change = False
                            break
                    # change the state to pressed
                    if can_change:
                        self.__control_list[control_id]['state'] = 'pressed'
                        self.__control_list[control_id]['mouse_pos_lclick'] = None
                        self.__control_list[control_id]['mouse_pos_ldown'] = mouse_pos
                        if self.__control_list[control_id]['on_hover_called']:
                            self.__draw_tip = None

                        if (time.clock() - self.__control_list[control_id][
                            'dbl_timer'] >= self.__dbl_click_delay) and \
                                (time.clock() - self.__control_list[control_id]['timer'] <= self.__dbl_click_speed):
                            if self.__event_mode:
                                if self.__control_list[control_id]['on_dbl_lclick']:
                                    self.__control_list[control_id]['on_dbl_lclick']()
                            else:
                                self.__messages.append(self.Message(control_id, PGC_LBUTTONDBLCLK))
                                # print('Double click', self.__control_list[control_id]['text'])
                            self.__control_list[control_id]['dbl_timer'] = time.clock()
                            self.__control_list[control_id]['timer'] = -1
                        break
    # go through the controls from top to bottom first
    for control_id in reversed(self.__z_order):
        state = self.__control_list[control_id]['state']

        if state in ('deleted', 'disabled') or not self.__control_list[control_id]['draw']:
            continue

        # check if the mouse is over this control
        if self.__control_list[control_id]['rect'].collidepoint(mouse_pos):
            # left mouse is not down
            if not button[0]:
                # state is currently pressed
                if state == 'pressed':
                    # check if there's a timer initiated for this control
                    # this prevents 2 clicks + a double click message
                    if self.__control_list[control_id]['timer'] >= 0:
                        self.__control_list[control_id]['dbl_timer'] = -1
                        self.__control_list[control_id]['timer'] = time.clock()
                        # if the event mode
                        if self.__event_mode:
                            # call the function if there is one
                            if self.__control_list[control_id]['on_lclick']:
                                self.__control_list[control_id]['on_lclick']()
                        else:
                            # post the message to the messages queue
                            self.__messages.append(self.Message(control_id, PGC_LBUTTONUP))
                            # print('Click', self.__control_list[control_id]['text'])
                    # the timer is < 0 (should be -1), double click just happened
                    else:
                        # reset the timer to 0 so clicking can happen again
                        self.__control_list[control_id]['timer'] = 0
                    # go through all of the ids below this control
                    for x in self.__z_order[0:control_id - 1]:
                        # set all the hot controls to normal
                        if self.__control_list[x]['state'] == 'hot':
                            self.__control_list[x]['state'] = 'normal'
                    can_change = True
                    # go through all the controls on top of this control
                    for x in self.__z_order[control_id + 1:]:
                        # something else is on top of this and it's already hot, can't change this control
                        if self.__control_list[x]['state'] == 'hot':
                            can_change = False
                            break
                    if can_change:
                        self.__control_list[control_id]['state'] = 'hot'
                        self.__control_list[control_id]['mouse_pos_lclick'] = mouse_pos
                        self.__control_list[control_id]['mouse_pos_ldown'] = None

                # state is not currently hot (but we're hovering over this control)
                elif state != 'hot':
                    # check for any other contorls
                    for x in self.__z_order[0:control_id - 1]:
                        if self.__control_list[x]['state'] == 'hot':
                            self.__control_list[x]['state'] = 'normal'
                    can_change = True
                    for x in self.__z_order[control_id + 1:]:
                        if self.__control_list[x]['state'] == 'hot':
                            can_change = False
                            break
                    # change the state to hot
                    if can_change:
                        self.__control_list[control_id]['state'] = 'hot'
                        self.__control_list[control_id]['mouse_pos_hover'] = mouse_pos
                        # used to start a tooltip (needs work)
                        self.__control_list[control_id]['mouse_pos_rect'] = pygame.Rect(mouse_pos[0] - 7,
                                                                                        mouse_pos[1] - 7,
                                                                                        mouse_pos[0] + 7,
                                                                                        mouse_pos[1] + 7)
                # state is currently 'hot'
                else:
                    # timer for on_hover hasn't been initialized
                    if self.__control_list[control_id]['timer_on_hover'] == 0:
                        self.__control_list[control_id]['timer_on_hover'] = time.clock()
                    # mouse is in the area
                    if self.__control_list[control_id]['mouse_pos_rect'].collidepoint(mouse_pos):
                        # if the on_hover hasn't been triggered and there is a timer for the on_hover
                        if not self.__control_list[control_id]['on_hover_called'] and self.__control_list[control_id]['timer_on_hover']:
                            # if the mouse has been in the hover area for 1.5 seconds or more
                            if time.clock() - self.__control_list[control_id]['timer_on_hover'] >= 1.5:
                                # trigger the hover
                                self.__control_list[control_id]['on_hover_called'] = True
                                # on_hover is a function call, call the function
                                if self.__control_list[control_id]['on_hover']['type'] == 'function':
                                    self.__control_list[control_id]['on_hover']['func'](self.__control_list[control_id]['on_hover']['args'])
                                # on_hover is a tip, set the self.__draw_tip variable to the tip we need
                                else:
                                    self.__draw_tip = self.__control_list[control_id]['on_hover'].copy()
                                    self.__draw_tip['rect'].x = mouse_pos[0]
                                    self.__draw_tip['rect'].y = mouse_pos[1]

        # mouse is not in the control rect and the state is not currently normal
        elif state != 'normal':
            # set it to normal
            self.__control_list[control_id]['state'] = 'normal'
            # clear the on_hover stuff
            if self.__control_list[control_id]['on_hover_called']:
                self.__control_list[control_id]['on_hover_called'] = False
                self.__draw_tip = None
            if self.__control_list[control_id]['timer_on_hover']:
                self.__control_list[control_id]['timer_on_hover'] = 0

【问题讨论】:

    标签: python pygame controls custom-controls z-order


    【解决方案1】:

    (明天24小时超时后选择正确答案)

    终于弄明白了。我已经把它复杂化了。

    1. 反向浏览控件列表,即 .collidepoint 成功处理的第一个控件,保存该控件并中断循环
    2. 检查所有控件,将未禁用且当前不正常的控件设置为正常
    3. 根据需要使用已保存的控件

      def process_events(self):
          button = pygame.mouse.get_pressed()
          mouse_pos = pygame.mouse.get_pos()
          top_id = -1
      
          for control_id in reversed(self.__z_order):
              if self.__control_list[control_id]['state'] != 'disabled' and \
                      self.__control_list[control_id]['draw'] and \
                      self.__control_list[control_id]['rect'].collidepoint(mouse_pos):
                  top_id = control_id
                  break
      
          if top_id != -1:
              # go through all of the controls
              for control_id in self.__z_order:
                  # skip the top most control and any that are disabled/deleted
                  if self.__control_list[control_id]['state'] != 'disabled' and \
                                  self.__control_list[control_id]['state'] != 'normal' and \
                                  control_id != top_id:
                      # set it to normal
                      self.__control_list[control_id]['state'] = 'normal'
                      # clear the on_hover stuff
                      if self.__control_list[control_id]['on_hover_called']:
                          self.__control_list[control_id]['on_hover_called'] = False
                          self.__draw_tip = None
                      if self.__control_list[control_id]['timer_on_hover']:
                          self.__control_list[control_id]['timer_on_hover'] = 0
          else:
              for control_id in self.__z_order:
                  if self.__control_list[control_id]['state'] != 'disabled':
                      # set it to normal
                      self.__control_list[control_id]['state'] = 'normal'
                      # clear the on_hover stuff
                      if self.__control_list[control_id]['on_hover_called']:
                          self.__control_list[control_id]['on_hover_called'] = False
                          self.__draw_tip = None
                      if self.__control_list[control_id]['timer_on_hover']:
                          self.__control_list[control_id]['timer_on_hover'] = 0
              return
      
          if button[0]:
              # current state of this control is hot and left mouse is pressed on it
              if self.__control_list[top_id]['state'] == 'hot':
                  self.__control_list[top_id]['state'] = 'pressed'
                  self.__control_list[top_id]['mouse_pos_lclick'] = None
                  self.__control_list[top_id]['mouse_pos_ldown'] = mouse_pos
                  if self.__control_list[top_id]['on_hover_called']:
                      self.__draw_tip = None
      
                  if (time.clock() - self.__control_list[top_id][
                      'dbl_timer'] >= self.__dbl_click_delay) and \
                          (time.clock() - self.__control_list[top_id]['timer'] <= self.__dbl_click_speed):
                      if self.__event_mode:
                          if self.__control_list[top_id]['on_dbl_lclick']:
                              self.__control_list[top_id]['on_dbl_lclick']()
                      else:
                          self.__messages.append(self.Message(top_id, PGC_LBUTTONDBLCLK))
                          # print('Double click', self.__control_list[top_id]['text'])
                      self.__control_list[top_id]['dbl_timer'] = time.clock()
                      self.__control_list[top_id]['timer'] = -1
          elif not button[0]:
              # state is currently pressed
              if self.__control_list[top_id]['state'] == 'pressed':
                  # check if there's a timer initiated for this control
                  # this prevents 2 clicks + a double click message
                  if self.__control_list[top_id]['timer'] >= 0:
                      self.__control_list[top_id]['dbl_timer'] = -1
                      self.__control_list[top_id]['timer'] = time.clock()
                      # if the event mode
                      if self.__event_mode:
                          # call the function if there is one
                          if self.__control_list[top_id]['on_lclick']:
                              self.__control_list[top_id]['on_lclick']()
                      else:
                          # post the message to the messages queue
                          self.__messages.append(self.Message(top_id, PGC_LBUTTONUP))
                          # print('Click', self.__control_list[top_id]['text'])
                  # the timer is < 0 (should be -1), double click just happened
                  else:
                      # reset the timer to 0 so clicking can happen again
                      self.__control_list[top_id]['timer'] = 0
                  # go through all of the ids below this control
                  for x in self.__z_order[0:top_id - 1]:
                      # set all the hot controls to normal
                      if self.__control_list[x]['state'] == 'hot':
                          self.__control_list[x]['state'] = 'normal'
                  can_change = True
                  # go through all the controls on top of this control
                  for x in self.__z_order[top_id + 1:]:
                      # something else is on top of this and it's already hot, can't change this control
                      if self.__control_list[x]['state'] == 'hot':
                          can_change = False
                          break
                  if can_change:
                      self.__control_list[top_id]['state'] = 'hot'
                      self.__control_list[top_id]['mouse_pos_lclick'] = mouse_pos
                      self.__control_list[top_id]['mouse_pos_ldown'] = None
      
              # state is not currently hot (but we're hovering over this control)
              elif self.__control_list[top_id]['state'] != 'hot':
                  self.__control_list[top_id]['state'] = 'hot'
                  self.__control_list[top_id]['mouse_pos_hover'] = mouse_pos
                  # used to start a tooltip (needs work)
                  self.__control_list[top_id]['mouse_pos_rect'] = pygame.Rect(mouse_pos[0] - 7,
                                                                              mouse_pos[1] - 7,
                                                                              mouse_pos[0] + 7,
                                                                              mouse_pos[1] + 7)
              # state is currently 'hot'
              else:
                  # timer for on_hover hasn't been initialized
                  if self.__control_list[top_id]['timer_on_hover'] == 0:
                      self.__control_list[top_id]['timer_on_hover'] = time.clock()
                  # mouse is in the area
                  if self.__control_list[top_id]['mouse_pos_rect'].collidepoint(mouse_pos):
                      # if the on_hover hasn't been triggered and there is a timer for the on_hover
                      if not self.__control_list[top_id]['on_hover_called'] and \
                              self.__control_list[top_id]['timer_on_hover']:
                          # if the mouse has been in the hover area for 1.5 seconds or more
                          if time.clock() - self.__control_list[top_id]['timer_on_hover'] >= 1.5:
                              # trigger the hover
                              self.__control_list[top_id]['on_hover_called'] = True
                              # on_hover is a function call, call the function
                              if self.__control_list[top_id]['on_hover']['type'] == 'function':
                                  self.__control_list[top_id]['on_hover']['func'] \
                                      (self.__control_list[top_id]['on_hover']['args'])
                              # on_hover is a tip, set the self.__draw_tip variable to the tip we need
                              else:
                                  self.__draw_tip = self.__control_list[top_id]['on_hover'].copy()
                                  self.__draw_tip['rect'].x = mouse_pos[0]
                                  self.__draw_tip['rect'].y = mouse_pos[1]
      

    【讨论】:

      猜你喜欢
      • 2019-06-19
      • 2015-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      • 2015-06-26
      相关资源
      最近更新 更多