RenderContext Spritesheet动画

时间:2019-02-28 09:49:44

标签: python kivy glsl mesh kivy-language

在标准Kivy中,您可以使用Image和Animation类来存档几乎所有内容。但是,如果您想走得更快,您将需要其他一些东西。您将必须手动处理所有事情。这是RenderContext真正变得非常有用的地方,此example显示了RenderContext的功能。但Spritesheet Animation缺少了一些东西。似乎如果我想切换粒子纹理,则必须手动修改顶点,但是当我这样做时,我会得到非常奇怪的闪烁效果。我在这里想念什么吗?还是我做错了??

下面的代码不需要任何外部资源。

from __future__ import division
from collections import namedtuple
import json
import math
from random import randint, random,  choice
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("""{
    "invader.png": {
        "frame1": [0, 0, 32, 32],
        "frame2": [32, 0, 32, 32]
    }
}""") 


    tex_name, mapping = atlas.popitem()
    data = """iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAC3UlEQVRoQ9VZy01EMQzcbYFWOHKhAwqhHgqhAy4caYUWQInWkeOM7XHe0wo4IHZfEnvG488L18vl8nPBP1fn+92vkZ2zbWjfPFyypttuv36eP18mUB9P7/JsF6zd153RdoCNMwIxzrCYtEM32x3jQgAJvuLsQnCzbOwsBKk1KAhWORRwQEKZAOho4iyK/CRDpEJPeip605Io4ugsCQCTAlO0q4YQ68AhqJIs/4SMzCe0ThPQ7Hh1YIpe5lD2PEivYd9GWMC17zOg1r45awq2JQCSkAFinwNHliLZwOnipBa4aZfZdwgXRY8uIOdsyTBzAhQ8tEWnmRS4beBexaerqdMWx/5NKUY9v4HVz722WU6DLACwndh+/f31OsA/PL71vyskkK11KNGe3/aLD81+xXaVgKkWaMNWPlVHCiRMqYh8qNqOSECynCqyjr4mATmhi92B6XKbgB37FAEi+0aAJ0UbKUvQUQWID82+R75N1WT07vEsEyAkIIDinCgFOODZ1OJyZxK9CCmMIGCxv0WABWiLFCJA5WFGQpkAsc+o5ObbwB0SIE7byEZVWsB7hYpIhejlCbZilgDZrH1Y2iBqMd54qiVZXOPNBOHwE83+jH1DQFciRYBtgbufmehXZ4yjvvw5AqpDztkETIPQ7uFon/OiM1X/M6NvUwLVragaU4XIAiWuocL3gSz6CFQQJGrMp4sREcGuHi+KSf5n+zTOIxepk53mEx0RooCJk9H9nkt48gZ6BLQVyTTqZwejm5psz1JHCPXc42Z6BEjfMpXAEEBgESXVs0t2pVYvL1olAmQyvFmEU6RXbZ33jqkLMC8vFbRgrSV5GYTQ+RV5VtYuuanJI1VT4WOJPpoEvQPh5ohheVYEMgqoaXmMUjMy/gUBOwU0A7605+hlCMrSyetKDahGECmheobbllkCsmmwOkRVAaCrcibaTG1pa7o/2VW1Z5ABc+9/h0fkWF+G/79XyyU5pxo9EQAAAABJRU5ErkJgggAA"""
    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 PSWidget(Widget):
    indices = []
    vertices = []
    particles = []
    tv = [] 
    refList = [] 
    refDict = {}
    updated = False

    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 make_particles(self, Cls, num):
        count = len(self.particles)
        uv = self.uvmap[Cls.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))
            vet = [
                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,
           ] 
            self.refList.append(vet)
            self.vertices.extend(vet) 

            p = Cls(self, i)
            self.particles.append(p)
            self.refDict[p] = vet
            vet = None

    def update_texture(self,  me):
        self.updated = False
        uv = self.uvmap[me.tex_name]
        ind = self.refList.index(self.refDict[me])

        oo = [
                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,
               ] 
        self.refDict[me] = oo
        self.refList[ind] = oo
        oo = None
        self.vertices = [item for sublist in self.refList for item in sublist]   #sum(self.refList, [])
        self.updated = True
    def update_glsl(self, nap):

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

        if self.updated:
            self.canvas.clear()

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




class Enemy(Particle):
    active = False
    tex_name = 'frame1'
    v = 0
    time = 0.0
    rate = 0.2
    def reset(self, created=False):
        self.active = False
        self.x = -100
        self.y = -100
        self.v = 0

    def advance(self, nap):
        if self.active:
            self.time += nap
            if (self.time > self.rate):
                if self.tex_name == "frame1":
                    self.tex_name ="frame2"
                else:
                    self.tex_name = "frame1"
                self.time -= self.rate
                self.parent.update_texture(self) 

            if self.check_hit():
                self.reset()
                return

            self.x -= 200 * nap
            if self.x < -50:
                self.reset()
                return

            self.y += self.v * nap
            if self.y <= 0:
                self.v = abs(self.v)
            elif self.y >= self.parent.height:
                self.v = -abs(self.v)

        elif self.parent.spawn_delay <= 0:
            self.active = True
            self.x = self.parent.width + 50
            self.y = self.parent.height * random()
            self.v = randint(-100, 100)
            self.parent.spawn_delay += 1

    def check_hit(self):
        #Not implementec
        return False



class Game(PSWidget):

    spawn_delay = 1

    use_mouse = platform not in ('ios', 'android')

    def initialize(self):
        self.make_particles(Enemy, 10)

    def update_glsl(self, nap):
        self.spawn_delay -= nap

        PSWidget.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()

如何在RenderContext中存档Spritesheet动画?

0 个答案:

没有答案