McTweak.ai

Chapter 8, Episode 9

Collision Basics

The Collision Incident

TrashyMcTweak
TrashyMcTweak:

BEHOLD MY MASTERPIECE! The most revolutionary ball-and-paddle game in THE ENTIRE HISTORY OF GAMING! I call it "Quantum Pong" — where the laws of physics are more like... suggestions.

CodyMcTweak
CodyMcTweak:

Um, Trashy? I don't think the ball is supposed to... pass through the paddle like that. And why does it occasionally explode into 50 smaller balls?

TrashyMcTweak
TrashyMcTweak:

EXACTLY! That's the quantum part! Sometimes it collides, sometimes it doesn't! Like Schrödinger's ball! It's not a bug, it's a feature!

GrumpyMcTweak
GrumpyMcTweak:

This is a SECURITY NIGHTMARE! What happens when these objects start invading each other's memory space!? It'll be like the great buffer overflow of '98 all over again! OBJECTS HAVE BOUNDARIES FOR A REASON, TRASHY!

FattyMcTweak
FattyMcTweak:

*sigh* For just $899.99, I could provide you with my PREMIUM collision detection system that uses military-grade algorithms to determine when objects intersect. It even accounts for quantum tunneling... which is clearly what's happening here.

AshleyMcTweak
AshleyMcTweak:

Has anyone considered the legal ramifications here? What if the virtual ball damages other virtual objects? Do we have insurance for that? I need to draft a terms of service agreement for this game IMMEDIATELY!

AllyMcTweak
AllyMcTweak:

Or—and I'm just throwing this out there—we could implement ACTUAL collision detection? You know, the thing that's been a solved problem since the dawn of gaming? Let's start with the basics, like checking if two rectangles overlap.

*Trashy backs up dramatically to make a point but collides with Grumpy, sending them both tumbling into Fatty, who drops his coffee onto Ashley's legal papers*

GarbageMcTweak
GarbageMcTweak:

*walks in, glances at the chaos* Looks like we have a collision detection problem in the real world too. Let's fix the virtual one first, then maybe work on your spatial awareness.

What is Collision Detection?

AllyMcTweak

AllyMcTweak Explains:

Collision detection is exactly what it sounds like — detecting when two objects touch or overlap. In games and interactive applications, we need to know when objects interact with each other.

The simplest form of collision detection is checking if two rectangles overlap — like our ball and paddle. This is the foundation of most 2D games.

Common Collision Types:

  • Rectangle collision: Checking if two rectangular areas overlap
  • Circle collision: Checking if two circular areas overlap
  • Point collision: Checking if a point is inside a shape
  • Complex shapes: Using combinations of the above or more advanced techniques
TrashyMcTweak

TrashyMcTweak Interrupts:

BORING! Let me spice this up! Collision detection is like knowing when two train cars smash together, except it's virtual so nobody calls insurance companies!

Think about it: Every frame, your code has to ask "Are these things touching?" about EVERY SINGLE OBJECT! It's like being the chaperone at a middle school dance making sure everyone stays 6 inches apart!

Trashy's "Real World" Examples:

  • The ball hits the paddle = Your hand catching a falling phone
  • The ball misses = Your coffee mug sliding off the desk when you're not looking
  • Multiple collisions = That time I tried to exit the elevator while others were trying to enter

Basic Rectangle Collision

GarbageMcTweak

GarbageMcTweak States:

The most fundamental collision check is between two rectangles. In our case, that's our ball and paddle. Despite Trashy's quantum nonsense, this is straightforward.

Let's look at some basic, uncommented code and improve it:

// Trashy's original "quantum" collision code
function checkCollision(ball, paddle) {
    if (Math.random() > 0.5) {
        return true;
    } else {
        return false;
    }
}

function updateGame() {
    moveBall();
    
    if (checkCollision(ball, paddle)) {
        // Sometimes bounce
        if (Math.random() > 0.3) {
            ball.dy = -ball.dy;
        }
        
        // Sometimes split
        if (Math.random() > 0.9) {
            createExtraBalls(50);
        }
    }
    
    drawEverything();
    requestAnimationFrame(updateGame);
}
GrumpyMcTweak

GrumpyMcTweak Rages:

THIS IS AN ABOMINATION! You're using RANDOM NUMBER GENERATION for collision detection?! This violates every principle of physics and computer science!

Let me fix this horrific code with a PROPER collision detection function that actually checks if rectangles overlap:

/**
 * Checks if two rectangular objects are colliding
 * @param {Object} rect1 - First rectangle with x, y, width, height
 * @param {Object} rect2 - Second rectangle with x, y, width, height
 * @return {boolean} - True if colliding, false otherwise
 */
function checkCollision(rect1, rect2) {
    // Check if the rectangles are NOT overlapping
    if (
        rect1.x + rect1.width < rect2.x ||  // rect1 is left of rect2
        rect1.x > rect2.x + rect2.width ||  // rect1 is right of rect2
        rect1.y + rect1.height < rect2.y || // rect1 is above rect2
        rect1.y > rect2.y + rect2.height    // rect1 is below rect2
    ) {
        return false; // No collision
    }
    
    return true; // Rectangles are colliding
}

