Getting Started with 2D Games Using Tkinter (Part 8): Add Demon Images to Sprites

Published: (December 24, 2025 at 12:54 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

1. Prepare Demon Images

Please copy and use the following demon images in your game.

TypeAlive ImageFile Name (alive)Dead ImageFile Name (dead)
Blue DemonBlue demon alivedmn_alive_b.pngBlue demon deaddmn_dead_b.png
Green DemonGreen demon alivedmn_alive_g.pngGreen demon deaddmn_dead_g.png
Red DemonRed demon alivedmn_alive_r.pngRed demon deaddmn_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 with tkinter.PhotoImage() and displayed using canvas.create_image().
  • A random demon colour ("r", "g" or "b") is chosen.
  • 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.
# 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"
    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)

    # Start with the alive image
    self.image = cvs.create_image(x, y, image=self.photo_alive)

Update the image inside the die() method using canvas.itemconfig():

# sprite.py (excerpt)
def die(self, cvs):
    self.dead = True
    self.stop()

    # Change the collision circle colour
    cvs.itemconfig(self.oval, fill="red")

    # Switch to the dead sprite image
    cvs.itemconfig(self.image, image=self.photo_dead)

After implementing this, clicking a sprite will stop it and switch its image.

Demo GIF

Complete Code

Below is the full 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)

        # Start with the alive image
        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

    # Additional methods (e.g., stop, die) would go here

Feel free to integrate this class into your main game loop and enjoy the animated demons!

Additional Helper Functions

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

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-1>", 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!

Back to Blog

Related posts

Read more »