Python Kivy自定义着色器触摸事件

Python Kivy自定义着色器触摸事件,python,kivy,kivy-language,Python,Kivy,Kivy Language,我在游戏中使用自定义着色器,由于性能原因,我必须使用自定义着色器。现在我正处于我想要将触碰事件绑定到我的粒子/Ufo的位置,这样我就可以决定当有人触碰它们时该做什么,但我不知道如何计算它们的宽度和高度。我目前能够说出触摸事件发生的位置,但我的碰撞点功能始终返回False,因为我没有游戏粒子的正确宽度和高度碰撞点功能需要粒子的右侧和顶部,粒子的右侧和顶部需要粒子的宽度和高度才能工作。文件中说 width和height属性受布局逻辑的约束 但是我没有使用任何布局,而是使用小部件。如何计算游戏粒子的宽

我在游戏中使用自定义着色器,由于性能原因,我必须使用自定义着色器。现在我正处于我想要将触碰事件绑定到我的粒子/Ufo的位置,这样我就可以决定当有人触碰它们时该做什么,但我不知道如何计算它们的宽度和高度。我目前能够说出触摸事件发生的位置,但我的
碰撞点
功能始终返回
False
,因为我没有游戏粒子的正确
宽度
高度
<代码>碰撞点功能需要粒子的
右侧
顶部
,粒子的右侧和顶部需要粒子的
宽度
高度
才能工作。文件中说

width
height
属性受布局逻辑的约束

但是我没有使用任何
布局
,而是使用
小部件
。如何计算游戏粒子的宽度和高度。下面是代码

from __future__ import division
from collections import namedtuple
import json
import math
import random
from kivy import platform
from kivy.app import App
from kivy.base import EventLoop
from kivy.clock import Clock
from kivy.core.image import Image
from kivy.core.window import Window
from kivy.graphics import Mesh
from kivy.graphics.instructions import RenderContext
from kivy.uix.widget import Widget
from kivy.utils import get_color_from_hex
import base64

UVMapping = namedtuple('UVMapping', 'u0 v0 u1 v1 su sv')

GLSL = """
---vertex
$HEADER$

attribute vec2  vCenter;
attribute float vScale;

void main(void)
{
    tex_coord0 = vTexCoords0;
    mat4 move_mat = mat4
        (1.0, 0.0, 0.0, vCenter.x,
         0.0, 1.0, 0.0, vCenter.y,
         0.0, 0.0, 1.0, 0.0,
         0.0, 0.0, 0.0, 1.0);
    vec4 pos = vec4(vPosition.xy * vScale, 0.0, 1.0)
        * move_mat;
    gl_Position = projection_mat * modelview_mat * pos;
}

---fragment
$HEADER$

void main(void)
{
    gl_FragColor = texture2D(texture0, tex_coord0);
}

"""

with open("game.glsl", "wb")  as glslc:
    glslc.write(GLSL)