function updateGame() {
    moveBall();
    
    // Create proper rectangle objects with position and size
    const ballRect = {
        x: ball.x - ball.radius,
        y: ball.y - ball.radius,
        width: ball.radius * 2,
        height: ball.radius * 2
    };
    
    const paddleRect = {
        x: paddle.x,
        y: paddle.y,
        width: paddle.width,
        height: paddle.height
    };
    
    // Check for collision
    if (checkCollision(ballRect, paddleRect)) {
        // Ball hits the paddle, so bounce it
        ball.dy = -ball.dy;
    }
    
    drawEverything();
    requestAnimationFrame(updateGame);
}
AshleyMcTweak

AshleyMcTweak Clarifies:

While Grumpy's code is technically correct, I'm concerned about its readability. Clear comments and documentation are like a legal contract — they protect everyone involved.

Let me add more thorough comments and rename some variables to make the code more self-explanatory:

/**
 * Determines if two rectangular objects are colliding
 * This uses the "Axis-Aligned Bounding Box" (AABB) collision detection method
 * 
 * @param {Object} objectA - First rectangular object with properties: x, y, width, height
 * @param {Object} objectB - Second rectangular object with properties: x, y, width, height
 * @return {boolean} - True if the objects are colliding, false otherwise
 */
function isColliding(objectA, objectB) {
    // Calculate edges of each rectangle for easier reference
    const leftA = objectA.x;
    const rightA = objectA.x + objectA.width;
    const topA = objectA.y;
    const bottomA = objectA.y + objectA.height;
    
    const leftB = objectB.x;
    const rightB = objectB.x + objectB.width;
    const topB = objectB.y;
    const bottomB = objectB.y + objectB.height;
    
    // Check for non-collision cases (if any of these are true, there's NO collision)
    if (
        rightA < leftB ||   // A is completely to the left of B
        leftA > rightB ||   // A is completely to the right of B
        bottomA < topB ||   // A is completely above B
        topA > bottomB      // A is completely below B
    ) {
        return false;  // No collision detected
    }
    
    // If none of the non-collision cases are true, the rectangles must be colliding
    return true;
}

Creating the Ball-and-Paddle Game

AllyMcTweak

AllyMcTweak Explains:

Now we need to apply this collision detection to a simple ball-and-paddle game. We'll create a basic setup with a moving ball and a paddle the player can control.

Let's see how we can implement this:

// Game objects
let ball = {
    x: 250,       // x position
    y: 150,       // y position
    radius: 10,   // ball radius
    dx: 4,        // x velocity (horizontal speed)
    dy: -3,       // y velocity (vertical speed)
    color: '#FFFFFF'
};

let paddle = {
    x: 200,       // x position
    y: 350,       // y position
    width: 100,   // paddle width
    height: 15,   // paddle height
    color: '#00b3ff'
};

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

/**
 * Main game loop
 */
function gameLoop() {
    // Clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // Move the ball
    ball.x += ball.dx;
    ball.y += ball.dy;
    
    // Wall collision (left/right)
    if (ball.x - ball.radius < 0 || ball.x + ball.radius > canvas.width) {
        ball.dx = -ball.dx;  // Reverse horizontal direction
    }
    
    // Wall collision (top)
    if (ball.y - ball.radius < 0) {
        ball.dy = -ball.dy;  // Reverse vertical direction
    }
    
    // Create rectangular representations for collision detection
    const ballRect = {
        x: ball.x - ball.radius,
        y: ball.y - ball.radius,
        width: ball.radius * 2,
        height: ball.radius * 2
    };
    
    // Check for paddle collision
    if (isColliding(ballRect, paddle)) {
        ball.dy = -Math.abs(ball.dy);  // Always bounce upward
    }
    
    // Check if ball is below paddle (game over condition)
    if (ball.y > canvas.height) {
        // Reset ball
        ball.x = canvas.width / 2;
        ball.y = canvas.height / 2;
    }
    
    // Draw ball
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
    ctx.fillStyle = ball.color;
    ctx.fill();
    ctx.closePath();
    
    // Draw paddle
    ctx.fillStyle = paddle.color;
    ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
    
    // Continue the game loop
    requestAnimationFrame(gameLoop);
}

// Handle mouse movement to control paddle
canvas.addEventListener('mousemove', (event) => {
    const canvasRect = canvas.getBoundingClientRect();
    paddle.x = event.clientX - canvasRect.left - paddle.width / 2;
    
    // Keep paddle inside the canvas
    if (paddle.x < 0) {
        paddle.x = 0;
    }
    if (paddle.x + paddle.width > canvas.width) {
        paddle.x = canvas.width - paddle.width;
    }
});

// Start the game
gameLoop();
FattyMcTweak

FattyMcTweak Offers an Enhancement:

That's fine for a basic implementation, but my premium collision detection can do so much more. For example, we can make the ball bounce in different directions depending on where it hits the paddle.

