Python pygame检测鼠标是否在表面的非透明部分上

时间:2013-06-07 04:43:26

标签: python opengl python-2.7 pygame

我正在尝试为我的游戏制作用户界面,并且有一些用户界面的曲线。现在我可以检测两个表面之间的碰撞。我可以通过两个精灵之间的像素检测,但似乎像素的鼠标检测暗指我。基本上我想检测鼠标何时在UI上,然后在获取UI时忽略下面的所有内容。

这是我到目前为止的照片。如果您注意到粉红色正方形,则鼠标位于GUI上方,而黄色选择器框位于图块上方。黄色选择器是瓷砖上方的框架。

我正在使用带有openGL的pygame,但此时我正在寻找任何解决方案。我可以很容易地适应,因为我不是编程新手,而是非常寻找任何解决方案。 此外,我会发布代码,但要发布很多代码,所以如果需要特定的东西,请告诉我。

需要注意的一点是,GUI是灵活的,因为左上区域会滑入和滑出。此外白色只是占位符,因此不使用最终颜色,并且很难检查。点击z顺序时,是否可以在鼠标下面获取表面元素?

纹理

import pygame
from OpenGL.GL import *
from OpenGL.GLU import *

class Texture(object):
    image = None
    rect = None
    src = ''
    x = 0
    y = 0
    '''
    zOrder Layers
    0  - background
    1  - 
    2  - 
    3  - Tile Selector
    s  - Tiles
    5  - 
    6  - 
    7  - Panels
    8  - Main Menu
    9  - GUI Buttons
    10 - 

    '''

    def __init__(self, src):
        self.src = src
        self.image = pygame.image.load(src)
        self.image.set_colorkey(pygame.Color(255,0,255,0))
        self.rect = self.image.get_rect()
        texdata = pygame.image.tostring(self.image,"RGBA",0)
        # create an object textures
        self.texid = glGenTextures(1)

        # bind object textures 
        glBindTexture(GL_TEXTURE_2D, self.texid)

        # set texture filters
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

        # Create texture image
        glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,self.rect.w,self.rect.h,0,GL_RGBA,GL_UNSIGNED_BYTE,texdata)


        self.newList = glGenLists(2)
        glNewList(self.newList, GL_COMPILE)
        glBindTexture(GL_TEXTURE_2D, self.texid)
        glBegin(GL_QUADS)
        glTexCoord2f(0, 0); glVertex3f(0, 0 ,0)
        glTexCoord2f(0, 1); glVertex3f(0, self.rect.h, 0)
        glTexCoord2f(1, 1); glVertex3f(self.rect.w, self.rect.h, 0)
        glTexCoord2f(1, 0); glVertex3f(self.rect.w, 0, 0)

        glEnd()
        glEndList()

    def getImg(self):
        return self.image

    def getPos(self):
        rect = self.getImg().get_rect()
        pos = dict(x=self.x,y=self.y,w=rect[2],h=rect[3])
        return pos

    def draw(self,x,y,rotate=0):
        glLoadIdentity()
        self.x = int(x)
        self.y = int(y-self.rect.h+32)

        glTranslatef(x,y-self.rect.h+32,0)

        glPushAttrib(GL_TRANSFORM_BIT)
        glMatrixMode(GL_TEXTURE)
        glLoadIdentity()
        glRotatef(rotate,0,0,1)
        glPopAttrib()

        if glIsList(self.newList):
            glCallList(self.newList)

gui Class

import hashlib, string, pygame
from classes.texture import Texture

'''
Created on Jun 2, 2013

@author: Joel
'''

class gui(object):
    INSTANCES = 0           # Count of instances of buildings
    ID = 0                  # Building ID
    TYPE = 0                # Building type
    NAME = ''               # name of Building
    DESCRIPTION = ''        # Description of building
    IMAGE = ''              # Image name of building 

    zOrder = 0
    clickable = True

    def __init__(self, Game, name = 'Building', description = '', image = 'panel'):
        self.INSTANCES += 1
        self.setName(name)
        self.setDescription(description)
        self.setImage(Game, Game.SETTING["DIR"]["IMAGES"] + Game.SETTING["THEME"] + '\\gui\\'+image+'.png')
        self.setType(name.lower())
        self.setZ(6)

    def getDescription(self):
        return self.DESCRIPTION

    def setDescription(self, description):
        self.DESCRIPTION = description

    def getID(self):
        return self.ID

    def setID(self, i):
        allchr = string.maketrans('','')
        nodigits = allchr.translate(allchr, string.digits)
        s = hashlib.sha224(i).hexdigest()
        s = s.translate(allchr, nodigits)
        self.ID = s[-16:]

    def getImage(self):
        return self.IMAGE

    def setImage(self, Game, i):
        self.IMAGE = Texture(Game.CWD + '\\' + i)

    def getName(self):
        return self.NAME

    def setName(self, name):
        self.NAME = name

    def getType(self):
        return self.TYPE

    def setType(self, t):
        self.TYPE = t

    def click(self, x, y):
        if pygame.mouse.get_pressed()[0] == 1:
            if x > self.x and x < (self.x + self.rect.w):
                if y > self.y and y < (self.y + self.rect.h):
                    print("Clicked: " + str(self.x) + ', ' + str(self.y) + ', ' + str(self.rect.w) + ', ' + str(self.rect.y))

    def getClickable(self):
        return self.clickable

    def setClickable(self, c):
        self.clickable = c

    def getZ(self):
        return self.zOrder

    def setZ(self, z):
        self.zOrder = z

