How to make images move in kivy - python

I'm trying to make my bird image move up and then automatically come down. I was watching a youtube video on this here's the link https://www.youtube.com/watch?v=2dn_ohAqkus&ab_channel=ErikSandberg
his seems to be working but not mines. Around Minute 14 he starts coding about what I'm talking about. One thing that does work for me is when I click on the screen the bird changes. Here's my code
class GameScreen(Screen):
pass
class Bird(Image):
velocity = NumericProperty(0)
def on_touch_down(self, touch):
self.source = "icons/bird2.png"
self.velocity = 150
super().on_touch_down(touch)
def on_touch_up(self, touch):
self.source = "icons/bird1.png"
super().on_touch_up(touch)
def move_bird(self, time_passed):
bird = self.root.ids.bird
bird.y = bird.y + bird.velocity * time_passed
bird.velocity = bird.velocity - self.GRAVITY * time_passed
Clock.schedule_interval(self.move_bird, 1/60)
my kivy code note this in inside a FloatLayout
Bird:
source: "icons/bird1.png"
size_hint: None, None
size: 475, 475
pos_hint: { "center_x": .5, "center_y": .4}
id: bird

You will not be able to change the bird position when you have set pos_hint (as you did in the kivy code). The pos_hint will take priority over pos. Try replacing the pos_hint with some initial pos in the kivy code.

Related

kivy move image only when image is pressed

I'm making a game in kivy where when you press on the soccer ball it moves. I want the ball to move only when I press exactly on the ball, if I press anywhere else it shouldn't move. Currently, the ball moves if I press anywhere close to the ball which Is not what I want. Is there anything I can do so that it only moves when I press exactly on the ball? Below is my code!
main.py
class Ball(Image):
velocity = NumericProperty(0)
def on_touch_down(self, touch):
if self.collide_point(*touch.pos):
self.source = "icons/ball.png"
self.velocity = 275
return super().on_touch_down(touch)
def on_touch_up(self, touch):
if self.collide_point(*touch.pos):
self.source = "icons/ball.png"
return super().on_touch_up(touch)
class MainApp(App):
GRAVITY = 300
def move_ball(self, time_passed):
ball = self.root.ids.game_screen.ids.ball
ball.y = ball.y + ball.velocity * time_passed
ball.velocity = ball.velocity - self.GRAVITY * time_passed
def start_game(self):
Clock.schedule_interval(self.move_ball, 1/60.)
kivy code
#:import utils kivy.utils
<GameScreen>:
FloatLayout:
canvas:
Color:
rgb: utils.get_color_from_hex("#39B3F2")
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .1
Image:
source: "icons/sun.png"
GridLayout:
rows: 1
pos_hint: {"top": 1, "left": 1}
size_hint: 1, .2
Image:
source: "icons/clouds.png"
GridLayout:
rows: 1
pos_hint: {"bottom": 1, "left": 1}
size_hint: 1, .5
Image:
source: "icons/Field4.png"
allow_stretch: True
keep_ratio: False
pos: self.pos
Button:
size_hint: None, None
font_size: dp(20)
font_name: 'SackersGothicStd-Medium.otf'
text: "Start Game"
color: "gold"
pos_hint: { "center_x": 0.5, "center_y": 0.3}
size: 150, 55
size_hint: None, None
background_normal: ''
background_color: (57/255.0, 179/255.0, 242/255.0, .10)
on_release:
self.disabled = True
self.opacity = 0
root.play_sound()
app.start_game()
Ball:
source: "icons/ball.png"
size_hint: None, None
size: 500, 500
pos_hint: {"center_x": 0.5}
id: ball
I also tried keep_ratio: True
allow_stretch: True
but it still didn't work
ball is a circle (or rather disk) which has some radius - if you check distance between ball center and mouse position and it is smaller then radius then you clicked inside this circle or disk.
More precisely using the Pythagorean formula a**2 + b**2 == c**2
or rather disk's definition : x**2 + y**2 <= r**2
(ball.centerx - touch.pos.x)**2 + (ball.centery - touch.pos.y)**2 <= radius**2
Kivy has class Vector to make it simpler - but I can't test it.
Vector(ball.center).distance(touch.pos) <= radius
And you should use it instead of collide_point()
class Ball(Image):
velocity = NumericProperty(0)
radius = 50 # you have to get it manually from image
def on_touch_down(self, touch):
if Vector(self.center).distance(touch.pos) <= radius:
self.source = "icons/ball.png"
self.velocity = 275
return super().on_touch_down(touch)
def on_touch_up(self, touch):
if Vector(self.center).distance(touch.pos) <= radius:
self.source = "icons/ball.png"
return super().on_touch_up(touch)
I'm not sure but maybe it even keeps ball position as Vector() and you could use directly
ball.center.distance(touch.pos) <= radius

