Flappy Bird Game is a classic game where a player controls a bird to avoid pipes by tapping on the screen to make the bird “flap.” In this article, we’ll go step-by-step to build the game using Kivy, a popular Python library for developing multitouch applications.
We’ll cover setting up the Kivy library, creating the game environment, and adding basic game mechanics like gravity and collision detection.
Prerequisites
- Python 3.x installed on your system.
- Familiarity with Python and basic game concepts.
- Installation of Kivy (if not already installed) via:
pip install kivy
Step 1: Load essential modules
import kivy
kivy.require('1.7.2')
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.core.window import Window
from kivy.properties import NumericProperty
from kivy.clock import Clock
from kivy.graphics import Rectangle
from random import
Step 2: Configure the graphics
from kivy.config import Config
Config.set('graphics','resizable',0) #don't make the app re-sizeable
#Graphics fix
#this fixes drawing issues on some phones
Window.clearcolor = (0,0,0,1.)
The game includes three main components: the spaceship, the asteroids, and the main application. We’ll create a dedicated class for each, as well as a separate class for buttons. Since multiple game objects will need to handle graphical elements, it’s helpful to have a general-purpose class to set up Kivy widgets that can be displayed and moved around the screen. This approach streamlines the graphics setup and makes it easier to manage object movement on the screen.
class WidgetDrawer(Widget):
#This widget is used to draw all of the objects on the screen
#it handles the following:
# widget movement, size, positioning
#whever a WidgetDrawer object is created, an image string needs to be specified
#example: wid - WidgetDrawer('./image.png')
#objects of this class must be initiated with an image string
#;You can use **kwargs to let your functions take an arbitrary number of keyword arguments
#kwargs ; keyword arguments
def __init__(self, imageStr, **kwargs):
super(WidgetDrawer, self).__init__(**kwargs) #this is part of the **kwargs notation
#if you haven't seen with before, here's a link http://effbot.org/zone/python-with-statement.html
with self.canvas:
#setup a default size for the object
self.size = (Window.width*.002*25,Window.width*.002*25)
#this line creates a rectangle with the image drawn on top
self.rect_bg=Rectangle(source=imageStr,pos=self.pos,size = self.size)
#this line calls the update_graphics_pos function every time the position variable is modified
self.bind(pos=self.update_graphics_pos)
self.x = self.center_x
self.y = self.center_y
#center the widget
self.pos = (self.x,self.y)
#center the rectangle on the widget
self.rect_bg.pos = self.pos
def update_graphics_pos(self, instance, value):
#if the widgets position moves, the rectangle that contains the image is also moved
self.rect_bg.pos = value
#use this function to change widget size
def setSize(self,width, height):
self.size = (width, height)
#use this function to change widget position
def setPos(xpos,ypos):
self.x = xpos
self.y = ypos
The class above is responsible for rendering graphics, making images movable, and automatically updating their position whenever the position property changes. This allows changes in the widget’s x position to directly affect the x position of its image. By having each widget with graphical elements inherit from this class, we can streamline the code and improve efficiency.
Next, we’ll create two classes—one for the ship and one for the asteroids—that will utilize the WidgetDrawer class to handle image rendering.
class Asteroid(WidgetDrawer):
#Asteroid class. The flappy ship will dodge these
velocity_x = NumericProperty(0) #initialize velocity_x and velocity_y
velocity_y = NumericProperty(0) #declaring variables is not necessary in python
#update the position using the velocity defined here. every time move is called we change the position by velocity_x
def move(self):
self.x = self.x + self.velocity_x
self.y = self.y + self.velocity_y
def update(self):
#the update function moves the astreoid. Other things could happen here as well (speed changes for example)
self.move()
The next class is for the ship. It looks similar to the asteroid class.
class Ship(WidgetDrawer):
#Ship class. This is for the main ship object.
#velocity of ship on x/y axis
impulse = 3 #this variable will be used to move the ship up
grav = -0.1 #this variable will be used to pull the ship down
velocity_x = NumericProperty(0) #we wont actually use x movement
velocity_y = NumericProperty(0)
def move(self):
self.x = self.x + self.velocity_x
self.y = self.y + self.velocity_y
#don't let the ship go too far
if self.y Window.height*0.95: #don't let the ship go up too high
self.impulse = -3
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 #the gravitational velocity should increase
#set a grav limit
if self.grav < -4: #set a maximum falling down speed (terminal velocity)
self.grav = -4
#the ship has a propety called self.impulse which is updated
#whenever the player touches, pushing the ship up
#use this impulse to determine the ship velocity
#also decrease the magnitude of the impulse each time its used
self.velocity_y = self.impulse + self.grav
self.impulse = 0.95*self.impulse #make the upward velocity decay
def update(self):
self.determineVelocity() #first figure out the new velocity
self.move() #now move the ship
Next we will create the main widget for the screen. This widget will have the ship and asteroids drawn on top of it.
class GUI(Widget):
#this is the main widget that contains the game.
asteroidList =[] #use this to keep track of asteroids
minProb = 1700 #this variable used in spawning asteroids
def __init__(self, **kwargs):
super(GUI, self).__init__(**kwargs)
l = Label(text='Flappy Ship') #give the game a title
l.x = Window.width/2 - l.width/2
l.y = Window.height*0.8
self.add_widget(l) #add the label to the screen
#now we create a ship object
#notice how we specify the ship image
self.ship = Ship(imageStr = './ship.png')
self.ship.x = Window.width/4
self.ship.y = Window.height/2
self.add_widget(self.ship)
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 = 10
tmpAsteroid.velocity_x = -0.1*vel
self.asteroidList.append(tmpAsteroid)
self.add_widget(tmpAsteroid)
#handle input events
#kivy has a great event handler. the on_touch_down function is already recognized
#and doesn't need t obe setup. Every time the screen is touched, the on_touch_down function is called
def on_touch_down(self, touch):
self.ship.impulse = 3 #give the ship an impulse
self.ship.grav = -0.1 #reset the gravitational velocity
def gameOver(self): #this function is called when the game ends
#add a restart button
restartButton = MyButton(text='Restart')
#restartButton.background_color = (.5,.5,1,.2)
def restart_button(obj):
#this function will be called whenever the reset button is pushed
print 'restart button pushed'
#reset game
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 = 1700
self.asteroidList = []
self.parent.remove_widget(restartButton)
#stop the game clock in case it hasn't already been stopped
Clock.unschedule(self.update)
#start the game clock
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.5
#bind the button using the built-in on_release event
#whenever the button is released, the restart_button function is called
restartButton.bind(on_release=restart_button)
#*** 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)
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 = self.minProb -1
for k in self.asteroidList:
#check for collision with ship
if k.collide_widget(self.ship):
print 'death'
#game over routine
self.gameOver()
Clock.unschedule(self.update)
#add reset button
k.update()
Full Source Code:
import kivy
kivy.require('1.7.2')
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.core.window import Window
from kivy.properties import NumericProperty
from kivy.clock import Clock
from kivy.graphics import Rectangle
from kivy.config import Config
from random import randint
# Set the app to non-resizable and fix drawing issues on some devices
Config.set('graphics', 'resizable', False)
Window.clearcolor = (0, 0, 0, 1) # Set background color to black
class WidgetDrawer(Widget):
"""
WidgetDrawer class handles drawing images and updating positions.
Initialize with an image path for the object to be displayed.
"""
def __init__(self, imageStr, **kwargs):
super(WidgetDrawer, self).__init__(**kwargs)
with self.canvas:
self.size = (Window.width * 0.05, Window.width * 0.05)
self.rect_bg = Rectangle(source=imageStr, pos=self.pos, size=self.size)
self.bind(pos=self.update_graphics_pos)
self.center = (Window.width / 2, Window.height / 2)
self.rect_bg.pos = self.pos
def update_graphics_pos(self, instance, value):
# Update rectangle position when widget position changes
self.rect_bg.pos = value
def setSize(self, width, height):
# Set the size of the widget
self.size = (width, height)
def setPos(self, xpos, ypos):
# Set the position of the widget
self.pos = (xpos, ypos)
class Ship(WidgetDrawer):
"""
Ship class, defining movement and properties specific to the player's ship.
"""
impulse = NumericProperty(0)
grav = NumericProperty(-0.1)
def update(self):
# Update the ship's position based on impulse and gravity
self.y += self.impulse
self.impulse += self.grav
# Ensure the ship stays within the window
if self.y < 0:
self.y = 0
self.impulse = 0
class Asteroid(WidgetDrawer):
"""
Asteroid class, defines movement for asteroids.
"""
velocity_x = NumericProperty(0)
velocity_y = NumericProperty(0)
def move(self):
self.x += self.velocity_x
self.y += self.velocity_y
def update(self):
self.move()
class GUI(Widget):
"""
Main game widget containing the ship, asteroids, and game logic.
"""
asteroidList = []
minProb = 1700 # Used to control asteroid spawning probability
def __init__(self, **kwargs):
super(GUI, self).__init__(**kwargs)
# Title Label
title = Label(text='Flappy Ship')
title.center_x = Window.width / 2
title.y = Window.height * 0.8
self.add_widget(title)
# Create and position the ship
self.ship = Ship(imageStr='./ship.png')
self.ship.pos = (Window.width / 4, Window.height / 2)
self.add_widget(self.ship)
def addAsteroid(self):
# Add an asteroid with random image and position
imageStr = f'./sandstone_{randint(1, 4)}.png'
tmpAsteroid = Asteroid(imageStr)
tmpAsteroid.x = Window.width * 0.99
tmpAsteroid.y = randint(1, 16) * Window.height * 0.0625
tmpAsteroid.velocity_x = -1
tmpAsteroid.velocity_y = 0
self.asteroidList.append(tmpAsteroid)
self.add_widget(tmpAsteroid)
def on_touch_down(self, touch):
# Apply impulse and reset gravity on touch
self.ship.impulse = 3
self.ship.grav = -0.1
def gameOver(self):
# Function to handle game over event
restartButton = Button(text='Restart')
def restart_button(instance):
print("Restart button pushed")
for asteroid in self.asteroidList:
self.remove_widget(asteroid)
self.ship.pos = (Window.width * 0.25, Window.height * 0.5)
self.minProb = 1700
self.asteroidList = []
self.parent.remove_widget(restartButton)
Clock.unschedule(self.update)
Clock.schedule_interval(self.update, 1.0 / 60.0)
restartButton.size = (Window.width * 0.3, Window.width * 0.1)
restartButton.pos = (Window.width / 2 - restartButton.width / 2, Window.height / 2)
restartButton.bind(on_release=restart_button)
self.parent.add_widget(restartButton)
def update(self, dt):
# Main game update function, runs every frame
self.ship.update()
if randint(1, 1800) > self.minProb:
self.addAsteroid()
self.minProb = max(1300, self.minProb - 1)
for asteroid in self.asteroidList:
if asteroid.collide_widget(self.ship):
print("Game Over: Ship collided with an asteroid")
self.gameOver()
Clock.unschedule(self.update)
asteroid.update()
class ClientApp(App):
def build(self):
parent = Widget() # Main container for widgets
game = GUI()
Clock.schedule_interval(game.update, 1.0 / 60.0)
parent.add_widget(game)
return parent
if __name__ == '__main__':
ClientApp().run()
You can download the images used in the game form below





Final Output:

Conclusion
Building a Flappy Bird game with the Kivy library in Python provides a rewarding experience that combines creativity, logical thinking, and hands-on programming. This project allows you to apply foundational programming concepts such as object-oriented design, event handling, and animation, all while leveraging Kivy’s powerful UI and graphics capabilities. Through the development process, you learn to manage game elements like gravity, collision detection, and scoring, giving you insight into how classic 2D games are constructed.
Kivy’s flexibility makes it ideal for such applications, offering intuitive handling of touch events, easy manipulation of widgets, and a wide range of customizations for interactive graphics. This project serves as a strong stepping stone for anyone interested in game development, mobile app development, or simply creating engaging visual applications in Python. With this foundation, you can expand your game further by adding features such as increasing difficulty levels, scoring systems, or even creating unique obstacles, making it an excellent way to deepen your skills and understanding of game mechanics and Python-based UI development.





Leave a Reply