Based on the code given in https://realpython.com/pygame-a-primer/
We install "pygame" if it is missing.
try:
import pygame # import the library "pygame"
except: # install "pygame" if it is missing
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "pygame"])
import pygame
import random # we use randomness for the position and speed of enemies
# This module contains various constants used by pygame.
from pygame.locals import ( # import Arrow Keys and ESC
K_UP,
K_DOWN,
K_LEFT,
K_RIGHT,
K_ESCAPE,
)
Define the screen width and height
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 320
Initiate the pygame and define the screen variable
pygame.init() # initiate pygame
screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT]) # define the main screen
FPS (Frame per second): The number of frames shown in one second. E.g., TV shows/movies -> 24fps, live broadcast -> 30 fps, video games -> 30-120 fps.
We prepare each frame and then it is shown on the screen.
We use an infinite loop, but we can adjust FPS for our game.
To exit from the loop, the user can close the window, which is an event that we can check inside the loop.
running = True
while running:
for event in pygame.event.get(): # check all events one by one since the last frame
if event.type == pygame.QUIT: # if the window is closed
running = False
screen.fill((0, 0, 0)) # the screen background color is set to black (Red=0,Green=0,Blue=0)
# update and show the ship and enemies
pygame.display.flip() # show everything since the last frame
pygame.time.Clock().tick(30) # maximum number of frames per second <- set the FPS rate
Note that we can specify color with a tuple (Red,Green,Blue), where each entry is an integer between 0 and 255.
ship = pygame.sprite.Sprite()
ship.surf = pygame.Surface((60, 20)) # create a surface
ship.surf.fill((170, 255, 0)) # color of our photonic ship
ship.rect = ship.surf.get_rect() # create a variable to access the surface as a rectangle
We use "blit()" method of "screen": Block transfer, "blit(surface, position or rectangle)", the position of rectange is used when a rectange is provided
Place this code in the game-loop, just before "pygame.display.flip()":
screen.blit(ship.surf, ship.rect) # ship is "transferred as a block" on the screen
We can directly access all pressed keys by "pygame.key.get_pressed()".
We define a function "updateShip(presses_keys)" class taking "pressed_keys" as the input.
def updateShip(pressed_keys):
if pressed_keys[K_UP]:
ship.rect.move_ip(0, -5)
if pressed_keys[K_DOWN]:
ship.rect.move_ip(0, 5)
if pressed_keys[K_LEFT]:
ship.rect.move_ip(-5, 0)
if pressed_keys[K_RIGHT]:
ship.rect.move_ip(5, 0)
# Keep the ship within the screen
if ship.rect.left < 0:
ship.rect.left = 0
if ship.rect.right > SCREEN_WIDTH:
ship.rect.right = SCREEN_WIDTH
if ship.rect.top <= 0:
ship.rect.top = 0
if ship.rect.bottom >= SCREEN_HEIGHT:
ship.rect.bottom = SCREEN_HEIGHT
Now, let's call this function in the game-loop, before "blitting" the ship.
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_ESCAPE]: running = False # let's exit the game if the ship press "ESC"
updateShip(pressed_keys)
The ship should move within the screen amd should not be off screen.
We define a certain time interval to create each enemy randomly on the right side of the screen.
For this purpose, we will create a new EVENT not in the system and call it in every 250 miliseconds.
CREATING_ENEMY_TIME_INTERVAL = 250 # milliseconds
ADDENEMY = pygame.USEREVENT + 1 # each event is associated with an integer
pygame.time.set_timer(ADDENEMY, CREATING_ENEMY_TIME_INTERVAL)
Now, we have a new event ADDENEMY, and we catch it within the event loop of the game-loop.
REPLACE
while running:
for event in pygame.event.get(): # check all events one by one since the last frame
if event.type == pygame.QUIT: # if the window is closed
running = False
WITH
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == ADDENEMY: # we catch the new event here and then we will create a new enemy
print("The code for creating new enemy will come here") # we will replace this line in Step 11
Enemies and our photonic ship are basically the same, but their sizes and moves are defined differently.
First, we create groups for enemies and all sprites.
enemies = pygame.sprite.Group() # keep all enemies - the enemies will be added automatically
all_sprites = pygame.sprite.Group() # keep all enemies and ship(s)
all_sprites.add(ship) # add ship to the group of all sprites
Second, we define the function "createEnemy()".
def createEnemy():
enemy = pygame.sprite.Sprite() # create a new enemy
enemy.surf = pygame.Surface((20, 10)) # create a surface
enemy.surf.fill((255, 0, 0)) # color of enemy
enemy_X = random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100) # position of x
enemy_Y = random.randint(0, SCREEN_HEIGHT) # position of y
enemy.rect = enemy.surf.get_rect(center=(enemy_X,enemy_Y)) # position of the new enemy
enemy.speed = random.randint(5, 20) # we assign a random speed - how many pixel to move to the left in each frame
enemies.add(enemy) # add the new enemy
all_sprites.add(enemy) # add the new enemy
def updateEnemies():
for enemy in enemies:
enemy.rect.move_ip(-enemy.speed, 0) # change the horizontal position x
if enemy.rect.right < 0: # remove any enemy moving out side of the screen
enemy.kill() # a nice method of Sprite
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == ADDENEMY: # we catch the new event here and then we will create a new enemy
print("The code for creating new enemy will come here") # we will replace this line in Step 11
WITH
while running:
for event in pygame.event.get(): # check all events one by one since the last frame
if event.type == pygame.QUIT: # if the window is closed
running = False
elif event.type == ADDENEMY: # we catch the new event here and then we will create a new enemy
createEnemy()
Then, we update all enemies before updating the ship in the game-loop:
updateEnemies()
After that, we show all sprites (ship and enemies) by using "all_sprites":
REPLACE
screen.blit(ship.surf, ship.rect) # ship is "transferred as a block" on the screen
WITH
for entity in all_sprites:
screen.blit(entity.surf, entity.rect)
Thanks to Sprite and Sprite groups. We can detect if any enemy hits our photonic ship by using a single line of code.
if pygame.sprite.spritecollideany(ship, enemies): # check if "ship" is hit by any enemy in the "enemies" group
ship.kill() # ship is killed
running = False # the game-loop will be terminated
Step 13. Displaying "Game Over!"
Let's display "Game Over!" message before quitting the game.
We define a variable "there_is_message" initially set to "False". Whenever there is a message to be displayed, we set it to "True".
Before the game-loop:
there_is_message = False
Prepare the "Game Over!" message:
REPLACE
if pygame.sprite.spritecollideany(ship, enemies): # check if "ship" is hit by any enemy in the "enemies" group
ship.kill() # ship is killed
running = False # the game-loop will be terminated
WITH
if pygame.sprite.spritecollideany(ship, enemies): # check if "ship" is hit by any enemy in the "enemies" group
ship.kill()
running = False
my_font = pygame.font.SysFont('Comic Sans MS', 48) # create a font object
# we create a text surface to blit on the screen
text_surface = my_font.render("Game Over! ", False, (255, 0, 0), (0, 0, 0)) # message / anti-aliasing effect / text color / background color
screen.blit( text_surface, (SCREEN_WIDTH // 3,SCREEN_HEIGHT // 3) ) # blit the text on the screen with the specified position
there_is_message = True
This message can stay for example 2 seconds before the exiting the game.
REPLACE
pygame.display.flip() # show everything since the last frame
pygame.time.Clock().tick(30) # maximum number of frames per second <- set the FPS rate
WITH
pygame.display.flip() # show everything since the last frame
if there_is_message: pygame.time.wait(2000) # wait for 2000 milliseconds (= 2 seconds)
pygame.time.Clock().tick(30) # maximum number of frames per second <- set the FPS rate
Use the following tasks to practice and deepen your understanding of game design and Pygame features.
Add a score to the game and display it in the top-right or top-left corner.
A simple method: The player earns 1 point for every frame they survive.
The score can also increase based on elapsed time.
Our spaceship can fire bullets, and enemies hit by the bullets will be removed from the game.
Press the space key to shoot.
When the F key is pressed, all enemies will pause for a certain duration while the player can still move their spaceship./p>
This duration can be 1 or 2 seconds (equivalent to 30 or 60 frames).
Pressing the R key teleports the player's ship to a random location, ensuring it does not land on an enemy.
If you have extra time, consider adding more features from the list below or come up with your own creative ideas: