Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Custom Events

In this lesson, you’ll add custom events to your game to create new enemies. The design calls for enemies to appear at regular intervals. This means that, at set intervals, you need to do two things:

  1. Create a new Enemy
  2. Add it to all_sprites and enemies

Let’s see how to create a custom event that’s generated every few seconds. You can create a custom event by naming it:

78# Create the screen object
79# The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
80screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
82# Create a custom event for adding a new enemy
84pygame.time.set_timer(ADDENEMY, 250)
86# Instantiate player. Right now, this is just a rectangle.
87player = Player()

Next, you need to insert this new event into the event queue at regular intervals throughout the game. That’s where the time module comes in. Line 84 fires the new ADDENEMY event every 250 milliseconds, or four times per second. You call .set_timer() outside the game loop since you only need one timer, but it will fire throughout the entire game.

Add the code to handle your new event:

 99# Main loop
100while running:
101    # Look at every event in the queue
102    for event in pygame.event.get():
103        # Did the user hit a key?
104        if event.type == KEYDOWN:
105            # Was it the Escape key? If so, stop the loop.
106            if event.key == K_ESCAPE:
107                running = False
109        # Did the user click the window close button? If so, stop the loop.
110        elif event.type == QUIT:
111            running = False
113        # Add a new enemy?
114        elif event.type == ADDENEMY:
115            # Create the new enemy and add it to sprite groups
116            new_enemy = Enemy()
117            enemies.add(new_enemy)
118            all_sprites.add(new_enemy)
120    # Get the set of keys pressed and check for user input
121    pressed_keys = pygame.key.get_pressed()
122    player.update(pressed_keys)
124    # Update enemy position
125    enemies.update()

For more information about the time module, check out the pygame documentation.

00:00 In this lesson, you’ll design some custom events. You might remember in the tutorial game design, that there would be enemies that would appear at regular intervals and head toward the player. By the end of this lesson, you’ll have something that looks like this. This means that at set intervals, you need to do a couple things: create an enemy, and then add those enemies to all_sprites and to the enemies group also.

00:25 You already have code that handles random events. The event loop is designed to look for random events occurring every frame and then deal with them appropriately. Luckily, pygame doesn’t restrict you to using only the event types it has defined.

00:40 You can actually define custom events to handle as you see fit. PyGame defines events internally inside this event system as integers, and the last event PyGame reserves is called USEREVENT.

00:55 So, there’s kind of an interesting thing that you can do. By using pygame.USEREVENT and then adding 1 to it, it’ll ensure that that event is unique, so that’s one of the ways that you’ll create these events.

01:08 But how do you trigger them? What about timing? PyGame has a time module, pygame.time, and not only does it monitor time, but it’ll also let you do things like set timers.

01:18 So, pygame.time.set_timer() can be called outside of the game loop and then you can have it set to fire custom events throughout the entire game, and that’s how you’re going to generate these enemies.

01:31 Let me take you into the code.

01:36 Scroll down until you get to about line 80, 82. And right before you instantiate the player, you’re going to create your custom event.

01:45 It’s going to be for adding a new enemy. It’ll be called ADDENEMY, all caps like most of the other events you’ve worked with. pygame.USEREVENTand you might remember that was an integer—and to make it unique, you’re going to add 1 to it.

01:58 Ruh-roh. I missed a space here. I’ll fix it in the next video. And then here’s the second part, which is you’re going to use that time module, pygame.time, and set a timer.

02:07 Now, that method can take two arguments. The first is going to be what you want it to do when the timer goes off. In this case, it’s add an enemy—that event to be generated. And then how often, or basically the amount of time in between instances—in this case, it’s going to be 250 milliseconds, so every quarter of a second a new enemy would appear. So, that will happen.

02:27 Now, this is outside of the game loop, just like you’re instantiating the player outside of the game loop. It’s going to be continuously running outside of the game loop, generating these events that the game loop will then pick up.

02:41 So, down much lower, in here—here’s your main game loop. You’re going to add a new line after here. Now, I know I removed a space here. I’m going to re-add it, so move that down one, so 108 goes down to 109. And here, at 113—again, you need to indent so it’ll be in that main event loop. Not only within the game loop, but within the event loop. The big question is here # Add an enemy? Did that happen?

03:05 So it’s an elif also. And if the event.type—and like you can see, these are all all caps, right? So, here’s the new one you made—ADDENEMY. With a colon (:) at the end because it’s an if statement.

03:18 So, you’re going to create that new enemy and then add it to the sprite groups which you’ve created. So first off, you’re going to make your new_enemy object, which is going to be an Enemy. Enemies were a class that you created earlier, right? Okay.

03:31 So now you’re going to finally generate some of these, and then there’s an enemies group, and then you use the method .add() to add new_enemy to that group.

03:39 But there’s also the group of all of the sprites, which you’ll use for updating. And you’re going to use the .add() method for it, so new_enemy—there. Cool!

03:47 Go ahead and save. Now, here you are getting the set of keys pressed and then checking for user input, and then you were updating the player sprite based on those keypresses.

03:55 So, why don’t we combine those?

04:00 And we can then free up a little space. Because right after that happens, the player.update(), you’re going to do the enemy .update().

04:08 And enemies is a group, but if you pass this method to it of .update(), it will pass it to all the individual sprites and run that .update() code for all of them. And I’m going to save here, going back from 125 all the way back to your Enemyyou might remember creating it, that’s the class you created here. And again, it’s going to call this .update(), right? Where it moves from left—or from the right to the left until it goes off the screen, and then it would be killed and removed from the group. All right, fantastic.

04:39 Let’s try it out, with python sky_dodge.py. Okay! Here comes those enemies. You can see the random positions and the random speeds that you created.

04:52 As you can see, if I get hit by something, it currently doesn’t do anything with that.

05:00 That’s really what’s up next, with collision detection.

05:05 Go ahead and press Escape. All right, all those enemies are coming across the screen now. Great! Now, I need to detect when the collisions happen.

Avatar image for jamesbrown68

jamesbrown68 on July 11, 2020

Like someone else commented, I had to slow the enemy speed way down on my Windows 10 machine to make the game at all playable. I used ‘self.speed = random.randint(1, 2)’, as anything more than 2 was just a streak across the screen.

But then, that only leaves two speeds, slow and fast. I hunted around and found a way to use a float rather than an integer:

‘self.speed = random.uniform(1,2)

This seemed no different from random.randint(1,2) when it comes to enemy speed.

I also tried values less than 1 and greater than 2, such as (0.5, 2.5) but the results seemed the same. It feels like the slowest speed was 1. Going as high as 3 made some of the enemies into mere blurs–way too fast.

So I was expecting a range of float values, but it feels like perhaps some rounding to the nearest integer was occurring, maybe?

Become a Member to join the conversation.