hello My problem is that I dont know how to place a button inside my game without making the gamescreen gone.
whenever I try to add return btn1 inside the code it just shows the button. but not the game. I am sure this is a beginner question but looking it up did not work for me.
BTW yes this is the code from a tutorial which I use privately
my code is:
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
import kivy.uix.button
from kivy.app import App
from kivy.uix.image import Image
from kivy.uix.behaviors import ButtonBehavior
from kivy.uix.button import Button
def callback(instance):
print('The button <%s> is being pressed' % instance.text)
btn1 = Button(text='Hello world 1')
btn1.bind(on_press=callback)
btn2 = Button(text='Hello world 2')
btn2.bind(on_press=callback)
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))
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)
if __name__ == '__main__':
PongApp().run()
If you want one widget to be part of another, this widget must be the son of the other widget or its ancestor is the son of that widget, and when you return btn1 in the build() you are indicating that it is the root, that is, the window, it does not show the game, to make a widget's son an option is to use add_widget():
class PongApp(App):
def build(self):
game = PongGame()
game.add_widget(btn1) # <--
btn2.pos = 100, 100
game.add_widget(btn2) # <--
game.serve_ball()
Clock.schedule_interval(game.update, 1.0 / 60.0)
return game
Related
I have a simple code in kivy where the ball bounces off the paddle and what i would like to achieve is make ball grounded after the bouncing of the ball ends. So i would like to simulate gravity. Problem is that collide_widget does not detect collision properly. I expect collide_widget will detect collision when ball.y == paddle.top but it detect collision when ball.y < paddle.top and it is always different depending on starting position of ball.
gravity.py
import kivy
kivy.require('1.11.1')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.properties import NumericProperty, ReferenceListProperty
from kivy.vector import Vector
from kivy.clock import Clock
DELTA_TIME = 1.0 / 60.0
class Ball(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self, dt):
self.pos = Vector(*self.velocity) *dt + self.pos
class Paddle(Widget):
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
velocity = ReferenceListProperty(velocity_x, velocity_y)
def move(self, dt):
self.pos = Vector(*self.velocity) *dt + self.pos
class GravityBall(Ball):
def update(self, dt, idle):
if idle == 0:
self.velocity_y -= 20
self.move(dt)
else:
self.velocity_y = 0
self.move(dt)
class GravityGame(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.idle = 0
self.ball = GravityBall()
self.ball.center = (200,300)
self.add_widget(self.ball)
self.paddle = Paddle()
self.paddle.center = (200,100)
self.add_widget(self.paddle)
Clock.schedule_interval(self.update, DELTA_TIME)
def update(self, dt):
self.ball.update(dt, self.idle)
self.paddle.move(dt)
if self.ball.collide_widget(self.paddle) and self.ball.y+5 >= self.paddle.top:
self.ball.velocity_y *= -1
self.idle = 0
elif self.ball.collide_widget(self.paddle) and self.ball.y+5 < self.paddle.top:
self.idle = 1
class GravityApp(App):
def build(self):
return GravityGame()
if __name__ == '__main__':
GravityApp().run()
gravity.kv
#:kivy 1.11.1
<GravityBall>:
size: 30, 30
canvas:
Ellipse:
pos: self.pos
size: self.size
<Paddle>:
size: 100, 30
canvas:
Color:
rgb: 1, 0, 1
Rectangle:
pos: self.pos
size: self.size
The collide_point() method is not likely to capture the exact moment when your ball touches the paddle, so you must an approximation. Here is a modified version of your GravityGame class that does so:
class GravityGame(Widget):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.idle = 0
self.ball = GravityBall()
self.ball.center = (200, 300)
self.add_widget(self.ball)
self.paddle = Paddle()
self.paddle.center = (200, 100)
self.add_widget(self.paddle)
self.sched = Clock.schedule_interval(self.update, DELTA_TIME) # save a reference to this event
def update(self, dt):
self.ball.update(dt, self.idle)
self.paddle.move(dt)
# capture approximate collision event
if self.ball.collide_widget(self.paddle):
self.ball.y = self.paddle.top # adjust ball position to top of paddle
self.ball.velocity_y *= -0.75 # collision is not perfectly elastic, so use a value between 0 and -1
# check if ball velocity is nearly stopped (does not move a single pixel in an interval)
if self.ball.velocity_y * DELTA_TIME < 1.0:
self.idle = 1
self.sched.cancel() # stop updating
else:
self.idle = 0
Note a reference to the scheduled update event is saved, so that the updates can be cancelled when the ball stops. The reversing of the velocity on a collision should emulate a non-elastic collision, so I have changed *= -1 to *= -0.75. Finally, if the ball velocity is low enough that the ball does not move in a time interval, then consider the ball to have stopped.
I'm trying to get into game creation using these tools.. Python, Kivy, Buildozer. I was following this good tutorial from Richard Jones, tutorial from Richard Jones
I was using my own graphics then what he used in video as I didn't have access to his files initially. Everything worked in python and buildozer would compile ok with no errors discernable. However when running in android simulator, the app would load and show the kivy logo along with 'loading'. After a few seconds the screen would go black and then it would switch to the android home screen. Not sure here, where to check to see if any errors occurred. I was eventually able to find a post in youtube vid on where to get the files he used in his project, Richard's code. I built his code in buildozer and it ran ok on the android simulator. I then tried to see if it was my graphics... Sure enough, that caused the problem.. My background image and ground image were a different resolution.. His graphics dim were, background 288, 384, mine 500, 592. Ground, his 336, 50 , mine was 700, 76. (all in px). His code did have scaling logic to scale the graphics to fit the screen so I'm not sure why the graphics dimensions would cause the android simulator to have those symptoms. I'll paste his code...
import random
import kivy
kivy.require('1.8.0')
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.uix.label import Label
from kivy.core.audio import SoundLoader
from kivy.logger import Logger
from kivy.metrics import Metrics
from kivy.graphics import Color, Rectangle
from kivy_fix import SpriteAtlas
class MultiSound(object):
def __init__(self, file, num):
self.num = num
self.sounds = [SoundLoader.load(file) for n in range(num)]
self.index = 0
def play(self):
self.sounds[self.index].play()
self.index += 1
if self.index == self.num:
self.index = 0
sfx_flap = MultiSound('audio/flap.wav', 3)
sfx_score = SoundLoader.load('audio/score.wav')
sfx_die = SoundLoader.load('audio/die.wav')
class Sprite(Image):
def __init__(self, **kwargs):
super(Sprite, self).__init__(allow_stretch=True, **kwargs)
self.texture.mag_filter = 'nearest'
w, h = self.texture_size
self.size = (params.scale * w, params.scale * h)
class Bird(Sprite):
def __init__(self, pos):
self.images = SpriteAtlas('graphics/bird_anim.atlas')
super(Bird, self).__init__(texture=self.images['wing-up'], pos=pos)
self.velocity_y = 0
self.gravity = -.3 * params.scale
def update(self):
self.velocity_y += self.gravity
self.velocity_y = max(self.velocity_y, -10 * params.scale)
self.y += self.velocity_y
if self.velocity_y < -5 * params.scale:
self.texture = self.images['wing-up']
elif self.velocity_y < 0:
self.texture = self.images['wing-mid']
def on_touch_down(self, *ignore):
self.velocity_y = 5.5 * params.scale
self.texture = self.images['wing-down']
sfx_flap.play()
class Background(Widget):
def __init__(self, source):
super(Background, self).__init__()
self.image = Sprite(source=source)
self.add_widget(self.image)
self.size = self.image.size
self.image_dupe = Sprite(source=source, x=self.width)
self.add_widget(self.image_dupe)
def update(self):
self.image.x -= 1 * params.scale
self.image_dupe.x -= 1 * params.scale
if self.image.right <= 0:
self.image.x = 0
self.image_dupe.x = self.width
class Ground(Sprite):
def update(self):
self.x -= 2 * params.scale
if self.x < -24 * params.scale:
self.x += 24 * params.scale
class Pipe(Widget):
def __init__(self, pos):
super(Pipe, self).__init__(pos=pos)
self.top_image = Sprite(source='graphics/pipe_top.png')
self.top_image.pos = (self.x, self.y + 3.5 * 66 * params.scale)
self.add_widget(self.top_image)
self.bottom_image = Sprite(source='graphics/pipe_bottom.png')
self.bottom_image.pos = (self.x, self.y - self.bottom_image.height)
self.add_widget(self.bottom_image)
self.width = self.top_image.width
self.scored = False
def update(self):
self.x -= 2 * params.scale
self.top_image.x = self.bottom_image.x = self.x
if self.right < 0:
self.parent.remove_widget(self)
class Pipes(Widget):
add_pipe = 0
def update(self, dt):
for child in list(self.children):
child.update()
self.add_pipe -= dt
if self.add_pipe < 0:
y = random.randint(int(self.y + 50 * params.scale),
int(self.height - 50 * params.scale - 3.5 * 66 * params.scale))
self.add_widget(Pipe(pos=(self.width, y)))
self.add_pipe = 1.5
class Blank(Widget):
def __init__(self, pos, size):
super(Blank, self).__init__()
with self.canvas:
Color(0, 0, 0)
Rectangle(pos=pos, size=size)
Color(1, 1, 1)
class Game(Widget):
def __init__(self):
super(Game, self).__init__()
self.background = Background(source='graphics/background.jpg')
self.size = self.background.size
self.add_widget(self.background)
self.ground = Ground(source='graphics/ground.png')
self.pipes = Pipes(pos=(0, self.ground.height), size=self.size)
self.add_widget(self.pipes)
self.add_widget(self.ground)
self.score_label = Label(center_x=self.center_x,
top=self.top - 30 * params.scale, text="0")
self.add_widget(self.score_label)
self.over_label = Label(center=self.center, opacity=0,
text="Game Over")
self.add_widget(self.over_label)
self.bird = Bird(pos=(20 * params.scale, self.height / 2))
self.add_widget(self.bird)
self.add_widget(Blank(*params.blank_rect))
Clock.schedule_interval(self.update, 1.0/60.0)
self.game_over = False
self.score = 0
def update(self, dt):
if self.game_over:
return
self.background.update()
self.bird.update()
self.ground.update()
self.pipes.update(dt)
if self.bird.collide_widget(self.ground):
self.game_over = True
for pipe in self.pipes.children:
if pipe.top_image.collide_widget(self.bird):
self.game_over = True
elif pipe.bottom_image.collide_widget(self.bird):
self.game_over = True
elif not pipe.scored and pipe.right < self.bird.x:
pipe.scored = True
self.score += 1
self.score_label.text = str(self.score)
sfx_score.play()
if self.game_over:
self.over_label.opacity = 1
sfx_die.play()
self.bind(on_touch_down=self._on_touch_down)
def _on_touch_down(self, *ignore):
parent = self.parent
parent.remove_widget(self)
parent.add_widget(Menu())
class Menu(Widget):
def __init__(self):
super(Menu, self).__init__()
self.add_widget(Sprite(source='graphics/background.jpg'))
self.size = self.children[0].size
self.add_widget(Ground(source='graphics/ground.png'))
self.add_widget(Label(center=self.center, text="tap to start"))
self.add_widget(Blank(*params.blank_rect))
def on_touch_down(self, *ignore):
parent = self.parent
parent.remove_widget(self)
parent.add_widget(Game())
class FlappyApp(App):
def build(self):
params.init()
top = Widget()
top.add_widget(Menu())
return top
class params(object):
def init(self):
self.bg_width, self.bg_height = 288, 384
#self.bg_width, self.bg_height = 500, 592
self.width, self.height = Window.size
self.center = Window.center
ws = float(self.width) / self.bg_width
hs = float(self.height) / self.bg_height
self.scale = min(ws, hs)
Logger.info('size=%r; dpi=%r; density=%r; SCALE=%r',
Window.size, Metrics.dpi, Metrics.density, self.scale)
if ws > hs:
gap = self.width - (self.bg_width * hs)
self.blank_rect = ((self.width - gap, 0), (gap, self.height))
else:
gap = self.height - (self.bg_height * ws)
self.blank_rect = ((0, self.height - gap), (self.width, gap))
params = params()
if __name__ == '__main__':
FlappyApp().run()
It would seem this is something with Kivy? In his code he does scaling math to compensate for different screen sizes on android devices so I'm not sure why the different graphics dimensions would cause android to behave in the odd fashion when the graphics dimensions didn't match. Is there a possible place to check on the android simulator to see what maybe causing the problem? The code itself doesn't seem to be a problem since build/compilation seems to be ok for both. The python/kivy app runs fine for both on the desktop... It seems it's just running under kivy after build with buildozer there is a difference and the difference is just with the dimensions of the graphics for the background image and the ground image.
Grateful for any help...
My dev env is using win 10 ,notepad++, wsl - ubuntu - buildozer. No problems on desktop, just diff on android simulator (from android studio chipmunk)
So i tried to create an apk with buildozer and kivy that connects to my raspberry pi with Paramiko scp. My code works totally fine until i import paramiko, bs4 and requests although i put these in the requirements section in my buildozer.spec file. I can build the app without any errors but wenn i start the app it immediately closes again and every other module imported does not seem to be a problem. I put my code into the kivy-pong example to see if it works with just importing the modules.
Code:
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 scp import SCPClient
from bs4 import BeautifulSoup
import paramiko
import urllib
import requests
import os
import glob
import time
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))
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()
Ok i ran adb logcat while running my app and it turns out that modules like paramiko relies on other modules that i have to install too.
I am having problems with the below code. Can someone please help and explain why the game over/reset and score functions are not working?
Below is the code im working from. My problem is that i can not see why the game over/reset function does not work. When the ship crashes into an astroid the game abruptly ends, but when it crashes into the barrier it does not indicate game over. Also, it's not reflecting the score.
Sorry if i wasn't clear before.
Here starts the code:
import kivy
kivy.require('1.8.0')
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.properties import NumericProperty, ObjectProperty
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.graphics import Rectangle, Color, Canvas
from kivy.config import Config
from functools import partial
from random import *
Config.set('graphics', 'resizable', 0)
Window.clearcolor = (0, 0, 0, 1.)
class MyButton(Button):
#class used to get uniform button styles
def __init__(self, **kwargs):
super(MyButton, self).__init__(**kwargs)
self.font_size = Window.width*0.018
class SmartMenu(Widget):
#the instance created by this class will appear
#when the game is started for the first time
buttonList = []
def __init__(self, **kwargs):
#create custom events first
self.register_event_type('on_button_release')
super(SmartMenu, self).__init__(**kwargs)
self.layout = BoxLayout(orientation = 'vertical')
self.layout.width = Window.width/2
self.layout.height = Window.height/2
self.layout.x = Window.width/2 - self.layout.width/2
self.layout.y = Window.height/2 - self.layout.height/2
self.add_widget(self.layout)
def on_button_release(self, *args):
#print 'The on_button_release event was just dispatched', args
#don't need to do anything here. needed for dispatch
pass
def callback(self,instance):
#print('The button %s is being pressed' % instance.text)
self.buttonText = instance.text
self.dispatch('on_button_release') #dispatching the callback event 'on_button_release' to tell teh parent instance to read the button text
def addButtons(self):
for k in self.buttonList:
tmpBtn = MyButton(text = k)
tmpBtn.background_color = [.4, .4, .4, .4]
tmpBtn.bind(on_release = self.callback) #when the button is released the callback function is called
self.layout.add_widget(tmpBtn)
def buildUp(self):
#self.colorWindow()
self.addButtons()
class SmartStartMenu(SmartMenu):
#setup the menu button names
buttonList = ['start', 'about']
def __init__(self, **kwargs):
super(SmartStartMenu, self).__init__(**kwargs)
self.layout = BoxLayout(orientation = 'vertical')
self.layout.width = Window.width/2
self.layout.height = Window.height/2
self.layout.x = Window.width/2 - self.layout.width/2
self.layout.y = Window.height/2 - self.layout.height/2
self.add_widget(self.layout)
self.msg = Label(text = 'Flappy Ship')
self.msg.font_size = Window.width*0.07
self.msg.pos = (Window.width*0.45,Window.height*0.75)
self.add_widget(self.msg)
self.img = Image(source = 'lens2.png')
self.img.size = (Window.width*1.5,Window.height*1.5)
self.img.pos = (-Window.width*0.2,-Window.height*0.2)
self.img.opacity = 0.35
self.add_widget(self.img)
class WidgetDrawer(Widget):
#This widget is used to draw all of the objects on the screen
#it handles the following:
# widget movement, size, positioning
def __init__(self, imageStr, **kwargs):
super(WidgetDrawer, self).__init__(**kwargs)
with self.canvas:
self.size = (Window.width*.002*25,Window.width*.002*25)
self.rect_bg=Rectangle(source=imageStr, pos=self.pos, size=self.size)
self.bind(pos=self.update_graphics_pos)
self.x = self.center_x
self.y = self.center_y
self.pos = (self.x, self.y)
self.rect_bg.pos = self.pos
def update_graphics_pos(self, instance, value):
self.rect_bg.pos = value
def setSize(self, width, height):
self.size = (width, height)
def setPos(self, xpos, ypos):
self.x = xpos
self.y = ypos
class ScoreWidget(Widget):
def __init__(self, **kwargs):
super(ScoreWidget, self).__init__(**kwargs)
self.asteroidScore = 0
self.currentScore = 0
with self.canvas:
tmpPos = (Window.width*0.25, Window.height*0.25)
tmpSize = (Window.width*0.5, Window.height*0.5)
Color(0.1, .1, .1)
self.scoreRect = Rectangle(pos=tmpPos, size=tmpSize)
def prepare(self):
#calculate the score
try:
self.finalScore = self.asteroidScore*100
except:
print 'problems getting score'
self.animateScore()
def animateScore(self):
#display score at 0 and every time interval add 100 until
#we reach the final score
#draw a score widget and schedule updates
scoreText = 'Score: 0'# + str(self.finalScore)
self.scoreLabel = Label(text=scoreText,font_size = '20sp')
self.scoreLabel.x = Window.width*0.3
self.scoreLabel.y = Window.height*0.3
self.add_widget(self.scoreLabel)
Clock.schedule_once(self.updateScore, .1)
self.drawStars()
def updateScore(self,dt):
self.currentScore = self.currentScore +100
self.scoreLabel.text = 'Score: ' + str(self.currentScore)
if self.currentScore < self.finalScore:
Clock.schedule_once(self.updateScore, 0.1)
def drawStars(self):
#0-10 asteroids 0 stars
#11-50 asteroids 1 star
#51-200 asteroids 2 stars
#201-500 asteroids 3 stars
#501-1000 asteroids 4 stars
#1001+ asteroids 5 stars
starNumber = 0
if self.asteroidScore > 10:
starNumber = 1
if self.asteroidScore > 50:
starNumber = 2
if self.asteroidScore > 200:
starNumber = 3
if self.asteroidScore > 500:
starNumber = 4
if self.asteroidScore > 1000:
starNumber = 5
with self.canvas:
#draw stars
#rect one
starPos = Window.width*0.27, Window.height*0.42
starSize = Window.width*0.06,Window.width*0.06
starString = 'gold_star.png'
if starNumber < 1:
starString = 'gray_star.png'
starRectOne = Rectangle(source=starString,pos=starPos, size = starSize)
#rect two
starPos = Window.width*0.37, Window.height*0.42
if starNumber < 2:
starString = 'gray_star.png'
starRectTwo = Rectangle(source=starString,pos=starPos, size = starSize)
#rect three
starPos = Window.width*0.47, Window.height*0.42
if starNumber < 3:
starString = 'gray_star.png'
starRectThree = Rectangle(source=starString,pos=starPos, size = starSize)
#rect four
starPos = Window.width*0.57, Window.height*0.42
if starNumber < 4:
starString = 'gray_star.png'
starRectFour = Rectangle(source=starString,pos=starPos, size = starSize)
#rect five
starPos = Window.width*0.67, Window.height*0.42
if starNumber < 5:
starString = 'gray_star.png'
starRectFive = Rectangle(source=starString,pos=starPos, size = starSize)
class Asteroid(WidgetDrawer):
#Asteroid class. The flappy ship will dodge these
imageStr = './sandstone_1.png'
rect_bg = Rectangle(source=imageStr)
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
def move(self):
self.x = self.x + self.velocity_x*5
self.y = self.y + self.velocity_y
def update(self):
self.move()
class Ship(WidgetDrawer):
#Ship class. This is for the main ship object.
#velocity of ship on x/y axis
#setup constants, health, etc
#choose default image:
impulse = 3
grav = -0.1
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
flameSize = (Window.width*.03, Window.width*.03)
def move(self):
self.x += self.velocity_x
self.y += self.velocity_y
#don't let the ship go too far
if self.y < Window.height*0.05:
#give upwards impulse
Clock.unschedule(self.update)
self.explode()
if self.y > Window.height*0.95:
Clock.unschedule(self.update)
self.explode()
def checkBulletNPCCollision(self, j):
if self.k.collide_widget(j):
j.health = j.health - self.k.bulletDamage
j.attackFlag = 'True'
#age the bullet
self.k.age = self.k.lifespan+10
def checkBulletStageCollision(self,q):
if self.k.collide_widget(q):
#if object type is asteorid
try:
if q.type == 'asteroid':
q.health = q.health - self.k.bulletDamage
self.k.age = self.k.lifespan+10
except:
print 'couldnt hit asteroid'
def determineVelocity(self):
#move the ship up and down
#we need to take into account our acceleration
#also want to look at gravity
self.grav = self.grav*1.05 #increase gravity
#set a grav limit
if self.grav < -4:
self.grav = -4
self.velocity_y = self.impulse + self.grav
self.impulse = 0.95*self.impulse
def drawArrow(self, *largs):
#draw the arrows directly onto the canvas
with self.canvas:
flamePos = (self.pos[0]-Window.width*.02, self.pos[1]+Window.width*.01)
flameRect = Rectangle(source='./flame.png', pos=flamePos, size=self.flameSize)
#schedule removal
def removeArrows(arrow, *largs):
self.canvas.remove(arrow)
Clock.schedule_once(partial(removeArrows, flameRect), .5)
Clock.schedule_once(partial(self.updateArrows, flameRect), 0.1)
def updateArrows(self, arrow, dt):
with self.canvas:
arrow.pos = (arrow.pos[0]-10, arrow.pos[1])
Clock.schedule_once(partial(self.updateArrows, arrow), 0.1)
return
def explode(self):
tmpSize = Window.width*0.25,Window.width*0.2
tmpPos = (self.x-Window.width*0.095, self.y-Window.width*0.08)
with self.canvas:
self.explosionRect = Rectangle(source ='./explosion1.png',pos=tmpPos,size=tmpSize)
def changeExplosion(rect, newSource, *largs):
rect.source = newSource
Clock.schedule_once(partial(changeExplosion, self.explosionRect, './explosion2.png'), 0.2)
Clock.schedule_once(partial(changeExplosion, self.explosionRect, './explosion3.png'), 0.4)
Clock.schedule_once(partial(changeExplosion, self.explosionRect, './explosion4.png'), 0.6)
Clock.schedule_once(partial(changeExplosion, self.explosionRect, './explosion5.png'), 0.8)
def removeExplosion(rect, *largs):
self.canvas.remove(rect)
Clock.schedule_once(partial(removeExplosion, self.explosionRect), 1)
def update(self):
self.determineVelocity()
self.move()
class GUI(Widget):
#this is the main widget that contains the game. This is the primary object
#that runs
asteroidList =[]
#important to use numericproperty here so we can bind a callback
#to use every time the number changes
asteroidScore = NumericProperty(0)
minProb = 1780
def __init__(self, **kwargs):
super(GUI, self).__init__(**kwargs)
#setup label for the score
self.score = Label(text = '0')
self.score.y = Window.height*0.8
self.score.x = Window.width*0.2
def check_score(self, obj):
#update credits
self.score.text = str(self.asteroidScore)
self.bind(asteroidScore = check_score)
self.add_widget(self.score)
#now we create a ship object
self.ship = Ship(imageStr = './ship.png')
self.ship.x = Window.width/4
self.ship.y = Window.height/2
self.add_widget(self.ship)
#self.ship.drawArrow()#start the flames
Clock.schedule_interval((self.ship.drawArrow), 0.1)
def addAsteroid(self):
#add an asteroid to the screen
#self.asteroid
imageNumber = randint(1, 4)
imageStr = './sandstone_'+str(imageNumber)+'.png'
tmpAsteroid = Asteroid(imageStr)
tmpAsteroid.x = Window.width*0.99
#randomize y position
ypos = randint(1, 16)
ypos = ypos*Window.height*.0625
tmpAsteroid.y = ypos
tmpAsteroid.velocity_y = 0
vel = 550#randint(10,25)
tmpAsteroid.velocity_x = -0.1*vel
self.asteroidList.append(tmpAsteroid)
self.add_widget(tmpAsteroid)
def drawTouchResponse(self,x,y):
#draw the arrows directly onto the canvas
with self.canvas:
tmpSize = Window.width*0.07, Window.width*0.07
tmpPos = (x-self.width/4,y-self.height/4)
self.arrowRect = Rectangle(source='./flame1.png',pos=tmpPos, size = tmpSize)
#schedule removal
def removeArrows(arrow, *largs):
self.canvas.remove(arrow)
def changeExplosion(rect, newSource, *largs):
rect.source = newSource
#schedule explosion two
Clock.schedule_once(partial(changeExplosion, self.arrowRect, './flame2.png'), 0.15)
#schedule explosion three
Clock.schedule_once(partial(changeExplosion, self.arrowRect, './flame3.png'), 0.3)
#schedule explosoin four
Clock.schedule_once(partial(changeExplosion, self.arrowRect, './flame4.png'), 0.45)
Clock.schedule_once(partial(removeArrows, self.arrowRect), 0.6)
#handle input events
def on_touch_down(self, touch):
self.ship.impulse = 3
self.ship.grav = -0.1
self.drawTouchResponse(touch.x, touch.y)
def showScore(self):
#this function will draw the score keeping widget, tabulate the score
#and rank with stars
self.scoreWidget = ScoreWidget()
self.scoreWidget.asteroidScore = self.asteroidScore #pass on score
self.scoreWidget.prepare()
self.add_widget(self.scoreWidget)
def removeScore(self):
self.remove_widget(self.scoreWidget)
def gameOver(self):
#add a restart button
restartButton = MyButton(text='Try Again')
#restartButton.background_color = (.5,.5,1,.2)
def restart_button(obj):
#reset game
self.removeScore()
for k in self.asteroidList:
self.remove_widget(k)
self.ship.xpos = Window.width*0.25
self.ship.ypos = Window.height*0.5
self.minProb = 1780
self.asteroidScore = 0
self.asteroidList = []
self.parent.remove_widget(restartButton)
Clock.unschedule(self.update)
Clock.schedule_interval(self.update, 1.0/60.0)
restartButton.size = (Window.width*.3,Window.width*.1)
restartButton.pos = Window.width*0.5-restartButton.width/2, Window.height*0.53
restartButton.bind(on_release=restart_button)
#we will want to bind the parent to listen for things from certain bubbles
#*** It's important that the parent get the button so you can click on it
#otherwise you can't click through the main game's canvas
self.parent.add_widget(restartButton)
#now draw the score widget
self.showScore()
def update(self, dt):
#This update function is the main update function for the game
#All of the game logic has its origin here
#events are setup here as well
#update game objects
#update ship
self.ship.update()
#update asteroids
#randomly add an asteroid
tmpCount = randint(1, 1800)
if tmpCount > self.minProb:
self.addAsteroid()
if self.minProb < 1300:
self.minProb = 1300
self.minProb -= 1
for k in self.asteroidList:
#check for collision with ship
if k.collide_widget(self.ship):
#game over routine
self.gameOver()
Clock.unschedule(self.update)
#add reset button
self.ship.explode()
k.update()
#check to see if asteroid is off of screen
if k.x < -100:
#since it's off the screen, remove the asteroid
self.remove_widget(k)
self.asteroidScore += 1
#remove asteroids off screen
tmpAsteroidList = self.asteroidList
tmpAsteroidList[:] = [x for x in tmpAsteroidList if (x.x > - 100)]
self.asteroidList = tmpAsteroidList
class ClientApp(App):
def build(self):
#this is where the root widget goes
#should be a canvas
self.parent = Widget() #
self.app = GUI()
#Start the game clock (runs update function once every (1/60) seconds
#Clock.schedule_interval(app.update, 1.0/60.0)
#add the start menu
self.sm = SmartStartMenu()
self.sm.buildUp()
def check_button(obj):
#check to see which button was pressed
if self.sm.buttonText == 'start':
#remove menu
self.parent.remove_widget(self.sm)
#start the game
print ' we should start the game now'
Clock.unschedule(self.app.update)
Clock.schedule_interval(self.app.update, 1.0/60.0)
try:
self.parent.remove_widget(self.aboutText)
except:
pass
if self.sm.buttonText == 'about':
self.aboutText = Label(text = 'Flappy Ship is made by Molecular Flow Games \n Check out: https://kivyspacegame.wordpress.com')
self.aboutText.pos = (Window.width*0.45,Window.height*0.35)
self.parent.add_widget(self.aboutText)
#bind a callback function that repsonds to event 'on_button_release' by calling function check_button
self.sm.bind(on_button_release = check_button)
#setup listeners for smartstartmenu
self.parent.add_widget(self.sm)
self.parent.add_widget(self.app) #use this hierarchy to make it easy to deal w/buttons
return self.parent
if __name__ == '__main__':
ClientApp().run()
I'm trying to make a basic visual exercise in Python and Kivy, but i'm stuck in this part of the code, how to make the ball start on the bottom of the screen?
Here's the .py file:
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
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)
def serve_ball(self, vel=(0, 4)):
self.ball.center = self.center
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
# bounce ball off bottom or top or left or right
if (self.ball.y < self.y) or (self.ball.top > self.top):
self.ball.velocity_y = -2
self.ball.velocity_x = -4
# bounce ball off
if(self.ball.x < 0):
self.ball.velocity_x = 4
self.ball.velocity_y = 0
if(self.ball.x + self.ball.width > self.right):
self.ball.velocity_x *= -1
self.ball.velocity_y = 4
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()
.kv file:
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
PongBall:
id: pong_ball
center: self.parent.center
This code make the widget starts on center of the screen, i'm trying to make start on bottom, without success
Thanks for any tips!
The problem in your case is that the PongBall's initial position is the center of the PongGame:
<PongGame>:
ball: pong_ball
PongBall:
id: pong_ball
center: self.parent.center # <---
What you must do is that when you create the initial position is established. Another error is that you are launching the game before the window is displayed, instead of launching it in build() you must do it in on_start():
*.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
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)
def serve_ball(self, vel=(0, 4)):
self.ball.center = self.initial_pos
self.ball.velocity = vel
def update(self, dt):
self.ball.move()
# bounce ball off bottom or top or left or right
if self.ball.y < self.y or self.ball.top > self.top:
self.ball.velocity_y = -2
self.ball.velocity_x = -4
# bounce ball off
if self.ball.x < 0:
self.ball.velocity_x = 4
self.ball.velocity_y = 0
if self.ball.x + self.ball.width > self.right:
self.ball.velocity_x *= -1
self.ball.velocity_y = 4
class PongApp(App):
def build(self):
game = PongGame()
return game
def on_start(self):
self.root.serve_ball()
Clock.schedule_interval(self.root.update, 1.0 / 60.0)
if __name__ == '__main__':
PongApp().run()
*.kv
<PongBall>:
size: 50, 50
canvas:
Ellipse:
pos: self.pos
size: self.size
<PongGame>:
ball: pong_ball
initial_pos: [self.center_x , pong_ball.height/2]
PongBall:
id: pong_ball
In build() an incorrect size of the widgets is obtained since these are updated instants before being launched, it is convenient to use on_start() to obtain the size on which the initial position depends.