In this lesson, you will learn how to make two boxes and check if they are touching each other. You will use a special Pygame tool to see if the boxes overlap. Then, you will print a message to see if they are touching or not.
In this lesson, you will learn how to make two boxes and check if they are touching each other. You will use a special Pygame tool to see if the boxes overlap. Then, you will print a message to see if they are touching or not.
Before you start coding, let's check how the tests look now.
Run this command in your terminal:
pytest module-1.3/blueprint-BP1/quest-45
What you should see:
ā Some tests will fail. This is okay for now! It means you haven't written the code for them yet, and the tests are waiting for you to do it.
Now, let's write the code in main.py
to make our boxes touch!
In Pygame, we use something called a Rect
(short for Rectangle) to help us with things like drawing boxes and checking if they touch. A Rect
has information about where the box is and how big it is.
To see if two Rect
objects are touching, Pygame has a special tool called colliderect()
. It will tell you True
if they are touching, and False
if they are not.
Here is a simple example of how you can make two boxes and check if they touch:
import pygame
# Make two imaginary boxes
box_1 = pygame.Rect(0, 0, 30, 30) # x, y, width, height
box_2 = pygame.Rect(20, 20, 30, 30)
# Check if they are touching
is_touching = box_1.colliderect(box_2)
# Print the answer
print(f"Are the boxes touching? {is_touching}")
Use these ideas to make your own boxes and check if they are touching in main.py
.
You've written your code! Now, let's check if it works.
Run this command in your terminal:
pytest module-1.3/blueprint-BP1/quest-45
What you should see:
ā All tests should pass! This means your code correctly checks if the boxes are touching.
This series of micro-quests focuses on building the foundational element of a game: the player's paddle. You will transition from representing game data using simple dictionaries to creating a dedicated Paddle
class, giving it attributes and methods to manage its state and behavior. Finally, you will learn how to save your progress using version control.
Objective: Create a Pygame script that creates a window and draws a single, static rectangle. The paddle's properties (x, y, width, height, color) must be stored in a Python dictionary.
Core Concept Introduced: Representing Game Objects with Dictionaries
Game objects, like a paddle, have properties such as position, size, and color. Initially, these properties can be stored together in a Python dictionary. This allows you to group related data under a single name.
Pygame uses pygame.Rect
objects to represent rectangular areas. These are useful for drawing and collision detection. A pygame.Rect
is typically created using pygame.Rect(x, y, width, height)
.
To draw a rectangle in Pygame, you use the pygame.draw.rect()
function. It requires the screen surface, the color, and the pygame.Rect
object.
Consider how you would define a dictionary to hold the paddle's data:
# Example of a dictionary structure
object_data = {
"position_x": 100,
"position_y": 200,
"size_width": 50,
"size_height": 10,
"object_color": (255, 0, 0) # Red
}
# Accessing values from the dictionary
print(object_data["position_x"])
You will need to create a pygame.Rect
using values from your dictionary and then use pygame.draw.rect()
with the screen, the dictionary's color, and the created Rect
.
Objective: Define an empty Python class named Paddle
. In the main part of the script, create an instance of this Paddle
class and print the object to the console to verify its creation.
Core Concept Introduced: Class Definition and Instantiation
A class is a blueprint for creating objects. It defines the structure (what data an object will hold) and behavior (what actions an object can perform) that objects of that class will have.
Defining a class in Python uses the class
keyword:
# Example of a simple class definition
class MyBlueprint:
pass # 'pass' is used when a block needs a statement but you don't want to add any code yet
Creating an object from a class is called instantiation. You do this by calling the class name followed by parentheses, like calling a function:
# Example of creating an object (instantiation)
my_object = MyBlueprint()
Printing an object instance to the console will show you its type and a unique identifier (often related to its memory address), confirming that the object was successfully created.
Objective: Enhance the Paddle
class by adding an __init__
method. The method should accept parameters for x, y, width, and height, and assign them to instance attributes (e.g., self.x
, self.y
). Create an instance and print one of its attributes (e.g., paddle_instance.width
) to the console.
Core Concept Introduced: The __init__
Method and Instance Attributes
The __init__
method is a special method in Python classes. It's automatically called when you create a new instance of the class. It's often referred to as the constructor because it's used to initialize the object's state.
The first parameter of any instance method, including __init__
, is conventionally named self
. self
refers to the instance of the object itself. You use self
to access or set the object's attributes.
Instance attributes are variables that belong to a specific instance of a class. They store the data that makes each object unique. You create and assign instance attributes within the __init__
method using the self.attribute_name = value
syntax.
Consider a simple class with an __init__
method:
# Example class with a constructor
class Box:
def __init__(self, length, width):
# Assign parameters to instance attributes
self.length = length
self.width = width
# Creating an instance calls __init__
my_box = Box(length=10, width=5)
# Accessing instance attributes
print(my_box.length)
print(my_box.width)
You will add an __init__
method to your Paddle
class that takes x
, y
, width
, and height
as arguments and stores them as attributes on the self
object. Then, create a Paddle
instance and print one of these attributes.
Objective: Add a draw
method to the Paddle
class. This method should take the Pygame screen
surface as an argument and draw the paddle on it using its own attributes. In the main game loop, create a Paddle
instance and call its draw
method to display it.
Core Concept Introduced: Instance Methods and Encapsulation
Instance methods are functions defined within a class that perform actions related to the object. Like __init__
, they take self
as the first parameter, allowing them to access and modify the object's attributes.
Encapsulation is the practice of bundling data (attributes) and the methods that operate on that data within a single unit (the class). This hides the internal details and provides a clear interface (the methods) for interacting with the object.
Instead of having a separate function that knows how to draw a paddle based on a dictionary (as in MQ10), the Paddle
object itself will now contain the logic to draw itself. The draw
method will use the paddle's own self.x
, self.y
, self.width
, self.height
, and self.color
(you might need to add color to __init__
and attributes) to create or update its pygame.Rect
and then call pygame.draw.rect()
.
Consider a class with a method that uses its attributes:
# Example class with a method
class Circle:
def __init__(self, radius, color):
self.radius = radius
self.color = color
def describe(self):
print(f"This is a {self.color} circle with radius {self.radius}.")
# Create an instance and call its method
my_circle = Circle(radius=5, color="blue")
my_circle.describe()
Your Paddle
class will need a draw
method that accepts the screen
object and uses self.rect
and self.color
to draw. The main part of your script will create a Paddle
object and then call player_paddle.draw(screen)
inside the game loop.
Objective: Add a move
method to the Paddle
class that accepts a speed
integer. The method should update the paddle's x
attribute by that speed. In the main script, create a paddle, call its move(10)
method once, and then draw it to show it has moved from its initial position.
Core Concept Introduced: Methods Modifying Object State
Methods can not only use an object's attributes (like the draw
method) but also change them. This allows objects to have dynamic behavior.
The move
method will take a speed
value and use it to change the paddle's horizontal position (self.x
). A positive speed will move it right, and a negative speed will move it left.
# Example class with a method that modifies state
class Counter:
def __init__(self, initial_value=0):
self.value = initial_value
def increment(self, amount=1):
self.value += amount
print(f"Value is now: {self.value}")
# Create an instance and modify its state
my_counter = Counter()
my_counter.increment() # value becomes 1
my_counter.increment(5) # value becomes 6
Your Paddle
class will need a move
method that takes a speed
argument and updates self.x
using addition (+=
). Remember that the draw
method uses self.x
and self.y
(or updates self.rect
based on them) to determine where to draw, so calling move
before draw
will show the paddle in its new position.
Objective: In your project directory containing the final paddle script, initialize a new Git repository. Add all the project files to the staging area and make your first commit with the message 'feat: Create initial Paddle class'.
Core Concept Introduced: Initializing a Git Repository and First Commit
Version control systems like Git help you track changes to your code over time. This allows you to revert to previous versions, collaborate with others, and understand the history of your project.
To start tracking a project with Git, you initialize a repository in the project's root directory. This creates a hidden .git
folder where Git stores all its tracking information.
Once a repository is initialized, you need to tell Git which files you want to track. This is done by adding them to the "staging area". The staging area is like a waiting area for changes you want to include in your next save point (commit).
A commit is a snapshot of your project's files at a specific point in time. Each commit has a unique identifier and a message describing the changes it contains.
The basic Git commands for this quest are:
git init
: Initializes a new Git repository in the current directory.git add .
: Adds all changes (new files, modified files, deleted files) in the current directory and its subdirectories to the staging area. (You could also specify individual files like git add main.py paddle.py
).git commit -m "Your commit message"
: Creates a new commit with the changes currently in the staging area. The -m
flag allows you to provide the commit message directly.You will execute these commands in your terminal within the directory where your main.py
and paddle.py
files are located.
Objective: Create two pygame.Rect
objects with overlapping coordinates and use pygame.Rect.colliderect()
to check if they overlap. Print the boolean result to the console.
Core Concept Introduced: Basic pygame.Rect
Collision Detection
pygame.Rect
objects are not just for drawing; they are also fundamental for handling collisions in Pygame. A pygame.Rect
object has several built-in methods for checking overlaps with other Rect
objects or points.
The colliderect()
method is one such method. When called on a Rect
object, it takes another Rect
object as an argument and returns True
if the two rectangles overlap anywhere, and False
otherwise.
Consider two rectangles:
import pygame
# Rectangle 1: starts at (50, 50), is 100 wide, 80 tall
rect1 = pygame.Rect(50, 50, 100, 80)
# Rectangle 2: starts at (120, 100), is 100 wide, 80 tall
# This will overlap with rect1
rect2 = pygame.Rect(120, 100, 100, 80)
# Check for collision
overlap_result = rect1.colliderect(rect2)
# Print the result
print(overlap_result) # This should print True
You will create two pygame.Rect
objects with coordinates that you know will cause them to overlap. Then, call the colliderect()
method on one of them, passing the other as an argument, and print the boolean value returned by the method.
Objective: Write a simple unit test using pytest
for the Paddle
class's __init__
method, verifying that its x
and y
attributes are correctly assigned upon instantiation.
Core Concept Introduced: Introduction to Unit Testing with Pytest (__init__
method)
Unit testing involves testing individual components (units) of your code in isolation. For a class, a unit is often a method. Testing the __init__
method ensures that objects are created with the correct initial state.
pytest
is a popular Python testing framework. Tests are typically written as functions that start with test_
. Inside a test function, you use assert
statements to check if a condition is true. If an assert
statement is false, the test fails.
To test the __init__
method of your Paddle
class, you will:
Paddle
class into your test file.test_paddle_initial_position
).Paddle
with specific x
and y
values.assert
statements to check if the x
and y
attributes of the created instance match the values you passed to the constructor.Consider a simple test for a class:
# Assume you have a class like this in 'my_module.py'
# class Point:
# def __init__(self, x, y):
# self.x = x
# self.y = y
# In your test file (e.g., test_my_module.py)
# from my_module import Point
def test_point_creation():
"""Tests that a Point object is created with correct coordinates."""
# Arrange: Define expected values
expected_x = 10
expected_y = 20
# Act: Create the object
my_point = Point(x=expected_x, y=expected_y)
# Assert: Check the object's attributes
assert my_point.x == expected_x, f"Expected x to be {expected_x}, but got {my_point.x}"
assert my_point.y == expected_y, f"Expected y to be {expected_y}, but got {my_point.y}"
# To run this test, save it as a file starting with 'test_' (like test_paddle.py)
# and run 'pytest' in your terminal in the same directory.
You will write a similar test function for your Paddle
class, focusing specifically on the x
and y
attributes set by __init__
. Note that because your Paddle
class might use pygame
internally (e.g., for creating a rect
), you might need to use testing techniques like mocking pygame
to run the test without a display. However, the core task is the assert
statements checking the attributes.