Table of Contents
- Introduction
- Prerequisites
- Setup
- Creating the Game Window
- Creating the Card Objects
- Displaying the Cards
- Adding Interactivity
- Implementing the Game Logic
- Adding Sound Effects
- Conclusion
Introduction
In this tutorial, we will be building a memory game using Python and Pygame. A memory game is a popular game that tests your memory by requiring you to remember the locations of matching pairs of cards. By the end of this tutorial, you will have a fully functional memory game that you can play and customize.
Prerequisites
To follow this tutorial, you should have a basic understanding of Python programming language concepts. Familiarity with object-oriented programming (OOP) will be beneficial but not essential. Additionally, you will need to have Pygame installed on your system. Pygame is a popular library for game development in Python. If you haven’t installed Pygame yet, follow the setup instructions in the next section.
Setup
To install Pygame, open your terminal or command prompt and run the following command:
pip install pygame
Once Pygame is installed, let’s proceed to build our memory game step by step.
Creating the Game Window
First, let’s import the necessary libraries and set up the Pygame window. Create a new Python file and add the following code: ```python import pygame
pygame.init()
# Set up the window
window_width = 800
window_height = 600
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Memory Game")
# Game loop
running = True
while running:
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Update game state
# Render
pygame.display.update()
# Quit the game
pygame.quit()
``` Let's go through the code step by step. First, we import the `pygame` module and initialize it using `pygame.init()`. This will initialize all the pygame modules. Next, we set up the game window by defining its width and height. We create the window using `pygame.display.set_mode()` and set the window's caption using `pygame.display.set_caption()`.
Next, we enter the game loop, which allows our game to continuously run until we choose to exit. Inside the game loop, we handle events using pygame.event.get()
. In the current implementation, we handle only the QUIT
event, which occurs when we click the close button on the window. When this event is detected, we set running
to False
to exit the game loop.
Finally, outside the game loop, we call pygame.quit()
to quit the game properly.
Run the script, and you should see an empty game window titled “Memory Game.”
Creating the Card Objects
To create a memory game, we need a set of cards that the player can flip to find matching pairs. Each card should have a hidden state and be associated with a specific image. Let’s create a Card
class to represent these card objects.
python
class Card:
def __init__(self, image_path):
self.image = pygame.image.load(image_path)
self.hidden = True
In this code snippet, we define the Card
class with an __init__()
method. In the __init__()
method, we load the card’s image using pygame.image.load()
and assign it to the image
attribute. We also set the initial hidden
state as True
.
To use the Card
class, update your code as follows:
```python
import pygame
pygame.init()
# Set up the window
window_width = 800
window_height = 600
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Memory Game")
# Create card objects
card_images = ["card1.png", "card2.png", "card3.png", "card4.png"]
cards = [Card(image_path) for image_path in card_images]
# Game loop
running = True
while running:
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Update game state
# Render
pygame.display.update()
# Quit the game
pygame.quit()
``` In this updated code, we create a list `card_images` containing the paths to the images for our cards. We then create a list `cards` using a list comprehension, where each element is an instance of the `Card` class initialized with the respective image path.
Displaying the Cards
Now that we have our card objects, let’s display them on the game window. We’ll lay out the cards in a grid-like fashion. ```python import pygame
pygame.init()
# Set up the window
window_width = 800
window_height = 600
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Memory Game")
# Create card objects
card_images = ["card1.png", "card2.png", "card3.png", "card4.png"]
cards = [Card(image_path) for image_path in card_images]
# Set up the card grid
grid_width = 2
grid_height = 2
card_width = 200
card_height = 200
padding_x = (window_width - (card_width * grid_width)) // (grid_width + 1)
padding_y = (window_height - (card_height * grid_height)) // (grid_height + 1)
# Game loop
running = True
while running:
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Render
window.fill((255, 255, 255)) # Fill the window with white color
# Display cards
for i, card in enumerate(cards):
row = i // grid_width
col = i % grid_width
x = (padding_x * (col + 1)) + (card_width * col)
y = (padding_y * (row + 1)) + (card_height * row)
if card.hidden:
pygame.draw.rect(window, (0, 0, 0), (x, y, card_width, card_height))
else:
window.blit(card.image, (x, y))
pygame.display.update()
# Quit the game
pygame.quit()
``` In this updated code, we define the card grid's dimensions (`grid_width` and `grid_height`) and the size of each card (`card_width` and `card_height`). We also define the padding between cards (`padding_x` and `padding_y`). All these values are calculated based on the window's dimensions and the desired layout.
Inside the game loop, we fill the window with a white color using window.fill()
to clear the previous frame. We then loop through each card object and calculate its position using the padding_x
, padding_y
, card_width
, and card_height
values. If a card is hidden, we draw a black rectangle using pygame.draw.rect()
. Otherwise, we display the card’s image using window.blit()
.
Run the script, and you should see the cards displayed in a grid-like layout.
Adding Interactivity
Now that we have the cards displayed, let’s make them interactive. Specifically, we want to allow the player to flip the cards by clicking on them. When a card is flipped, it should reveal its image. ```python import pygame
pygame.init()
# Set up the window
window_width = 800
window_height = 600
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Memory Game")
# Create card objects
card_images = ["card1.png", "card2.png", "card3.png", "card4.png"]
cards = [Card(image_path) for image_path in card_images]
# Set up the card grid
grid_width = 2
grid_height = 2
card_width = 200
card_height = 200
padding_x = (window_width - (card_width * grid_width)) // (grid_width + 1)
padding_y = (window_height - (card_height * grid_height)) // (grid_height + 1)
# Game loop
running = True
while running:
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
for i, card in enumerate(cards):
row = i // grid_width
col = i % grid_width
x = (padding_x * (col + 1)) + (card_width * col)
y = (padding_y * (row + 1)) + (card_height * row)
if x <= mouse_x <= x + card_width and y <= mouse_y <= y + card_height:
card.hidden = not card.hidden
# Render
window.fill((255, 255, 255)) # Fill the window with white color
# Display cards
for i, card in enumerate(cards):
row = i // grid_width
col = i % grid_width
x = (padding_x * (col + 1)) + (card_width * col)
y = (padding_y * (row + 1)) + (card_height * row)
if card.hidden:
pygame.draw.rect(window, (0, 0, 0), (x, y, card_width, card_height))
else:
window.blit(card.image, (x, y))
pygame.display.update()
# Quit the game
pygame.quit()
``` In this updated code, we handle the `MOUSEBUTTONDOWN` event using `pygame.MOUSEBUTTONDOWN`. We get the mouse's position when the event occurs using `pygame.mouse.get_pos()`. We then loop through each card and check if the mouse's position falls within the card's boundaries. If it does, we toggle the card's `hidden` attribute.
Run the script, and you should be able to flip the cards by clicking on them.
Implementing the Game Logic
Now that our cards are interactive, let’s implement the game logic to check for matching pairs. We’ll keep track of the flipped cards and compare their images to determine if they match. If they do, we’ll keep them face up; otherwise, we’ll flip them back. ```python import pygame import time
pygame.init()
# Set up the window
window_width = 800
window_height = 600
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Memory Game")
# Create card objects
card_images = ["card1.png", "card2.png", "card3.png", "card4.png"]
cards = [Card(image_path) for image_path in card_images]
# Set up the card grid
grid_width = 2
grid_height = 2
card_width = 200
card_height = 200
padding_x = (window_width - (card_width * grid_width)) // (grid_width + 1)
padding_y = (window_height - (card_height * grid_height)) // (grid_height + 1)
# Game variables
flipped_cards = []
matched_pairs = 0
# Game loop
running = True
while running:
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
for i, card in enumerate(cards):
row = i // grid_width
col = i % grid_width
x = (padding_x * (col + 1)) + (card_width * col)
y = (padding_y * (row + 1)) + (card_height * row)
if x <= mouse_x <= x + card_width and y <= mouse_y <= y + card_height:
if card.hidden and len(flipped_cards) < 2:
card.hidden = False
flipped_cards.append(card)
if len(flipped_cards) == 2:
if flipped_cards[0].image == flipped_cards[1].image:
matched_pairs += 1
flipped_cards.clear()
else:
# Wait for a short duration before flipping the unmatched cards back
time.sleep(1)
for flipped_card in flipped_cards:
flipped_card.hidden = True
flipped_cards.clear()
# Render
window.fill((255, 255, 255)) # Fill the window with white color
# Display cards
for i, card in enumerate(cards):
row = i // grid_width
col = i % grid_width
x = (padding_x * (col + 1)) + (card_width * col)
y = (padding_y * (row + 1)) + (card_height * row)
if card.hidden:
pygame.draw.rect(window, (0, 0, 0), (x, y, card_width, card_height))
else:
window.blit(card.image, (x, y))
pygame.display.update()
# Check if all pairs are matched
if matched_pairs == len(cards) // 2:
running = False
# Game over
print("Congratulations! You won!")
# Quit the game
pygame.quit()
``` In this updated code, we introduce two new variables: `flipped_cards` (to keep track of the cards that are currently face up) and `matched_pairs` (to keep track of the number of matched pairs).
Inside the MOUSEBUTTONDOWN
event, we check if a card is hidden and if the number of currently flipped cards is less than 2. If both conditions are met, we flip the card by setting its hidden
attribute to False
and add it to the flipped_cards
list.
When two cards are flipped, we compare their images. If they match, we increment matched_pairs
and clear the flipped_cards
list. If they don’t match, we briefly wait for one second using time.sleep()
and then flip the unmatched cards by setting their hidden
attribute back to True
. We also clear the flipped_cards
list.
After rendering the cards and updating the display, we check if all pairs are matched by comparing matched_pairs
to half the number of cards. If they are, we exit the game loop and display a “Congratulations! You won!” message.
Run the script, and you should be able to play the memory game. When all pairs are matched, the game will end, and the winning message will be displayed.
Adding Sound Effects
To make our game more engaging, let’s add sound effects for flipping matched and unmatched cards. We’ll use the pygame.mixer.Sound
class to load and play the sound effects.
```python
import pygame
import time
pygame.init()
# Set up the window
window_width = 800
window_height = 600
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Memory Game")
# Create card objects
card_images = ["card1.png", "card2.png", "card3.png", "card4.png"]
cards = [Card(image_path) for image_path in card_images]
# Set up the card grid
grid_width = 2
grid_height = 2
card_width = 200
card_height = 200
padding_x = (window_width - (card_width * grid_width)) // (grid_width + 1)
padding_y = (window_height - (card_height * grid_height)) // (grid_height + 1)
# Load sound effects
flip_sound = pygame.mixer.Sound("flip.wav")
match_sound = pygame.mixer.Sound("match.wav")
unmatch_sound = pygame.mixer.Sound("unmatch.wav")
# Game variables
flipped_cards = []
matched_pairs = 0
# Game loop
running = True
while running:
# Handle events
for event in pygame.event.get():
if event.type