Somebody asked in StackOverflow how to do smooth movements of objects (widgets) in python game platforms. I didn't have the time to reply there because the question was closed because the question was posted in a way that promotes debate not answers. But here is an answer. I think. I hope. I just wanted to say that in Kivy is possible to do smooth movements, not complicated, very flexible and there is a tutorial about it: Pong Game Tutorial

This is a simplified code based of the tutorial where I just make the bool move slowly in diagonal direction from bottom left to top right. I hope it will help somebody.

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
    ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.lang import Builder

Builder.load_string("""
<PongBall>:
    size: 50, 50 
    canvas:
        Ellipse:
            pos: self.pos
            size: self.size

<PongGame>:
    ball: pong_ball

    PongBall:
        id: pong_ball
        pos: 0,0
""")

class PongBall(Widget):
    velocity_x = NumericProperty(1)
    velocity_y = NumericProperty(1)
    velocity = ReferenceListProperty(velocity_x, velocity_y)

    def move(self):
        self.pos = Vector(*self.velocity) + self.pos

class PongGame(Widget):
    ball = ObjectProperty(None)

    def update(self, dt):
        self.ball.move()

class PongApp(App):
    def build(self):
        game = PongGame()
        Clock.schedule_interval(game.update, 1.0 / 60.0)
        return game

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

So yesterday I posted that I was puzzled by the way Kivy deal with transformations (Rotate, Translate and Scale). Especially, the  way RelativeLayout relates with all of this. Please read my previous post first: Rotate, Translate, Scale and RelativeLayout in Kivy .

I actually asked in StackOverflow and I get an insight and I think I am about to understand what is happening here.

This involves 2 Kivy basic instructions (PushMatrix and PopMatrix) that I completely ignored before. Their names are unfortunately very misleading from the way I am seeing things. Here is a what the kivy documentation indicates:

  • PushMatrix: "PushMatrix on context’s matrix stack" [1]
  • PopMatrix: 2Pop Matrix from context’s matrix stack onto model view" [2]

I don't think that is very helpful. This has something to see with OpenGl which I have never worked with and it is probably the source of all my misunderstandings here.

So I ll try to put things in the way I see it. I understand that Push and Pop are operations in a Stack [3]. But what are they pushing and popping? They push and pop the current context of the canvas. Let's see the example first. In my previous post I had two options to solve my transformation: undo all the transformations and use RelativeLayout. Here is a 3rd option using push and pop.

Option 3 - Using Push and Pop:

<Example>:
    Button:
        pos: [0,0]
        size_hint: [None, None]
        size: [200,200]
        canvas.after:
            PushMatrix:
            Line:
                circle: [50, 100, 50]
            Translate:
                x: 100
            Line:
                circle: [50, 100, 50]
            PopMatrix:
    Button:
        pos: [200,0]
        size_hint: [None, None]
        size: [200,200]
        background_color: [0,0,1,1]

So the PushMatrix and PopMatrix actually push and pop the original context. Moreover, this is what RelativeLayout is doing in its internal code, together with an expected translation to the position of the widget.

So what is this matrix? I resisted to believe that the matrix is a drawing space of the size of the screen. For example, a matrix of 800x600 that we are popping and pushing constantly. The Kivy documentation explains it here and I think I got the answer. It reminds me my course on linear Maths 10 years ago.

The idea is that you can save many transformations in a simple matrix and then applied to any vertex instruction (lines, polygons, etc) you were about to draw. For example, before you are going to display a Line,you apply (by mathematical matrix operators) the matrix to the points of the line. A matrix can save many transformations, I honestly don't remember if all of them but surely can rotate, translate and scale figures.

I started to see my general problem here. A Kivy canvas is not a canvas like when you paint but this will be another post.

[1]  http://kivy.org/docs/api-kivy.graphics.html?highlight=pushmatrix#kivy.graphics.PushMatrix

[2] http://kivy.org/docs/api-kivy.graphics.html?highlight=popmatrix#kivy.graphics.PopMatrix

[3] http://en.wikipedia.org/wiki/Stack_(abstract_data_type)

I have been puzzled by the way Kivy deal with transformations (Rotate, Translate and Scale). The Kivy documentation says that "each widget in Kivy already have by default their Canvas"[1] but that doesn't seem the case. At least in practice, it seems that all widgets share the same canvas as if it were a Reference.

Here is the very basic main.py

# main.py
from kivy.app import App

class Example(Widget):
    pass

class ExampleApp(App):
    def build(self):
        return Example()

ExampleApp().run()

Now we have this very simple python code. There are 2 buttons, both of 200x200 pixels. I want the first next to each other, so I position the first in the (0,0) coordinate and the second one in the (200,0) coordinate. Also, I want to circles in the first Button so I use the canvas.after for that. I draw one circle of radio 50px, translate the canvas of the Button (which is a Widget) 100px (the diameter of the first circle), and draw exactly the same circle again after the tanslation so I have one circle next to each other.

<Example>:
    Button:
        pos: [0,0]
        size_hint: [None, None]
        size: [200,200]
        canvas.after:
            Line:
                circle: [50, 100, 50]
            Translate:
                x: 100
            Line:
                circle: [50, 100, 50]
    Button:
        pos: [200,0]
        size_hint: [None, None]
        size: [200,200]
        background_color: [0,0,1,1]

I executed this with python main.py --size=500x100. The following Figure shows my result.

one

How come there is a black space in between both of them? I was expecting for this instead:

two

The Translate is affecting the whole drawing space of the window. This is a very control environment so we can guess what is happening but in real scenarios things can get completely misleading. A rotation could be the end of your interface. At this point we have two options:

Option 1 - Undo all the transformations:

Make sure to undo all the transformations we applied to the canvas. This is why it seems that the canvas is share by all the Widgets.

<Example>:
    Button:
        pos: [0,0]
        size_hint: [None, None]
        size: [200,200]
        canvas.after:
            Line:
                circle: [50, 100, 50]
            Translate:
                x: 100
            Line:
                circle: [50, 100, 50]
            Translate:
                x: -100
    Button:
        pos: [200,0]
        size_hint: [None, None]
        size: [200,200]
        background_color: [0,0,1,1]

Option 2 - Use the RelativeLayout:

If we embed the button inside the RelativeLayout, our problems are also solved.

<Example>:
    RelativeLayout:
        pos: [0,0]
        Button:
            pos: [0,0]
            size_hint: [None, None]
            size: [200,200]
            canvas.after:
                Line:
                    circle: [50, 100, 50]
                Translate:
                    x: 100
                Line:
                    circle: [50, 100, 50]
    Button:
        pos: [200,0]
        size_hint: [None, None]
        size: [200,200]
        background_color: [0,0,1,1]

This brings me to a question I am still trying to find. What exactly a RelativeLayout does? The documentation talks about relative positioning [2] but I was not expecting the impact in the whole transformation sets of Rotate, Translate and Scale. The first two could be considered positioning in certain way. Not that clear for scaling.

EDIT: I found and answer for this and posted it here. It leads to another option:

Option 3 - Using PushMatrix and PopMatrix

 

[1] http://kivy.org/docs/api-kivy.graphics.html

[2]http://kivy.org/docs/api-kivy.uix.relativelayout.html?highlight=relativelayout#kivy.uix.relativelayout.RelativeLayout