pygame

3 个答案:

答案 0 :(得分:1)

两个可能的答案:

1)静态创建一个与屏幕一样大的True或False的2D数组 - 如果单击此处则为True将单击UI,如果单击此处则为False将不会单击UI。测试此阵列中位置的点击次数。

2)使用“绘画和检查”算法(不记得真实姓名)。你知道当你绘制到屏幕时绘制背景,然后是背景对象,然后是前景对象吗?你可以使用类似的技巧来检测你点击的对象 - 用一种纯色绘制背景,用另一种纯色绘制每个对象,用另一种纯色绘制每个UI元素等等......只要每种纯色都是unique,您可以在此缓冲区中测试光标下的像素,并使用它来确定鼠标可见和点击的内容。

答案 1 :(得分:1)

您可以创建UI的遮罩(如果UI包含在一个表面然后应用于屏幕表面,这将是最简单的),并将遮罩的阈值设置为适当的值,以便您的透明像素在掩码中设置为0

http://www.pygame.org/docs/ref/mask.html#pygame.mask.from_surface

使用掩码对象的get_at((x,y))函数,您可以测试是否设置了掩码的特定像素(如果设置了像素,则返回非零值)。

http://www.pygame.org/docs/ref/mask.html#pygame.mask.Mask.get_at

如果您传入鼠标的位置,如果您收到非零值,则可以验证它是否在UI的可见部分上。

答案 2 :(得分:1)

好吧我认为这是最好的选择而不是其他一些选择。如果这有效,将使每个人都保持最新状态。

全局点击变量,用于在dict中存储数据 对象的层变量范围从1到?从最低层到最高层(类似于html zIndex)

  1. Primary Loop
    1. 重置全局点击变量
    2. 点击事件获取位置
  2. 遍历可点击的对象以获取鼠标下的所有内容
    1. 循环鼠标下的所有内容以获得最高层
    2. 返回全局点击变量
  3. 在对象中运行点击代码。
  4. 目前可以修改的图层组织。

    zOrder图层

    1. 背景
    2. NA
    3. 瓦片
    4. 平铺选择器
    5. NA
    6. NA
    7. 面板
    8. 主菜单
    9. GUI按钮
    10. NA
    11. <强>循环

      for i in range(len(self.OBJECTS)):
          #img = Texture(see op)
          img = self.OBJECTS[i].IMAGE
          print(img)
          e = None
          if self.OBJECTS[i].zOrder == 4: # is isometric image
              # tx and ty are translated positions for screen2iso. See Below
              if ((self.tx >= 0 and self.tx < self.SETTING['MAP_WIDTH']) and (self.ty >= 0 and self.ty < self.SETTING['MAP_HEIGHT'])):
                  # map_x and map_y are starting points for the map its self.
                  ix, iy = self.screen2iso(
                      (x - (self.map_x + (self.SETTING['TILE_WIDTH'] / 2))),
                      (y - (self.map_y))
                  )
                  imgx, imgy = self.screen2iso(
                      (img.x - (self.map_x + (self.SETTING['TILE_WIDTH'] / 2))),
                      (img.y - (self.map_y))
                  )
                  if (imgx+2) == ix:
                      if (imgy+1) == iy:
                          e = self.OBJECTS[i]
                      else:
                          continue
                  else:
                      continue
          else: # Not an isometric image
              if x > img.x and x < (img.x + img.rect[2]):
                  if y > img.y and y < (img.y + img.rect[3]):
                      #is click inside of visual area of image?
                      if self.getCordInImage(x, y, self.OBJECTS[i].IMAGE):
                          if self.getAlphaOfPixel(self.OBJECTS[i]) != 0:
                              e = self.OBJECTS[i]
                      else:
                          continue
                  else:
                      continue
              else:
                  continue
          if e != None:
              if self.CLICKED['zOrder'] < e.getZ():
                  self.CLICKED['zOrder'] = e.getZ()
                  self.CLICKED['e'] = e
              else:
                  continue
          else:
              continue
      

      <强> getCordInImage

      def getCordInImage(self, x, y, t):
          return [x - t.x, y - t.y]
      

      <强> getAlphaOfPixel

      def getAlphaOfPixel(self, t):
          mx,my = pygame.mouse.get_pos()
          x,y = self.getCordInImage(mx,my,t.IMAGE)
          #mask = pygame.mask.from_surface(t.IMAGE.image)
          return t.IMAGE.image.get_at([x,y])[3]
      

      <强> screen2iso

      def screen2iso(self, x, y):
          x = x / 2
          xx = (y + x) / (self.SETTING['TILE_WIDTH'] / 2)
          yy = (y - x) / (self.SETTING['TILE_WIDTH'] / 2)
          return xx, yy
      

      <强> iso2screen

      def iso2screen(self, x, y):
          xx = (x - y) * (self.SETTING['TILE_WIDTH'] / 2)
          yy = (x + y) * (self.SETTING['TILE_HEIGHT'] / 2)
          return xx, yy