How to handle multiple keyboard inputs in kivy? - python

so im developing this game, which is a platformer, so im working on the movements now, the problem is:
i can move the player to the left and right side, i can also jump, however i can't move and jump at the same time, like, while im moving forward i want to be able to jump forward and that would require me to press the forward and the up buttons.
i've tried many ways to get this but with no success and i haven't found this issue googling around, so... here's the minimal code to help things out.
the issue is the player either jump or move, it doesn't do both when i press the up and forward keys.
Main.py
from kivy.core.window import Window
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.floatlayout import MDFloatLayout
from kivy.properties import Clock
from Player import PlayerImage
class LevelOneLayout(MDFloatLayout):
pass
class LevelOne(MDScreen):
from PlayerAction import _keyboard_closed, _on_keyboard_down, _on_keyboard_up
gravity_force = 1800
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down = self._on_keyboard_down)
self._keyboard.bind(on_key_up = self._on_keyboard_up)
Clock.schedule_once(self.setup, 1/60)
def setup(self, dt):
self.player_image = PlayerImage()
self.add_widget(self.player_image)
Clock.schedule_interval(self.game_gravity, 1/30)
def game_gravity(self, dt):
if self.player_image.on_air:
self.player_image.player_gravity_vel -= self.gravity_force * 1/30
self.player_image.y += self.player_image.player_gravity_vel * 1/30
if self.player_image.y <= 60:
self.player_image.y = 60
self.player_image.player_gravity_vel = 0
self.player_image.on_air = False
class MainApp(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Red"
return
MainApp().run()
Main.kv
LevelOne:
LevelOneLayout:
<LevelOneLayout>:
<PlayerImage>:
source: "player_77x100.png"
size_hint: None, None
size: "77dp", "100dp"
x: "0dp"
y: "50dp"
Player.py
from kivy.uix.image import Image
from kivy.animation import Animation
from kivymd.uix.floatlayout import MDFloatLayout
class PlayerImage(Image):
texture_side = "right"
def __init__(self):
super().__init__()
self.velx = 20
self.player_gravity_vel = 0
self.on_air = False
def move_left(self, state, *args):
if state:
self.x -= self.velx
self.texture_left()
self.forced_dash = False
else:
return None
def move_right(self, state, *args):
if state:
self.x += self.velx
self.texture_right()
self.forced_dash = False
else:
return None
def jump(self, state, *args):
if not self.on_air and state:
self.player_gravity_vel = 830
self.on_air = True
else:
return None
def texture_left(self):
if self.texture.tex_coords[0] == 0:
self.texture.flip_horizontal()
self.texture.ask_update(self.update_texture)
self.texture_side = "left"
def texture_right(self):
if self.texture.tex_coords[0] == 1:
self.texture.flip_horizontal()
self.texture.ask_update(self.update_texture)
self.texture_side = "right"
def update_texture(self, texture):
self.texture = None
self.texture = texture
PlayerAction.py
key_state = True
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down = self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, *args):
if keycode[1] == "up":
self.player_image.jump(key_state)
if keycode[1] == "left":
self.player_image.move_left(key_state)
elif keycode[1] == "right":
self.player_image.move_right(key_state)
return True
def _on_keyboard_up(self, keyboard, keycode, *args):
key_state = False
if keycode[1] == "up":
self.player_image.jump(key_state)
if keycode[1] == "left":
self.player_image.move_left(key_state)
elif keycode[1] == "right":
self.player_image.move_right(key_state)
return True

