DEV Community

Kajiru
Kajiru

Posted on

Getting Started with 2D Games Using Pyxel (Part 11): Implement Game Over Detection

Implement Game Over Detection

In this chapter, we will detect collisions between the player and asteroids

and display “GAME OVER” on the screen.

The complete code is provided at the end.

1. Prepare a Game Over Flag

In the constructor of the Game class in main.py,

add a flag to manage the game over state.

When the player collides with an asteroid,

this flag will be set to True.

# main.py (inside the Game class constructor)

# Game over flag
self.game_over_flg = False
Enter fullscreen mode Exit fullscreen mode

2. Monitor the Flag in the Update Process

In the update() method of the Game class,

check the game over flag.

If the flag is True,

use return to stop further processing.

This ensures that after the game is over,

the player, bullets, and other objects are no longer updated.

# main.py (inside the Game.update() method)

# Game over
if self.game_over_flg:
    return
Enter fullscreen mode Exit fullscreen mode

3. Display Text in the Draw Process

In the draw() method of the Game class,

display the text “GAME OVER” only when the game is over.

# main.py (inside the Game.draw() method)

# Game over
if self.game_over_flg:
    msg = "GAME OVER"
    pyxel.text(W / 2 - len(msg) * 2, H / 2, msg, 13)
Enter fullscreen mode Exit fullscreen mode

4. Detect Collision Between Player and Asteroids

Inside the update() method of the Game class,

check for collisions between the player and asteroids.

When a collision occurs,

set self.game_over_flg to True to trigger game over.

# main.py (inside the Game.update() method)

# Update asteroids
for asteroid in self.asteroids:
    asteroid.update()
    self.overlap_spr(asteroid)
    # Collision detection (asteroid x player)
    if asteroid.intersects(self.ship):
        self.game_over_flg = True  # Game over
Enter fullscreen mode Exit fullscreen mode

Complete Code

Below is the complete code with all features implemented.

# sprite.py
import pyxel
import math
import random

class BaseSprite:

    def __init__(self, x, y, w=8, h=8):
        """ Constructor """
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.vx = 0
        self.vy = 0

    def update(self):
        """ Update """
        self.x += self.vx
        self.y += self.vy

    def draw(self):
        """ Draw (implemented in subclasses) """
        pass

    def move(self, spd, deg):
        """ Move """
        rad = deg * math.pi / 180
        self.vx = spd * math.cos(rad)  # X velocity
        self.vy = spd * math.sin(rad)  # Y velocity

    def flip_x(self):
        """ Flip X direction """
        self.vx *= -1

    def intersects(self, other):
        """ Rectangle-based collision detection (AABB) """
        if other.x + other.w < self.x: return False
        if self.x + self.w < other.x: return False
        if other.y + other.h < self.y: return False
        if self.y + self.h < other.y: return False
        return True

class ShipSprite(BaseSprite):

    def __init__(self, x, y):
        """ Constructor """
        super().__init__(x, y)

    def draw(self):
        """ Draw """
        pyxel.blt(
            self.x, self.y, 0,
            0, 0,
            self.w, self.h, 0  # Ship
        )

class AsteroidSprite(BaseSprite):

    def __init__(self, x, y):
        """ Constructor """
        super().__init__(x, y)
        self.index = random.randint(2, 7)  # Asteroid image index

    def draw(self):
        """ Draw """
        pyxel.blt(
            self.x, self.y, 0,
            self.w * self.index, 0,
            self.w, self.h, 0  # Asteroid
        )

class BulletSprite(BaseSprite):

    def __init__(self, x, y):
        """ Constructor """
        super().__init__(x, y)
        self.x += self.w / 2 - 1  # Center alignment

    def draw(self):
        """ Draw """
        pyxel.rect(self.x, self.y, 2, 2, 12)
Enter fullscreen mode Exit fullscreen mode
# main.py
import pyxel
import math
import random
import sprite

W, H = 160, 120
SHIP_SPD = 1.4

ASTEROID_INTERVAL = 20
ASTEROID_LIMIT = 30

ASTEROID_SPD_MIN = 1.0
ASTEROID_SPD_MAX = 2.0
ASTEROID_DEG_MIN = 30
ASTEROID_DEG_MAX = 150

