Your browser does not support the canvas tag.

previous        Show / Hide Source        Download        next
/* Title: Physics Playing Blocks
 * Author: Adam Gill
 * Description:
 *   The goal is to just play around! You can drag the blocks with your mouse and fling them too.
 *   Try to stack the blocks on top of eachother, or even try to bounce one on top of the other.
 * 
 * To fix the block merging glitch, click and hold on one of the two blocks merging together and they should split.
 * Alternatively, you can press R to reset the blocks.
 * You can also re-randomize the colors by pressing C.
 */


float gravity;
float box1LocX, box1LocY, box2LocX, box2LocY, box3LocX, box3LocY;
float box1VelX, box1VelY, box2VelX, box2VelY, box3VelX, box3VelY;
int box1Size, box2Size, box3Size, halfBox1Size, halfBox2Size, halfBox3Size;
boolean isMouseOverBox1, isMouseOverBox2, isMouseOverBox3;
color box1Color, box2Color, box3Color;

void setup() {
  // Set canvas size
  size(400, 400);

  // Set variables
  gravity = 0.98f;
  box1LocX = 195f;
  box1LocY = 340f;
  box2LocX = 205f;
  box2LocY = 230f;
  box3LocX = 195f;
  box3LocY = 120f;
  box1VelX = 0f;
  box1VelY = 0f;
  box2VelX = 0f;
  box2VelY = 0f;
  box3VelX = 0f;
  box3VelY = 0f;
  box1Size = 100;
  box2Size = 100;
  box3Size = 100;
  halfBox1Size = box1Size / 2;
  halfBox2Size = box2Size / 2;
  halfBox3Size = box3Size / 2;
  isMouseOverBox1 = false;
  isMouseOverBox2 = false;
  isMouseOverBox3 = false;
  box1Color = color((int) random(0, 255), (int) random(0, 255), (int) random(0, 255));
  box2Color = color((int) random(0, 255), (int) random(0, 255), (int) random(0, 255));
  box3Color = color((int) random(0, 255), (int) random(0, 255), (int) random(0, 255));
}

void draw() {  
  // Draw Methods
  drawBackground();
  drawBlock(box1LocX, box1LocY, box1Size, box1Color);
  drawBlock(box2LocX, box2LocY, box2Size, box2Color);
  drawBlock(box3LocX, box3LocY, box3Size, box3Color);

  // Call update methods for each box, detect collisions, and finally apply the velocities to all boxes for next frame.
  updateBox1();
  updateBox2();
  updateBox3();
  detectCollisions();
  applyVelocityToBoxes();

  if (keyCode == 82) {
    box1LocX = 195f;
    box1LocY = 340f;
    box2LocX = 205f;
    box2LocY = 230f;
    box3LocX = 195f;
    box3LocY = 120f;
    box1VelX = 0f;
    box1VelY = 0f;
    box2VelX = 0f;
    box2VelY = 0f;
    box3VelX = 0f;
    box3VelY = 0f;
    isMouseOverBox1 = false;
    isMouseOverBox2 = false;
    isMouseOverBox3 = false;
    keyCode = 0;
  }

  if (keyCode == 67) {
    box1Color = color((int) random(0, 255), (int) random(0, 255), (int) random(0, 255));
    box2Color = color((int) random(0, 255), (int) random(0, 255), (int) random(0, 255));
    box3Color = color((int) random(0, 255), (int) random(0, 255), (int) random(0, 255));
    keyCode = 0;
  }
}

