how to implement AABB collision detection in Pygame for 2D games

Setting Up Pygame Collision Detection

Welcome back! Before we can worry about objects bumping into each other, we need to build the stage where the action happens. Think of this as setting up your workbench before building a model airplane. We need the tools (the library), the workspace (the window), and a clear understanding of where everything sits (the coordinates).

Professor Pixel's Tip:
Don't rush! A solid foundation prevents "ghost bugs" later. If your window doesn't open, your collision logic won't matter.

1. Installing and Importing Pygame

First things first: you need the library. Open your terminal (Command Prompt on Windows, Terminal on Mac/Linux) and run the installer.

pip install pygame

Once installed, you bring it into your Python script. By convention, we just say import pygame. This unlocks all the magic functions.

2. The Most Important Step: Initialization

Here is the number one mistake beginners make. You imported the library, but you haven't woken it up yet.

You must run pygame.init(). This initializes the internal modules for sound, display, and input. Without this, your game will crash with confusing errors.

import pygame

# Always do this first!
pygame.init()

3. Creating the Game Window

Now, let's create the canvas. We define a width and height, then tell Pygame to create the window.

WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("My Game")

4. Understanding the Coordinate System

This is crucial for collision detection. In standard math class, the origin (0,0) is usually at the bottom-left, and Y goes up.

In computer graphics (and Pygame), the origin (0,0) is at the top-left corner.

  • X-axis: Increases as you move Right.
  • Y-axis: Increases as you move Down.

Try it yourself: The Screen Grid

Move the sliders to see how the coordinate system works. Notice that increasing Y moves the dot down.

(0,0)
x →
↓ y

Now that you have your window and understand where the top-left corner is, you are ready to place objects. Next, we'll define those objects (like a player or a wall) and give them a size so we can check if they touch!

Basics of 2D Game Collision

Now that we have our window and our coordinate system, let's get to the exciting part: interaction. In a game, objects rarely exist in isolation. They bump, crash, and interact.

The most fundamental way to detect this interaction is called AABB (Axis-Aligned Bounding Box) collision. It sounds fancy, but the intuition is incredibly simple:

Professor Pixel's Intuition:
Imagine two cardboard boxes on a table. If they share any space—even a single millimeter—they are colliding. If there is even a tiny gap of air between them, they are not.

1. The Four Critical Edges

Since we are working in a 2D grid where (0,0) is the top-left, every rectangle is defined by four boundaries. To check if two boxes are touching, we just need to compare these edges.

Edge Logic Meaning
left x The starting point on the X-axis
right x + width Where the box ends on the X-axis
top y The starting point on the Y-axis
bottom y + height Where the box ends on the Y-axis

Interactive: Test the Overlap

Move the Red Rectangle (Player) to see when it collides with the Blue Rectangle (Wall). Notice the "Collision Status" indicator.

Status: NO COLLISION
Checking 4 conditions: Left < Right, Right > Left, Top < Bottom, Bottom > Top.
Wall
Player
(0,0)

2. The Code Logic

Pygame makes this incredibly easy with the pygame.Rect object. It automatically calculates the edges for you. However, understanding the underlying logic helps you debug when things go wrong.

def check_collision(rect_a, rect_b):
    return (rect_a.left < rect_b.right and
              rect_a.right > rect_b.left and
              rect_a.top < rect_b.bottom and
              rect_a.bottom > rect_b.top)

Notice the strict inequalities (< and >). This means if the rectangles are merely touching (e.g., rect_a.right == rect_b.left), the function returns False. This is usually what you want—you don't want a player to die just by grazing a wall.

3. Limitations & Edge Cases

While this logic is robust, it has assumptions.

Watch Out: Degenerate Rectangles

This logic assumes your rectangles have positive width and height. If a rectangle has width = 0, it becomes a line or a point. While the math technically holds up, a player with zero size is usually a bug in your game loading logic, not a feature.

Additionally, AABB only works for rectangles that are axis-aligned (straight up and down). If you start rotating your player or enemies, this simple check will fail (it will detect collisions in the empty corners of the rectangle). For rotated objects, we need more complex math, but for now, this is the perfect foundation!

Understanding AABB Collision in Pygame

Now that we have the logic, let's talk about the shape of our collision. In Pygame, we rarely use complex 3D physics engines. Instead, we rely on a concept called AABB (Axis-Aligned Bounding Box).

