简体   繁体   English

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

[英]Python pygame Detect if mouse is over non transparent part of surface

I am trying to make a UI for my game and there are some curves to the UI. 我正在尝试为我的游戏制作一个UI,并且UI有一些曲线。 Now I can detect collision between two surfaces. 现在我可以检测两个表面之间的碰撞。 I can detect by pixel between two sprites, but it seems mouse detection by pixel is alluding me. 我可以通过两个精灵之间的像素检测,但似乎像素的鼠标检测暗指我。 Basically I want to detect when the mouse is over the UI and then ignore everything below that while getting the UI. 基本上我想检测鼠标何时在UI上,然后在获取UI时忽略下面的所有内容。

This is a picture of what I have so far. 这是我到目前为止所拥有的图片。 If you notice the pink square the mouse is over the GUI while the yellow selector box is over a tile. 如果您注意到粉红色正方形,则鼠标位于GUI上方,而黄色选择器框位于图块上方。 The yellow selector is a box frame over a tile. 黄色选择器是瓷砖上方的框架。

I am using pygame with openGL but at this point I am looking for ANY solution to this. 我正在使用pygame和openGL,但此时我正在寻找任何解决方案。 I can adapt pretty easily as I am not new to programming and pretty much looking for any solution. 我可以很容易地适应,因为我不是编程新手,而是非常寻找任何解决方案。 Also I would post the code but to much code to post so if thing specific is needed let me know. 此外,我会发布代码,但要发布很多代码,所以如果需要特定的东西让我知道。

One thing to note is that the GUI is flexable in that the upper left area will slide in and out. 需要注意的一点是,GUI是灵活的,左上区域将滑入和滑出。 Also the white is just placeholder so final colors are not used and would be difficult to check. 此外白色只是占位符,因此不使用最终颜色,并且很难检查。 Is it possible to get the surface elements under the mouse when clicked by z order? 当按z顺序点击时,是否可以在鼠标下面获取表面元素?

Texture 质地

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 gui班

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的

Two possible answers: 两个可能的答案:

1) Statically create a 2D array of True or False that is as big as the screen - True if clicking here would click on the UI, False if clicking here would not click on the UI. 1)静态创建一个与屏幕一样大的真或假的2D数组 - 如果单击此处则为True将单击UI,如果单击此处则为False将不会单击UI。 Test clicks against the position in this array. 测试此阵列中位置的点击次数。

2) Use the 'paint and check' algorithm (don't recall the real name). 2)使用“绘制和检查”算法(不记得真实姓名)。 You know how when you draw to the screen you draw the background, then background objects, then foreground objects? 你知道当你绘制到屏幕时绘制背景,然后是背景对象,然后是前景对象吗? You can use a similar trick to detect what object you have clicked on - draw the background in one solid colour, each object in another solid colour, each UI element in another solid colour, etc... and as long as each solid colour is unique, you can test what colour pixel is under the cursor in this buffer and use it to determine what was visible and clicked on by the mouse. 你可以使用类似的技巧来检测你点击的对象 - 用一种纯色绘制背景,用另一种纯色绘制每个对象,用另一种纯色绘制每个UI元素等等......只要每种纯色都是unique,您可以在此缓冲区中测试光标下的像素,并使用它来确定鼠标可见和点击的内容。

You could create a mask of the UI (this would be easiest if the UI is contained in one surface which is then applied to the screen surface), and set the threshold of the mask to the appropriate value so that your transparent pixels are set to 0 in the mask. 您可以创建UI的掩码(如果UI包含在一个表面然后应用于屏幕表面,这将是最简单的),并将掩码的阈值设置为适当的值,以便将透明像素设置为掩码中为0

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

With the mask object's get_at((x,y)) function you can test if a specific pixel of the mask is set (a non-zero value is returned if the pixel is set). 使用mask对象的get_at((x,y))函数,您可以测试是否设置了掩码的特定像素(如果设置了像素,则返回非零值)。

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

If you pass in the mouse's position, you can verify that it is over a visible part of the UI if you receive a non-zero value. 如果传入鼠标的位置,如果收到非零值,则可以验证它是否在UI的可见部分上。

Okay I am thinking of this as the best option rather then some of the alternatives. 好吧,我认为这是最好的选择,而不是其他一些选择。 Will keep everyone up to date if this works or not. 如果这是否有效,将使每个人都保持最新状态。

global click variable to store data in a dict Objects have layer variable ranging from 1 to ? 全局点击变量在dict中存储数据对象有层变量,范围从1到? from lowest to greatest layer(similar to html zIndex) 从最低层到最高层(类似于html zIndex)

  1. Primary Loop 主循环
    1. reset the global click var 重置全局点击变量
    2. click event get position 点击事件获取位置
  2. loop over clickable objects to get everything under mouse 循环遍历可点击的对象以获取鼠标下的所有内容
    1. loop over everything under mouse to get highest layer 循环鼠标下的所有内容以获得最高层
    2. Return for global click var 返回全局click var
  3. run click code in object. 在对象中运行单击代码。

Layer organization currently which can be modified. 当前可以修改的层组织。

zOrder Layers zOrder图层

  1. background 背景
  2. na
  3. Tiles 瓷砖
  4. Tile Selector 平铺选择器
  5. na
  6. na
  7. Panels 面板
  8. Main Menu 主菜单
  9. GUI Buttons GUI按钮
  10. na

Loop

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 getCordInImage

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

getAlphaOfPixel 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 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 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM