Python 3 Tkinter Tank Wars Game Script GUI Desktop App Full Project For Beginners

 

 

Welcome folks today in this blog post we will be building a tank wars game in python using tkinter framework. All the full source code of the application is shown below.

 

 

 

Get Started

 

 

 

In order to get started you need to install the below library using the pip command as shown below

 

 

pip install tkinter

 

 

After installing this library make an app.py file and copy paste the following code

 

 

app.py

 

 

# coding=utf-8
# Tank War Game
# A very simple tank war game programed by Python and Tkinter.
# last update: 2019-03-28 by. Yijie Xu
#
# How to play?
# Use arrow keys to control the direction of the Tank
# Use space key to fire
# Enjoy! ;)

from tkinter import *
import time, random

# Width and height of the window
WIN_WIDTH = 600
WIN_HEIGHT = 600

# Width and height of enemies' tanks
E_TANK_WIDTH = 45
E_TANK_HEIGHT = 45

# Number of enemy tanks
E_TANK_NUM = 10

# Width and height of the bullet of enemies' tank
E_BULLET_WIDTH = 16
E_BULLET_HEIGHT = 16

# Width and height of player's tank
P_TANK_WIDTH = 62
P_TANK_HEIGHT = 76

# Width and height of player's bullet
P_BULLET_WIDTH = 28
P_BULLET_HEIGHT = 28

# status of tanks
ACTIVE = "active"
EXPLODE = "explode"
USELESS = "useless"

DIRECTIONS = ["up", "left", "down", "right"]

win = Tk()
can = Canvas(win, width=WIN_WIDTH, height=WIN_HEIGHT)
can.pack()

# import background pictures
bg = PhotoImage(file="pic/bg.gif")
can.create_image(0, 0, image=bg, anchor="nw")

# A dictionart to store tanks' images
tanks_img = {}

# images of a blue tank (4 directions)
blue_tank = {}
for d in DIRECTIONS:
    blue_tank[d] = PhotoImage(file="pic/tank_blue_" + d + ".gif")

# images of a black tank (4 directions)
black_tank = {}
for d in DIRECTIONS:
    black_tank[d] = PhotoImage(file="pic/tank_black_" + d + ".gif")

# images of a red tank (4 directions)
red_tank = {}
for d in DIRECTIONS:
    red_tank[d] = PhotoImage(file="pic/tank_red_" + d + ".gif")

# images of a huge(player's) tank (4 directions)
huge_tank = {}
for d in DIRECTIONS:
    huge_tank[d] = PhotoImage(file="pic/tank_huge_" + d + ".gif")

tanks_img["blue"] = blue_tank
tanks_img["red"] = red_tank
tanks_img["black"] = black_tank
tanks_img["huge"] = huge_tank

# images of exploring tank
explosion = []
for i in range(5):
    explosion.append(PhotoImage(file="pic/explosion" + str(i + 1) + ".gif"))

bullets_img = {}

# images of the blue bullet(4 directions)
blue_bullet = {}
for d in DIRECTIONS:
    blue_bullet[d] = PhotoImage(file="pic/bullet_blue_" + d + ".gif")

# images of the red bullet(4 directions)
red_bullet = {}
for d in DIRECTIONS:
    red_bullet[d] = PhotoImage(file="pic/bullet_red_" + d + ".gif")

# images of the black bullet(4 directions)
black_bullet = {}
for d in DIRECTIONS:
    black_bullet[d] = PhotoImage(file="pic/bullet_black_" + d + ".gif")

# images of player's bullet(4 directions)
huge_bullet = {}
for d in DIRECTIONS:
    huge_bullet[d] = PhotoImage(file="pic/bullet_huge_" + d + ".gif")

bullets_img["blue"] = blue_bullet
bullets_img["red"] = red_bullet
bullets_img["black"] = black_bullet
bullets_img["huge"] = huge_bullet