Think of an AABB as an invisible, non-rotating cardboard box that tightly wraps around your game object. The "axis-aligned" part is key: the sides of this box are always parallel to your screen's edges. It never tilts, even if your character inside it is spinning!

Professor Pixel's Visualizer: The Rotated Box Problem

Move the slider to rotate the Red Player. Notice how the Yellow AABB (the bounding box) must grow larger to contain the corners. This "empty space" is where false collisions happen.

What's happening?
  • The Yellow Box is the AABB. It is always a perfect rectangle.
  • The Red Shape is your sprite. It rotates inside the box.
  • As you rotate, the AABB gets bigger to fit the corners.
AABB

Why We Use AABB

You might wonder, "Why not check the exact pixels?" The answer is speed.

Checking if two rectangles overlap is incredibly fast for a computer—it's just four integer comparisons (Left, Right, Top, Bottom). If you have 100 enemies on screen, doing this simple math 60 times a second is easy. Doing complex pixel-perfect math for all of them would slow your game down.

# Pygame does this automatically for you!
if player_rect.colliderect(enemy_rect):
    print("Ouch! You hit something.")

The "Trap": When AABB Fails

This brings us to the most important misconception to avoid. AABB only approximates collision.

⚠️ The "Ghost Corner" Problem

Look at the visualizer above. When the red player rotates, the yellow box gets bigger. If another object touches the empty yellow corner, Pygame thinks you collided, even though your red shape didn't touch it. This is called a false positive.

Because of this, AABB is perfect for:

  • Platformers (Mario, Mega Man) where characters are boxy.
  • Tile-based games (Minecraft) where everything is a square.
  • Top-down games where objects don't rotate much.

However, if you are building a game with lots of rotating bullets or circular characters, you will eventually need to learn more advanced techniques like Circle-Circle Collision or Separating Axis Theorem (SAT). But for now, mastering the AABB box is your first and most important step!

Step-by-Step: Implementing Collision in Pygame

Now that we understand the theory, let's write the code. In Pygame, collision isn't magic—it's just math wrapped in a convenient object. The most important tool in your kit is the pygame.Rect.

1. Defining Bounding Boxes for Game Objects

Your first practical step is to give every interactive game object—player, enemy, wall, item—an invisible AABB. In Pygame, this is simply a pygame.Rect stored as an attribute of your object. The Rect should tightly match the visible sprite's dimensions and follow it as it moves.

Professor Pixel's Tip:
The Rect is the source of truth for collision. If your sprite's visual size changes (e.g., a power-up makes the player bigger), you must update self.rect to match. A mismatched Rect causes "ghost collisions" (hitting air) or "tunneling" (walking through walls).

If you are using pygame.sprite.Sprite, the convention is to name it self.rect. Here is how you attach it:

