Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kivy: what is the proper method for animating images with canvas?

I am not fully understanding how to use canvas correctly for images with animations.

See the attached snippet, where I load an animated icon into an Image and do both: (1) add_widget the Image (2) create a Rectangle canvas instruction with a texture = Image's texture

The Image animates The Rectangle texture does not

I have read through all of the Kivy manual and read through Image and Canvas and I get the idea that Image is a nice high level class with all of this image animation handling and Canvas is more of a raw low-level drawing canvas.

So here is my question - what is the Kivy-correct architecture for handling animations on a Canvas? I looked at Animation but that seems for more matrix-like animations such as translation, scaling, rotation.

Here is what I am doing now: I have game with large map window and then a bunch of game UX in helper windows The game UX helper windows I do all the kivy layouts and such and use generally Images and so my icons are animating nicely

However in the game map, I am using canvas:

Drawing all of my game objects using this paradigm:

r=Rectangle(texture=some_Image.texture)
map.canvas.add(r)

When the world needs to be re-drawn:

1) map.canvas.clear()

2) draw all of the stuff in their new positions and states (to be faster, I should just track the dirty objects and locations and just draw those, but to be honest I am getting fantastic fps even with this nuclear-level clear on each draw)

This is of course a lot faster and lighter weight than creating and destroying hundreds of widget-classes - what map canvas is for - right?

But the problem is that my icons with animations in a zip file are not animating

Q: Am I thinking of canvas wrong? Should I instead be adding an Image for each of my game objects instead? (And take advantage of all the animated image support?)

from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.image import Image
from kivy.app import App
from kivy.graphics import Rectangle


class MainApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.root = RelativeLayout()

        # use any zip file of an animated image
        self.animated_icon = Image(source='factory_icon.zip')

        # If I add an Image, the icon animates
        self.root.add_widget(self.animated_icon)

        # If I add the Image's texture on to a Rectangle instruction, no animation
        r = Rectangle(texture=self.animated_icon.texture, size=(100, 100), pos=(100, 100))
        self.root.canvas.add(r)

    def build(self):
        return self.root


if __name__ == '__main__':
    MainApp().run()
like image 778
Erik Bethke Avatar asked Jan 07 '18 19:01

Erik Bethke


People also ask

What is canvas before in KIVY?

canvas: # add your instruction for main canvas here with self. canvas. before: # you can use this to add instructions rendered before with self.

What is texture in KIVY?

Texture is a class that handles OpenGL textures. Depending on the hardware, some OpenGL capabilities might not be available (BGRA support, NPOT support, etc.) You cannot instantiate this class yourself. You must use the function Texture.create() to create a new texture: texture = Texture.


1 Answers

Image.texture property changes in time. It schedules internally methods to update it as the animation goes. This change doesn't propagate to your rectangle because you created it with texture value captured at a very certain point in time, between updates. Consider this example (I use a .gif file for the animation, but the principle should be the same):

from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.image import Image
from kivy.app import App
from kivy.graphics import Rectangle


class MainApp(App):
    def __init__(self, **kwargs):
        super(MainApp, self).__init__(**kwargs)
        self.root = RelativeLayout()

        animated_icon = Image(source='test.gif')
        animated_icon.bind(texture=self.update_texture)

        self.r = Rectangle(texture=animated_icon.texture, size=(500, 255), pos=(100, 100))
        self.root.canvas.add(self.r)

    def update_texture(self, instance, value):
        self.r.texture = value

    def build(self):
        return self.root


if __name__ == '__main__':
    MainApp().run()

Here I bind my own update_texture method to image's texture property so every time it changes I can update the rectangle accordingly.

like image 141
Nykakin Avatar answered Nov 03 '22 10:11

Nykakin