so for an example that manages multiple key presses i recommend this link:
https://groups.google.com/g/kivy-users/c/b0Qmv160GBk
which is the answer to this question i made in the kivy group support, so i've changed a bit the code to suit my code and with Mr.inclements help i did this:
i've put the changes in blockquotes.
Main.py file:
from kivy.core.window import Window
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivymd.uix.floatlayout import MDFloatLayout
from kivy.properties import Clock
from Player import PlayerImage
class LevelOneLayout(MDFloatLayout):
pass
class LevelOne(MDScreen):
from PlayerAction import _keyboard_closed, _on_keyboard_down, _on_keyboard_up, """process_keys"""
gravity_force = 1800
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_down = self._on_keyboard_down)
self._keyboard.bind(on_key_up = self._on_keyboard_up)
Clock.schedule_once(self.setup, 1/60)
def setup(self, dt):
self.player_image = PlayerImage()
self.add_widget(self.player_image)
Clock.schedule_interval(self.game_gravity, 1/30)
"""Clock.schedule_interval(self.process_keys, 1/30)"""
def game_gravity(self, dt):
if self.player_image.on_air:
self.player_image.player_gravity_vel -= self.gravity_force * 1/30
self.player_image.y += self.player_image.player_gravity_vel * 1/30
if self.player_image.y <= 60:
self.player_image.y = 60
self.player_image.player_gravity_vel = 0
self.player_image.on_air = False
class MainApp(MDApp):
def build(self):
self.theme_cls.theme_style = "Dark"
self.theme_cls.primary_palette = "Red"
return
MainApp().run()
PlayerAction.py File:
"""pressed_keys = set()"""
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down = self._on_keyboard_down)
"""self._keyboard.unbind(on_key_up = self._on_keyboard_up)"""
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, *args):
"""pressed_keys.add(keycode[1])"""
return True
def _on_keyboard_up(self, keyboard, keycode, *args):
"""pressed_keys.remove(keycode[1])"""
return True
"""
def process_keys(self, dt):
if pressed_keys.issuperset({'up', 'right'}):
self.player_image.jump()
self.player_image.move_right()
elif pressed_keys.issuperset({'up', 'left'}):
self.player_image.jump()
self.player_image.move_left()
elif pressed_keys.issuperset({'left', 'up'}):
self.player_image.move_left()
self.player_image.up()
elif pressed_keys.issuperset({'right', 'up'}):
self.player_image.move_right()
self.player_image.up()
elif pressed_keys.issuperset({'up'}):
self.player_image.jump()
elif pressed_keys.issuperset({'left'}):
self.player_image.move_left()
elif pressed_keys.issuperset({'right'}):
self.player_image.move_right()
"""
Player.py file:
in the player.py file i just deleted the "state" parameter from the "jump", "move_left" and "move_right" methods, since i've deleted the "key_state" variable from the PlayerAction.py file, i wouldn't need the "state" parameter in here.
Notice that the multiple key presses example provided in the kivy support group doesnt use a Clock to call the "process_key" method, instead it just calls from the "_on_keyboard_down" method, however doing this way still makes you feel like the second key is overriding the first, so i've tried using here Mr.inclement's answer by calling the "process_key" method using the Clock, and it works. that it.

Related

Kivy Window.request_keyboard only working on the second screen

