Shooting Game using Python

This is a shooting game sample to demonstrate Python's GUI capabilities. Python is one of the most suited languages to develop simple as well as complex games.

One of the most challenging part in every game is to handle moving objects and Python just has lots of Libraries to handle this. Some of these libraries are simple and suited for beginners, while some libraries provide advanced functionalities to develop more complex games. And most of these are free to use.

Advantages of using Python

  1. Python is simple to understand and easy to use
  2. Python is open sourced
  3. Can develop games that works on almost all kinds of OS without much changes
  4. Lots of free to use libraries to suit different needs
  5. Even when we work on different types of applications like Web or Desktop, it feels like we are working on Core Python itself. Everything is more Pythonized.

Below sample is developed using Kivy, one of Python's most used GUI libraries. This is a simple shooting game, where we have to shoot down the objects coming from top with the gun at the base. We can use left and right arrows to move the gun. Up arrow to shoot.

Doing this sample will help you to get a better understanding of Kivy. Particularly, on how to use canvas, create, shapes in it, move the shapes and bind event to these shapes.
Below are the step by step explanations.

  1. Install Python

    Download and install the latest version of python from Python Website.

    Set up virtual environment also to avoid any unexpected errors.

    Open command prompt and navigate to the folder where you are going to place the code and activate virtual environment.

    Install Kivy using command python -m pip install kivy[base]. Kivy installations may fail some times because of incompatibility with latest version of Python. If any such error occurs, use the command py -3.9 -m pip install kivy[base] (for windows) or python3.9 -m pip install kivy[base] based on the OS.
    If you wish to get a quick tutorial of Kivy before starting with the sample. Please have a look at the Kivy Quick Reference.

  2. Running the game

    Create a file name game.py. Here we are going to place our code.

    To run the game navigate to the folder in cmd and use commands python game.py or py game.py depending on the OS.

    If we get any error on using above commands, it is likely because we have set up VE using older version of Python. Here we have to use commands python game.py or py game.py or python3.9 -m game or py -3.9 -m game depending on the OS and python version used.

  3. Starting the code

    Here we are importing kivy, required widgets and clock that helps in moving the objects.
    Game is the class where our page load method build is defined. Here we create a window of size 400*400.
    ShootGame is the class where we are going to define all our logic.

    Copied
    import kivy
    kivy.require('2.0.0')
    
    from kivy.app import App
    from kivy.core.window import Window
    from kivy.graphics import *
    from kivy.uix.widget import Widget
    from kivy.uix.label import Label
    from kivy.clock import Clock
    import time
    import random
    
    class ShootGame(Widget):
        pass
    
    class Game(App):
        def build(self):
            self.title = 'Arrows ← and → to move gun. ↑ to shoot.'
            Window.size = (400, 400)
            return ShootGame()
    
    if __name__ == '__main__':
        Game().run()
    
  4. __init__ method

    __init__ method contains code that should execute only once at the start of the game.
    Here we can bind the keypress events to move the gun and shoot.
    Then create a canvas and draw the gun, labels to show score and fail message.
    2 clocks to move bullet and ball from top. Clocks should always be set within the init method.

    Copied
        global isFailed
        
        def __init__(self, **kwargs):
            super(ShootGame, self).__init__(**kwargs)
            self._keyboard = Window.request_keyboard(self.movegun, self)
            self._keyboard.bind(on_key_down=self.movegun)
            self._keyboard.bind(on_key_up=self.shoot)
            global isFailed
            isFailed = False
    
            with self.canvas:
                Color(1,1,0,1, mode="rgba")
                self.gun = Ellipse(segments=5, pos=(185, 0), size=(30,30))
                self.bullet = Rectangle(pos=(300, 400), size=(5,20))
                Clock.schedule_interval(self.movebullet, .001)
                self.lblRes = Label(text="0")
                self.lblRes.pos = (310, 330)
            self.newball()
            Clock.schedule_interval(self.moveball, .2)
    
  5. movegun function to move gun

    Handles left and right movement of gun at base.
    Also handles the restart after game fails. If enter key is pressed and game is in failed mode, move gun to the center and clear labels.

    Copied
        def movegun(self, keyboard, keycode, text, modifiers):
            m = 0
            gp = self.gun.pos[0]
            if(keycode[1] == 'left'):
                m = -3
            elif(keycode[1] == 'right'):
                m = 3
            elif(keycode[1] == 'enter'):#Restart            
                global isFailed
                if(isFailed == True):
                    isFailed = False
                    with self.canvas:
                        Color(1,1,0,1, mode="rgba")
                        self.gun = Ellipse(segments=5, pos=(185, 0), size=(30,30))
                        self.lblFail.text = ''
                        self.lblRes = Label(text="0")
                        self.lblRes.pos = (310, 330)
                    self.newball()
            if ((gp > 0 and m < 0) or (gp < 370 and m > 0)):
                self.gun.pos = (gp + m, 0)
    
  6. movebullet function to move the bullet, check the hit and game fail

    Moves bullet up. Check for the hit using x and y positions of bullet and ball. If hit, create a new ball.
    Game fails when ball moves below the gun. Check if the game failed by using the y position of ball. If failed, show message.

    Copied
        def movebullet(self, *args):
            x = self.gun.pos[0] + 15
            y = self.bullet.pos[1] + 30
            self.bullet.pos = (x, y)
            blx, bly = self.ball.pos
            #Check for hit. Ball size is 15. Bullet size is 5.
            if(x + 5 > blx and x < blx + 15 and y > bly and y < 400): 
                self.canvas.remove(self.ball)
                self.newball()
                self.lblRes.text = str(int(self.lblRes.text) + 100)
            if(bly < 30): #Fail
                global isFailed
                isFailed = True
                self.canvas.clear()
                with self.canvas:
                    Color(1,1,0,1, mode="rgba")
                    self.lblFail = Label(text="Failed!!!Press Enter to restart.")
                    self.lblFail.pos = (150, 150)
    
  7. moveball function to continously move ball

    Copied
        def moveball(self, *args):
            rspeed = 7
            y = self.ball.pos[1] - rspeed
            self.ball.pos = (self.ball.pos[0], y)
    
  8. shoot function to create a bullet

    shoot function to create a bullet in canvas and move it up.

    Copied
        def (self, keyboard, keycode):
            if(keycode[1] == 'up'):
                with self.canvas:
                    Color(1,1,0,1, mode="rgba")
                    self.bullet = Rectangle(pos=(300, 0), size=(5,20))
                    g = self.gun.pos[0] + 15
                    self.bullet.pos = (g, 30)
    
  9. newball function to create a new ball

    Create a new ball each time after successful hit.

    Copied
        def newball(self):
            x = random.randint(15, 385)
            with self.canvas:
                Color(1,0,0,1, mode="rgba")
                self.ball = Ellipse(pos=(x, 350), size=(15, 15))
    

