Getting Started with 2D Games Using Tkinter (Part x): Display the Remaining Time
In this final article, we will display the remaining time on the screen and implement game-over conditions.
(This is the last part of the series!)
1. Prepare a Timer Variable
First, create a variable named timer to store the remaining time.
Here, we calculate a value equivalent to about 10 seconds using the frame rate. (A bit tricky!)
# main.py (excerpt)
# Timer
timer = F_RATE * 10 # Remaining time (10 seconds)
2. Decrease the Timer
Inside the update() function, display the timer in the top-right corner of the screen and decrement the timer value by 1 every frame.
# main.py (excerpt)
def update():
""" Update function """
global timer
cvs.delete("hud")
msg = "x:{}, y:{}".format(mx, my)
cvs.create_text(
mx, my,
text=msg,
fill="white",
font=FONT,
tag="hud"
)
msg = "Remaining Demons: {}".format(counter)
cvs.create_text(
20, 20,
text=msg,
fill="white",
font=FONT,
tag="hud",
anchor="nw"
)
# Draw timer
msg = "Time Left: {:.2f}s".format(timer / F_RATE)
cvs.create_text(
W - 20, 20,
text=msg,
fill="white",
font=FONT,
tag="hud",
anchor="ne"
)
# Demon group
for demon in demons:
overlap_area(demon)
demon.update(cvs)
# Timer countdown
timer = timer - 1
root.after(F_INTERVAL, update)
3. Game Over Conditions
Finally, complete the game logic by checking the game state.
- If the remaining demon count reaches 0, display GAME CLEAR
- If the timer reaches 0, display GAME OVER
# main.py (excerpt)
# Screen update
if 0 < counter and 0 <= timer:
root.after(F_INTERVAL, update)
else:
# Game result
msg = "GAME CLEAR" if counter <= 0 else "GAME OVER"
cvs.create_text(
W / 2, H - 40,
text=msg,
fill="white",
font=FONT,
tag="hud"
)
Complete Code
Below is the complete code with the timer and game-ending logic implemented.
# sprite.py (complete code)
import math
import random
import tkinter
class DemonSprite:
def __init__(self, cvs, x, y, r):
self.x = x
self.y = y
self.r = r
self.vx = 0
self.vy = 0
self.dead = False
# Circle (collision detection)
self.oval = cvs.create_oval(
x - r, y - r, x + r, y + r,
fill="white", width=0
)
# Random demon type: r, g, b
self.type = random.choice(("r", "g", "b"))
file_alive = f"images/dmn_alive_{self.type}.png"
self.photo_alive = tkinter.PhotoImage(file=file_alive)
file_dead = f"images/dmn_dead_{self.type}.png"
self.photo_dead = tkinter.PhotoImage(file=file_dead)
self.image = cvs.create_image(x, y, image=self.photo_alive)
def update(self, cvs):
self.x += self.vx
self.y += self.vy
cvs.coords(
self.oval,
self.x - self.r, self.y - self.r,
self.x + self.r, self.y + self.r
)
cvs.coords(self.image, self.x, self.y)
def set_x(self, x):
self.x = x
def set_y(self, y):
self.y = y
def move(self, spd, deg):
radian = deg * math.pi / 180
self.vx = spd * math.cos(radian)
self.vy = spd * math.sin(radian)
def stop(self):
self.move(0, 0)
def die(self, cvs):
self.dead = True
self.stop()
cvs.itemconfig(self.oval, fill="red")
cvs.itemconfig(self.image, image=self.photo_dead)
def is_dead(self):
return self.dead
def is_inside(self, x, y):
dx = (self.x - x) ** 2
dy = (self.y - y) ** 2
dist = (dx + dy) ** 0.5
return dist < self.r
# main.py (complete code)
import random
import sprite
import tkinter
# Canvas size
W, H = 480, 320
# Frame rate
F_RATE = 30
F_INTERVAL = int(1000 / F_RATE)
# Font
FONT = ("Arial", 16)
# Mouse position
mx, my = 0, 0
# Background image
bg_photo, bg_image = None, None
# Total number of demons
TOTAL_DEMONS = 10
# Demon list
demons = []
# Demon counter
counter = TOTAL_DEMONS
# Timer (10 seconds)
timer = F_RATE * 10
def init():
global bg_photo, bg_image
bg_photo = tkinter.PhotoImage(file="images/bg_jigoku.png")
bg_image = cvs.create_image(W / 2, H / 2, image=bg_photo)
for _ in range(TOTAL_DEMONS):
x = random.random() * W
y = random.random() * H
demon = sprite.DemonSprite(cvs, x, y, 20)
demon.move(random.randint(1, 4), random.randint(0, 360))
demons.append(demon)
def update():
global timer
cvs.delete("hud")
msg = "x:{}, y:{}".format(mx, my)
cvs.create_text(mx, my, text=msg, fill="white", font=FONT, tag="hud")
msg = "Remaining Demons: {}".format(counter)
cvs.create_text(20, 20, text=msg, fill="white", font=FONT, tag="hud", anchor="nw")
msg = "Time Left: {:.2f}s".format(timer / F_RATE)
cvs.create_text(W - 20, 20, text=msg, fill="white", font=FONT, tag="hud", anchor="ne")
for demon in demons:
overlap_area(demon)
demon.update(cvs)
timer -= 1
if 0 < counter and 0 <= timer:
root.after(F_INTERVAL, update)
else:
msg = "GAME CLEAR" if counter <= 0 else "GAME OVER"
cvs.create_text(W / 2, H - 40, text=msg, fill="white", font=FONT, tag="hud")
def overlap_area(obj):
if obj.x < 0: obj.set_x(W)
if obj.x > W: obj.set_x(0)
if obj.y < 0: obj.set_y(H)
if obj.y > H: obj.set_y(0)
def on_mouse_clicked(e):
global counter
for demon in demons:
if demon.is_inside(e.x, e.y):
if demon.is_dead():
continue
demon.die(cvs)
counter -= 1
break
def on_mouse_moved(e):
global mx, my
mx, my = e.x, e.y
root = tkinter.Tk()
root.title("Hello, Tkinter!!")
root.resizable(False, False)
root.bind("<Button>", on_mouse_clicked)
root.bind("<Motion>", on_mouse_moved)
cvs = tkinter.Canvas(width=W, height=H, bg="black")
cvs.pack()
init()
update()
root.mainloop()
Final Words
Thank you very much for reading this series.
With this article, “Getting Started with 2D Games Using Tkinter” comes to an end.
Great job making it all the way through!
Although the game we created is very simple, it includes all the essential elements of game development:
- Sprite management
- Mouse input
- Game loop
- Game over conditions
If you understand everything up to this point, you should now be able to create simple games using Tkinter on your own.
I encourage you to build your own small game based on what you’ve learned here.
I hope this series becomes the starting point for your next creation!

Top comments (0)