def load_atlas():
    atlas = json.loads('''{"game-0.png": {"Elien": [2, 26, 100, 100]}}''') 

    tex_name, mapping = atlas.popitem()
    data = '''iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAACJklEQVR4nO3dy1ICQRAF0YLw/39ZVxMBGCjEMF23JvOsXPgounMaN61VQrtsH3x3TqFfLv9/ykdcq9z8RKv25Lro5yiUAcAZAJwBwBkAnAHAGQCcAcAZAJwBwBkAnAHAGQCcAcAZAJwBwBkAnAHAGQCcAcAZAJwBwBkAnAHAfXUPsNM79ydWXbYZZVoAey7MPH6tQdScAI64KbV9T3QI6QGsuCKHDiH5l8DVd1aRd2QTT4DOjcCdBmknQMpTmDLH4ZICSFv0tHkOkRJA6mKnzvUxCQGkL3L6fLskBKBG3QFMebqmzPm2zgCmLeq0eV/SfQKoWVcAU5+mqXM/5QkA1xHA9Kdo+vx3PAHgDADOAOBWB3CW98+zvA5PADoDgDMAOAOAMwA4A4AzADgDgDMAuNUBnOXCxVlehycAnQHAGQBcRwDT3z+nz3/HEwCuK4CpT9HUuZ/yBIDrDGDa0zRt3pd0nwBTFnXKnG/rDkDNEgJIf7rS59slIYCq3EVOnetjUgKoylvstHkOkRRAVc6ip8xxuMS/E7gtfsflC8zGb9JOgFurNwO3+VWZJ8CtFacBcuM36QFsjggBvfGbKQFsHjfNfxix07QAHrmpOyX/EqgFDADOAOAMAM4A4AwAzgDgDADOAOAMAM4A4AwAzgDgDADOAOAMAM4A4AwAzgDgDADOAOAMAM4A4AwA7lrl7YpE7okkSZIkSZIkSZIkSZIkSZIkSZL+9AMvSSThyPfOhQAAAABJRU5ErkJggg=='''
    with open(tex_name, "wb")  as co:
        co.write(base64.b64decode(data))
    tex = Image(tex_name).texture
    tex_width, tex_height = tex.size

    uvmap = {}
    for name, val in mapping.items():
        x0, y0, w, h = val
        x1, y1 = x0 + w, y0 + h
        uvmap[name] = UVMapping(
            x0 / tex_width, 1 - y1 / tex_height,
            x1 / tex_width, 1 - y0 / tex_height,
            0.5 * w, 0.5 * h)

    return tex, uvmap


class Particle:
    x = 0
    y = 0
    size = 1

    def __init__(self, parent, i):
        self.parent = parent
        self.vsize = parent.vsize
        self.base_i = 4 * i * self.vsize
        self.reset(created=True)

    def update(self):
        for i in range(self.base_i,
                       self.base_i + 4 * self.vsize,
                       self.vsize):
            self.parent.vertices[i:i + 3] = (
                self.x, self.y, self.size)

    def reset(self, created=False):
        raise NotImplementedError()

    def advance(self, nap):
        raise NotImplementedError()


class GameScreen(Widget):
    indices = []
    vertices = []
    particles = []

    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
        self.canvas = RenderContext(use_parent_projection=True)
        self.canvas.shader.source = "game.glsl"

        self.vfmt = (
            (b'vCenter', 2, 'float'),
            (b'vScale', 1, 'float'),
            (b'vPosition', 2, 'float'),
            (b'vTexCoords0', 2, 'float'),
        )

        self.vsize = sum(attr[1] for attr in self.vfmt)
        self.texture, self.uvmap = load_atlas()

    def on_touch_down(self, touch):
        for w in self.particles:
            if w.collide_point(*touch.pos):
                w.reset() #Not Working
        return super(GameScreen, self).on_touch_down(touch) 

    def on_touch_move(self, touch):
        for w in self.particles:
            if w.collide_point(*touch.pos):
                w.reset() #Not Working
        return super(GameScreen, self).on_touch_move(touch) 

    def make_particles(self, Ap, num):
        count = len(self.particles)
        uv = self.uvmap[Ap.tex_name]

        for i in range(count, count + num):
            j = 4 * i
            self.indices.extend((
                j, j + 1, j + 2, j + 2, j + 3, j))

            self.vertices.extend((
                0, 0, 1, -uv.su, -uv.sv, uv.u0, uv.v1,
                0, 0, 1,  uv.su, -uv.sv, uv.u1, uv.v1,
                0, 0, 1,  uv.su,  uv.sv, uv.u1, uv.v0,
                0, 0, 1, -uv.su,  uv.sv, uv.u0, uv.v0,
            ))

            p = Ap(self, i)
            self.particles.append(p)

    def update_glsl(self, nap):
        for p in self.particles:
            p.advance(nap)
            p.update()

        self.canvas.clear()

        with self.canvas:
            Mesh(fmt=self.vfmt, mode='triangles',
                 indices=self.indices, vertices=self.vertices,
                 texture=self.texture)