Kivy PongApp ball goes through paddle at high velocity

I am new to python and kivy, and was following the Kivy tutorial on creating the PongApp line by line when I noticed that after the ball collides with a paddle 25 times, it will not register the 26th collision and thus the player's score will increase.
I assume this issue is related to the velocity moving past a certain speed along the x axis where the ball's position will never interact with the paddle's x position. However what's confusing me is when changing the speed increase per collision from 1.1 to 1.2, the maximum number of collisions before score increases varies from 11 to 18 times.
My question is, what is actually the root cause of this issue, and how can I go about ensuring the ball will always collide with the paddle regardless of the velocity?
main.py:
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import (NumericProperty, ReferenceListProperty, ObjectProperty)
from kivy.vector import Vector
from kivy.clock import Clock
from random import randint
class PongPaddle(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
vx, vy = ball.velocity
offset = (ball.center_y - self.center_y) / (self.height / 2)
bounced = Vector(-1 * vx, vy)
vel = bounced * 3
ball.velocity = vel.x, vel.y + offset
class PongBall(Widget):
# velocity of the ball on x and y axis
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
# referencelist property so we can use ball.velocity as
# a shorthand, just like e.g. w.pos for w.x and w.y
velocity = ReferenceListProperty(velocity_x, velocity_y)
# ``move`` function will move the ball one step. This
# will be called in equal intervals to animate the ball
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
player1 = ObjectProperty(None)
player2 = ObjectProperty(None)
def serve_ball(self, vel=(4, 0)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
#bounce off paddles
self.player1.bounce_ball(self.ball)
self.player2.bounce_ball(self.ball)
# 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 to score point
if self.ball.x < self.x:
self.player2.score += 1
self.serve_ball(vel=(4, 0))
if self.ball.x > self.width:
self.player1.score += 1
self.serve_ball(vel=(-4, 0))
def on_touch_move(self, touch):
if touch.x < self.width / 3:
self.player1.center_y = touch.y
if touch.x > self.width - self.width / 3:
self.player2.center_y = touch.y
class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0/60.0)
return game
if __name__ == '__main__':
PongApp().run()
pong.kv:
#:kivy 2.0.0
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongPaddle>:
size: 25, 200
canvas:
Rectangle:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
player1: player_left
player2: player_right
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str(root.player1.score)
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: str(root.player2.score)
PongBall:
id: pong_ball
center: self.parent.center
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
This is a fundamental problem with simple collision algorithms: collision is only checked once each frame after moving the objects, and if they move through each other during that frame the algorithm has no way to notice. The issue is well known, for instance versions of this problem are one reason it's often possible to contrive to clip through walls in computer games (although in general games nowadays do use more complex algorithms, and are tricked in more complex ways).
In a general sense the way to solve this is to use a better collision detection algorithm. For instance, for two spheres you can work out if they ever will have intersected during that frame by working out the point of closest approach of their centre points during the linear movement step of the frame, and comparing that distance to their radii to see if they would have collided.
Doing generic collisions efficiently is in general a hard problem, with various trade-offs you can make depending on what you want to optimise for (e.g. if you want to handle approximate collisions of many objects efficiently you have different problems to if you want to handle precisely a small number of objects with complex shapes). Kivy's example uses a simple algorithm to avoid making the example complex, not because it's a good solution. If you really care about doing collisions efficiently I'd recommend using a physics library to handle it, there are various libraries with python interfaces.

Play Again function in kivy

I am currently building a game with kivy. Everything works fine except the play again button.
So here is what I want for my play again. The score should be set to 0 and everything has to freeze and a play again button should appear. When I click on it the enemies should respawn to the top.
I tried to t´do it with Clock.schedule_interval and Clock.unschedule_interval. But that only works for the resume button. When I try it with enemies. Everything freezes but then when I click play again. Nothing happens. And the score also doesn't go to 0.
Here is my code:
Main GameClass: The play again button appers after stop game. And then when I click on it it does not unfreeze
class PongGame(Widget):
ball = ObjectProperty(None)
ship = ObjectProperty(None)
enemy = ObjectProperty(None)
badenemy = ObjectProperty(None)
resume_button = ObjectProperty(None)
play_again = ObjectProperty(None)
x_change = NumericProperty(3)
start_button = ObjectProperty(None)
label_text = StringProperty()
def __init__(self, **kwargs):
super(PongGame, self).__init__(**kwargs)
self.count = 0
self.label_text = str(self.count)
def increment(self, **kwargs):
self.count += 5
self.label_text = str(self.count)
print(self.label_text)
enemies = ListProperty([])
badenemies = ListProperty([])
def add_enemy(self, *args):
enemy = Enemy()
enemy.pos = (random.randint(0,600),random.randint(30,600))
self.add_widget(enemy)
self.enemies.append(enemy)
def add_badenemy(self, *args):
badenemy = BadEnemy()
badenemy.pos = (random.randint(0,600),randint(350,600))
self.add_widget(badenemy)
self.badenemies.append(badenemy)
def update(self,dt):
self.ball.move()
for enemy in self.enemies:
enemy.move()
enemy.collision(self.ball)
enemy.ship_collision(self.ship)
for badenemy in self.badenemies:
badenemy.move()
def stop(self):
Clock.unschedule(self.update)
Clock.unschedule(self.add_enemy)
self.resume_button.x = self.width / 2.25 -50
self.resume_button.y = self.height - 350
def resume(self):
Clock.schedule_interval(self.update, 1 / 60.0)
self.count = 0
new_enemy_event = Clock.schedule_interval(self.add_enemy, 2)
self.resume_button.x = 1000
self.resume_button.y = 1000
def stop_game(self):
Clock.unschedule(self.update)
Clock.unschedule(self.add_enemy)
Clock.unschedule(self.add_badenemy)
self.count = 0
self.play_again.x = self.width / 2.25 - 50
self.play_again.y = self.height - 350
Enemy Class. So here I cant call the stopgame function
class Enemy(Widget):
x_change = NumericProperty(3)
y_change = NumericProperty(-50)
score = NumericProperty(0)
def collision(self, ball):
if self.collide_widget(ball):
self.y = 1000
PongGame.increment(self.parent)
ball.x = -1000
ball.y = self.height / self.width
def ship_collision(self,ship):
if self.collide_widget(ship):
PongGame.stop_game(self.parent)
def move(self, *args):
self.x -= self.x_change
if self.x >= self.parent.width - 64:
self.x_change = 3
self.y += self.y_change
elif self.x <= 0:
self.x_change = -3
self.y += self.y_change
App class
class PongApp(App):
def build(self):
game = PongGame()
# game.serve_ball()
# pro second 60 frames are shown
self.sound = SoundLoader.load('kgf.mp3')
self.sound.play()
Clock.schedule_interval(self.loop_sound, 1)
return game
Some of my kv code
<PongGame>:
ball: pong_ball
ship: space_ship
start_button: start_button
resume_button:resume_button
play_again:play_again
canvas.before:
Rectangle:
pos: self.pos
size: self.size
source: 'background.png'
Label:
font_size: 40
x:root.width/ 1/4 +70
y:root.height -350
text: 'Your score :'
Label:
font_size: 40
x:root.width/ 1/2
y:root.height -350
text: root.label_text
Button:
id:play_again
font_size:40
size:200,100
x:10000
y:10000
text:'Play Again'
on_release:
self.parent.play_again_game()
PongBall:
id: pong_ball
x: root.width /1.4
y: root.height / root.width
SpaceShip:
id: space_ship
x: root.width / 2.25
y: root.height / root.width +60
So I think my main problem is ,that I cant refer to the method in my game class. And my score is not going to 0. I think I just cant write self.count = 0

How can i fix my bouncing ball glitching through the bottom of my screen?

If your run it once the ball is about to stop it starts skipping up and down a little bit and disappears through the floor.
So i was following the kivy tutorial for the pong game and half way through i thought why not make the ball have gravity...i kind of figured that out though I don't know wether it's good code or not but that's not my problem. I already compared it to some other code basically doing the same and didn't find any differences. Could somebody tell me what i have done wrong? (Sorry i had to paste all my code in here but i don't know where the problem is...)
import kivy
kivy.require("1.10.1")
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty, ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
from random import randint
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def update_speed(self):
self.velocity[1] = self.velocity[1] - 15/60
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
def serve_ball(self):
self.ball.center = self.center
self.ball.velocity = Vector(0, 0)
def update(self, dt):
self.ball.update_speed()
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.velocity_y *= -1
if (self.ball.x < 0) or (self.ball.right > self.width):
self.serve_ball()
class PongApp(App):
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0/60.0)
return game
if __name__ == '__main__':
PongApp().run()
This is my kv file:
#:kivy 1.10.1
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
canvas:
Rectangle:
pos: self.center_x - 5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.height * 9 / 10
text: "0"
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.height * 9 / 10
text: str(pong_ball.velocity[1])
PongBall:
id: pong_ball
center: self.parent.center
I want the ball to slow down until it lays on the ground and doesn't move.
this is just a common problem with floats ...
just in your update speed function do something like
if velocity <= 1e-6: # some really small number
velocity = 0.0
its also probably a good idea to fix your y position in your move function
if (self.ball.y < 0) or (self.ball.top > self.height):
self.ball.y = 0.1
self.ball.velocity_y *= -1

How to display game on the screen?

I'm trying to make an kivy app with starting menu, but I can't display my Pong Game on the second screen. How should I refer to the game to make it visible? I tried and searched but can't find anything. I'm 100% sure that PongGame work corectly, I just can't display it. It would be great if someone could show me how to do it corectly.
Main.py:
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen, WipeTransition
from kivy.properties import ObjectProperty
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty,\
ObjectProperty
from kivy.vector import Vector
from kivy.clock import Clock
from kivy.uix.popup import Popup
from kivy.uix.label import Label
class PongPaddle(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
vx, vy = ball.velocity
offset = (ball.center_y - self.center_y) / (self.height / 2)
bounced = Vector(-1 * vx, vy)
vel = bounced * 1.1
ball.velocity = vel.x, vel.y + offset
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
player1 = ObjectProperty(None)
player2 = ObjectProperty(None)
def serve_ball(self, vel=(4, 0)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
#bounce of paddles
self.player1.bounce_ball(self.ball)
self.player2.bounce_ball(self.ball)
#bounce ball off bottom or top
if (self.ball.y < self.y) or (self.ball.top > self.top):
self.ball.velocity_y *= -1
#went of to a side to score point?
if self.ball.x < self.x:
self.player2.score += 1
self.serve_ball(vel=(4, 0))
if self.ball.x > self.width:
self.player1.score += 1
self.serve_ball(vel=(-4, 0))
if self.player1.score == 10:
popup = Popup(title='Test popup', content=Label(text='Hello world'), auto_dismiss=False)
return popup
def on_touch_move(self, touch):
if touch.x < self.width / 3:
self.player1.center_y = touch.y
if touch.x > self.width - self.width / 3:
self.player2.center_y = touch.y
class ScreenThree(Screen):
pass
class ScreenTwo(Screen):
pass
class ScreenOne(Screen):
pass
class Manager(ScreenManager):
screen_one = ObjectProperty(None)
screen_two = ObjectProperty(None)
screen_three = ObjectProperty(None)
class ScreensApp(App):
def build(self):
m = Manager(transition=WipeTransition())
return m
if __name__ == '__main__':
ScreensApp().run()
screen.kv:
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongPaddle>:
size: 25, 200
canvas:
Rectangle:
pos:self.pos
size:self.size
<PongGame>:
ball: pong_ball
player1: player_left
player2: player_right
canvas:
Rectangle:
pos: self.center_x-5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str(root.player1.score)
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: str(root.player2.score)
PongBall:
id: pong_ball
center: self.parent.center
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
<ScreenOne>:
Button:
text: "Screen 1 >> Screen 2"
on_press: root.manager.current = 'screen2'
<ScreenTwo>:
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
<ScreenThree>:
Button:
text: "Screen 3 >> Screen 1"
on_press: root.manager.current = 'screen1'
<Manager>:
id: screen_manager
screen_one: screen_one
screen_two: screen_two
screen_three: screen_three
ScreenOne:
id: screen_one
name: 'screen1'
manager: screen_manager
ScreenTwo:
id: screen_two
name: 'screen2'
manager: screen_manager
ScreenThree:
id: screen_three
name: 'screen3'
manager: screen_manager
Well! there were a lot of errors in your program and I had to make ton's of improvement. (I understand it as you are beginner)
First of all, please read complete kivy language documentation, as I can clearly see that you directly started with coding without grasping the basics.
You may make couple of good games but in the long run you will face such problems which can't be solved without clear concepts.
And unfortunately you won't be able to discover the true power of kivy. :)
You might also wanna do revision of your python concepts.
Some improvements aren't worth mentioning but were important, you will get an idea when you read the code.
Improvement 1:
An application can be built if you return a widget on build(), or if you set self.root.(But you cannot make the application again n again)
as you did here:
<ScreenTwo>:
def build(self):
game = PongGame()
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
Improvement 2:
As you click on button play ping pong which is of screen game. your game starts with serve of ball.
on_release: root.current = 'game';game.serve_ball()
(for knowledge)
If you still get black screen you might want to check the name of kivy file, for that you could either go to kivy documentation or this link
class PongPaddle(Widget):
score = NumericProperty(0)
def bounce_ball(self, ball):
if self.collide_widget(ball):
vx, vy = ball.velocity
offset = (ball.center_y - self.center_y) / (self.height / 2)
bounced = Vector(-1 * vx, vy)
vel = bounced * 1.1
ball.velocity = vel.x, vel.y + offset
class PongBall(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self):
self.pos = Vector(*self.velocity) + self.pos
class PongGame(Widget):
ball = ObjectProperty(None)
player1 = ObjectProperty(None)
player2 = ObjectProperty(None)
def __init__(self, *args, **kwargs):
super(PongGame, self).__init__(*args, **kwargs)
Clock.schedule_interval(self.update, 1.0 / 60.0)
def serve_ball(self, vel=(4, 0)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
#bounce of paddles
self.player1.bounce_ball(self.ball)
self.player2.bounce_ball(self.ball)
#bounce ball off bottom or top
if (self.ball.y < self.y) or (self.ball.top > self.top):
self.ball.velocity_y *= -1
#went of to a side to score point?
if self.ball.x < self.x:
self.player2.score += 1
self.serve_ball(vel=(4, 0))
if self.ball.x > self.width:
self.player1.score += 1
self.serve_ball(vel=(-4, 0))
def on_touch_move(self, touch):
if touch.x < self.width / 3:
self.player1.center_y = touch.y
if touch.x > self.width - self.width / 3:
self.player2.center_y = touch.y
class Manager(ScreenManager):
pass
class ScreensApp(App):
def build(self):
self.load_kv('t6.kv')
return Manager(transition=WipeTransition())
if __name__ == '__main__':
ScreensApp().run()
Here is the kv file.
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongPaddle>:
size: 25, 200
canvas:
Rectangle:
pos:self.pos
size:self.size
<PongGame>:
ball: pong_ball
player1: player_left
player2: player_right
canvas:
Rectangle:
pos: self.center_x-5, 0
size: 10, self.height
Label:
font_size: 70
center_x: root.width / 4
top: root.top - 50
text: str(root.player1.score)
Label:
font_size: 70
center_x: root.width * 3 / 4
top: root.top - 50
text: str(root.player2.score)
PongBall:
id: pong_ball
center: self.parent.center
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
<Manager>:
id: screen_manager
Screen:
name: 'home'
Button:
text: 'Play Ping Pong'
halign: 'center'
valign: 'middle'
font_size: 100
text_size: self.size
on_release: root.current = 'game';game.serve_ball()
Screen:
name: 'game'
PongGame:
id: game

Categories

Resources