// Updates physics for box 1
void updateBox1() {
  boolean applyGravity = true;

  // Check if the mouse is in the box
  if ((mouseX > box1LocX - halfBox1Size && mouseX < box1LocX + halfBox1Size) && (mouseY > box1LocY - halfBox1Size && mouseY < box1LocY + halfBox1Size)) {
    isMouseOverBox1 = true;
  } else {
    isMouseOverBox1 = false;
  }

  // Vertical Collision Detection
  if (box1LocY + halfBox1Size + box1VelY > height) {
    applyGravity = false;
    box1VelY *= -0.5;
    box1LocY = height - halfBox1Size;
  } else if (box1LocY - halfBox1Size + box1VelY < 0) {
    applyGravity = false;
    box1VelY *= -0.5;
    box1LocY = halfBox1Size;
  }

  // Horizontal Collision Detection
  if (box1LocX + halfBox1Size + box1VelX > width) {
    box1LocX = height - halfBox1Size;
    box1VelX *= -0.5;
  } else if (box1LocX - halfBox1Size + box1VelX < 0) {
    box1LocX = halfBox1Size;
    box1VelX *= -0.5;
  }

  // Box Grabber Code
  // Calculates a vector between box 1 and the mouse then calculates a vector to move the box
  if (mousePressed && isMouseOverBox1) {
    applyGravity = false;
    float tempXDist = mouseX - box1LocX;
    float tempYDist = mouseY - box1LocY;
    float tempAngle = atan(tempYDist / tempXDist);
    float tempMagnitude = sqrt(sq(tempXDist) + sq(tempYDist));

    float resultMagnitude = tempMagnitude * 0.9;
    if (tempXDist > 0 && tempYDist > 0) {
      box1VelX = resultMagnitude * cos(tempAngle);
      box1VelY = resultMagnitude * sin(tempAngle);
    } else if (tempXDist < 0 && tempYDist > 0) {
      box1VelX = -resultMagnitude * cos(tempAngle);
      box1VelY = -resultMagnitude * sin(tempAngle);
    } else if (tempXDist > 0 && tempYDist < 0) {
      box1VelX = resultMagnitude * cos(tempAngle);
      box1VelY = resultMagnitude * sin(tempAngle);
    } else if (tempXDist < 0 && tempYDist < 0) {
      box1VelX = -resultMagnitude * cos(tempAngle);
      box1VelY = -resultMagnitude * sin(tempAngle);
    } else {
      box1VelX = 0;
      box1VelY = 0;
    }
  }

  // Apply gravity if enabled
  // If not then collision is probably happening so apply friction
  if (applyGravity) {
    box1VelY += gravity;
  } else {
    box1VelX *= 0.6;
    box1VelY *= 0.6;
  }
}

// Updates physics for box 2
void updateBox2() {
  boolean applyGravity = true;

  // Check if the mouse is in the box
  if ((mouseX > box2LocX - halfBox2Size && mouseX < box2LocX + halfBox2Size) && (mouseY > box2LocY - halfBox2Size && mouseY < box2LocY + halfBox2Size)) {
    isMouseOverBox2 = true;
  } else {
    isMouseOverBox2 = false;
  }

  // Vertical Collision Detection
  if (box2LocY + halfBox2Size + box2VelY > height) {
    applyGravity = false;
    box2VelY *= -0.5;
    box2LocY = height - halfBox2Size;
  } else if (box2LocY - halfBox2Size + box2VelY < 0) {
    applyGravity = false;
    box2VelY *= -0.5;
    box2LocY = halfBox2Size;
  }

  // Horizontal Collision Detection
  if (box2LocX + halfBox2Size + box2VelX > width) {
    box2LocX = height - halfBox2Size;
    box2VelX *= -0.5;
  } else if (box2LocX - halfBox2Size + box2VelX < 0) {
    box2LocX = halfBox2Size;
    box2VelX *= -0.5;
  }

  // Box Grabber Code
  // Calculates a vector between box 2 and the mouse then calculates a vector to move the box
  if (mousePressed && isMouseOverBox2) {
    applyGravity = false;
    float tempXDist = mouseX - box2LocX;
    float tempYDist = mouseY - box2LocY;
    float tempAngle = atan(tempYDist / tempXDist);
    float tempMagnitude = sqrt(sq(tempXDist) + sq(tempYDist));

    float resultMagnitude = tempMagnitude * 0.9;
    if (tempXDist > 0 && tempYDist > 0) {
      box2VelX = resultMagnitude * cos(tempAngle);
      box2VelY = resultMagnitude * sin(tempAngle);
    } else if (tempXDist < 0 && tempYDist > 0) {
      box2VelX = -resultMagnitude * cos(tempAngle);
      box2VelY = -resultMagnitude * sin(tempAngle);
    } else if (tempXDist > 0 && tempYDist < 0) {
      box2VelX = resultMagnitude * cos(tempAngle);
      box2VelY = resultMagnitude * sin(tempAngle);
    } else if (tempXDist < 0 && tempYDist < 0) {
      box2VelX = -resultMagnitude * cos(tempAngle);
      box2VelY = -resultMagnitude * sin(tempAngle);
    } else {
      box2VelX = 0;
      box2VelY = 0;
    }
  }

  // Apply gravity if enabled
  // If not then collision of a wall is probably happening so apply friction
  if (applyGravity) {
    box2VelY += gravity;
  } else {
    box2VelX *= 0.6;
    box2VelY *= 0.6;
  }
}