class Ufo(Particle):
    plane = 2.0
    tex_name = 'Elien' 
    texture_size = 129
    right = top = 129

    def reset(self, created=False):
        self.plane = random.uniform(2.0, 2.8)
        self.x = random.randint(15, self.parent.right-15)
        self.y = self.parent.top+random.randint(100, 2500)
        self.size = random.uniform(0.5, 1.0) #every particle must have a random size
        self.top = self.size * self.texture_size 
        self.right = self.size * self.texture_size 

    def collide_point(self, x, y):
        '''Check if a point (x, y) is inside the Ufo's axis aligned bounding box.'''
        with open('TouchFeedback.txt', 'wb') as c:
            c.write(str(x)+', '+str(y)) 

        return self.x <= x <= self.right and self.y <= y <= self.top

    def advance(self, nap):
        self.y -= 100 * self.plane * nap
        if self.y < 0:
            self.reset()

class Game(GameScreen):
    def initialize(self):
        self.make_particles(Ufo, 20)

    def update_glsl(self, nap):
        GameScreen.update_glsl(self, nap)

class GameApp(App):
    def build(self):
        EventLoop.ensure_window()
        return Game()

    def on_start(self):
        self.root.initialize()
        Clock.schedule_interval(self.root.update_glsl, 60 ** -1)

if __name__ == '__main__':
    Window.clearcolor = get_color_from_hex('111110')
    GameApp().run()
来自未来进口部的