So I am making an app in kivy python and it has 2 screens that I want the app to have keyboard access to. So i have implemented the standard request keyboard as given on the Kivy documentation. But it is only working on Screen2 and not on screen1. Plus, when I am on screen1, it still implements the on_keyboard_down() of screen2.
Below is a reproducible code with the same problem.
import kivy
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
Builder.load_string("""
<screen1>:
Label:
text: "Screen 1"
Button:
text: "go s2"
on_press: root.c1()
<screen2>:
Label:
text: "Screen 2"
Button:
text: "go s1"
on_press: root.c2()
""")
class screen1(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.request_keyboard()
def request_keyboard(self):
print("s1")
self._keyboard = Window.request_keyboard(self._keyboard_closed, self, "text")
if self._keyboard.widget:
pass
self._keyboard.bind(on_key_down=self._on_keyboard_down)
self._keyboard.bind(on_key_up=self._on_keyboard_up)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == "escape":
app = App.get_running_app()
app.stop()
else:
#self.encry_times +=1
pressed_letter = keycode[1].upper()
c = pressed_letter
print("s1" + c)
return True
def _on_keyboard_up(self, keyboard, keycode):
pass
def c1(self):
eni_app.screen_manager.current = "s2"
class screen2(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.request_keyboard()
def request_keyboard(self):
print("s2")
self._keyboard = Window.request_keyboard(self._keyboard_closed, self, "text")
if self._keyboard.widget:
pass
self._keyboard.bind(on_key_down=self._on_keyboard_down)
self._keyboard.bind(on_key_up=self._on_keyboard_up)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == "escape":
app = App.get_running_app()
app.stop()
else:
#self.encry_times +=1
pressed_letter = keycode[1].upper()
c = pressed_letter
print("s2" + c)
return True
def _on_keyboard_up(self, keyboard, keycode):
pass
def c2(self):
eni_app.screen_manager.current = "s1"
class TestApp(App):
def build(self):
self.screen_manager = ScreenManager(transition = FadeTransition())
self.s1_page = screen1()
screen = Screen(name = "s1")
screen.add_widget(self.s1_page)
self.screen_manager.add_widget(screen)
self.s2_page = screen2()
screen = Screen(name = "s2")
screen.add_widget(self.s2_page)
self.screen_manager.add_widget(screen)
return self.screen_manager
if __name__ == '__main__':
eni_app = TestApp()
eni_app.run()
I have tried adding a on_enter function but it completely blocks any access to the keyboard. on_pre_enter has a similar effect on the code.
The expectation from the code above is: it should print s1+ (whatever key is pressed) and on screen2 it should press s2+(whatever key is pressed)

Is there a way to count how many Kivy Clock.schedule objects there are present at runtime?

I am new to Kivy. I am trying to create android app with two screens, whitch both display list of dynamicaly created Buttons. Generaly I add or remove Buttonor edit the its content and want to see changes made in it as they are happening. Here is simplified example where Button changes from column to column:
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
KV = '''
BoxLayout:
MyListA:
MyListB:
'''
class Content:
def __init__(self):
self.contentA =[]
self.contentB = []
for i in range(10):
self.contentA.append(f"X{i}")
self.change_made = 0
def switch_to_content_A(self, content_id):
content = self.contentB.pop(content_id)
self.contentA.append(content)
self.change_made = 2
def switch_to_content_B(self, content_id):
content = self.contentA.pop(content_id)
self.contentB.append(content)
self.change_made = 2
class MyListA(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
content_listA = []
for i in c.contentA:
content_listA.append(Button(text=str(i), on_release=lambda x, id=len(content_listA): self.switsch_to_B(id)))
self.add_widget(content_listA[-1])
Clock.schedule_interval(self.update, 0.5)
def update(self, *args):
if c.change_made > 0:
self.clear_widgets()
self.__init__()
def switsch_to_B(self, content_id):
c.switch_to_content_B(content_id)
self.update()
c.change_made -= 1
class MyListB(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
content_listB = []
for i in c.contentB:
content_listB.append(Button(text=str(i), on_release=lambda x, id=len(content_listB): self.switsch_to_A(id)))
self.add_widget(content_listB[-1])
Clock.schedule_interval(self.update, 0.5)
def update(self, *args):
if c.change_made > 0:
self.clear_widgets()
self.__init__()
def switsch_to_A(self, content_id):
c.switch_to_content_A(content_id)
self.update()
c.change_made -= 1
class MyApp(App):
def build(self):
return Builder.load_string(KV)
if __name__ == "__main__":
c = Content()
MyApp().run()
The way I understand it script Clock.schedule_interval(self.update, 0.5) is nessesary to update the other column. However I suspect that there are multiple schedule_intervals created (but never killed) because my program begins to slow significaly and then freeze after a while. I am looking to see if I can detect and kill older schedule_intervals. Though if that is not to reason of freezing I would like to know what is.
Try changing MyListA to:
class MyListA(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = "vertical"
Clock.schedule_interval(self.update, 0.5)
def update(self, *args):
if c.change_made > 0:
self.clear_widgets()
content_listA = []
for i in c.contentA:
content_listA.append(Button(text=str(i), on_release=lambda x, id=len(content_listA): self.switsch_to_B(id)))
self.add_widget(content_listA[-1])
def switsch_to_B(self, content_id):
c.switch_to_content_B(content_id)
self.update()
c.change_made -= 1
and similar for MyListB. Also Content.change_made has to be 1 or 2 for the first update to happen. If you need the first update before 0.5 seconds, yu can just add one call to self.update() in __init__()

How to update the size of the parent widget from the size of the children in Kivy?

This snippet that runs on Kivy for Python draws some rectangles (Boxes) as child widgets of a parent one (RootWidget).
By pressing ALT + D you create another box (added to the RootWidget).
I'm trying to implement a touch and drag behavior on the parent widget so that it moves all the child boxes together when they are dragged with the mouse.
However, the on_touch_down method (see self.collide_point(*touch.pos)) just gets the position of the original child widget (the one created by default) but not of the newly created ones.
Why? Is there a way to update the size of the parent so that it gets grabbed when a box other than the first is touched?
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics import Ellipse, Color, Rectangle, Line
from kivy.core.window import Window
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.properties import NumericProperty, ListProperty
from random import randint
Builder.load_string('''
<Box>:
canvas:
Color:
rgba: .1, .1, 1, .9
Line:
width: 2.
rectangle: (self.x, self.y, self.width, self.height)
''')
class Tree(Widget):
pass
class Node(Widget):
pass
class Box(Widget):
def __init__(self, **kwargs):
super(Box, self).__init__(**kwargs)
self.size = [500, 300]
self.height = self.size[1]
self.width = self.size[0]
self.pos = [500,200]
# bind change of pos to redraw
self.bind(pos=self.redraw, size=self.redraw)
def redraw(self, *args):
# compute head and sisters' positions
self.x = self.pos[0]
self.y = self.pos[1]
#self.height = self.size[0]
#self.width = self.size[1]
class Branches(Widget):
pass
class Terbranch(Widget):
pass
class RootWidget(Widget):
def __init__(self, **kwargs):
super(RootWidget, self).__init__(**kwargs)
for i in range(2):
self.add_widget(Box())
self._keyboard = Window.request_keyboard(
self._keyboard_closed, self, 'text')
if self._keyboard.widget:
# If it exists, this widget is a VKeyboard object which you can use
# to change the keyboard layout.
pass
self._keyboard.bind(on_key_down=self._on_keyboard_down)
self.bind(pos=self.redraw, size=self.redraw)
def redraw (self, *args):
pass
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
# if the touch collides with our widget, let's grab it
touch.grab(self)
#print ('touched')
# and accept the touch.
return True
return super(RootWidget, self).on_touch_down(touch)
def on_touch_up(self, touch):
# check if it's a grabbed touch event
if touch.grab_current is self:
# don't forget to ungrab ourself, or you might have side effects
touch.ungrab(self)
# and accept the last up
return True
return super(RootWidget, self).on_touch_up(touch)
def on_touch_move(self, touch):
# check if it's a grabbed touch event
if touch.grab_current is self:
#self.pos = touch.pos
self.pos[0] += touch.dx
self.pos[1] += touch.dy
#redraw moved children
for child in self.children:
child.pos[0] += touch.dx
child.pos[1] += touch.dy
child.redraw()
# and accept the last move
return True
return super(RootWidget, self).on_touch_move(touch)
def _keyboard_closed(self):
print('My keyboard have been closed!')
self._keyboard.unbind(on_key_down=self._on_keyboard_down)
self._keyboard = None
def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
#print('The key', keycode, 'have been pressed')
#print(' - text is %r' % text)
#print(' - modifiers are %r' % modifiers)
# Keycode is composed of an integer + a string
# If we hit escape, release the keyboard
if keycode[1] == 'escape':
keyboard.release()
elif keycode[1] == 'd' and modifiers[0] == 'alt':
newbox = Box()
self.add_widget(newbox)
# Return True to accept the key. Otherwise, it will be used by
# the system.
return True
def update(self, dt):
pass
class MyApp(App):
def build(self):
rw = RootWidget()
#Clock.schedule_interval(rw.update, 0.2)
return rw
if __name__ == "__main__":
MyApp().run()
Not sure I understand your question completely, but in your on_touch_move method you are moving all the child Box instances. But you are also changing the pos of the RootWidget itself. Since the RootWidget is the root window of the App, changing its pos property doesn't have any visual effect. However, that change affects the self.collide_point method (it uses pos and size to check for collision). So, if I understand your question, you just need to eliminate changing the pos of the RootWidget:
def on_touch_move(self, touch):
# check if it's a grabbed touch event
if touch.grab_current is self:
#self.pos = touch.pos
# comment out the next two lines
#self.pos[0] += touch.dx
#self.pos[1] += touch.dy
#redraw moved children
for child in self.children:
child.pos[0] += touch.dx
child.pos[1] += touch.dy
child.redraw()
# and accept the last move
return True

kivy image collider size way too big

What I tried to do:
I wanted my program to print a message when it collides with the sprite in the middle.
Problem:
It doesn't stop printing that it collided. I looked into it with pdb and it says that the Player Sprite is the size of the whole screen, which is weird because I set the image size to 32px by 32px.
What it looks like
Code and Sprites mentioned
Link to mentioned block
Link to mentioned sprite
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
import random
import time
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
#############################################################################
class Collider(Image):
pass
#############################################################################
root = FloatLayout()
class GameScreen(Screen):
def __init__(self, **kwargs):
super(GameScreen, self).__init__(**kwargs)
self.wimg = Basic(size = (32,32), source='sprite.png', pos=(270,-120))
self.block = Collider(size = (32,32), source = 'block.png')
def playy(self):
root.add_widget(self.wimg)
Clock.schedule_interval(self.wimg.update, 1.0 / 60.0)
def worldgen1(self):
self.add_widget(root)
root.add_widget(self.block)
def on_enter(self):
self.worldgen1()
self.playy()
#############################################################################
class Basic(Image):
pFrame = 0
def __init__(self, **kwargs):
super(Basic, 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] == 'left':
self.x -= 10
elif keycode[1] == 'right':
self.x += 10
elif keycode[1] == 'up':
self.y += 10
elif keycode[1] == 'down':
self.y -= 10
else:
return False
return True
def update(self, dt):
self.pFrame += 1
if self.parent.parent.block.collide_widget(self.parent.parent.wimg):
print ("collide with", self.parent.parent.block)
#############################################################################
class MeinApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(GameScreen(name='gameplay'))
return sm
if __name__ == '__main__':
MeinApp().run()
In this case the problem is that FloatLayout uses the size_hint to set the size of the widget when it is added, since it does not set it, they take the value of (1, 1), that is, the size of the window.
The solution is to set size_hint to (None, None), in addition to using allow_stretch in True and keep_ratio in False. In the code I have set the block pos_hint to (0.5, 0.5) and it will anchor it to that position so it will not move, if you want it to be able to move you must set the pos, not the pos_hint.
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
import random
import time
from kivy.uix.image import Image
from kivy.uix.widget import Widget
from kivy.uix.button import Button
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
#############################################################################
class Collider(Image):
pass
#############################################################################
root = FloatLayout()
class GameScreen(Screen):
def __init__(self, **kwargs):
super(GameScreen, self).__init__(**kwargs)
self.wimg = Basic(size = (32,32), pos=(200, 100), source='sprite.png', size_hint=(None, None), allow_stretch=True, keep_ratio=False)
self.block = Collider(size = (32,32), pos_hint={'x': 0.5, 'y':0.5}, source = 'block.png', size_hint=(None, None), allow_stretch=True, keep_ratio=False)
def playy(self):
root.add_widget(self.wimg)
Clock.schedule_interval(self.wimg.update, 1.0 / 60.0)
def worldgen1(self):
self.add_widget(root)
root.add_widget(self.block)
def on_enter(self):
self.worldgen1()
self.playy()
#############################################################################
class Basic(Image):
pFrame = 0
def __init__(self, **kwargs):
super(Basic, 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] == 'left':
self.x -= 10
elif keycode[1] == 'right':
self.x += 10
elif keycode[1] == 'up':
self.y += 10
elif keycode[1] == 'down':
self.y -= 10
else:
return False
return True
def update(self, dt):
self.pFrame += 1
if self.parent.parent.block.collide_widget(self.parent.parent.wimg):
print ("collide with", self.parent.parent.block)
#############################################################################
class MeinApp(App):
def build(self):
sm = ScreenManager()
sm.add_widget(GameScreen(name='gameplay'))
return sm
if __name__ == '__main__':
MeinApp().run()

Why is collide_widget not working here?

I am wondering why my 'collide_with_hero' method does not seem to be working? Is there something wrong with my Npcs class?
I am just trying to detect when widgets collide (the hero and the tree widget), I stripped out all the additional code in the method, and I am at the point now where I am just simply trying to detect collision and print something if True. When I run the game and walk my hero character into the tree, nothing prints, nothing happens.
I call self.tree.collide_with_widget in the build. Any advice on what I am doing wrong here?
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.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.core.audio import SoundLoader
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import FallOutTransition
gamelayout = RelativeLayout(size=(300, 300))
bglayout = RelativeLayout()
class Game(Screen):
pass
class Bg(Image):
def __init__(self, **kwargs):
super(Bg, self).__init__(**kwargs)
self.allow_stretch = True
self.size_hint = (None, None)
self.size = (1440, 1440)
class Npcs(Image):
def __init__(self, **kwargs):
super(Npcs, self).__init__(**kwargs)
def collide_with_hero(self, hero):
if self.collide_widget(hero):
print "you ran into a tree"
#dir1 = self.hero.x - self.x
#if self.x < self.hero.x:
# self.hero.x = self.x + dir1
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)
self._keyboard.bind(on_key_up=self.on_keyboard_up)
self.size_hint = (.11, .11)
self.y = (Window.height/2.1)
self.app = App.get_running_app()
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
self.source = 'selectionscreen/left.zip'
self.anim_delay=.20
if self.x < (Window.width * .25):
bglayout.x += 4
else:
self.x -= 6
elif keycode[1] == 'right':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.x > (Window.width * .70):
bglayout.x -= 4
else:
self.x += 6
elif keycode[1] == 'down':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.y < (Window.height * .25):
bglayout.y += 4
else:
self.y -= 6
elif keycode[1] == 'up':
self.source = 'selectionscreen/back.zip'
self.anim_delay=.1
if self.y > (Window.height * .70):
bglayout.y -= 4
else:
self.y += 6
else:
return False
return True
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'left':
self.source = 'selectionscreen/left1.png'
elif keycode[1] == 'right':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'down':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'up':
self.source ='selectionscreen/back2.png'
else:
return False
return True
class gameApp(App):
def build(self):
global sm
sm = ScreenManager()
game = Game(name='game')
sm.add_widget(game)
hero = MoveableImage(source='selectionscreen/right1.png', pos=(75, 40))
self.tree = Npcs(source='selectionscreen/tree.zip', pos=(100, 200))
self.tree.collide_with_hero(hero)
self.background=Bg(source='selectionscreen/background9.png')
#add widgets to bglayout
bglayout.add_widget(self.background)
bglayout.add_widget(self.tree)
#add bglayout and moveable hero to gamelayout
gamelayout.add_widget(bglayout)
gamelayout.add_widget(hero)
game.add_widget(gamelayout)
return sm
if __name__ == '__main__':
gameApp().run()
You need a main game loop to check for collision after some interval in time or on a keypress event. You can take a look at the Pong example here: http://kivy.org/docs/tutorials/pong.html to get an idea about how to achieve that.
More specifically start reading from this section: http://kivy.org/docs/tutorials/pong.html#adding-ball-animation.
Clock.schedule_interval(game.update, 1.0/60.0)
It uses Clock.schedule_interval to schedule a main loop function called "update".
def update(self, dt):
self.ball.move()
#bounce off top and bottom
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.velocity_y *= -1
#bounce off left and right
if (self.ball.x < 0) or (self.ball.right > self.width):
self.ball.velocity_x *= -1
You will need to write the collision detection code in a function like that. If your game involves more complex Physics simulation then you may consider using a library like http://box2d.org/. It will manage all the collisions and stuff for you.
First, you made self.tree an Image, but this doesn't have a collide_with_hero method. You need to make it an instance of the new class you defined, Npcs.
Second, you didn't call the collide_with_hero method. You need to do self.tree.collide_with_hero(hero) to actually call it including passing your hero as an argument to the function.

Categories