Wednesday, June 18, 2014

29. Bouncing Balls

This application will have two bouncing balls. It illustrates the use of attributes.




If you search for kivy, you should see the indicated image and then you may right-click on it, to download it. It is 48 by 48 pixels in size. You should save it as kivy.png, and save it to the folder where the python and kivy files will reside.




These are the imports. The balls will be based on the Widget class, as will be the root class. The ListProperty holds the ball's velocity. The importance of the Vector class lies in helping write cleaner code, instead of referring to individual components of velocity. It is imported as the alias Vec. The Clock will set the framerates for the two balls. Lastly, we require the python program run with a Kivy version of 1.8.0 or higher.




The Ball class is created, a subclass of the Widget class. It is empty, since the kv file defines its visual properties and its behavior is modified by use of attributes, ball1 and ball2, in the root class. For the root class we create 2 ListProperties holding the velocity of the balls. In the constructor, we set the balls initial positions. To set the initial positions, we had to use the ball1 and ball2 attribute. These are defined in the kv file.




There will be elastic collisions off the bounding box defined by the window size. For example if the ball bounces off a horizontal wall, its velocity in the y-direction changes sign.




The function update1 operates at 60 Hz. The ball position is updated. Then there are four conditional statements seeing if ball1 has reached either of the 4 edges. If so, it's x- or y-velocity changes sign.




We do likewise for ball2 in update2.




This is the app class. It sets the title of the window as 'Two Bouncing Balls'. It sets two clocks operating at a 60 Hz rate.


# ex29.py

from kivy.uix.widget import Widget
from kivy.app import App
from kivy.properties import ListProperty
from kivy.vector import Vector as Vec
from kivy.clock import Clock
import kivy
kivy.require('1.8.0')

class Ball(Widget):
    pass
    
class Ex29(Widget):
    vel1 = ListProperty([4,3])
    vel2 = ListProperty([3,4])

    def __init__(self, **kwargs):
        super(Ex29, self).__init__(**kwargs)
        self.ball1.pos = self.width/2, self.height/2
        self.ball2.pos = self.width/4, self.height/4
            
    def update1(self,dt):
        self.ball1.pos = Vec(self.vel1) + self.ball1.pos 
        if self.ball1.x<0: self.vel1[0] *= -1
        if self.ball1.x>self.width-50: self.vel1[0] *= -1
        if self.ball1.y<0: self.vel1[1] *= -1
        if self.ball1.y>self.height-50: self.vel1[1] *= -1
        
    def update2(self,dt):
        self.ball2.pos = Vec(self.vel2) + self.ball2.pos 
        if self.ball2.x<0: self.vel2[0] *= -1
        if self.ball2.x>self.width-50: self.vel2[0] *= -1
        if self.ball2.y<0: self.vel2[1] *= -1
        if self.ball2.y>self.height-50: self.vel2[1] *= -1
       
class Ex29App(App):
    def build(self):
        self.title = "Two Bouncing Balls"
        ex29 = Ex29()
        Clock.schedule_interval(ex29.update1, 1.0/60)
        Clock.schedule_interval(ex29.update2, 1.0/60)
        return ex29

if __name__=='__main__':
    Ex29App().run()



In the kv file, we use a directive to see if we are running Kivy 1.8.0 or higher. The Ball widget has a size of 50 by 50 pixels, and we define the canvas.after as circle of that size with the downloaded image.




For the root, the Widget size is 800 by 600. The attributes ball1 and ball2 are created. The background for the screen is green.




Then ball1 is defined. We also define canvas.before so the color of red will be applied to this ball.




The second ball will have a color of magenta. For ball1 and ball2, we created ids, we had earlier used, in defining the attributes. The attributes are the way our Python program can access ball1 and ball2.


# ex29.kv
#:kivy 1.8.0

<Ball>:
    size: 50,50
    canvas.after:
        Ellipse:
            pos: self.pos
            size: self.size
            source: 'kivy.png'
    
<Ex29>:
    size: 800, 600
    ball1: ball_id1
    ball2: ball_id2
    canvas:
        Color:
            rgb: 0,1,0
        Rectangle:
            size: root.width,root.height
            pos: 0,0
    Ball:
        id: ball_id1
        canvas.before:
            Color:
                rgb: 1, 0, 0
            
    Ball:
        id: ball_id2
        canvas.before:
            Color:
                rgb: 1, 0, 1     


Demo of App on Youtube

7 comments:

  1. Is there a way to move the ball logic inside the ball class?
    And then instantiate the object inside the main class to have n*ball?
    Thanks!!

    ReplyDelete
  2. i'm thinking at something livke that:

    class Ball(Widget):
    vel=ListProperty([4,3])
    def __init__(self,**kwargs):
    super(Ball,self).__init__(**kwargs)
    self.sx=300
    self.sy=300
    self.vel=[3,4]
    self.pos=800/self.sx,600/self.sy

    def update(self,dt):
    self.pos=Vec(self.vel)+self.pos
    if self.x<0: self.vel[0] *= -1
    if self.x>750: self.vel[0] *= -1
    if self.y<0: self.vel[1] *= -1
    if self.y>550: self.vel[1] *= -1

    class Ex29(Widget):
    ball1=Ball()
    #ball2=Ball()
    def update_ball(self,dt):
    self.dtt=dt
    self.ball1.update(self.dtt)
    #self.ball2.update(self.dtt)

    But it works only with one istance of the ball!!

    ReplyDelete
    Replies
    1. Sorry but the system didn't retain the indentation!

      Delete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.

    Python Training in electronic city

    ReplyDelete
  5. I am learning kivy, Thanks for uploading this tutorials, these are very helpful to me.

    ReplyDelete