3

I'm struggling with how to properly implement simultaneous movement and rotation using Kivy (in python, not kv lang). My goal is to rotate an object so it's facing its destination then move it towards the destination using Animation. Using the code below I typically get movement in relation to the angle rotated instead of in relation to my general playing area. For example the animation without rotation might move an image to point [1,1] whereas with rotation of 180* the animation is moving the image to [-1,-1]. The image is properly rotated in this scenario, meaning it's facing the right way but going the wrong way.

My understanding is that the push/pop matrix functions should provide the appropriate context for the animation rather than the rotated element context. Because the PopMatrix function is happening in Canvas.after it seems like this has no effect - my animation is completed before the original Matrix is restored.

I'm lacking some key piece of information here that's causing a lot of headache. Can someone explain why the code below causes an image to move to (-1,-1) rather than the (1,1) indicated, and how to properly implement this?

I threw this code together as an example, my game code is far more complex. That's why I'm hoping for an explanation rather than a direct solution for my code. Thanks.

    with self.image.canvas.before:
        PushMatrix()
        self.rot = Rotate()
        self.rot.axis = (0, 0, 1)
        self.rot.origin=self.center
        self.rot.angle=180
    with self.image.canvas.after:
        PopMatrix()

    self.anim = Animation(pos = (1,1), duration=1)
    self.anim.start(self)
    self.image.pos = self.pos
    self.image.size = self.size

2 Answers2

4

In case others are interested in how to make this work consistently - I've found that setting origin and angle on each frame, along with binding the image widget to any pos change on it's parent will ensure the widget moves with its parent and in the proper direction. I implemented that like this:

Instantiate the image like this:

with self.image.canvas.before:
    PushMatrix()
    self.rot = Rotate()
    self.rot.axis = (0, 0, 1)
    self.rot.origin=self.center
    self.rot.angle = 0
with self.image.canvas.after:
    PopMatrix()

Bind it like this:

self.bind(pos = self.binding)

def binding(self, *args):
    self.image.center = self.center
    self.image.size = self.size

On each frame call a function that does similar to the below.

self.rot.origin = self.center
self.rot.angle = getangle()#you can use a set angle or generate a new angle every frame
  • There's a way using kv language to make this work reliably. Set an angle property in your widget upon creation and set the rotation properties on the canvas (similar to the above) in KV. Then just update the angle property from python when needed. – Scott Rossignol May 07 '18 at 10:52
  • I'm not entirely clear - did this manage to (or were you trying to), decouple the bitmap-display-angle, from widget motion? Can a bitmap/widget, be pointed in a random angle (say 45 degrees), while its movement is, say, horizontal across the screen? Or does changing the angle, always change the frame-of-motion to follow? I might even make this a Question, but I figured the Answer would just be "it can't be done that way". :) – John C Mar 16 '22 at 22:22
  • Never mind, got it working. Didn't need the binding, as my object was itself an Image - but using `rot.origin` and `rot.angle` inside the frame-update function, actually worked. The Image moved where I wanted it, but its bitmap-angle changed. Not entirely sure *why* this worked, will have to research it more, but it does. The joys of using pure-python, instead of kv-lang. :) – John C Mar 16 '22 at 23:10
2

Rotate effectively changes the coordinate system used by the entire canvas, so after you've rotated by 180 degrees the position [1, 1] really is in the opposite direction to what it was before, as far as any canvas instruction is concerned.

I don't know what self.image is (maybe an Image widget?), but presumably whatever you see is something like a Rectangle drawn on its canvas, whose pos and size match those of the widget. When you update that pos and size, the Rectangle is positioned according to the current coordinate system, which is in the rotated frame.

Thinking about it, I'm not sure if there's a neat way to combine Rotate instructions with Kivy's high level widget coordinates in quite this way. Of course you can work around it in various ways, such as by accounting for the rotation when setting the position of the Rectangle, but that's kind of fiddly, and inconvenient when working with prebuilt widgets. You can also look at what the Scatter widget does to enable arbitrary transformations.

If you just want to rotate by 180 degrees, you can instead adjust the image being displayed, either before displaying it or by adjusting the tex_coords of the Rectangle to change the displayed orientation. However, this won't work for arbitrary rotations, which it looks like you may want.

inclement
  • 29,124
  • 4
  • 48
  • 60
  • Thanks. I've had some success by rotating the object then moving it after the rotation is complete. Simultaneous movement and rotation doesn't seem possible. The (seemingly) best way to do this is to set an object variable ("self.image.angle") specific to the rotation angle then update that property as needed. It seems like Kivy has an observer automatically attached to the property so by updating the angle the image is rotated appropriately. – Scott Rossignol Feb 04 '18 at 14:53