从集合导入namedtuple
导入json
输入数学
随机输入
来自kivy导入平台
从kivy.app导入应用程序
从kivy.base导入EventLoop
从kivy.clock导入时钟
从kivy.core.image导入图像
从kivy.core.window导入窗口
从kivy.graphics导入网格
从kivy.graphics.instructions导入RenderContext
从kivy.uix.widget导入widget
从kivy.utils导入从
导入base64
UVMapping=namedtuple('UVMapping','u0 v0 u1 v1 su sv')
GLSL=”“”
---顶点
$HEADER$
属性向量2 vCenter;
属性浮动vScale;
真空总管(真空)
{
tex_coord0=vTexCoords0;
mat4移动\u mat=mat4
(1.0,0.0,0.0,vCenter.x,
0.0,1.0,0.0,vCenter.y,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
vec4位置=vec4(vPosition.xy*vScale,0.0,1.0)
*移动垫;
gl_位置=投影_垫*模型视图_垫*位置;
}
---碎片
$HEADER$
真空总管(真空)
{
gl_FragColor=纹理2D(纹理0,纹理0);
}
"""
以open(“game.glsl”、“wb”)作为glslc:
glslc.write(GLSL)
def load_atlas():
atlas=json.loads(''{“game-0.png”:{“Eline”:[2,26,100100]}}')
tex_name,mapping=atlas.popitem()
数据=“IVBORW0KGGOAAAANSUHEUGAAIAAACACACAAAAADPMHLAAACJKLEQVR4NO3DY1ICQRAF0YLW/39ZVxMBGCJMF23JVOSXPGOUNMAN61VQRTSH3X3TQFFLV9/YKDCQ9Z8RKV25LRO5YUACAZAJWBKANAHAGQCAJWBKANAGQCAJWBCAZHAKJWBKAJWBKANKKKHAKKKKKKKZWIKKKZWIK7PH6QDSCAI64KBV9KKK9KKKKKKKKKKKZZZZZQ2Q2Q2QQ2QTL6KKKKKK4FKKKKK6FKKKKKKKKK6FL6PTL6L6FKKKKKKKKKKKKKKKKKKK2.中国政府对ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ达多阿MAM4A4AWAZGDADOAMAM4A4AWAZGDADOAMAM4A7LRL7YPE7OKKSZIKSZIKSZL+9AMVSTHYPFOQAAABJRU5ERKJGG='''
以open(tex_名称,“wb”)作为公司:
co.write(base64.b64解码(数据))
tex=图像(tex_名称)。纹理
tex_宽度,tex_高度=tex.size
uvmap={}
对于名称,映射中的val.items():
x0,y0,w,h=val
x1,y1=x0+w,y0+h
uvmap[名称]=UVMapping(
x0/tex_宽度,1-y1/tex_高度,
x1/tex_宽度,1-y0/tex_高度,
0.5*w,0.5*h)
返回tex,uvmap
类粒子:
x=0
y=0
尺寸=1
定义初始化(自、父、i):
self.parent=parent
self.vsize=parent.vsize
self.base_i=4*i*self.vsize
self.reset(已创建=真)
def更新(自我):
对于范围内的i(self.base_i,
self.base_i+4*self.vsize,
self.vsize):
self.parent.vertices[i:i+3]=(
self.x、self.y、self.size)
def重置(自创建=假):
引发未实现的错误()
def高级(自我、nap):
引发未实现的错误()
类游戏屏幕(小部件):
指数=[]
顶点=[]
粒子=[]
定义初始(自我,**kwargs):
Widget.\uuuu初始化(self,**kwargs)
self.canvas=RenderContext(使用\u parent\u projection=True)
self.canvas.shader.source=“game.glsl”
self.vfmt=(
(b'vCenter',2'float'),
(b'vScale',1'float'),
(b‘位置’,2‘浮动’,
(b'vTexCoords0',2'float'),
)
self.vsize=sum(self.vfmt中attr的attr[1]
self.texture,self.uvmap=load_atlas()
def on_触控向下(自身,触控):
对于self.particles中的w:
如果w.collide_点(*touch.pos):
w、 重置()#不工作
返回超级(游戏屏幕,自我)。打开触摸(触摸)
def on_touch_move(自我,触摸):
对于self.particles中的w:
如果w.collide_点(*touch.pos):
w、 重置()#不工作
返回超级(游戏屏幕,自我)。触摸移动(触摸)
def生成粒子(自身、Ap、num):
计数=len(自粒子)
uv=self.uvmap[Ap.tex\u name]
对于范围内的i(计数,计数+num):
j=4*i
self.index.extend((
j、 j+1、j+2、j+2、j+3、j))
self.extend((
0,0,1,-uv.su,-uv.sv,uv.u0,uv.v1,
0,0,1,uv.su,-uv.sv,uv.u1,uv.v1,
0,0,1,uv.su,uv.sv,uv.u1,uv.v0,
0,0,1,-uv.su,uv.sv,uv.u0,uv.v0,
))
p=Ap(自我,i)
self.particles.append(p)
def更新\u glsl(自我,nap):
对于self.particles中的p:
p、 预付款(nap)
p、 更新()
self.canvas.clear()
使用self.canvas:
网格(fmt=self.vfmt,mode='triangles',
索引=自索引,顶点=自顶点,
def on_touch_down(self, touch):
    if self.collide_point(*touch.pos):
        for w in self.particles:
            if self.collide_widget(w):
                w.reset()
                return True
    return super(GameScreen, self).on_touch_down(touch) 
collide_widget(wid)
from __future__ import division
import kivy
from kivy.config import Config
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
from kivy.lang import Builder

Config.set('modules', 'monitor', '')


from collections import namedtuple
import json
import math
import random
from kivy import platform
from kivy.app import App
from kivy.base import EventLoop
from kivy.clock import Clock
from kivy.core.image import Image
from kivy.core.window import Window
from kivy.event import EventDispatcher
from kivy.graphics import Mesh
from kivy.graphics.instructions import RenderContext
from kivy.properties import NumericProperty
from kivy.uix.widget import Widget
from kivy.utils import get_color_from_hex
import base64

UVMapping = namedtuple('UVMapping', 'u0 v0 u1 v1 su sv')

GLSL = """
---vertex
$HEADER$

attribute vec2  vCenter;
attribute float vScale;

void main(void)
{
    tex_coord0 = vTexCoords0;
    mat4 move_mat = mat4
        (1.0, 0.0, 0.0, vCenter.x,
         0.0, 1.0, 0.0, vCenter.y,
         0.0, 0.0, 1.0, 0.0,
         0.0, 0.0, 0.0, 1.0);
    vec4 pos = vec4(vPosition.xy * vScale, 0.0, 1.0)
        * move_mat;
    gl_Position = projection_mat * modelview_mat * pos;
}

---fragment
$HEADER$

void main(void)
{
    gl_FragColor = texture2D(texture0, tex_coord0);
}

"""

with open("game.glsl", "wb")  as glslc:
    glslc.write(GLSL.encode())

def load_atlas():
    atlas = json.loads('''{"game-0.png": {"Elien": [2, 26, 100, 100]}}''')

    tex_name, mapping = atlas.popitem()
    data = '''iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAACJklEQVR4nO3dy1ICQRAF0YLw/39ZVxMBGCjEMF23JvOsXPgounMaN61VQrtsH3x3TqFfLv9/ykdcq9z8RKv25Lro5yiUAcAZAJwBwBkAnAHAGQCcAcAZAJwBwBkAnAHAGQCcAcAZAJwBwBkAnAHAGQCcAcAZAJwBwBkAnAHAfXUPsNM79ydWXbYZZVoAey7MPH6tQdScAI64KbV9T3QI6QGsuCKHDiH5l8DVd1aRd2QTT4DOjcCdBmknQMpTmDLH4ZICSFv0tHkOkRJA6mKnzvUxCQGkL3L6fLskBKBG3QFMebqmzPm2zgCmLeq0eV/SfQKoWVcAU5+mqXM/5QkA1xHA9Kdo+vx3PAHgDADOAOBWB3CW98+zvA5PADoDgDMAOAOAMwA4A4AzADgDgDMAuNUBnOXCxVlehycAnQHAGQBcRwDT3z+nz3/HEwCuK4CpT9HUuZ/yBIDrDGDa0zRt3pd0nwBTFnXKnG/rDkDNEgJIf7rS59slIYCq3EVOnetjUgKoylvstHkOkRRAVc6ip8xxuMS/E7gtfsflC8zGb9JOgFurNwO3+VWZJ8CtFacBcuM36QFsjggBvfGbKQFsHjfNfxix07QAHrmpOyX/EqgFDADOAOAMAM4A4AwAzgDgDADOAOAMAM4A4AwAzgDgDADOAOAMAM4A4AwAzgDgDADOAOAMAM4A4AwA7lrl7YpE7okkSZIkSZIkSZIkSZIkSZIkSZL+9AMvSSThyPfOhQAAAABJRU5ErkJggg=='''
    with open(tex_name, "wb")  as co:
        co.write(base64.b64decode(data))
    tex = Image(tex_name).texture
    tex_width, tex_height = tex.size

    uvmap = {}
    for name, val in mapping.items():
        x0, y0, w, h = val
        x1, y1 = x0 + w, y0 + h
        uvmap[name] = UVMapping(
            x0 / tex_width, 1 - y1 / tex_height,
            x1 / tex_width, 1 - y0 / tex_height,
            0.5 * w, 0.5 * h)

    return tex, uvmap


class Particle(EventDispatcher):
    # x = 0
    # y = 0
    x = NumericProperty(0)
    y = NumericProperty(0)
    size = 1

    def __init__(self, parent, i):
        super(Particle, self).__init__()
        self.parent = parent
        self.vsize = parent.vsize
        self.base_i = 4 * i * self.vsize
        self.reset(created=True)

    def update(self):
        for i in range(self.base_i,
                       self.base_i + 4 * self.vsize,
                       self.vsize):
            self.parent.vertices[i:i + 3] = (
                self.x, self.y, self.size)

    def reset(self, created=False):
        raise NotImplementedError()

    def advance(self, nap):
        raise NotImplementedError()


class GameScreen(Widget):
    indices = []
    vertices = []
    particles = []

    def __init__(self, **kwargs):
        Widget.__init__(self, **kwargs)
        self.canvas = RenderContext(use_parent_projection=True)
        self.canvas.shader.source = "game.glsl"

        self.vfmt = (
            (b'vCenter', 2, 'float'),
            (b'vScale', 1, 'float'),
            (b'vPosition', 2, 'float'),
            (b'vTexCoords0', 2, 'float'),
        )

        self.vsize = sum(attr[1] for attr in self.vfmt)
        self.texture, self.uvmap = load_atlas()

    def on_touch_down(self, touch):
        for w in self.particles:
            if w.collide_point(*touch.pos):
                w.reset() #Not Working
        return super(GameScreen, self).on_touch_down(touch)

    def on_touch_move(self, touch):
        for w in self.particles:
            if w.collide_point(*touch.pos):
                w.reset() #Not Working
        return super(GameScreen, self).on_touch_move(touch)

    def make_particles(self, Ap, num):
        count = len(self.particles)
        uv = self.uvmap[Ap.tex_name]

        for i in range(count, count + num):
            j = 4 * i
            self.indices.extend((
                j, j + 1, j + 2, j + 2, j + 3, j))

            self.vertices.extend((
                0, 0, 1, -uv.su, -uv.sv, uv.u0, uv.v1,
                0, 0, 1,  uv.su, -uv.sv, uv.u1, uv.v1,
                0, 0, 1,  uv.su,  uv.sv, uv.u1, uv.v0,
                0, 0, 1, -uv.su,  uv.sv, uv.u0, uv.v0,
            ))

            p = Ap(self, i)
            self.particles.append(p)

    def update_glsl(self, nap):
        for p in self.particles:
            p.advance(nap)
            p.update()

        self.canvas.clear()
        self.canvas.before.clear()  # temporary

        with self.canvas.before:  # temporary code block
            for p in self.particles:
                Rectangle(pos=(p.left, p.bottom), size=(p.size*p.texture_size, p.size*p.texture_size))

        with self.canvas:
            Mesh(fmt=self.vfmt, mode='triangles',
                 indices=self.indices, vertices=self.vertices,
                 texture=self.texture)


class Ufo(Particle):
    plane = 2.0
    tex_name = 'Elien'
    texture_size = 129
    right = NumericProperty(129)
    top = NumericProperty(129)
    left = NumericProperty(0)
    bottom = NumericProperty(0)

    def reset(self, created=False):
        self.plane = random.uniform(2.0, 2.8)
        self.size = random.uniform(0.5, 1.0) #every particle must have a random size
        self.x = random.randint(15, self.parent.right-15)
        self.y = self.parent.top+random.randint(100, 2500)

    def collide_point(self, x, y):
        '''Check if a point (x, y) is inside the Ufo's axis aligned bounding box.'''
        return self.left <= x <= self.right and self.bottom <= y <= self.top

    def advance(self, nap):
        self.y -= 100 * self.plane * nap
        if self.y < 0:
            self.reset()

    def on_x(self, instance, new_x):
        self.right = new_x + self.size * self.texture_size / 2.0
        self.left = new_x - self.size * self.texture_size / 2.0

    def on_y(self, instance, new_y):
        self.top = new_y + self.size * self.texture_size / 2.0
        self.bottom = new_y - self.size * self.texture_size / 2.0

class Game(GameScreen):
    def initialize(self):
        self.make_particles(Ufo, 20)

    def update_glsl(self, nap):
        GameScreen.update_glsl(self, nap)

class GameApp(App):
    def build(self):
        EventLoop.ensure_window()
        return Game()

    def on_start(self):
        self.root.initialize()
        Clock.schedule_interval(self.root.update_glsl, 60 ** -1)

if __name__ == '__main__':
    Window.clearcolor = get_color_from_hex('111110')
    GameApp().run()