class Tank():

    def __init__(self, x, y, d, c):
        
        self.color = c
        self.dir = d
        self.speed = 5
        self.drawable = can.create_image(x, y,
                                         image=tanks_img[self.color][self.dir],
                                         anchor="nw")
        self.state = ACTIVE
        self.e_count = 0
 
        if self.color == "huge":
            self.width = P_TANK_WIDTH
            self.height = P_TANK_HEIGHT
        else:
            self.width = E_TANK_WIDTH
            self.height = E_TANK_HEIGHT

    # Update the appearance and state of the tank
    def update_pos_img(self):
        if self.state == USELESS:
            pass

        elif self.state == EXPLODE:
            # change images
            can.itemconfig(self.drawable, image=explosion[self.e_count])
            self.e_count += 1
            # if it is the last one
            if self.e_count == 5:
                self.state = USELESS
                self.e_count = 0

        elif self.state == ACTIVE:
            t_pos = self.get_pos()

            # When touching wall, change direction arbitarily
            if t_pos[0] < 0:
                self.dir = random.choice(["down", "up", "right"])
            elif t_pos[1] < 0:
                self.dir = random.choice(["down", "left", "right"])
            elif t_pos[2] > WIN_WIDTH:
                self.dir = random.choice(["down", "up", "left"])
            elif t_pos[3] > WIN_HEIGHT:
                self.dir = random.choice(["left", "up", "right"])

            can.itemconfig(self.drawable, image=tanks_img[self.color][self.dir])

            # change tank's position
            if self.dir == "up":
                can.move(self.drawable, 0, -self.speed)
            elif self.dir == "left":
                can.move(self.drawable, -self.speed, 0)
            elif self.dir == "down":
                can.move(self.drawable, 0, self.speed)
            elif self.dir == "right":
                can.move(self.drawable, self.speed, 0)

    def create_bullet(self):
        if self.color == "huge":
            b_e = P_BULLET_WIDTH

        else:
            b_w = E_BULLET_WIDTH
            b_h = E_BULLET_HEIGHT

        # To calculate the initial position of a bullet
        b_pos = self.get_pos()
        if self.dir == "up":
            b_pos = [b_pos[0] + (self.width-b_w)/2, b_pos[1]]
        elif self.dir == "down":
            b_pos = [b_pos[0] + (self.width-b_w)/2, b_pos[1] + self.height]
        elif self.dir == "left":
            b_pos = [b_pos[0], b_pos[1] + (self.height-b_w)/2]
        elif self.dir == "right":
            b_pos = [b_pos[0] + self.width, b_pos[1] + (self.height - b_w)/2]

        b = Bullet(b_pos, self.color, self.dir, b_w)
        return b


    def get_pos(self):
        b_pos = can.coords(self.drawable)
        b_pos = b_pos + [b_pos[0] + self.width, b_pos[1] + self.height]
        return b_pos


    def set_dir_up(self, event):
        self.dir = "up"


    def set_dir_down(self, event):
        self.dir = "down"

    def set_dir_left(self, event):
        self.dir = "left"

    def set_dir_right(self, event):
        self.dir = "right"


class Bullet():
    def __init__(self, pos, c, d, w):
        self.color = c
        self.width = w
        self.dir = d
        self.imgs = bullets_img[self.color]
        self.speed = 10
        self.drawable = can.create_image(pos[0], pos[1], image=self.imgs[self.dir], anchor="nw")
        self.state = ACTIVE

    def update_pos(self):
        can.itemconfig(self.drawable, image=self.imgs[self.dir])
        if self.dir == "up":
            can.move(self.drawable, 0, -self.speed)
        elif self.dir == "left":
            can.move(self.drawable, -self.speed, 0)
        elif self.dir == "down":
            can.move(self.drawable, 0, self.speed)
        elif self.dir == "right":
            can.move(self.drawable, self.speed, 0)

    def update_state(self):
        b_pos = self.get_pos()

        if b_pos[0] < 0 or b_pos[1] < 0 or \
                        b_pos[2] > WIN_WIDTH or b_pos[3] > WIN_HEIGHT:
            self.state = USELESS

    def get_pos(self):
        b_pos = can.coords(self.drawable)
        b_pos = b_pos + [b_pos[0] + self.width, b_pos[1] + self.width]
        return b_pos