// Updates physics for box 3
void updateBox3() {
  boolean applyGravity = true;

  // Check if the mouse is in the box
  if ((mouseX > box3LocX - halfBox3Size && mouseX < box3LocX + halfBox3Size) && (mouseY > box3LocY - halfBox3Size && mouseY < box3LocY + halfBox3Size)) {
    isMouseOverBox3 = true;
  } else {
    isMouseOverBox3 = false;
  }

  // Vertical Collision Detection
  if (box3LocY + halfBox3Size + box3VelY > height) {
    applyGravity = false;
    box3VelY *= -0.5;
    box3LocY = height - halfBox3Size;
  } else if (box3LocY - halfBox3Size + box3VelY < 0) {
    applyGravity = false;
    box3VelY *= -0.5;
    box3LocY = halfBox3Size;
  }

  // Horizontal Collision Detection
  if (box3LocX + halfBox3Size + box3VelX > width) {
    box3LocX = height - halfBox3Size;
    box3VelX *= -0.5;
  } else if (box3LocX - halfBox3Size + box3VelX < 0) {
    box3LocX = halfBox3Size;
    box3VelX *= -0.5;
  }

  // Box Grabber Code
  // Calculates a vector between box 3 and the mouse then calculates a vector to move the box
  if (mousePressed && isMouseOverBox3) {
    applyGravity = false;
    float tempXDist = mouseX - box3LocX;
    float tempYDist = mouseY - box3LocY;
    float tempAngle = atan(tempYDist / tempXDist);
    float tempMagnitude = sqrt(sq(tempXDist) + sq(tempYDist));

    float resultMagnitude = tempMagnitude * 0.9;
    if (tempXDist > 0 && tempYDist > 0) {
      box3VelX = resultMagnitude * cos(tempAngle);
      box3VelY = resultMagnitude * sin(tempAngle);
    } else if (tempXDist < 0 && tempYDist > 0) {
      box3VelX = -resultMagnitude * cos(tempAngle);
      box3VelY = -resultMagnitude * sin(tempAngle);
    } else if (tempXDist > 0 && tempYDist < 0) {
      box3VelX = resultMagnitude * cos(tempAngle);
      box3VelY = resultMagnitude * sin(tempAngle);
    } else if (tempXDist < 0 && tempYDist < 0) {
      box3VelX = -resultMagnitude * cos(tempAngle);
      box3VelY = -resultMagnitude * sin(tempAngle);
    } else {
      box3VelX = 0;
      box3VelY = 0;
    }
  }

  // Apply gravity if enabled
  // If not then collision is probably happening so apply friction
  if (applyGravity) {
    box3VelY += gravity;
  } else {
    box3VelX *= 0.6;
    box3VelY *= 0.6;
  }
}

