Categories
Reactickles 3

KeyboardSquares and KeyboardFountain with Matter.js

The original KeyboardSquares can be seen above – on key presses the larger square broke up into smaller squares and flew apart, as well as changing colour. It was a relatively quick port to create, based on previously ported Reactickles. One sticking point was making sure that the larger square was always centred – even if the user changed the size of the window of the browser. Luckily p5.js has a windowResized() function to detect that. I also found some different options for emptying an array in JavaScript. The ported version can be found here.

The next Reactickle was Keyboard Fountain which can be seen from 01:43- 02:20 on the video above.

After creating KeyboardFountain in my local Git repository, I set about creating the “fountain” at the bottom of the screen. Conveniently, p5.js has an existing triangle drawing function, called triangle(). Quoting from the reference:

Description
A triangle is a plane created by connecting three points. The first two arguments specify the first point, the middle two arguments specify the second point, and the last two arguments specify the third point.

Syntax
triangle(x1,y1,x2,y2,x3,y3)

I created the following function to draw my Fountain:

function drawFountain(){
 var fountainWidth = 50; //50 pixels wide
 var fountainHeight = 50; //50 pixels high
 var translatedX = this.fountainPosition.x * windowWidth;
 var translatedY = this.fountainPosition.y * windowHeight;
 var redColour = color(0,100,100,100);
 fill(redColour);
 triangle(translatedX-(fountainWidth/2),translatedY,translatedX+(fountainWidth/2),translatedY,translatedX,translatedY-fountainHeight); //https://p5js.org/reference/#/p5/triangle
}

After I was happy with the drawing of my Fountain, I added interactivity by using the p5.js keyCode variable to allow the user to use the left and right arrows keys to move the fountain:

function keyPressed(){
 var moveAmount = 0.01;
 if (keyCode == LEFT_ARROW) { //https://p5js.org/reference/#/p5/keyCode
 if(this.fountainPosition.x >= moveAmount){
 this.fountainPosition.x -= moveAmount;
 }
 } else if (keyCode == RIGHT_ARROW) {
 if(this.fountainPosition.x <= (1-moveAmount)){
 this.fountainPosition.x += moveAmount;
 }
 }
 return false; // prevent default
}

However, this meant that the user had to repeatedly press the arrow keys to move, whereas I wanted the Fountain to keep moving as long as I held the key down. I found the keyIsDown function which allowed me to have the Fountain move in the way that I wanted:

function moveFountain(){
 var moveAmount = 0.005;

if (keyIsDown(LEFT_ARROW) && (this.fountainPosition.x >= moveAmount))
 this.fountainPosition.x -= moveAmount;

if (keyIsDown(RIGHT_ARROW) && (this.fountainPosition.x <= (1-moveAmount)))
 this.fountainPosition.x += moveAmount;
}

In order to get the particles flying out of the fountain in a realistic way, I new I would have to create some kind of 2D physics simulation – at the very least using basic projectile physics. In the spirit of keeping it simple, I looked online to see if there were any existing physics libraries that worked with p5.js. Thanks to the fantastic Coding Train by Daniel Shiffman, I discovered matter.js.

Daniel was also kind enough to provide source code for p5.js running with matter.js on his GitHub.

I initialised matter.js by duplicating most of Daniel’s code in a new function called setupPhysics, after declaring the necessary variables for matter.js to run:

//matter-js and p5.js integration based on https://github.com/shiffman/p5-matter by Daniel Shiffman
//also see https://www.youtube.com/watch?v=urR596FsU68 introduction to matter.js by Daniel Shiffman
var Engine = Matter.Engine;
//var Render = Matter.Render; // commented out as we are using p5.js to render everything to the screen
var World = Matter.World;
var Bodies = Matter.Bodies;
var Body = Matter.Body;
var Composite = Matter.Composite;
var Composites = Matter.Composites;

var engine;
var world;
var bodies;
var canvas;

function setupPhysics(){
 // create an engine
 engine = Engine.create();
 world = engine.world;

//make walls to constrain everything
 var params = {
 isStatic: true
 }
 var ground = Bodies.rectangle(width / 2, height, width, 1, params);
 var leftWall = Bodies.rectangle(0, height / 2, 1, height, params);
 var rightWall = Bodies.rectangle(width, height / 2, 1, height, params);
 var top = Bodies.rectangle(width / 2, 0, width, 1, params);
 World.add(world, ground);
 World.add(world, leftWall);
 World.add(world, rightWall);
 World.add(world, top);

// run the engine
 Engine.run(engine);
}

I changed keyTyped to trigger a new function, addCircle – as well as adding code to draw all the new circles that would be created:

 

function addCircle(){
 var params = {
 restitution: 0.7,
 friction: 0.2
 }
 var translatedX = this.fountainPosition.x * windowWidth;
 var translatedY = this.fountainPosition.y * windowHeight;
 var radius = 21;
 var newCircle = Bodies.circle(translatedX, translatedY-(fountainHeight), radius, params);
 circles.push(newCircle);
 World.add(world, newCircle);

//set a random velocity of the new circle
 //see http://brm.io/matter-js/docs/classes/Body.html
 //from http://codepen.io/lilgreenland/pen/jrMvaB?editors=0010#0
 Body.setVelocity(newCircle, {
 x: random(-5,5),
 y: -random(15,30)
 });
}

function drawCircles(){
 stroke(255);
 strokeWeight(1);
 fill(randomColour);

for (var i = 0; i < circles.length; i++) {
 var circle = circles[i];
 var pos = circle.position;
 var r = circle.circleRadius;
 var angle = circle.angle;
 push();
 translate(pos.x, pos.y);
 rotate(angle);
 ellipse(0, 0, r * 2);
 line(0, 0, r, 0);
 pop();
 }
}

function keyTyped(){
 addCircle();
 return false; //https://p5js.org/reference/#/p5/keyTyped preventing default behaviour
}

After adding drawCircles to my overall draw function, I had a working prototype, with source code available on the project GitHub as normal.

The next steps will be to add the fountain to the simulation, as well as code to make each circle random in size and colour.