Let me show you how to add this advanced feature:

/**
 * Enhanced paddle collision that changes ball direction based on hit position
 * This creates more dynamic gameplay by allowing the player to aim
 * 
 * @param {Object} ball - The ball object with x, y, radius, dx, dy properties
 * @param {Object} paddle - The paddle object with x, y, width, height properties
 * @return {boolean} - True if collision occurred, false otherwise
 */
function handlePaddleCollision(ball, paddle) {
    // Create rectangular representation of the ball
    const ballRect = {
        x: ball.x - ball.radius,
        y: ball.y - ball.radius,
        width: ball.radius * 2,
        height: ball.radius * 2
    };
    
    // Check for collision
    if (isColliding(ballRect, paddle)) {
        // Ensure ball is above the paddle (prevents sticking)
        ball.y = paddle.y - ball.radius;
        
        // Calculate where ball hit the paddle (0 = left edge, 1 = right edge)
        const hitPosition = (ball.x - paddle.x) / paddle.width;
        
        // Calculate new angle (-60 to +60 degrees)
        const angle = (hitPosition - 0.5) * Math.PI * 2/3;
        
        // Set velocity based on angle, maintaining current speed
        const speed = Math.sqrt(ball.dx * ball.dx + ball.dy * ball.dy);
        ball.dx = Math.sin(angle) * speed;
        ball.dy = -Math.cos(angle) * speed;
        
        // Add a slight increase to speed each hit (optional)
        const speedIncrease = 1.05;
        ball.dx *= speedIncrease;
        ball.dy *= speedIncrease;
        
        return true;
    }
    
    return false;
}

Your Turn: Build a Ball-and-Paddle Game

CodyMcTweak

CodyMcTweak's Challenge:

Now it's your turn to create a simple ball-and-paddle game with proper collision detection! Here's what you'll need to do:

Tasks:

  1. Set up a canvas element in your HTML
  2. Create ball and paddle objects with appropriate properties
  3. Implement the isColliding function for collision detection
  4. Build the game loop that:
    • Moves the ball
    • Checks for wall collisions
    • Checks for paddle collisions
    • Handles "game over" condition when ball goes below the bottom
  5. Add mouse control for the paddle
  6. Bonus: Implement FattyMcTweak's enhanced paddle collision

Extension Ideas:

  • Add score tracking
  • Create blocks at the top that break when hit
  • Add different power-ups that affect ball or paddle behavior
  • Implement multiple lives
  • Add sound effects for collisions
TrashyMcTweak

TrashyMcTweak's Starter Code:

Here's some starter code to get you going. It's actually decent for once, since Garbage fixed it for me!

// Game canvas setup
const canvas = document.getElementById('gameCanvas');  // Assume this exists in your HTML
const ctx = canvas.getContext('2d');

// Game objects
let ball = {
    x: canvas.width / 2,
    y: canvas.height / 2,
    radius: 10,
    dx: 4,  // horizontal speed
    dy: -3, // vertical speed
    color: '#FFFFFF'
};

let paddle = {
    x: canvas.width / 2 - 50,
    y: canvas.height - 30,
    width: 100,
    height: 15,
    color: '#00b3ff'
};

// Collision detection function
function isColliding(rectA, rectB) {
    // TODO: Implement collision detection here!
    // Hint: Check if the rectangles are NOT overlapping on any axis
    return false;  // Replace this with your collision logic
}

// Drawing functions
function drawBall() {
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
    ctx.fillStyle = ball.color;
    ctx.fill();
    ctx.closePath();
}

function drawPaddle() {
    ctx.fillStyle = paddle.color;
    ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
}

// Game loop
function gameLoop() {
    // Clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // Draw objects
    drawBall();
    drawPaddle();
    
    // TODO: Move the ball
    
    // TODO: Check for wall collisions
    
    // TODO: Check for paddle collision using the isColliding function
    
    // TODO: Check if ball is below paddle (game over)
    
    // Continue the game loop
    requestAnimationFrame(gameLoop);
}

// TODO: Add event listener for paddle control

// Start the game
gameLoop();

Key Takeaways

GarbageMcTweak

GarbageMcTweak Summarizes:

  • Axis-Aligned Bounding Box (AABB): The simplest collision detection method checks if two rectangles overlap on both the x and y axes.
  • Collision Response: After detecting a collision, you need to implement an appropriate response (like bouncing the ball).
  • Edge Cases: Be careful with the timing and positioning of collision checks to prevent objects from getting stuck inside each other.
  • Performance: Simple collision detection is efficient, but as your game grows, you'll need optimization techniques for checking many objects.
  • Comments Matter: Clear documentation helps others understand how your collision detection works, and helps you remember it later.
SnowzieMcTweak

SnowzieMcTweak Has Arrived!

*excited tail wagging*

Woof! Ball collision detection looks great! No more quantum balls passing through paddles!

Proper collisions make happy games! Code is committed!

*happy zoomies around the office*

CODE COMMITTED SUCCESSFULLY!
← Previous: User Control Home Next: Bouncing Ball Game →