130 Widgets

Semi-random thoughts and tales of tinkering

Lesson 9: Breaking & Placing Blocks

The core of Minecraft! You click to break blocks and right-click to place them. This lesson brings mouse input, coordinate conversion, and world modification together. ⛏️🧱

Mouse Events

Just like keyboard events, the browser tells us when the mouse does things:

const mouse = { x: 0, y: 0, left: false, right: false };

canvas.addEventListener('mousemove', function(event) {
  mouse.x = event.clientX;  // Mouse X on screen
  mouse.y = event.clientY;  // Mouse Y on screen
});

canvas.addEventListener('mousedown', function(event) {
  if (event.button === 0) mouse.left = true;   // Left click
  if (event.button === 2) mouse.right = true;  // Right click
});

canvas.addEventListener('mouseup', function(event) {
  if (event.button === 0) mouse.left = false;
  if (event.button === 2) mouse.right = false;
});

// Prevent the right-click menu from appearing
canvas.addEventListener('contextmenu', function(event) {
  event.preventDefault();
});

Screen to World Coordinates

The mouse gives us screen coordinates, but we need to know which block in the world the mouse is pointing at. We add the camera position and divide by block size:

// Convert mouse screen position to world block coordinates
const worldMouseX = Math.floor((mouse.x + cameraX) / BLOCK_SIZE);
const worldMouseY = Math.floor((mouse.y + cameraY) / BLOCK_SIZE);

📐 The Coordinate Chain

  1. Screen pixels → What the mouse reports (e.g., 350, 200)
  2. + camera offset → World pixel position (e.g., 1350, 500)
  3. ÷ block size, floor → Block grid position (e.g., column 42, row 15)

Reach Distance

In Minecraft, you can only break/place blocks within arm's reach. We check the distance between the player and the mouse cursor:

const REACH = 5;  // 5 blocks

// Distance from player center to mouse block
const playerCenterCol = (playerX + playerW / 2) / BLOCK_SIZE;
const playerCenterRow = (playerY + playerH / 2) / BLOCK_SIZE;
const distance = Math.hypot(worldMouseX - playerCenterCol,
                           worldMouseY - playerCenterRow);

if (distance <= REACH) {
  // Close enough to interact!
}

Breaking Blocks

if (mouse.left && distance <= REACH) {
  const block = getBlock(worldMouseX, worldMouseY);
  if (block !== 0) {  // Not air
    setBlock(worldMouseX, worldMouseY, 0);  // Replace with air!
  }
}

Placing Blocks

if (mouse.right && distance <= REACH) {
  const block = getBlock(worldMouseX, worldMouseY);
  if (block === 0) {  // Only place in empty space
    setBlock(worldMouseX, worldMouseY, 3);  // Place stone
  }
}

Block Highlight

We draw a white outline around the block the mouse is pointing at — so the player knows which block they'll interact with:

// Draw highlight around hovered block
if (distance <= REACH) {
  ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';
  ctx.lineWidth = 2;
  ctx.strokeRect(
    worldMouseX * BLOCK_SIZE - cameraX,
    worldMouseY * BLOCK_SIZE - cameraY,
    BLOCK_SIZE, BLOCK_SIZE
  );
}

Try It! Mine and Build!

⬆️ Click here first! A D move, Space jump, Left-click = break, Right-click = place stone

⚠️ Don't Place Blocks On Yourself! In the full game, we check that the block we're placing doesn't overlap with the player. Otherwise, you'd get stuck inside your own block! We handle that in the demo above.

🏆 Challenges — Try These!

  • Dig straight down and see what happens — can you reach the bottom of the world?
  • Build a tower by stacking blocks upward
  • Build a small house with walls and a roof
  • Change the placed block type from stone (3) to dirt (2) or grass (1)
  • Add a "mining progress" bar that fills up before the block breaks (make it take multiple frames)

📝 What We Learned

  • Mouse events: mousemove, mousedown, mouseup
  • Converting screen → world → block coordinates
  • Reach distance limits how far the player can interact
  • Breaking = setting a block to 0 (air)
  • Placing = setting an air block to a block type
  • Block highlight shows which block will be affected