Control Sprites Using Keyboard Events
The program “sprite_display_move_bounce.py” moves the sprite without user action. However, computer games require interactivity between the user and the program.
To create games, we need to develop techniques such that the user can interact with the program using the keyboard keys or the mouse.
For example, in a collision game between a player sprite and several alien sprites. the alien sprites move independently on their own. On the other hand, the player’s sprite is moved by the user’s action via the keyboard or the mouse.
If the user moves the player sprite to collide with an alien sprite, the alien sprite will disappear from the screen and the program will make a “punch” sound effect or any sound of your choosing.
The sprite game program below uses the user-initiated keypress events to move the sprite image. The code defines a function move(x,y)
, which moves the sprite by x pixels in the x-direction and y pixels in the y-direction.
The next section’s sprite game program uses user-initiated mouse clicks to control the sprites.
PROGRAM EXAMPLE: KEYBOARD RVENTS AND SPRITES
Program Folder: C:\Users\YourUserName\Python_scripts\sprite_move_keyboard_event.py
Image Name: C:\Users\YourUserName\Python_scripts\sprite_images\ball.png
1 # PROGRAM TO MOVE A SPRITE USING KEYBOARD ARROW KEYS
2 # Program Name: sprite_move_keyboard_event.py
3 # Image Name: ball.png
4 import pygame
5 from pygame.locals import *
6 import os
7 # os Module provides a portable way of using operating system dependent functionality.
8
9 ################ VARIABLES ################
10 w = 640 # Width of game window (called 'screen')
11 h = 480 # Height of the game window.
12 FPS = 60
13 # Set number of times per second the MAIN LOOP repeats.
14
15 # Define colors
16 WHITE = (255, 255, 255)
17 BLACK = (0, 0, 0)
18 RED = (255, 0, 0)
19 GREEN = (0, 255, 0)
20 BLUE = (0, 0, 255)
21
22 ################ SETUP ################
23 # Initialize pygame and create game window and name it "screen".
24 pygame.init() # Intialize Pygame.
25 screen = pygame.display.set_mode((w, h))
26 # "screen" is display window of specified width (w) and height (h).
27 pygame.display.set_caption('Sprite Move Using Keyboard')
28 # Title of Display Window that appears on top of the screen window.
29 clock = pygame.time.Clock()
30 # Call "Clock" method to create a ‘clock’ object.
31 # The clock object is the MAIN LOOP's clock.
32 # pygame.time.clock()is used in association with clock.tick() in line 143.
33
34 ################ OBJECTS ################
35 ################ class Player(pygame.sprite.Sprite): ################
36 class MySprite(pygame.sprite.Sprite):
37 def __init__ (self):
38 pygame.sprite.Sprite.__init__(self)
39 self.change_x = 0
40 self.change_y = 0
41 # set up asset folders
42 sprite_program_folder = os.path.dirname(__file__)
43 sprite_img_folder = os.path.join(sprite_program_folder, 'sprite_images')
44 ball_img = pygame.image.load(os.path.join(sprite_img_folder, 'ball.png')).convert()
45 self.image = ball_img
46 self.image.set_colorkey(WHITE)
47 self.rect = self.image.get_rect()
48
49 # Define a procedure and name it "step".
50 def step(self,x, y): # move player by displacement (x,y)
51 self.change_x = self.change_x + x
52 self.change_y = self.change_y + y
53 #
54 # Define a procedure named update(). It is called in the MAIN LOOP
55 # to update the screen in every repeat of the MAIN LOOP.
56 def update(self):
57 self.rect.x = self.rect.x + self.change_x
58 self.rect.y = self.rect.y + self.change_y
59 # Screen Boundary Crossing Check.
60 # Test if the sprite has crossed the left or right boundaries
61 # of the screen. If the sprite reaches the left or the right boundary
62 # of the screen, bring it back to the opposite edge of the screen.
63 if self.rect.x > w:
64 self.rect.x = 0
65 if self.rect.x < 0:
66 self.rect.x = w
67
68 # Test if the sprite reaches the top or the bottom boundary of the screen,
69 # bring it back to the opposite edge of the screen.
70 if self.rect.y > h:
71 self.rect.y = 0
72 if self.rect.y < 0:
73 self.rect.y = h
74
75 # Create a Group called "all_sprites".
76 all_sprites = pygame.sprite.Group()
77 # Make an instance of "MySprite" class and call it "ballSprite".
78 ballSprite = MySprite()
79 # Add the sprite we just created to the "all_sprites" group.
80 all_sprites.add(ballSprite)
81 # Initialize sprite coordinates on screen.
82 ballSprite.rect.x = w // 2
83 ballSprite.rect.y = h // 2
84 stepsPerKeypress = 5 # Number of pixels the sprite moves per keypress.
85
86 ################ MAIN LOOP ################
87 running = True
88 while running:
89 # Check the Events Queue for events initiated by the user.
90 for event in pygame.event.get():
91 # Check if the user clicked the "X" on the window to exit the program.
92 # if "X" is clicked on the screen window, set "running" = False and end the MAIN LOOP.
93 if event.type == pygame.QUIT:
94 running = False
95 # Monitor Events Queue to check if the user presses the "q" key
96 # on the keyboard to quit the game.
97 # If "q" key is pressed, set "running" to False and end the MAIN LOOP.
98 elif event.type == pygame.KEYDOWN:
99 if event.key == ord('q'):
100 running = False
101 #
102 # Now check if any of the arrow keys are pressed by the user.
103 if event.key == pygame.K_LEFT:
104 ballSprite.step(-stepsPerKeypress, 0)
105 # Reduce x-coordinate by "stepsPerKeypress" pixels.
106 elif event.key == pygame.K_RIGHT:
107 ballSprite.step(stepsPerKeypress, 0)
108 # Increase x-coordinate by "stepsPerKeypress" pixels.
109
110 elif event.key == pygame.K_UP:
111 ballSprite.step(0, -stepsPerKeypress)
112 # Reduce y-coordinate by "stepsPerKeypress" pixels.
113 elif event.key == pygame.K_DOWN:
114 ballSprite.step(0, stepsPerKeypress)
115 # Increase y-coordinate by "stepsPerKeypress" pixels.
116
117 # Now check if the user has released the pressed key.
118 # if the key is released, do not move the sprite.
119 # The sprite stays in the current location.
120 if event.type == pygame.KEYUP:
121 if event.key == pygame.K_LEFT:
122 ballSprite.step(stepsPerKeypress,0)
123
124 if event.key == pygame.K_RIGHT:
125 ballSprite.step(-stepsPerKeypress,0)
126
127 if event.key == pygame.K_UP:
128 ballSprite.step(0, stepsPerKeypress)
129
130 if event.key == pygame.K_DOWN:
131 ballSprite.step(0, -stepsPerKeypress)
132
133 # Call procedure update() to update the screen.
134 all_sprites.update()
135 # Render the screen.
136 screen.fill(BLUE)
137 all_sprites.draw(screen)
138
139 # After drawing the screen Surface, call the pygame.display.flip() method.
140 # flip() method makes everything we have drawn on the screen Surface become visible.
141 pygame.display.flip()
142
143 clock.tick(FPS)
144 # tick() method runs the loop at "stepsPerKeypress" code in statement 12, FPS = 60.
145 pygame.quit()
146 # When the MAIN LOOP ends, pygame.quit() statement closes the window.
Below is the program’s output. The motion of the ball is the user playing the game using keypresses. Notice how the ball bounces off the display screen boundaries.
Detailed Line-by-line Explanation of the Sprite Game Program (Keyboard Events):
The explanation of all lines is the same as the program “sprite_display_move_bounce.py” except for the following lines of code.
Lines 36 – 40:
36 class MySprite(pygame.sprite.Sprite):
37 def __init__ (self):
38 pygame.sprite.Sprite.__init__(self)
39 self.change_x = 0
40 self.change_y = 0
The code in lines 36 – 40 is similar to lines in the program “sprite_display_move_bounce.py”.
The function __init__
creates the class MySprite and attributes “change_x” and “change_y”.
“change_x” specifies the number of pixels the sprite moves in the x-direction each time the left arrow key is pressed.
Similarly, “change_y” specifies the number of pixels the sprite moves in the y-direction each time the right arrow key is pressed.
Function Definition “step(self, x, y)”
Lines 50 – 52:
50 def step(self,x, y): # move player by displacement (x,y)
51 self.change_x = self.change_x + x
52 self.change_y = self.change_y + y
Lines 50 – 52 define a procedure named “step
“. The procedure is called by the Event Loop when a keypress event is detected. The “step” function moves the sprite by the displacement (x,y) every time the “step” function is called.
This procedure takes the current position of the pixels and adds the displacement (x,y) to calculate the new sprite position.
Function Definition “update()”
Lines 56 – 58:
56 def update(self):
57 self.rect.x = self.rect.x + self.change_x # Step 9)
58 self.rect.y = self.rect.y + self.change_y
The code in lines 56 – 58 defines the update
() procedure.
The update() procedure moves the sprite to its new position, before blitting it onto the screen in the new position. It adds the change_x
defined in the function step to the current x-coordinates (rect_x
) of the sprite.
Similarly, it adds change_y
defined in the function step to the current y-coordinate (rect_y
) of the ball. This happens in each loop iteration of the Main Loop.
Boundary Check in the LEFT and RIGHT Direction
Lines 63 – 66:
63 if self.rect.x > w:
64 self.rect.x = 0
65 if self.rect.x < 0:
66 self.rect.x = w
The code in lines 63 – 66 is part of the update(self) procedure and checks if the ball has crossed the left OR the right boundaries of the screen. If the sprite reaches the left ( x < 0) OR the right (x > w) boundaries of the screen, this code brings it back to the opposite edge of the screen.
Boundary Check in the TOP and BOTTOM Direction
Lines 70 – 73:
70 if self.rect.y > h:
71 self.rect.y = 0
72 if self.rect.y < 0:
73 self.rect.y = h
Lines 70 -73 are also part of the update(self) procedure and test if the sprite reaches the top (y < 0) or the bottom (y > h) boundary of the screen. In either of these two cases, the above code brings the sprite back to the opposite edge of the screen.
Initialize the Position of The Sprite
Lines 82 – 84:
82 ballSprite.rect.x = w // 2
83 ballSprite.rect.y = h // 2
84 stepsPerKeypress = 5
This code initializes the position of the sprite to the center of the screen and defines the number of pixels the sprite moves per key press. Line 84 sets the number of pixels to move for each keypress to 5 pixels.
The Event Loop
Line 87 – 100: The Event Loop
This code is the same as in the program sprite_display_move_bounce.py” and checks if the user clicked “X” on the game window or pressed the “q” key.
Lines 103 – 107:
103 if event.key == pygame.K_LEFT:
104 ballSprite.step(-stepsPerKeypress, 0)
105 # Reduce x-coordinate by "stepsPerKeypress" pixels.
106 elif event.key == pygame.K_RIGHT:
107 ballSprite.step(stepsPerKeypress, 0)
Event Loop Checks For Key Presses LEFT ARROW and RIGHT ARROW
The Event Loop code in lines 103 – 108 checks for keypress events.
If the left arrow key event (pygame.K_LEFT) is detected, the function “step” is called, which moves the ball to the left by “stepPerKeypress” pixels. “stepPerKeypress” variable was set to be 5 in code line #84. If the right arrow keypress event is detected, the “step” function moves the ball to the right by “stepPerKeypress” pixels.
Event Loop Checks for Key Presses UP ARROW And DOWN ARROW
Lines 110 – 114:
110 elif event.key == pygame.K_UP:
111 ballSprite.step(0, -stepsPerKeypress)
112 # Reduce y-coordinate by "stepsPerKeypress" pixels.
113 elif event.key == pygame.K_DOWN:
114 ballSprite.step(0, stepsPerKeypress)
The above code does the same logic as code lines 103 – 107, but it does it for the y-direction (UP/DOWN direction).
Lines 120 -131:
120 if event.type == pygame.KEYUP:
121 if event.key == pygame.K_LEFT:
122 ballSprite.step(stepsPerKeypress,0)
123
124 if event.key == pygame.K_RIGHT:
125 ballSprite.step(-stepsPerKeypress,0)
126
127 if event.key == pygame.K_UP:
128 ballSprite.step(0, stepsPerKeypress)
129
130 if event.key == pygame.K_DOWN:
131 ballSprite.step(0, -stepsPerKeypress)
Event Loop Checks For Release of The Keys
The code in lines #120 – 131 checks if the user has released the pressed key. This code makes sure that if the key is released, the sprite is not allowed to move. The sprite stays in its current location. If we did not have this code, the sprite will keep on moving even after the key has been released.
The rest of the program does the same functionality as the “sprite_display_move_bounce.py“ program.
Control Sprite Using Mouse Events
In this section, the sprite game program does the same event processing as in the “sprite_move_keyboard_event.py” program, except instead of the keyboard events, it uses mouse motion events.
For details of the mouse events, refer to the page “Events in Pygame”.
PROGRAM EXAMPLE: MOUSE EVENTS AND SPRITES
Program Folder: C:\Users\YourUserName\Python_scripts\sprite_move_mouse_event.py
Image Name: C:\Users\YourUserName\Python_scripts\sprite_images\ball.png
1 # PROGRAM TO MOVE A SPRITE USING MOUSE
2 # Program Name: sprite_move_mouse_event.py
3 # Image Name: ball.png
4 import pygame
5 from pygame.locals import *
6 import os
7 # os Module provides a way of using operating system dependent functionality.
8
9 ################ VARIABLES ################
10 w = 640 # Width of game window (called 'screen')
11 h = 480 # Height of the game window.
12 FPS = 60
13 # Set number of times per second the MAIN LOOP repeats.
14
15 # define colors
16 WHITE = (255, 255, 255)
17 BLACK = (0, 0, 0)
18 RED = (255, 0, 0)
19 GREEN = (0, 255, 0)
20 BLUE = (0, 0, 255)
21
22 ################ SETUP ################
23 # Initialize pygame and create game window and name it "screen".
24 pygame.init() # Intialize Pygame.
25 screen = pygame.display.set_mode((w, h))
26 # "screen" is display window of specified width (w) and height (h).
27 pygame.display.set_caption('Sprite Move Using Keyboard')
28 # Title of Display Window that appears on top of the screen window.
29 clock = pygame.time.Clock()
30 # Call "Clock" method to create a ‘clock’ object.
31 # The clock object is the MAIN LOOP's clock.
32 # pygame.time.clock()is used in association with clock.tick() in step 15.
33
34 ################ OBJECTS ################
35 ################ class Player(pygame.sprite.Sprite): ################
36 class MySprite(pygame.sprite.Sprite):
37 def __init__ (self):
38 pygame.sprite.Sprite.__init__(self)
39 self.change_x = 0
40 self.change_y = 0
41 # set up asset folders
42 sprite_program_folder = os.path.dirname(__file__)
43 sprite_img_folder = os.path.join(sprite_program_folder, 'sprite_images')
44 ball_img = pygame.image.load(os.path.join(sprite_img_folder, 'ball.png')).convert()
45 self.image = ball_img
46 self.image.set_colorkey(WHITE)
47 self.rect = self.image.get_rect()
48
49 # Define a procedure and name it "move".
50 def move(self,x, y): # move player movement
51 x, y = pygame.mouse.get_pos()
52 self.change_x = x
53 self.change_y = y
54 # Define a procedure update(), which is called in the MAIN LOOP
55 # to update the screen in every repeat of the MAIN LOOP.
56 def update(self):
57 self.rect.x = self.change_x
58 self.rect.y = self.change_y
59 # Screen Boundary Crossing Check.
60 # Test if the sprite has crossed the left or right boundaries
61 # of the screen. If the sprite reaches the left or the right boundary
62 # of the screen, bring it back to the opposite edge of the screen.
63 if self.rect.x > w -20:
64 self.rect.x = 20
65 if self.rect.x < 20:
66 self.rect.x = w - 100
67 #
68 # Test if the sprite reaches the top or the bottom boundary of the screen.
69 # If it does, bring it back to the opposite edge of the screen.
70 if self.rect.y > h - 20:
71 self.rect.y = 20
72 if self.rect.y < 20:
73 self.rect.y = h - 100
74
75 # Create a Group called "all_sprites".
76 all_sprites = pygame.sprite.Group()
77 # Make an instance of "MySprite" class and call it "ballSprite".
78 ballSprite = MySprite()
79 # Add the sprite we just created to the "all_sprites" group.
80 all_sprites.add(ballSprite)
81 # Initialize sprite coordinates on screen.
82 ballSprite.rect.x = w // 2
83 ballSprite.rect.y = h // 2
84
85 #
86 ################ MAIN LOOP ################
87 running = True
88 while running:
89 # Check the Events Queue for events initiated by the user.
90 for event in pygame.event.get():
91 # Check if the user clicked the "X" on the window to exit the program.
92 # if "X" is clicked, set "running" = False and end the MAIN LOOP.
93 if event.type == pygame.QUIT:
94 running = False
95 # Monitor events queue to check if the user presses the "q" key
96 # on the keyboard to quit the game.
97 # If "q" key is pressed, set "running" to False and end the MAIN LOOP.
98 elif event.type == pygame.KEYDOWN:
99 if event.key == ord('q'):
100 running = False
101
102 # Now check if the user moved the mouse.
103 if event.type == pygame.MOUSEMOTION:
104 x, y = pygame.mouse.get_pos() # Get mouse coordinates.
105 print('Mouse motion detected', event.pos)
106 ballSprite.move(x, y) # Call function move(x,y).
107 #
108 # Call procedure update() to update the screen.
109 all_sprites.update()
110 # Render the screen.
111 screen.fill(BLUE)
112 all_sprites.draw(screen)
113
114 # After drawing the screen Surface, call the pygame.display.flip() method.
115 # flip() method makes everything we have drawn on the screen Surface become visible.
116 pygame.display.flip()
117
118 clock.tick(FPS)
119 # tick() method runs the loop 60 times per second as defined
120 # in code line #12 by statement FPS = 60.
121 pygame.quit()
122 # When the MAIN LOOP ends, pygame.quit() statement closes the window.
Below is the sprite game program’s output. The sprite image is moving on the screen as the user is moving the mouse reacting to event type pygame.MOUSEMOTION
Detailed Line-by-line Explanation of the Sprite Game Program Using Mouse Events:
The explanation of this program is the same as the “sprite_move_keyboard_event.py” program, except for the following code line numbers.
Function Definition “move(self, x, y)”
Lines 50 – 53:
50 def move(self,x, y): # move player movement
51 x, y = pygame.mouse.get_pos()
52 self.change_x = x
53 self.change_y = y
The code above defines a procedure called “move”. The procedure calls the mouse.get_pos()
method and stores the current position coordinates in variables ”x” and “y”.
The pygame.mouse.get_pos(x,y)
function gets the mouse curser position. This function returns the mouse x and y cursor position relative to the left-top corner of the display. Refer to the Pygame page for details about this function.
Function Definition “update()”
Lines 63 – 73: The update() procedure
63 if self.rect.x > w -20:
64 self.rect.x = 20
65 if self.rect.x < 20:
66 self.rect.x = w - 100
67 #
68 # Test if the sprite reaches the top or the bottom boundary of the screen.
69 # If it does, bring it back to the opposite edge of the screen.
70 if self.rect.y > h - 20:
71 self.rect.y = 20
72 if self.rect.y < 20:
73 self.rect.y = h - 100
Lines 63 – 73 define the procedure update().
It is very similar to the code in the “sprite.move_keyboard_event.py” program, except for the boundary check code. The above code tests if the sprite has crossed the left OR the right boundaries of the screen.
If the sprite reaches the left OR the right boundary of the screen, this code brings the sprite back to the opposite edge of the screen.
Lines 63 – 66 do the test for the left and right boundaries of the display screen.
Lines 70 – 73 do the same test as the code in lines 60 – 63, except it checks for sprite boundary crossover for the screen’s top and bottom boundaries.
The Event Loop
Line 88 – 100: Part of the Event Loop
88 while running:
89 # Check the Events Queue for events initiated by the user.
90 for event in pygame.event.get():
91 # Check if the user clicked the "X" on the window to exit the program.
92 # if "X" is clicked, set "running" = False and end the MAIN LOOP.
93 if event.type == pygame.QUIT:
94 running = False
95 # Monitor events queue to check if the user presses the "q" key
96 # on the keyboard to quit the game.
97 # If "q" key is pressed, set "running" to False and end the MAIN LOOP.
98 elif event.type == pygame.KEYDOWN:
99 if event.key == ord('q'):
100 running = False
This code is the same as in the program “sprite_move_keyboard_event.py’ and checks if the user clicked “X” on the game window or pressed the “q” key.
Check for Mouse Motion Events
Lines 103 – 106: This is also part of the Event Loop.
103 if event.type == pygame.MOUSEMOTION:
104 x, y = pygame.mouse.get_pos() # Get mouse coordinates.
105 print('Mouse motion detected', event.pos)
106 ballSprite.move(x, y)
In the Event Loop, lines 103 – 106 test if the user moved the mouse. The test is done by checking if the event type is Pygame.MOUSEMOTION.
If the event type is MOUSEMOTION, the code calls the method mouse.get_pos() and stores the coordinates of the mouse position in variables “x” and “y”.
The code then prints the text “Mouse motion detected”. It also prints the coordinates of the mouse position when the event was detected.
Refer to the Pygame Events page for details of pygame.MOUSEMOTION
event type and for mouse.get_pos() method.