Building an Alien Invasion Game with Pygame: Window, Loop, and Player Ship
This guide walks through the first steps of a simple 2D game using Pygame: creating a window, handling events, and drawing a player spaceship on the screen.
Setup
Install Pygame with pip:
pip install pygame
If pip is outdated, upgrade it:
pip install --upgrade pip
Create a new project directory (for example, alien_invasion/) and an images/ subfolder. Place a ship image at images/ship.bmp.
Using Git for version control is recommended.
Pygame building blocks used here
pygame.init()- Initializes Pygame modules and must be called before using most Pygame APIs.
pygame.display.set_mode((width, height))- Creates the main window and returns a
Surfacerepresenting the screen.
- Creates the main window and returns a
pygame.display.set_caption(title)- Sets the window title.
Surface.fill(color, rect=None)- Fills the entire surface or an optional rectangular area with a color. Colors are RGB tuples like
(255, 0, 0).
- Fills the entire surface or an optional rectangular area with a color. Colors are RGB tuples like
pygame.display.flip()orpygame.display.update()- Updates the screen with whatever you’ve drawn since the last flip/udpate.
pygame.event.get()- Retrieves a list of pending events (keyboard, mouse, window events, etc.).
pygame.QUIT- Event fired when the window is closed.
sys.exit()- Terminates the program (typically after
pygame.quit()).
- Terminates the program (typically after
pygame.image.load(path)- Loads an image file as a
Surface. BMP is universally supported; PNG works well in most setups.
- Loads an image file as a
Surface.get_rect()- Produces a
Rectthat describes the surface’s size and position.Rectsupports attributes likecenter,centerx,centery,top,bottom,left,right,midtop,midbottom, etc., as well asxandyfor the top-left corner. Rect coordinates are integers. The origin(0, 0)is the top-left of the screen and incresaes to the right and downward.
- Produces a
Surface.blit(image_surface, dest_rect)- Draws one surface onto another at the position determined by
dest_rect.
- Draws one surface onto another at the position determined by
Project structure
settings.py– basic configuration like screen dimensions and background colorship.py– player ship sprite and placement logicalien_invasion.py– game bootstrap (window, loop, eveent processing, drawing)
settings.py
Define a compact configuration object for screen size and background color.
# settings.py
class GameSettings:
"""Holds configuration for the game window and visuals."""
def __init__(self) -> None:
# Window configuration
self.width: int = 1200
self.height: int = 800
# Background color (light gray)
self.bg_color: tuple[int, int, int] = (230, 230, 230)
ship.py
Load and position a spaceship image. We’ll center it along the bottom edge of the screen.
# ship.py
import pygame
class PlayerShip:
"""Manage the player-controlled ship's image and placement."""
def __init__(self, screen: pygame.Surface) -> None:
self.screen = screen
self.screen_rect = self.screen.get_rect()
# Load the ship image and compute its rect
# .convert() speeds up blits for opaque images; use .convert_alpha() for images with transparency
self.image = pygame.image.load("images/ship.bmp").convert()
self.rect = self.image.get_rect()
# Place the ship centered at the bottom of the screen
self.rect.midbottom = self.screen_rect.midbottom
# Optionally lift it a bit above the edge
self.rect.y -= 10
def render(self) -> None:
"""Draw the ship at its current location."""
self.screen.blit(self.image, self.rect)
alien_invasion.py
Initialize Pygame, create the window, handle the event loop, and draw the background and ship.
# alien_invasion.py
import sys
import pygame
from settings import GameSettings
from ship import PlayerShip
class AlienDefense:
"""Initialize core game objects and run the main loop."""
def __init__(self) -> None:
pygame.init()
self.settings = GameSettings()
self.screen = pygame.display.set_mode(
(self.settings.width, self.settings.height)
)
pygame.display.set_caption("Alien Invasion")
# Cap the frame rate for consistent behavior
self.clock = pygame.time.Clock()
# Game entities
self.ship = PlayerShip(self.screen)
def run(self) -> None:
while True:
self._handle_events()
self._draw_frame()
# Limit to ~60 frames per second
self.clock.tick(60)
def _handle_events(self) -> None:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def _draw_frame(self) -> None:
# Clear the screen
self.screen.fill(self.settings.bg_color)
# Draw entities
self.ship.render()
# Present the frame
pygame.display.flip()
if __name__ == "__main__":
game = AlienDefense()
game.run()