3

Having completed the Pong Game tutorial from the official Kivy site I went on with their Crash Course. There in the very first video I saw this magic thing they call Scatter that pretty much enables you out-of-the-box to make UI things move with your mouse.

I thought this should provide a smoother way to control paddles in the Pong Game. The original way was to put the paddles-moving logic in on_touch_move() method of PongGame object (PongGame class inherits from Widget) and it was a simple:

if touch.x < self.width / 3:  # if you clicked in 1/3 of the window to the left
    player1.center_y = touch.y  # level the first paddle's center vertically with the mouse click position

This results in a jarring start if you happen to start moving your mouse cursor way below or above a paddle. I thought Scatter would work better. Alas so far I've failed to make it work.

What I started with was commenting out the on_touch_move() method, then adding a Scatter object as a child of PongGame class in the pong.kv file and making the existing PongPaddle objects children of the Scatter object. Like this:

<PongGame>:
    Scatter:
        do_scale: False
        do_rotation: False
        do_translation_x: False

        PongPaddle:
            id: player_left
            x: root.x
            center_y: root.center_y  

        PongPaddle:
            id: player_right
            x: root.width - self.width
            center_y: root.center_y

As I used a single Scatter object and both paddles need to move independently I envisioned this will probably cause a problem (clicking one would make both move at the same time) but thought nevertheless it would be a good start.

No luck! This does not make paddles move with a mouse cursor. They still bounce the ball (even though they moved down in the widget tree and I haven't changed the Python code other than commenting out the on_touch_move() method in the PongGame class body - I guess the references to ObjectProperty instances for paddles hooked up in the pong.kv file still work), but they won't move.

Whole runnable code (my own version with the scatter)

Whole runnable code (my own version without the scatter)

Any ideas how to make it work?

z33k
  • 3,280
  • 6
  • 24
  • 38
  • Can you post the whole runnable code (your own version with the scatter)? I think I know what the problem is but I'd like to check before posting the answer. I'll include the full code in the answer as a bonus. – Edvardas Dlugauskas Jul 23 '17 at 17:08
  • Just added the links. My version of the Pong Game differs a bit from the one at the end of the tutorial. Following the author's advice I added a PLAYER WINS label and a button to restart a game. – z33k Jul 24 '17 at 08:23

1 Answers1

1

So the problem is that the paddle jumps to a new location and the on_touch_move method is responsible for that. In your runnable code without scatter I changed lines 84-88 to:

def on_touch_move(self, touch):
    if touch.x < self.width / 3:
        self.player1.center_y += touch.dy
    if touch.x > self.width - self.width / 3:
        self.player2.center_y += touch.dy

Basically, the touch contains delta values for y (how much the y changed) and so you can just move the paddle as much as you moved the mouse instead of moving the paddle center to the mouse's y. This makes the game really smooth and nice. I actually wonder why they didn't make it like so in the first place.

There is one problem though - the paddles can go way off the game screen now. This can be easily fixed by checking if the paddle's center is off the screen (use the PongGame height). I'll leave this as an exercise, but feel free to ask if you get stuck.

So, since you're curious, there is a way to do this with Scatter. So, first of all, Scatter itself is the widget which is dragged and resized and rotated, it's not a layout (it can be, but we only need the paddles themselves to move, not the whole screen). So, the Paddle inherits from Scatter. Remove the on_touch we used for moving the paddles.

Now you'll notice there is a visual bug once you do this. Scatter is weird in some ways. Remove the pos: self.pos of the Paddle in the .kv file. This post sums it up well:

...specific behavior makes the scatter unique, and there are some advantages / constraints that you should consider:

  • The children are positioned relative to 0, 0. The scatter position has no impact of the position of children.

So the canvas's position in the Paddle is relative to the Paddle (scatter), not the screen.

Now take a minute to appreciate the game you got. The paddles can be moved anywhere as well as rotated and such. You can do this with the mouse by using right click to set an imaginary "touch" indicated by a red dot and then do the mobile kind of gestures to resize and rotate. Have some fun, you deserve it. We will fix these "bugs" after your break.

Okay, so there's also some functionality of the Scatter you don't really need. Disable scaling, rotation, and dragging by the x in the .py file PongPaddle class:

do_rotation = do_scale = do_translation_x = Property(False)

Not sure if I got everything, Scatter does many things, some of which you don't need or want. Compared to the previous version, Scatter pong requires more precision. You'll still need the code to check if the paddle is out of boundaries as well. Overall, I liked the previous solution more.

Here you will find the full code with Scatter.

Edvardas Dlugauskas
  • 1,469
  • 1
  • 12
  • 14
  • All it took was changing 4 chars in code? Are you kidding me? :). That is exactly how I wanted the paddles to move. I also made them stay on screen: https://gist.github.com/ThreefoldBurly/66ddfe5ef72b1fe8be3e9a84ac245aa6 – z33k Jul 24 '17 at 18:15
  • I upvoted your answer, but won't mark as accepted as the question was really about using Scatter to make the paddles move. It still escapes me why the label in the first video of the Crash Course moves without hassle but using Scatter here with paddles doesn't work. Is it because here we have a Widget object as the base widget and there it is FloatLayout? Or is it because in the CC Scatter is the first widget added to the base one? – z33k Jul 24 '17 at 18:22
  • Sorry, I realized that using scatter wasn't very elegant, but I'll edit the answer to include the solution. Scatter has many implications, but it does have it's own place nonetheless. Just not this time, I fear. – Edvardas Dlugauskas Jul 24 '17 at 20:31
  • Ahh, so the trick was for PongPaddle class to inherit from Scatter instead of adding a Scatter widget to the PongGame object and then adding PongPaddle object to the scatter. I see how in the Crash Course the author needed a label so he couldn't really make it inherit from Scatter class. On the other hand PonglePaddle is a generic Widget with a Rectangle placed on a canvas, so its entirely doable and works well. Many thanks! – z33k Jul 26 '17 at 14:48
  • I still feel it was a fun idea that anybody who runs your code without "do_rotation = do_scale = do_translation_x = Property(False)" can easily check for oneself. Hilarity ensues! :) Also very informative about the inner works of Scatter. I encourage everybody to try it. – z33k Jul 26 '17 at 14:52
  • Yeah, Scatter is really fun to use but it is so confusingly different from other widgets and behaviours. And yes, this solution only worked because the Paddle was a generic widget we could change it to Scatter - it didn't have anything inside. But I'm pretty sure that Scatter can be used as a Layout itself, just be careful with the coordinate system when setting the children's position! It's different when using a Scatter, just as I've pointed out in the answer. – Edvardas Dlugauskas Jul 26 '17 at 14:55