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()
Related
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.
I am trying to display the speedometer in my MainWindow screen. Right now when i run the code, the speedometer works but it is not displayed on the MainWindow screen which i want rather it is just appearing on a normal screen. It is possible to combine class Gauge(Widget): and class MainWindow(Screen): together so that the speedometer will actually be displayed on the MainWindow?
.py file
import kivy
kivy.require('1.6.0')
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import NumericProperty
from kivy.properties import StringProperty
from kivy.properties import BoundedNumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.progressbar import ProgressBar
from os.path import join, dirname, abspath
from kivy.uix.screenmanager import Screen, ScreenManager
class WindowManager(ScreenManager):
pass
class MainWindow(Screen):
pass
class Gauge(Widget):
unit = NumericProperty(1.8)
value = BoundedNumericProperty(0, min=0, max=100, errorvalue=0)
path = dirname(abspath(__file__))
file_gauge = StringProperty(join(path, "cadran.png"))
file_needle = StringProperty(join(path, "needle.png"))
size_gauge = BoundedNumericProperty(128, min=128, max=256, errorvalue=128)
size_text = NumericProperty(10)
def __init__(self, **kwargs):
super(Gauge, self).__init__(**kwargs)
self._gauge = Scatter(
size=(1350,600),
do_rotation=False,
do_scale=False,
do_translation=False
)
_img_gauge = Image(
source=self.file_gauge,
size=(1350,600)
)
self._needle = Scatter(
size=(self.size_gauge, self.size_gauge),
do_rotation=False,
do_scale=False,
do_translation=False
)
_img_needle = Image(
source=self.file_needle,
size=(self.size_gauge, self.size_gauge)
)
self._glab = Label(font_size=self.size_text, markup=True)
self._progress = ProgressBar(max=100, height=20, value=self.value , size=(500,400))
self._gauge.add_widget(_img_gauge)
self._needle.add_widget(_img_needle)
self.add_widget(self._gauge)
self.add_widget(self._needle)
self.add_widget(self._glab)
self.add_widget(self._progress)
self.bind(pos=self._update)
self.bind(size=self._update)
self.bind(value=self._turn)
def _update(self, *args):
'''
Update gauge and needle positions after sizing or positioning.
'''
self._gauge.pos = self.pos
self._needle.pos = (self.x, self.y)
self._needle.center = self._gauge.center
self._glab.center_x = self._gauge.center_x
self._glab.center_y = self._gauge.center_y + (self.size_gauge / 4)
self._progress.x = self._gauge.x + (self.size_gauge/0.468 )
self._progress.y = self._gauge.y + (self.size_gauge/4 )
self._progress.width = self.size_gauge
def _turn(self, *args):
'''
Turn needle, 1 degree = 1 unit, 0 degree point start on 50 value.
'''
self._needle.center_x = self._gauge.center_x
self._needle.center_y = self._gauge.center_y
self._needle.rotation = (50 * self.unit) - (self.value * self.unit)
self._glab.text = "[b]{0:.0f}[/b]".format(self.value)
self._progress.value = self.value
class GaugeApp(App):
increasing = NumericProperty(1)
begin = NumericProperty(50)
step = NumericProperty(1)
def build(self):
box = BoxLayout(orientation='horizontal', padding=5)
self.gauge = Gauge(value=50, size_gauge=256, size_text=25)
box.add_widget(self.gauge)
Clock.schedule_interval(lambda *t: self.gauge_increment(), 0.05)
return box
def gauge_increment(self):
begin = self.begin
begin += self.step * self.increasing
if begin > 0 and begin < 100:
self.gauge.value = begin
else:
self.increasing *= -1
self.begin = begin
if __name__ == '__main__':
GaugeApp().run()
You need to create an instance of ScreenManager and an instance of Screen. Next you add_widget the Screen to ScreenManager and to the Screen you add box
sm = ScreenManager()
s1 = Screen()
s1.add_widget(box)
sm.add_widget(s1)
This is the complete code which uses your new classes
from kivy.app import App
from kivy.clock import Clock
from kivy.properties import NumericProperty
from kivy.properties import StringProperty
from kivy.properties import BoundedNumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.progressbar import ProgressBar
from os.path import join, dirname, abspath
from kivy.uix.screenmanager import Screen, ScreenManager
class WindowManager(ScreenManager):
pass
class MainWindow(Screen):
pass
class Gauge(Widget):
unit = NumericProperty(1.8)
value = BoundedNumericProperty(0, min=0, max=100, errorvalue=0)
path = dirname(abspath(__file__))
file_gauge = StringProperty(join(path, "cadran.png"))
file_needle = StringProperty(join(path, "needle.png"))
size_gauge = BoundedNumericProperty(128, min=128, max=256, errorvalue=128)
size_text = NumericProperty(10)
def __init__(self, **kwargs):
super(Gauge, self).__init__(**kwargs)
self._gauge = Scatter(
size=(1350,600),
do_rotation=False,
do_scale=False,
do_translation=False
)
_img_gauge = Image(
source=self.file_gauge,
size=(1350,600)
)
self._needle = Scatter(
size=(self.size_gauge, self.size_gauge),
do_rotation=False,
do_scale=False,
do_translation=False
)
_img_needle = Image(
source=self.file_needle,
size=(self.size_gauge, self.size_gauge)
)
self._glab = Label(font_size=self.size_text, markup=True)
self._progress = ProgressBar(max=100, height=20, value=self.value , size=(500,400))
self._gauge.add_widget(_img_gauge)
self._needle.add_widget(_img_needle)
self.add_widget(self._gauge)
self.add_widget(self._needle)
self.add_widget(self._glab)
self.add_widget(self._progress)
self.bind(pos=self._update)
self.bind(size=self._update)
self.bind(value=self._turn)
def _update(self, *args):
'''
Update gauge and needle positions after sizing or positioning.
'''
self._gauge.pos = self.pos
self._needle.pos = (self.x, self.y)
self._needle.center = self._gauge.center
self._glab.center_x = self._gauge.center_x
self._glab.center_y = self._gauge.center_y + (self.size_gauge / 4)
self._progress.x = self._gauge.x + (self.size_gauge/0.468 )
self._progress.y = self._gauge.y + (self.size_gauge/4 )
self._progress.width = self.size_gauge
def _turn(self, *args):
'''
Turn needle, 1 degree = 1 unit, 0 degree point start on 50 value.
'''
self._needle.center_x = self._gauge.center_x
self._needle.center_y = self._gauge.center_y
self._needle.rotation = (50 * self.unit) - (self.value * self.unit)
self._glab.text = "[b]{0:.0f}[/b]".format(self.value)
self._progress.value = self.value
class GaugeApp(App):
increasing = NumericProperty(1)
begin = NumericProperty(50)
step = NumericProperty(1)
def build(self):
box = BoxLayout(orientation='horizontal', padding=5)
self.gauge = Gauge(value=50, size_gauge=256, size_text=25)
box.add_widget(self.gauge)
Clock.schedule_interval(lambda *t: self.gauge_increment(), 0.05)
sm = WindowManager()
s1 = MainWindow()
s1.add_widget(box)
sm.add_widget(s1)
return sm
def gauge_increment(self):
begin = self.begin
begin += self.step * self.increasing
if begin > 0 and begin < 100:
self.gauge.value = begin
else:
self.increasing *= -1
self.begin = begin
if __name__ == '__main__':
GaugeApp().run()
I have a simple paint kivy I wish to add shape recognizer to it similar as in xournal.
Quoting docs:
"The shape recognizer is also a special operating mode
of the pen and highlighter tools. When it is enabled, Xournal attempts
to recognize geometric shapes as they are drawn, and if successful
will replace the drawn strokes accordingly. The shapes that can be
recognized are: line segments, circles, rectangles, arrows, triangles
and quadrilaterals. Polygonal shapes can be drawn in a single stroke
or in a sequence of consecutive strokes."
Code:
from kivy.base import EventLoop
from kivy.config import Config
from kivy.graphics import Color, Line
from kivy.uix.behaviors import ToggleButtonBehavior
from kivy.uix.togglebutton import ToggleButton
from kivy.uix.widget import Widget
from kivy.utils import get_color_from_hex
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.clock import Clock
class Update_Location(Widget):
pass
class CanvasWidget(Widget):
line_width = 2
def on_touch_down(self, touch):
if Widget.on_touch_down(self, touch):
return
with self.canvas:
touch.ud['current_line'] = Line(
points=(touch.x, touch.y),
width=self.line_width)
def on_touch_move(self, touch):
if 'current_line' in touch.ud:
touch.ud['current_line'].points += (touch.x, touch.y)
def set_color(self, new_color):
self.last_color = new_color
self.canvas.add(Color(*new_color))
def set_line_width(self, line_width='Normal'):
self.line_width = {
'Thin': 1, 'Normal': 2, 'Thick': 4
}[line_width]
def clear_canvas(self):
saved = self.children[:]
self.clear_widgets()
self.canvas.clear()
for widget in saved:
self.add_widget(widget)
self.set_color(self.last_color)
def start_server(self):
host = '127.0.0.1'
port = 5000
notification_text="Server started on host: "+host+" and port: "+str(port)
server_start=Popup(title='Notification',content=Label(text=notification_text),size_hint=(.75,.75),auto_dismiss=True)
server_start.open()
Clock.schedule_interval(server_start.dismiss, 3)
class PaintApp(App):
def build(self):
EventLoop.ensure_window()
if EventLoop.window.__class__.__name__.endswith('Pygame'):
try:
from pygame import mouse
a, b = pygame_compile_cursor()
mouse.set_cursor((24, 24), (9, 9), a, b)
except:
pass
#boxlayout
self.layout = BoxLayout(orientation='vertical')
self.canvas_widget = CanvasWidget()
self.canvas_widget.set_color(
get_color_from_hex('#2980b9'))
self.layout.add_widget(self.canvas_widget)
#self.layout.add_widget(Label(text="Started Server : False , Connected to Server : False",color=(1,1,1),size_hint=(1, .1)))
return self.layout
#return self.canvas_widget
class RadioButton(ToggleButton):
def _do_press(self):
if self.state == 'normal':
ToggleButtonBehavior._do_press(self)
def pygame_compile_cursor(black='#', white='-'):
aa, bb = [], []
a = b = 0
i = 8
for s in CURSOR:
for c in s:
a <<= 1
b <<= 1
i -= 1
if c == black:
a |= 1
b |= 1
elif c == white:
b |= 1
if not i:
aa.append(a)
bb.append(b)
a = b = 0
i = 8
return tuple(aa), tuple(bb)
if __name__ == '__main__':
Config.set('graphics', 'width', '960')
Config.set('graphics', 'height', '540') # 16:9
# Config.set('graphics', 'resizable', '0')
# Config.set('input', 'mouse', 'mouse,disable_multitouch')
from kivy.core.window import Window
Window.clearcolor = get_color_from_hex('#ffffff')
PaintApp().run()
Now, I have a set of points stored in touch.ud['current_line']. On on_touch_up I should pass these points/set of coordinates which replaces them with the shape of the corresponding size. Are there any such code in available, I wasn't to find in xounral source clearly.
I am using a screen widget to create a slider image with kivy. When triggering the event on_touch_down, it runs normally.
#!/usr/bin/env python
import kivy, os
from kivy.uix.screenmanager import Screen, FadeTransition
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.core.window import Window
#from kivy.properties import ObjectProperty
#Builder.load_file('/home/kzulfazriawan/Dropbox/project/Around/kv/introduct.kv')
clocking = 0
class INTRODUCTSCREEN(Screen):
def __init__(self, **kwargs):
super(INTRODUCTSCREEN, self).__init__(**kwargs)
global clocking
imaging = Image()
boxes = BoxLayout()
self.__image_location = kwargs['image_dir'].decode('utf-8')
try :
imaging.source = self.__image_location
except ValueError,e:
print e
self.__time_count = clocking
self.__limit_count = round(2 * kwargs['counting'])
self.__situation = kwargs['situation']
boxes.add_widget(imaging)
self.add_widget(boxes)
self.name = kwargs['current_name']
self.size = imaging.texture_size
Clock.schedule_interval(self.eventCountdown, 1.)
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
self._keyboard.bind(on_key_up = self._on_keyboard_up)
def eventCountdown(self, dt):
self.__time_count += 1
if self.__time_count > self.__limit_count:
self.changeEventIntro(self.__situation)
def _keyboard_closed(self):
self._keyboard.unbind(on_key_up=self._on_keyboard_up)
self._keyboard = None
def on_touch_down(self, touch):
self.changeEventIntro(self.__situation)
def _on_keyboard_up(self, *args):
keycode = args[1]
if keycode[1] == 'enter':
self.changeEventIntro(self.__situation)
return True
def changeEventIntro(self, situation):
Clock.unschedule(self.eventCountdown)
self.parent.transition = FadeTransition()
os.unlink(self.__image_location)
if situation == 'finalscreen':
self.parent.introduct('destroy')
self.parent.prepareGamePlay('startup_panel')
else:
self.parent.current = self.parent.next()
However, when I try to add some event widget with on_key_up, it triggers an error:
File "/home/kzulfazriawan/Dropbox/project/Around/data/script/ready_to_compile/introduct.py", line 76, in changeEventIntro
self.parent.transition = FadeTransition()
AttributeError: 'NoneType' object has no attribute 'transition'
If I run my application and click the screen then it works but when I try to press the "enter" button. The error is triggered.
Why does the reference to self.parent trigger an error for on_key_up but not on_touch_down?
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.