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:
- Create a new
Enemy
- Add it to
all_sprites
andenemies
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))
81
82# Create a custom event for adding a new enemy
83ADDENEMY = pygame.USEREVENT + 1
84pygame.time.set_timer(ADDENEMY, 250)
85
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
108
109 # Did the user click the window close button? If so, stop the loop.
110 elif event.type == QUIT:
111 running = False
112
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)
119
120 # Get the set of keys pressed and check for user input
121 pressed_keys = pygame.key.get_pressed()
122 player.update(pressed_keys)
123
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.USEREVENT
—and 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 Enemy
—you 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.
Become a Member to join the conversation.
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?