RenderContext анимация спрайтов - PullRequest
0 голосов
/ 28 февраля 2019

В стандартном Kivy вы можете использовать классы изображений и анимации для архивирования практически чего угодно.Но если вы хотите идти быстрее, вам нужно что-то еще.Вам придется обрабатывать все вручную.Вот где RenderContext становится действительно полезным, и этот пример показывает, насколько способен RenderContext.Но чего-то не хватает, Spritesheet Animation.Кажется, что если я хочу переключить текстуру частиц, мне придется вручную изменять вершины, но когда я это делаю, я получаю очень странный эффект мерцания.Я что-то здесь упускаю?или я делаю это совершенно неправильно.?

1004 * Ниже код не требует никаких внешних ресурсов.
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?

...