void detectCollisions() {
  // These MONSTER if statements detects collisions between the blocks
  /* Checks for the following:
   * |_Is the right side of box1 inside box2? OR...
   * |_Is the left side of box1 inside box2? 
   * |_...either of those AND either of...
   * |_Is the top of box1 inside box2? OR...
   * |_Is the bottom of box1 inside box2?
   * \_> if so then:
   *     |_take half of box1 velocity, store it in temp. variable 1
   *     |_do the same with box2 and temp. var. 2
   *     |_reflect, half, and add temp. var. 2 to box1 velocity
   *     \_do the same with box2 and temp. var. 1
   *     
   * Repeat for box1 & 3
   * Repeat for box2 & 3
   */

  // Box 1 & 2 collision
  if ((((box1LocX + halfBox1Size + box1VelX > box2LocX - halfBox2Size + box2VelX) && (box1LocX + halfBox1Size + box1VelX < box2LocX + halfBox2Size + box2VelX)) || 
    ((box1LocX - halfBox1Size + box1VelX > box2LocX - halfBox2Size + box2VelX) && (box1LocX - halfBox1Size + box1VelX < box2LocX + halfBox2Size + box2VelX))) && 
    (((box1LocY + halfBox1Size + box1VelY > box2LocY - halfBox2Size + box2VelY) && (box1LocY + halfBox1Size + box1VelY < box2LocY + halfBox2Size + box2VelY)) || 
    ((box1LocY - halfBox1Size + box1VelY > box2LocY - halfBox2Size + box2VelY) && (box1LocY - halfBox1Size + box1VelY < box2LocY + halfBox2Size + box2VelY)))) {
    // Calculate bounce for x
    float velocityXTemp1 = box1VelX / 2;
    float velocityXTemp2 = box2VelX / 2;
    box1VelX = -(box1VelX / 2) + velocityXTemp2;
    box2VelX = -(box2VelX / 2) + velocityXTemp1;
    // Calculate bounce for y
    float velocityYTemp1 = box1VelY / 2;
    float velocityYTemp2 = box2VelY / 2;
    box1VelY = -(box1VelY / 2) + velocityYTemp2;
    box2VelY = -(box2VelY / 2) + velocityYTemp1;
  }

  // Box 1 & 3 collision
  if ((((box1LocX + halfBox1Size + box1VelX > box3LocX - halfBox3Size + box3VelX) && (box1LocX + halfBox1Size + box1VelX < box3LocX + halfBox3Size + box3VelX)) || 
    ((box1LocX - halfBox1Size + box1VelX > box3LocX - halfBox3Size + box3VelX) && (box1LocX - halfBox1Size + box1VelX < box3LocX + halfBox3Size + box3VelX))) && 
    (((box1LocY + halfBox1Size + box1VelY > box3LocY - halfBox3Size + box3VelY) && (box1LocY + halfBox1Size + box1VelY < box3LocY + halfBox3Size + box3VelY)) || 
    ((box1LocY - halfBox1Size + box1VelY > box3LocY - halfBox3Size + box3VelY) && (box1LocY - halfBox1Size + box1VelY < box3LocY + halfBox3Size + box3VelY)))) {
    // Calculate bounce for x
    float velocityXTemp1 = box1VelX / 2;
    float velocityXTemp2 = box3VelX / 2;
    box1VelX = -(box1VelX / 2) + velocityXTemp2;
    box3VelX = -(box3VelX / 2) + velocityXTemp1;
    // Calculate bounce for y
    float velocityYTemp1 = box1VelY / 2;
    float velocityYTemp2 = box3VelY / 2;
    box1VelY = -(box1VelY / 2) + velocityYTemp2;
    box3VelY = -(box3VelY / 2) + velocityYTemp1;
  }

  // Box 2 & 3 collision
  if ((((box2LocX + halfBox2Size + box2VelX > box3LocX - halfBox3Size + box3VelX) && (box2LocX + halfBox2Size + box2VelX < box3LocX + halfBox3Size + box3VelX)) || 
    ((box2LocX - halfBox2Size + box2VelX > box3LocX - halfBox3Size + box3VelX) && (box2LocX - halfBox2Size + box2VelX < box3LocX + halfBox3Size + box3VelX))) && 
    (((box2LocY + halfBox2Size + box2VelY > box3LocY - halfBox3Size + box3VelY) && (box2LocY + halfBox2Size + box2VelY < box3LocY + halfBox3Size + box3VelY)) || 
    ((box2LocY - halfBox2Size + box2VelY > box3LocY - halfBox3Size + box3VelY) && (box2LocY - halfBox2Size + box2VelY < box3LocY + halfBox3Size + box3VelY)))) {
    // Calculate bounce for x
    float velocityXTemp1 = box2VelX / 2;
    float velocityXTemp2 = box3VelX / 2;
    box2VelX = -(box2VelX / 2) + velocityXTemp2;
    box3VelX = -(box3VelX / 2) + velocityXTemp1;
    // Calculate bounce for y
    float velocityYTemp1 = box2VelY / 2;
    float velocityYTemp2 = box3VelY / 2;
    box2VelY = -(box2VelY / 2) + velocityYTemp2;
    box3VelY = -(box3VelY / 2) + velocityYTemp1;
  }
}

void applyVelocityToBoxes() {
  // Box 1
  box1LocX += box1VelX;
  box1LocY += box1VelY;
  // Box 2
  box2LocX += box2VelX;
  box2LocY += box2VelY;
  // Box 3
  box3LocX += box3VelX;
  box3LocY += box3VelY;
}

// Draws a block at a location with a determined size
void drawBlock(float locX, float locY, float size, color boxColor) {
  float halfSize = size / 2f;
  float quartSize = halfSize / 2f;
  rectMode(CENTER);
  noStroke();
  fill(boxColor); // True Color
  rect(locX, locY, size, size);
  fill(255, 255, 255, 100); // Light 2
  quad(locX - halfSize, locY - halfSize, locX + halfSize, locY - halfSize, locX + quartSize, locY - quartSize, locX - quartSize, locY - quartSize);
  fill(255, 255, 255, 55); // Light 1
  quad(locX - halfSize, locY + halfSize, locX - halfSize, locY - halfSize, locX - quartSize, locY - quartSize, locX - quartSize, locY + quartSize);
  fill(0, 0, 0, 55); // Dark 1
  quad(locX + halfSize, locY - halfSize, locX + halfSize, locY + halfSize, locX + quartSize, locY + quartSize, locX + quartSize, locY - quartSize);
  fill(0, 0, 0, 100); // Dark 2
  quad(locX + halfSize, locY + halfSize, locX - halfSize, locY + halfSize, locX - quartSize, locY + quartSize, locX + quartSize, locY + quartSize);
}

// Draws the background texture
void drawBackground() {
  background(100);
  noFill();
  stroke(55);
  for (int i = -2; i < (width / 10) + 2; i++) {
    line((i * 10) + (10 * sin(frameCount / 25f)), 0, (i * 10) + (10 * cos(frameCount / 25f)), height);
  }
  for (int i = -2; i < (width / 10) + 2; i++) {
    line(0, (i * 10) + (10 * cos(frameCount / 25f)), width, (i * 10) + (10 * sin(frameCount / 25f)));
  }
}