# MAIN PROGRAM
# Tank and bullet list for enemies' tanks
enemy_tanks = []
enemy_bullets = []

for i in range(E_TANK_NUM):
    x = random.randint(0, WIN_WIDTH - E_TANK_WIDTH)
    y = random.randint(0, WIN_HEIGHT - E_TANK_WIDTH)
    d = random.choice(["up", "left", "down", "right"])
    c = random.choice(["red", "blue", "black"])
    enemy_tanks.append(Tank(x, y, d, c))

# Tank and bullet list for player's tanks
player_tank = Tank(10, 10, "down", "huge")
player_bullets = []


def shoot(event):
    player_bullets.append(player_tank.create_bullet())


# Bind keys with respond functions
can.bind_all('<Up>', player_tank.set_dir_up)
can.bind_all('<Left>', player_tank.set_dir_left)
can.bind_all('<Down>', player_tank.set_dir_down)
can.bind_all('<Right>', player_tank.set_dir_right)
can.bind_all('<space>', shoot)


def is_collide(a, b):
    if a[0] < b[2] and a[1] < b[3] and a[2] > b[0] and a[3] > b[1]:
        return True
    else:
        return False


def delete_useless(obj_list):
    new_obj_list = []
    for obj in obj_list:
        if obj.state == USELESS:
            can.delete(obj.drawable)
            del obj
        else:
            new_obj_list.append(obj)
    return new_obj_list


count = 0
player_lives = 10
score = 0
lives_text = can.create_text(10, 10, anchor='nw', text='lives: '+str(player_lives),
                       font=('Consolas', 15))

score_text = can.create_text(150, 10, anchor='nw', text='score: '+str(score),
                       font=('Consolas', 15))

running = True

while running:
    # Update position and images of player's tank
    player_tank.update_pos_img()

    # Update position and images of enemies' tanks
    for t in enemy_tanks:
        t.update_pos_img()
        if count % 20 == 0:
            enemy_bullets.append(t.create_bullet())

    # Update position and images of enemies' bullets
    for b in enemy_bullets:
        b.update_state()
        b.update_pos()
        if is_collide(b.get_pos(), player_tank.get_pos()):
            b.state = USELESS
            player_tank.state = EXPLODE

    # Update position and state of player's bullets
    for b in player_bullets:
        b.update_state()
        b.update_pos()
        for t in enemy_tanks:
            if is_collide(b.get_pos(), t.get_pos()):
                b.state = USELESS
                t.state = EXPLODE

    # Delete useless bullets that are out of window.
    # Delete Tanks that finished exploding.
    enemy_bullets = delete_useless(enemy_bullets)
    enemy_tanks = delete_useless(enemy_tanks)
    player_bullets = delete_useless(player_bullets)

    # Calculation of hp and score
    score = (E_TANK_NUM - len(enemy_tanks))*10

    if player_tank.state == USELESS:
        player_lives -= 1
        if player_lives > 0:
            player_tank.state = ACTIVE

    can.itemconfig(lives_text, text='lives: '+str(player_lives))
    can.itemconfig(score_text, text='score: '+str(score))

    # Game is end or not
    if player_tank.state == USELESS:
        can.create_text(WIN_WIDTH/2, WIN_HEIGHT/2,
                    text='YOU LOSE!\nscore:'+str(score),
                    font=('Lithos Pro Regular', 30))
        running = False

    if len(enemy_tanks) == 0:
        can.create_text(WIN_WIDTH / 2, WIN_HEIGHT / 2,
                        text='YOU WIN!\nscore:' + str(score),
                        font=('Lithos Pro Regular', 30))
        running = False

    count += 1
    can.update()
    time.sleep(0.1)

can.mainloop()

 

 

 

 

 

 

DOWNLOAD FULL SOURCE CODE

Leave a Reply