BULLET_SPD = 3

# Game
class Game:
    def __init__(self):
        """ Constructor """

        # Game over flag
        self.game_over_flg = False

        # Initialize score
        self.score = 0

        # Initialize player
        self.ship = sprite.ShipSprite(W / 2, H - 40)
        deg = 0 if random.random() < 0.5 else 180
        self.ship.move(SHIP_SPD, deg)

        # Asteroids
        self.asteroid_time = 0
        self.asteroids = []

        # Bullets
        self.bullets = []

        # Start Pyxel
        pyxel.init(W, H, title="Hello, Pyxel!!")
        pyxel.load("shooter.pyxres")
        pyxel.run(self.update, self.draw)

    def update(self):
        """ Update """

        # Game over
        if self.game_over_flg:
            return

        # Update player
        self.ship.update()
        self.control_ship()
        self.overlap_spr(self.ship)

        self.check_interval()  # Spawn asteroids

        # Update asteroids
        for asteroid in self.asteroids:
            asteroid.update()
            self.overlap_spr(asteroid)
            # Collision detection (asteroid x player)
            if asteroid.intersects(self.ship):
                self.game_over_flg = True

        # Update bullets (reverse order)
        for bullet in self.bullets[::-1]:
            bullet.update()
            # Remove bullets that leave the screen
            if bullet.y < 0:
                self.bullets.remove(bullet)
                continue
            # Collision detection (bullet x asteroid)
            for asteroid in self.asteroids[::-1]:
                if asteroid.intersects(bullet):
                    self.score += 1
                    self.bullets.remove(bullet)
                    self.asteroids.remove(asteroid)
                    return

    def draw(self):
        """ Draw """
        pyxel.cls(0)

        # Game over
        if self.game_over_flg:
            msg = "GAME OVER"
            pyxel.text(W / 2 - len(msg) * 2, H / 2, msg, 13)

        # Draw score
        pyxel.text(10, 10, "SCORE:{:04}".format(self.score), 12)

        # Draw player
        self.ship.draw()

        # Draw asteroids
        for asteroid in self.asteroids:
            asteroid.draw()

        # Draw bullets
        for bullet in self.bullets:
            bullet.draw()

    def control_ship(self):
        """ Player action """
        if pyxel.btnp(pyxel.KEY_SPACE):
            self.ship.flip_x()  # Reverse movement
            # Fire bullet
            bullet = sprite.BulletSprite(self.ship.x, self.ship.y)
            bullet.move(BULLET_SPD, 270)
            self.bullets.append(bullet)

    def overlap_spr(self, spr):
        """ Wrap sprite around the screen """
        if spr.x < -spr.w:
            spr.x = W
            return
        if W < spr.x:
            spr.x = -spr.w
            return
        if spr.y < -spr.h:
            spr.y = H
            return
        if H < spr.y:
            spr.y = -spr.h
            return

    def check_interval(self):
        # Asteroid spawn interval
        self.asteroid_time += 1
        if self.asteroid_time < ASTEROID_INTERVAL:
            return
        self.asteroid_time = 0
        # Limit asteroid count
        if ASTEROID_LIMIT < len(self.asteroids):
            return
        # Spawn asteroid
        x = random.random() * W
        y = 0
        spd = random.uniform(ASTEROID_SPD_MIN, ASTEROID_SPD_MAX)
        deg = random.uniform(ASTEROID_DEG_MIN, ASTEROID_DEG_MAX)
        asteroid = sprite.AsteroidSprite(x, y)
        asteroid.move(spd, deg)
        self.asteroids.append(asteroid)

def main():
    """ Main process """
    Game()

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Result

Running the game produces the following result:

Final Thoughts

Thank you very much for reading this far.

This concludes “Getting Started with 2D Games Using Pyxel”.

Although the game we built is very simple, it includes core elements of game development:

  • Sprite management
  • Keyboard input
  • Collision detection and object removal

If you understand everything up to this point,

you should now be able to create simple games using Pyxel.

I hope this series becomes a stepping stone into game development for you.

(If you enjoyed it, a 👍 would be greatly appreciated!!)

Top comments (0)