2

I have a code with an image that I can move with my keyboard. At first I had a very jerky movement and so I added a kivy clock. Now when you press a key to move the image the image moves 30 times per second and then stops, which allows for a rather fluid movement.

But when I keep the key, which allows to move the image, pressed the image no longer moves.

I also tried to have a smooth movement with the speed of kivy ( as in the pong game tutorial https://kivy.org/doc/stable/tutorials/pong.html) but it does not work

How to fix this problem please? ( or how to have a smooth image movement)

I hope my question is clear :),

Thank you in advance for your help

Here is my code:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.properties import (
    NumericProperty, ReferenceListProperty, ObjectProperty
)
from kivy.vector import Vector

class character(Widget):
    pass


class MoveableImage(Image):

    def __init__(self, **kwargs):
        super(MoveableImage, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(None, self)
        if not self._keyboard:
            return
        self._keyboard.bind(on_key_down=self.on_keyboard_down)

    def on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'right':
            Clock.schedule_interval(self.droite, 1.0 / 30.0)
            Clock.schedule_once(self.stop_droite , 0.1)
        elif keycode[1] == 'left':
            Clock.schedule_interval(self.gauche, 1.0 / 30.0)
            Clock.schedule_once(self.stop_gauche , 0.1)
        elif keycode[1] == 'up':
            Clock.schedule_interval(self.up, 1.0 / 30.0)
            Clock.schedule_once(self.stop_up, 0.1)
            Clock.schedule_once(self.down1, 0.2)
        else:
            return False
        return True
    
    def saut(self, keyboard):
        self.y -= 70

    def droite(self, keyboard):
        self.x += 12
        
    def stop_droite(self, dt):
        Clock.unschedule(self.droite)
        
    def gauche(self, keyboard):
        self.x -= 12
        
    def stop_gauche(self, dt):
        Clock.unschedule(self.gauche)
        
    def up(self, keyboard):
        self.y += 50
        
    def stop_up(self, dt):
        Clock.unschedule(self.up)
    
    def down1(self, keyboard):
        Clock.schedule_interval(self.down2, 1.0 / 30.0)
        Clock.schedule_once(self.stop_down, 0.1)
    
    def down2(self, keyboard):
        self.y -= 50
        
    def stop_down(self, dt):
        Clock.unschedule(self.down2)
        
            
class gameApp(App):
    def build(self):
        wimg = MoveableImage(source='tools/theming/defaulttheme/slider_cursor.png')
        m = character()
        m.add_widget(wimg)
        return m


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

1 Answers1

1

You are scheduling the repeating functions for movement and at the same time you schedule a function that unschedules the repeating fuction after 0.1 s, which will behave somewhat weird.

If you look at the pong tutorial more closely, the code they use is Clock.schedule_interval(game.update, 1.0/60.0) and that's it basically.

So for your needs, you would call Clock.schedule_interval(self.update, 1.0/30.0) in your __init__. From that point on, the self.update would be called 30 times per second. The self.update would look something like this:

def update(self):
    if self.rightPressed:
        self.x += 12
    if self.leftPressed:
        self.x -= 12
    if self.upPressed:
        self.y += 50

We should make sure to unschedule the self.update at some point. A destructor is convenient for this:

def __del__(self):
    self.unschedule(self.update)

And finally utilizing self._keyboard.bind(on_key_down=self.on_keyboard_down, on_key_up=self.on_keyboard_up):

def on_keyboard_down(self, keyboard, keycode, text, modifiers):
    if keycode[1] == 'right':
        self.rightPressed = True
    elif keycode[1] == 'left':
        self.leftPressed = True
    elif keycode[1] == 'up':
        self.upPressed = True
    else:
        return False
    return True

def on_keyboard_up(self, keyboard, keycode, text, modifiers):
    if keycode[1] == 'right':
        self.rightPressed = False
    elif keycode[1] == 'left':
        self.leftPressed = False
    elif keycode[1] == 'up':
        self.upPressed = False
    else:
        return False
    return True

So your code might look like this:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.properties import (
    NumericProperty, ReferenceListProperty, ObjectProperty
)
from kivy.vector import Vector

class character(Widget):
    pass


class MoveableImage(Image):

    def __init__(self, **kwargs):
        super(MoveableImage, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(None, self)
        if not self._keyboard:
            return
        self._keyboard.bind(on_key_down=self.on_keyboard_down, on_key_up=self.on_keyboard_up)
        Clock.schedule_interval(self.update, 1.0/30.0)

    def __del__(self):
        Clock.unschedule(self.update);

    def on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'right':
            self.rightPressed = True
        elif keycode[1] == 'left':
            self.leftPressed = True
        elif keycode[1] == 'up':
            self.upPressed = True
        else:
            return False
        return True

    def on_keyboard_up(self, keyboard, keycode, text, modifiers):
        if keycode[1] == 'right':
            self.rightPressed = False
        elif keycode[1] == 'left':
            self.leftPressed = False
        elif keycode[1] == 'up':
            self.upPressed = False
        else:
            return False
        return True


    def update(self):
        if self.rightPressed:
            self.x += 12
        if self.leftPressed:
            self.x -= 12
        if self.upPressed:
            self.y += 50   
            
class gameApp(App):
    def build(self):
        wimg = MoveableImage(source='tools/theming/defaulttheme/slider_cursor.png')
        m = character()
        m.add_widget(wimg)
        return m


if __name__ == '__main__':
    gameApp().run()
IsawU
  • 430
  • 3
  • 12