DEV Community

Kajiru
Kajiru

Posted on

Getting Started with 2D Games Using Tkinter (Part 6): Keeping Sprites Inside the Screen

Getting Started with 2D Games Using Tkinter (Part x): Keeping Sprites Inside the Screen

In this article, we will improve our game so that sprites cannot leave the game screen.
By checking sprite coordinates, we can keep them inside the visible area.

Improving the DemonSprite Class

First, we will enhance the DemonSprite class by adding two new methods: set_x() and set_y().
These methods allow us to directly modify the sprite’s x and y coordinates.

When a sprite goes outside the screen, we will use these methods to reposition it.

# sprite.py (excerpt)

def set_x(self, x):
    self.x = x  # change x coordinate

def set_y(self, y):
    self.y = y  # change y coordinate
Enter fullscreen mode Exit fullscreen mode

Implementing Screen Boundary Checks

Next, we will add a new function called overlap_area() in main.py.
This function checks whether a sprite has moved outside the screen.

It receives a sprite object as an argument and performs four checks:

  • If the x coordinate is smaller than 0 (left edge), move it to W (right edge)
  • If the x coordinate is larger than W (right edge), move it to 0 (left edge)
  • Apply the same logic to the y coordinate using 0 and H
# main.py (excerpt)

def overlap_area(obj):
    if obj.x < 0:
        obj.set_x(W)  # move to the right edge
    if W < obj.x:
        obj.set_x(0)  # move to the left edge
    if obj.y < 0:
        obj.set_y(H)
    if H < obj.y:
        obj.set_y(0)
Enter fullscreen mode Exit fullscreen mode

We use the overlap_area() function inside the update() function.

# main.py (excerpt)

# demon group
for demon in demons:
    overlap_area(demon)  # screen boundary check
    demon.update(cvs)
Enter fullscreen mode Exit fullscreen mode

With this logic in place, sprites are now confined within the game screen.
When a sprite exits on the right side, it reappears on the left, and vice versa.

Complete Code

Below is the complete code with all features implemented so far.

# sprite.py (complete)

import math
import random
import tkinter

# Demon sprite class
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

        # draw a circle
        self.oval = cvs.create_oval(
            x - r, y - r,
            x + r, y + r,
            fill="white", width=0
        )

    def update(self, cvs):
        # update position
        self.x = self.x + self.vx
        self.y = 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
        )

    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)
Enter fullscreen mode Exit fullscreen mode
# main.py (complete)

import math
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():
    """ initialization """
    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 i in range(TOTAL_DEMONS):
        x = random.random() * W
        y = random.random() * H
        demon = sprite.DemonSprite(cvs, x, y, 20)
        spd = random.randint(1, 4)
        deg = random.randint(0, 360)
        demon.move(spd, deg)
        demons.append(demon)

def update():
    """ update loop """
    cvs.delete("hud")

    msg = "x:{}, y:{}".format(mx, my)
    cvs.create_text(
        mx, my,
        text=msg,
        fill="white",
        font=FONT,
        tag="hud"
    )

    for demon in demons:
        overlap_area(demon)
        demon.update(cvs)

    root.after(F_INTERVAL, update)

def overlap_area(obj):
    if obj.x < 0:
        obj.set_x(W)
    if W < obj.x:
        obj.set_x(0)
    if obj.y < 0:
        obj.set_y(H)
    if H < obj.y:
        obj.set_y(0)

def on_mouse_clicked(e):
    print("Clicked:", e.x, e.y)

def on_mouse_moved(e):
    global mx, my
    mx, my = e.x, e.y

# Tkinter setup
root = tkinter.Tk()
root.title("Hello, Tkinter!!")
root.resizable(False, False)
root.bind("<Button>", on_mouse_clicked)
root.bind("<Motion>", on_mouse_moved)

# Canvas
cvs = tkinter.Canvas(width=W, height=H, bg="black")
cvs.pack()

init()
update()
root.mainloop()
Enter fullscreen mode Exit fullscreen mode

What’s Next?

Thank you for reading!
In the next article, we will learn how to stop sprites by clicking on them.

Stay tuned!

Top comments (0)