class Player(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        # Create the visual surface
        self.image = pygame.Surface((50, 50))
        self.image.fill((255, 0, 0))
        
        # Define the Rect based on the image
        self.rect = self.image.get_rect(topleft=(x, y))

The get_rect() call automatically sets self.rect.x, self.rect.y, width, and height. Whenever you move the player (e.g., self.rect.x += 5), the bounding box moves with it.

2. The Overlap Test: colliderect()

With rect attributes on your objects, the collision test becomes a one-liner using Pygame's built-in method:

if player.rect.colliderect(enemy.rect):
    print("Collision!")

colliderect() returns True the moment any pixel area overlaps. It performs the four-edge comparison we discussed earlier, but optimized and ready to use.

Interactive: The "Center Trap" Demonstration

Move the Red Player to the position (80, 80). You will see they overlap, but a "Center Distance" check would fail! This proves why we use edge checks.

Edge Check (Correct): NO OVERLAP
Center Check (Wrong): NO COLLISION
Wall
(0,0)
Player

3. Integrating into the Game Loop

Collision checks belong in your game loop's update phase, right after moving objects but before drawing. A minimal structure looks like this:

while running:
    # 1. Handle Input
    # 2. Update Positions
    player.rect.x += player.velocity_x
    
    # 3. Check Collisions (The Critical Step)
    for enemy in enemies:
        if player.rect.colliderect(enemy.rect):
            resolve_collision(player, enemy)
    
    # 4. Draw everything
    screen.fill(BLACK)
    all_sprites.draw(screen)
    pygame.display.flip()

Always resolve collisions immediately after detection (e.g., undo movement, trigger effects) so the same collision isn't processed repeatedly every frame.

4. Optimization: Don't Check Everything!

The naive loop for enemy in enemies is fine for a few objects. But if you have 1,000 enemies, checking every single one against every other one becomes slow.

⚠️ The "Naive Loop" Trap

Avoid checking walls against walls, or items against items. Only check objects that can collide (e.g., Player vs. Enemies).

For beginners, simply grouping your sprites helps. Pygame's pygame.sprite.groupcollide() is highly optimized and handles the loops for you.

Implementing Game Programming Collision

Welcome back! We've covered the theory of boxes and math, but now it's time to put on our engineer hats. In a real game, you aren't just checking two static rectangles on a piece of paper. You are managing dozens of moving objects.

The secret to managing this chaos is consistency. You need a strict rule for how every object tells the game "where I am."

1. Representing Player and Enemy Rectangles

The golden rule in Pygame is this: Every interactive object must have a pygame.Rect attribute.

Think of the Rect as the object's "official body." The image is just the costume; the Rect is the physical shape that the computer uses for calculations.

class Enemy(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((30, 30))
        self.image.fill((0, 255, 0))
        
        # The "Official Body"
        self.rect = self.image.get_rect(topleft=(x, y))

Notice the pattern: self.rect = self.image.get_rect(...). This links the visual size to the collision size. From this point on, whenever you want to move the enemy, you don't move the image—you move the rect.

Interactive: The "Ghost" Bug Demonstration

This visualizer demonstrates the most common beginner mistake: Updating the image but forgetting the Rect.

Move the sliders. Watch the Blue Player (Wrong). It moves visually, but its Collision Box (dashed line) stays behind. This causes "Ghost Collisions"—the player looks like they hit the wall, but the game thinks they are far away!

Correct Player (Red)
Wrong Player (Blue)
Red Player Status: SAFE
Blue Player Status: SAFE
WALL
CORRECT
WRONG
RECT

2. Updating Bounding Boxes

As you saw in the visualizer, the computer does not "see" your image moving. It only knows where the Rect is.

Inside your game loop or update method, you must update the rect directly.

# The Correct Way
def update(self):
    self.rect.x += 5 # Move the rect
    screen.blit(self.image, self.rect) # Draw image AT the rect

⚠️ The "Stale Variable" Trap

If you store your position in variables like self.x and self.y, you must remember to sync them with the rect: self.rect.topleft = (self.x, self.y). If you forget this line, your game will have "ghost" collisions just like the blue player above.

3. Handling Multiple Colliding Objects

What happens when you have one player and fifty enemies? You can't write fifty if statements. You need a loop.

# Sequential Check
for enemy in enemies:
    if player.rect.colliderect(enemy.rect):
        player.hp -= 10

This is called a Sequential Check. It works perfectly fine for most beginner games. However, there is a subtle danger: Modifying a list while iterating over it.

Professor Pixel's Tip:
If your collision logic removes an enemy from the list (e.g., enemies.remove(enemy)), you might accidentally skip the next enemy in the loop.

To fix this safely, iterate over a copy of the list:

# Safe Removal Loop
for enemy in enemies[:]: # Note the [:] slice
    if player.rect.colliderect(enemy.rect):
        enemies.remove(enemy)
        player.score += 1

Alternatively, if you are using Pygame's Sprite classes, you can use pygame.sprite.groupcollide() which handles all this complexity for you automatically. But for now, mastering the manual loop gives you total control!

Advanced AABB Techniques (Optimization)

Congratulations! You now have a game where objects bump into each other correctly. But as your game grows—adding 50 enemies, 100 bullets, and 200 coins—you might notice it slowing down.

This is where the "Advanced" part comes in. We are moving from Correctness (does it work?) to Efficiency (does it work fast enough?). Let's explore how professional game engines handle thousands of objects without crashing.

1. Fast Broad-Phase Filtering: Spatial Partitioning

Imagine a crowded party. If you want to find someone, asking every single person in the room if they know that person is slow and chaotic. A better strategy is to group people by tables. You only check the table you are at, and maybe the adjacent ones.

Spatial Partitioning does exactly this for your game. It divides your screen into a grid of cells. Each object is assigned to a cell based on its position. When checking for collisions, you only compare objects that are in the same cell or neighboring cells.

Interactive: The Party Grid

Move your mouse over the Red Dot to see how spatial partitioning works. Notice how we only check the Yellow Cells (current + neighbors), ignoring everyone else in the room.

Target Object
Cells to Check
Ignored Objects

Why this matters:
Without this grid, you check every object against every other object (O(n²)). With this grid, you only check neighbors (O(n)).

# The Logic: Assign objects to cells
CELL_SIZE = 100
def get_cell(rect):
    return (rect.x // CELL_SIZE, rect.y // CELL_SIZE)

# In your update loop:
for obj in objects:
    cell = get_cell(obj.rect)
    grid[cell].append(obj)

# Check only neighbors
for neighbor in get_neighbors(cell):
    if obj.rect.colliderect(neighbor.rect):
        handle_collision()

2. The Hybrid Approach: Circles as Pre-Check

Sometimes, checking the four edges of a rectangle is still too much math if you know two objects are far apart. A common trick is to use a Circle Check first.

Imagine wrapping your box in a circle. If the circles don't touch, the boxes definitely don't touch. Circle collision is just distance math: distance < radius1 + radius2.

# Fast Pre-Check
dx = center_a.x - center_b.x
dy = center_a.y - center_b.y

# If circles don't touch, skip the box check entirely
if dx*dx + dy*dy > (r1 + r2)**2:
    return False # Too far apart

# Only if circles touch, do the expensive box check
return rect_a.colliderect(rect_b)

⚠️ The "Over-Optimization" Trap

Don't add this unless you need it. If your game has fewer than 50 objects, standard AABB is faster because it has no overhead. Only use this if you have hundreds of objects and the game is lagging.

3. Handling Rotated Boxes (The "Hard Mode")

We mentioned earlier that AABB boxes cannot rotate. But what if your character spins (like a saw blade)?

If you rotate a box, its AABB (the bounding box) must grow to fit the corners. This creates "empty space" where collisions happen falsely. To fix this accurately, you need Oriented Bounding Boxes (OBB) and a math technique called the Separating Axis Theorem (SAT).

Professor Pixel's Advice:
Avoid this for your first game! Most 2D games simply keep colliders axis-aligned, even if the sprite rotates. It's a design trick that saves you from complex math. If you absolutely need rotation, look into physics libraries like Pymunk rather than writing it from scratch.

When to Use or Avoid AABB

Now that you have the code, the critical question for any engineer is: "Is this the right tool for my specific problem?"

AABB (Axis-Aligned Bounding Box) is incredibly fast, but it is also a rough approximation. It works beautifully for some games and feels terrible for others. Let's break down exactly when to use it and when to look for a different solution.

1. Suitable Scenarios: The "Boxy" World

You should reach for AABB when your game objects are roughly rectangular and do not rotate. Think of classic platformers like Super Mario or top-down dungeon crawlers like Zelda (the 2D ones).

Professor Pixel's Decision Checklist:

  • Are your sprites square or rectangular?
  • Do they stay upright (no spinning)?
  • Is performance critical (hundreds of objects)?

If you answered "yes", AABB is perfect. It operates on integers (pixel coordinates) and requires no complex math (like square roots or trigonometry). This makes it blazing fast and predictable.

2. The Pitfall: When AABB Fails

The problem with AABB is that it is a conservative approximation. It assumes your object fills the entire box. This leads to "false positives" in two main scenarios:

  • Circular/Organic Shapes: If you wrap a circle in a square box, the four corners of the box are empty space. A player might "hit" those invisible corners and feel like the game is unfair.
  • Concave Shapes (L-Shapes): If you have an L-shaped wall, the bounding box fills the empty corner. A player can walk into that empty corner, and the game will think they hit the wall.
  • Rotation: As we saw earlier, if an object spins, its AABB must grow to fit the corners, creating even more empty space.

Interactive: The "Concave" Problem

This visualizer demonstrates why AABB fails for complex shapes. Move the Red Player into the Empty Corner of the L-shaped wall.

Real Shape (Solid)
AABB Box (Invisible)
AABB Status: NO COLLISION
Notice how the box detects a hit in the empty corner?
AABB
YOU

In the visualizer above, the Yellow Box represents the AABB. When you move the player into the bottom-left corner of the L-shape, you are clearly not touching the wall. However, the AABB box fills that space, so Pygame thinks you collided. This is why AABB is bad for irregular shapes.

3. What to Do Instead

If you find yourself in these "failure cases", don't panic. You have options depending on your needs:

1. Circle Collision

If your objects are round (like billiard balls), use a distance check between centers. It's fast and accurate for circles.

2. Pixel Masks

Pygame has pygame.mask. This checks actual pixel transparency. It's perfect but slow. Use only for a few objects.

3. Multiple Boxes

For an L-shape, don't use one box. Use two smaller AABB boxes (one for the vertical part, one for the horizontal). This is often the best balance.

4. Physics Engines

If you need complex rotation and polygon collision, integrate a library like Pymunk or Box2D.

Professor Pixel's Final Advice: Start with AABB. It is the industry standard for 2D games because it is fast and "good enough" for 90% of cases. Only switch to these advanced methods if you have a specific design requirement that boxes cannot satisfy.

Optimizing Performance for Real-World Games

Congratulations! Your game is working, objects are colliding, and the logic is sound. But now, imagine you add 500 enemies. Or 1,000 bullets. Suddenly, your smooth 60 FPS game starts to stutter.

This is where we shift from making it work to making it fast. Optimization isn't about guessing; it's about measuring. Let's look at how to find the bottlenecks and why some "optimizations" can actually slow you down.

1. Profiling: Measure Before You Optimize

The biggest mistake developers make is optimizing something that isn't broken. Before you rewrite your code, you need to know where the time is actually going. Is it collision? Is it drawing? Is it loading images?

The simplest way to check is to time your collision loop using Python's built-in time.perf_counter().

import time

# Inside your game loop
collision_start = time.perf_counter()

# Your collision logic
for enemy in enemies:
    if player.rect.colliderect(enemy.rect):
        handle_collision(player, enemy)

collision_elapsed = time.perf_counter() - collision_start
print(f"Collision took: {collision_elapsed*1000:.2f}ms")

If this number stays under 1–2ms, you are safe! If it spikes to 5ms+, you have a problem. The issue is usually O(n²) behavior—as you add more objects, the time doesn't just grow linearly; it explodes.

Interactive: The Object Count vs. Time

Move the slider to increase the number of enemies. Watch how the "Collision Time" (the bar) grows. Notice how it starts flat but shoots up rapidly as the count increases. This is the "O(n²) Trap".

Time per Frame: 0.00 ms
Warning: If this exceeds 5ms, your game will lag!

Why does it spike?
With 10 enemies, you check ~100 pairs. With 200 enemies, you check ~40,000 pairs!

5ms Limit
Time

2. The Myth of "Caching" Bounding Boxes

Once you realize your game is slow, you might look for shortcuts. A common misconception is: "I'll make a copy of every object's rect and store it in a list so I don't have to access the object every time."

This is usually unnecessary and can actually slow you down.

⚠️ The Copy Trap

Accessing obj.rect is a simple pointer lookup—extremely fast. Creating a copy with obj.rect.copy() forces the computer to allocate new memory and duplicate data. Updating that copy every frame adds more work than just using the original!

The real performance win comes from reducing the number of checks (using spatial partitioning), not from caching the rectangles.

Visualizing the Cost: Access vs. Copy

Click the buttons to simulate 1,000,000 operations. Notice how "Copying" takes significantly more "Energy" (CPU time) than "Direct Access".

Operations: 1,000,000
Energy Used: 0%
Sprite
rect
Copy
Ready

The Rule of Thumb: Don't cache your rectangles. Instead, use Spatial Partitioning (like a grid system) to ensure you are only checking objects that are actually near each other. That is where the real speed comes from!

Debugging and Visualizing Collisions

You've written the logic, but your game feels "off". Maybe the player hits a wall too early, or an enemy takes damage from across the room. When logic is invisible, it's hard to trust.

Professional developers don't guess—they visualize. In this section, we'll learn how to turn on "X-Ray Vision" to see the invisible boxes your computer is using, and how to log collisions intelligently without freezing your game.

1. Drawing Bounding Boxes (The "X-Ray" Trick)

Your game objects have `pygame.Rect` attributes, but you never see them. The fastest way to debug collision is to draw these rectangles directly onto the screen. This turns abstract math into a visible picture.

We use pygame.draw.rect() with a bright color (like neon green) and a line width of 2 to create a wireframe effect.

Interactive: Toggle Debug Mode

Toggle the switch below to see the "Invisible" Collision Box. Notice how the Red Player is actually a circle, but the collision box is a Square. This "empty space" is often where bugs hide!

Debug Mode:
Why use this?

If your player looks like they are touching an enemy, but no damage happens, the boxes aren't overlapping. If the player dies too early, the box is too big.

Enemy
Player
# Inside your Game Loop (Drawing Phase)
DEBUG_MODE = True

if DEBUG_MODE:
    # Draw all rects in neon green
    for sprite in all_sprites:
        pygame.draw.rect(screen, (0, 255, 0), sprite.rect, 2)

2. Logging Collision Events (Don't Flood the Console!)

A common mistake is putting a print() statement inside the collision check.

⚠️ The "Console Flood" Trap

If you check for collision 60 times a second, and the player is touching the enemy for 3 seconds, you will print 180 lines of text instantly. This freezes your game and fills your console with garbage.

The Solution: Only log when the collision state changes (Starts or Ends). You need to remember if they were touching in the previous frame.

Interactive: The "Smart" Logger

Move the Red Player towards the Blue Wall. Watch the "Console Log" below. It only prints when you HIT or LEAVE the wall, not while you are just standing there.

Wall
Player
> System initialized...
> Waiting for input...
# The "State Change" Logic
previous_collisions = {}

for enemy in enemies:
    is_touching = player.rect.colliderect(enemy.rect)
    was_touching = previous_collisions.get(enemy.id, False)

    # Only log if state changed!
    if is_touching and not was_touching:
        print(f"START: Hit Enemy {enemy.id}")
    elif not is_touching and was_touching:
        print(f"END: Left Enemy {enemy.id}")

    previous_collisions[enemy.id] = is_touching

Frequently Asked Questions (FAQ)

Even with the best logic, you will run into edge cases. Here are the most common questions students ask when they hit a wall (pun intended). Let's troubleshoot the tricky stuff together.

1. Why does my collision detection fail when objects just touch?

This is the most common confusion! The standard AABB check uses strict inequalities (< and >).

If two rectangles are merely touching (e.g., rect_a.right == rect_b.left), the condition rect_a.right > rect_b.left is False. Therefore, no collision is reported.

Interactive: The "Touch" Test

Move the Red Player until it perfectly touches the Blue Wall. Notice that "Touching" is NOT "Colliding". You must overlap to trigger the check.

Math Check: 150 > 150? False
Collision Status: SAFE
Wall
Player

When is this bad? If you are building a platformer, you might want the player to "stand" on a platform. If you use strict inequalities, the player might fall through if they aren't overlapping slightly.

The Fix: Change your logic to use <= and >= (non-strict). However, be careful! This can sometimes cause "jitter" if objects get stuck touching each other.

2. How can I avoid false positives?

False positives happen when the bounding box includes empty space around your sprite.

  • Tighten the Box: Ensure your pygame.Rect is as small as possible while still covering the visible sprite.
  • Use Circles: If your object is round, use a circle check. It's often fairer for players.
  • Redesign: Sometimes the best fix is to design your game levels to match the boxy nature of AABB.

3. What about "Tunneling" (Fast objects passing through walls)?

Tunneling happens when an object moves so fast in a single frame that it jumps completely over a wall. The computer checks "Frame 1: Not touching", then "Frame 2: Not touching", missing the moment it actually passed through.

⚠️ The Solution

Clamp Speed: Make sure your object never moves more than its own width in a single frame.
Swept AABB: For advanced physics, calculate the path of movement (a line) and check for collisions along that path.

4. How do I debug invisible collisions?

Since the bounding boxes are invisible, you can't see why a collision is failing. The best trick is to draw them!

# Add this to your drawing loop
if DEBUG_MODE:
    for sprite in all_sprites:
        # Draw a green outline around every object
        pygame.draw.rect(screen, (0, 255, 0), sprite.rect, 2)

This lets you see exactly where the computer thinks your object is. Often, you'll find the box is slightly larger or shifted compared to the image.

Post a Comment

Previous Post Next Post