Complete Code

import kivy
kivy.require('2.0.0')

from kivy.app import App
from kivy.core.window import Window
from kivy.graphics import *
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.clock import Clock
import time
import random

class ShootGame(Widget):

    global isFailed
    
    def __init__(self, **kwargs):
        super(ShootGame, self).__init__(**kwargs)
        self._keyboard = Window.request_keyboard(self.movegun, self)
        self._keyboard.bind(on_key_down=self.movegun)
        self._keyboard.bind(on_key_up=self.shoot)
        global isFailed
        isFailed = False

        with self.canvas:
            Color(1,1,0,1, mode="rgba")
            self.gun = Ellipse(segments=5, pos=(185, 0), size=(30,30))
            self.bullet = Rectangle(pos=(300, 400), size=(5,20))
            Clock.schedule_interval(self.movebullet, .001)
            self.lblRes = Label(text="0")
            self.lblRes.pos = (310, 330)
        self.newball()
        Clock.schedule_interval(self.moveball, .2)
    
    def movegun(self, keyboard, keycode, text, modifiers):
        m = 0
        gp = self.gun.pos[0]
        if(keycode[1] == 'left'):
            m = -3
        elif(keycode[1] == 'right'):
            m = 3
        elif(keycode[1] == 'enter'):#Restart            
            global isFailed
            if(isFailed == True):
                isFailed = False
                with self.canvas:
                    Color(1,1,0,1, mode="rgba")
                    self.gun = Ellipse(segments=5, pos=(185, 0), size=(30,30))
                    self.lblFail.text = ''
                    self.lblRes = Label(text="0")
                    self.lblRes.pos = (310, 330)
                self.newball()
        if ((gp > 0 and m < 0) or (gp < 370 and m > 0)):
            self.gun.pos = (gp + m, 0)
    
    def movebullet(self, *args):
        x = self.gun.pos[0] + 15
        y = self.bullet.pos[1] + 30
        self.bullet.pos = (x, y)
        blx, bly = self.ball.pos
        #Check for hit. Ball size is 15. Bullet size is 5.
        if(x + 5 > blx and x < blx + 15 and y > bly and y < 400): 
            self.canvas.remove(self.ball)
            self.newball()
            self.lblRes.text = str(int(self.lblRes.text) + 100)
        if(bly < 30): #Fail
            global isFailed
            isFailed = True
            self.canvas.clear()
            with self.canvas:
                Color(1,1,0,1, mode="rgba")
                self.lblFail = Label(text="Failed!!!Press Enter to restart.")
                self.lblFail.pos = (150, 150)
    
    def moveball(self, *args):
        rspeed = 7
        y = self.ball.pos[1] - rspeed
        self.ball.pos = (self.ball.pos[0], y)
    
    def shoot(self, keyboard, keycode):
        if(keycode[1] == 'up'):
            with self.canvas:
                Color(1,1,0,1, mode="rgba")
                self.bullet = Rectangle(pos=(300, 0), size=(5,20))
                g = self.gun.pos[0] + 15
                self.bullet.pos = (g, 30)
    
    def newball(self):
        x = random.randint(15, 385)
        with self.canvas:
            Color(1,0,0,1, mode="rgba")
            self.ball = Ellipse(pos=(x, 350), size=(15, 15))

class Game(App):
    def build(self):
        self.title = 'Arrows ← and → to move gun. ↑ to shoot.'
        Window.size = (400, 400)
        return ShootGame()

if __name__ == '__main__':
    Game().run()
Absolute Code Works - Python Topics