Spawn Asteroids
In this chapter, we implement logic to spawn asteroids at regular intervals.
Here, we’ll build more game-like systems such as:
- Managing multiple sprites
- Executing actions at fixed time intervals
The complete code is shown at the end of this chapter.
1. Create an Asteroid Sprite
First, let’s prepare a sprite class for asteroids.
Add a new AsteroidSprite class to sprite.py.
In its constructor, we use a random value to select which asteroid image to display.
# sprite.py
# add this class
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
With this setup, each asteroid will have a different appearance
every time it is generated.
2. Prepare Constants and Variables
Next, add asteroid-related constants to main.py.
By grouping these values as constants,
it becomes much easier to adjust the game difficulty later.
# main.py
# add constants
ASTEROID_INTERVAL = 20 # Spawn interval
ASTEROID_LIMIT = 30 # Maximum number of asteroids
ASTEROID_SPD_MIN = 1.0 # Minimum speed
ASTEROID_SPD_MAX = 2.0 # Maximum speed
ASTEROID_DEG_MIN = 30 # Minimum angle
ASTEROID_DEG_MAX = 150 # Maximum angle
Next, add variables for managing asteroids
to the constructor of the Game class.
# main.py
# add to Game.__init__()
# Asteroids
self.asteroid_time = 0 # Spawn interval counter
self.asteroids = [] # Asteroid list
3. Asteroid Spawning Logic
Add a check_interval() method to the Game class.
This method spawns asteroids at fixed intervals.
Asteroids appear every ASTEROID_INTERVAL frames.
The total number of asteroids is limited by ASTEROID_LIMIT.
The spawn position is randomly chosen along the top edge of the screen.
Speed and angle are also randomly selected within predefined ranges.
# main.py
# add to Game class
def check_interval(self):
# Asteroid spawn interval
self.asteroid_time += 1
if self.asteroid_time < ASTEROID_INTERVAL:
return
self.asteroid_time = 0
# Do not exceed the maximum number of asteroids
if ASTEROID_LIMIT < len(self.asteroids):
return
# Add a new 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)
4. Updating and Drawing Asteroids
Next, add update and draw logic for asteroids.
In the update() method of the Game class,
handle asteroid spawning and updating.
# main.py
# add to Game.update()
self.check_interval() # Spawn asteroids
# Update asteroids
for asteroid in self.asteroids:
asteroid.update()
self.overlap_spr(asteroid)
In the draw() method,
draw all asteroids at once.
# main.py
# add to Game.draw()
# Draw asteroids
for asteroid in self.asteroids:
asteroid.draw()
Now, multiple asteroids will flow across the screen.
Complete Code
Below is the complete code implementing all the features in this chapter.
# 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-axis velocity
self.vy = spd * math.sin(rad) # y-axis velocity
def flip_x(self):
"""Flip movement in the x direction"""
self.vx *= -1
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
# main.py
import pyxel
import math
import random
import sprite
W, H = 160, 120
SHIP_SPD = 1.4
ASTEROID_INTERVAL = 20 # Spawn interval
ASTEROID_LIMIT = 30 # Maximum number of asteroids
ASTEROID_SPD_MIN = 1.0 # Minimum speed
ASTEROID_SPD_MAX = 2.0 # Maximum speed
ASTEROID_DEG_MIN = 30 # Minimum angle
ASTEROID_DEG_MAX = 150 # Maximum angle
# Game
class Game:
def __init__(self):
"""Constructor"""
# 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 # Spawn interval counter
self.asteroids = [] # Asteroid list
# Start Pyxel
pyxel.init(W, H, title="Hello, Pyxel!!")
pyxel.load("shooter.pyxres")
pyxel.run(self.update, self.draw)
def update(self):
"""Update"""
# 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)
def draw(self):
"""Draw"""
pyxel.cls(0)
# 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()
def control_ship(self):
"""Action"""
if pyxel.btnp(pyxel.KEY_SPACE):
self.ship.flip_x() # Reverse movement
def overlap_spr(self, spr):
"""Move to the opposite side when leaving 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
# Do not exceed the maximum number of asteroids
if ASTEROID_LIMIT < len(self.asteroids):
return
# Add a new 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"""
Game()
if __name__ == "__main__":
main()
When you run the game, it looks like this.
Next Time...
Thank you for reading!
In the next chapter, we’ll cover “Let’s Fire Bullets.”
Stay tuned!

Top comments (0)