Getting Started with 2D Games Using Tkinter (Part x): Add Demon Images to Sprites
In this article, we will apply demon images to our sprites.
1. Prepare Demon Images
Please copy and use the following demon images in your game.
| Type | Alive | File Name | Dead | File Name |
|---|---|---|---|---|
| Blue Demon | dmn_alive_b.png | dmn_dead_b.png | ||
| Green Demon | dmn_alive_g.png | dmn_dead_g.png | ||
| Red Demon | dmn_alive_r.png | dmn_dead_r.png |
Place all image files inside the images folder.
# Folder structure
project_folder/
├ main.py
├ sprite.py
└ images/
├ bg_jigoku.png
├ dmn_alive_b.png # Blue demon (alive)
├ dmn_alive_g.png # Green demon (alive)
├ dmn_alive_r.png # Red demon (alive)
├ dmn_dead_b.png # Blue demon (dead)
├ dmn_dead_g.png # Green demon (dead)
└ dmn_dead_r.png # Red demon (dead)
2. Improve the DemonSprite Class
Add image-loading logic inside the constructor of the DemonSprite class.
Image objects are created using tkinter.PhotoImage() together with create_image().
Here, we randomly select one type from "r" (red), "g" (green), or "b" (blue) to determine the demon color.
- The alive image is stored in
self.photo_alive - The dead image is stored in
self.photo_dead - The currently displayed image is stored in
self.image
Since the demon starts alive, self.photo_alive is used initially.
# sprite.py (excerpt)
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 reference)
self.oval = cvs.create_oval(
x - r, y - r, x + r, y + r,
fill="white", width=0
)
# Randomly choose demon type: r, g, or b
self.type = random.choice(("r", "g", "b"))
file_alive = f"images/dmn_alive_{self.type}.png" # Alive image
self.photo_alive = tkinter.PhotoImage(file=file_alive)
file_dead = f"images/dmn_dead_{self.type}.png" # Dead image
self.photo_dead = tkinter.PhotoImage(file=file_dead)
self.image = cvs.create_image(x, y, image=self.photo_alive)
Finally, update the image inside the die() method using cvs.itemconfig().
# sprite.py (excerpt)
def die(self, cvs):
self.dead = True
self.stop()
# Update circle color
cvs.itemconfig(self.oval, fill="red")
# Update sprite image
cvs.itemconfig(self.image, image=self.photo_dead)
After implementing this, clicking a sprite will stop it and switch its image.
Complete Code
Below is the complete implementation with all features applied.
# 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 for collision detection
self.oval = cvs.create_oval(
x - r, y - r, x + r, y + r,
fill="white", width=0
)
# Random demon type
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
# Update circle position
cvs.coords(
self.oval,
self.x - self.r, self.y - self.r,
self.x + self.r, self.y + self.r
)
# Update image position
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):
rad = deg * math.pi / 180
self.vx = spd * math.cos(rad)
self.vy = spd * math.sin(rad)
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
W, H = 480, 320
F_RATE = 30
F_INTERVAL = int(1000 / F_RATE)
FONT = ("Arial", 16)
mx, my = 0, 0
bg_photo, bg_image = None, None
TOTAL_DEMONS = 10
demons = []
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)
# Create demon sprites
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():
cvs.delete("hud")
msg = f"x:{mx}, y:{my}"
cvs.create_text(mx, my, text=msg, fill="white", font=FONT, tag="hud")
for demon in demons:
wrap_screen(demon)
demon.update(cvs)
root.after(F_INTERVAL, update)
def wrap_screen(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):
for demon in demons:
if demon.is_inside(e.x, e.y) and not demon.is_dead():
demon.die(cvs)
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()
What's Next?
Thank you for reading!
In the next article, we will display a counter on the screen.
Stay tuned!

Top comments (0)