I'm making a game with kivy for android that has the player controlled by touching on certain areas on the screen. I create invisible buttons (char_controls) that when tapped down, the character moves. The moment the finger is released, I would like the character to stop.
I've bound a function to each button that calls Clock.schedule_interval on a move_up function in the character class (only working with the up button right now). When the button is released, it calls another function that should unschedule the original function (with Clock.unschedule). However, it doesn't do this, and the character keeps on moving.
Am I misusing kivy's bind() function when I use it to bind the button's on_press and on_release behaviours to functions defined in another class? I've noticed that I get an AttributeError: 'Button' object has no attribute 'move_up' when I use self to reference move_up — I must instead refer to the move_up function as character.move_up even when I am referencing it in the character class. If the issue doesn't have to do with the bind() function, how can I make the program unschedule the move_up function?
Below is my code:
from kivy.uix.widget import Widget
from kivy.graphics import Canvas, Rectangle, Color
from kivy.uix.anchorlayout import AnchorLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.properties import *
from kivy.core.window import Window
from main import *
from render import Layer
class char_controls(FloatLayout):
'''controls where the character moves. There are 4 regions where the player can tap:
the top third to go up, the bottom third to go down, the center left to go left
and the center right to go right. They are buttons.'''
def __init__(self, **kwargs):
super(char_controls, self).__init__(**kwargs)
self.opacity = 0
self.size = (Window.width, Window.height)
anchor_bc = AnchorLayout(anchor_x = 'center', anchor_y = 'bottom')
down_btn = Button(text='', size_hint = (1, 0.3333))
down_btn.bind(on_press=character.move_down, on_release=character.stop)
down_btn.bind(on_press=Layer.move_down, on_release=Layer.stop)
anchor_bc.add_widget(down_btn)
self.add_widget(anchor_bc)
anchor_cl = AnchorLayout(anchor_x = 'left', anchor_y = 'center')
left_btn = Button(text='', size_hint = (0.5, 0.3333))
left_btn.bind(on_press=character.move_left, on_release=character.stop)
left_btn.bind(on_press=Layer.move_left, on_release=Layer.stop)
anchor_cl.add_widget(left_btn)
self.add_widget(anchor_cl)
anchor_cr = AnchorLayout(anchor_x = 'right', anchor_y = 'center')
right_btn = Button(text='', size_hint = (0.5, 0.3333))
right_btn.bind(on_press=character.move_right, on_release=character.stop)
right_btn.bind(on_press=Layer.move_right, on_release=Layer.stop)
anchor_cr.add_widget(right_btn)
self.add_widget(anchor_cr)
#button of interest
anchor_tc = AnchorLayout(anchor_x = 'center', anchor_y = 'top')
up_btn = Button(text='', size_hint = (1, 0.3333))
up_btn.bind(on_press=character.schedule_up, on_release=character.stop)
up_btn.bind(on_press=Layer.move_up, on_release=Layer.stop)
anchor_tc.add_widget(up_btn)
self.add_widget(anchor_tc)
class character(Widget):
'''The character class.'''
x_pos = 0
y_pos = 0
pos = (x_pos, y_pos)
def __init__(self, **kwargs):
super(character, self).__init__(**kwargs)
with self.canvas:
Color(1., 0, 0)
character.sprite = Rectangle(pos=self.pos, size=(32, 32))
#is there a cleaner way to call the movement functions than this? (Eg lambda)
def schedule_up(self):
Clock.schedule_interval(character.move_up, 1/30.)
def move_up(self):
character.y_pos += 1
character.pos = (character.x_pos, character.y_pos)
character.sprite.pos = character.pos
print('run')
def move_down(self):
print('down')
def move_right(self):
print('right')
def move_left(self):
print('left')
def stop(self):
Clock.unschedule(character.move_up) #this is not actually unscheduling the move_up function.
print('stop') #prints, so the function is working
Thanks in advance!