我在我的游戏中使用自定义着色器,我必须因为性能。现在,我想要将触摸事件绑定到我的粒子/ UFO,以便我可以决定当有人触摸它们时该做什么,但我不知道如何计算它们的宽度和高度。我目前能够告诉触摸事件发生在哪里,但我的collide_point
函数总是返回False
,因为我没有正确的Width
和我的游戏粒子的Height
。 collide_point
函数需要粒子的right
和top
和粒子的右边和顶部需要粒子的width
和height
才能工作。在文档中,据说
width
和height
属性受制于布局逻辑
但我没有使用任何Layout
,而是使用Widget
。如何计算我的游戏粒子的宽度和高度。下面是代码
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()
我想你不是在计算/更新你的top
物品的right
和Ufo
属性。此外,由于你的GL
代码认为(x,y)
是你的圆圈的中心,但是Kivy
认为(x,y)
是一个物体的左下角,你需要牢记这一点。为了正确计算collide_point()
,我在你的left
中添加了bottom
和Ufo
属性,并使用这些属性来计算碰撞。以下是包含这些更改的代码的更新版本:
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()
我还使用Ufo
添加绘制canvas.before
边界框。这只是为每个clickable
可视化Ufo
区域,并且可以很容易地删除。
尝试使用collide_widget()
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)
检查另一个小部件是否与此小部件发生冲突。默认情况下,此函数执行轴对齐的边界框交叉测试。
参数:
wid:用于测试碰撞的Widget类Widget。
返回: 布尔。如果另一个窗口小部件与此窗口小部件冲突,则为True,否则为False。