Creating a mask layer in p5js - p5.js

I would like to draw a pattern, and then only have that pattern ‘show through’ where it overlaps with a shape that I specify. Similar to how a mask layer works in Photoshop. Does anyone know how I can approach this?

I would use this 4 step process:
create your mask with beginShape()/endShape and a beginContour()/endContour() in the middle for the area to be shown. You do this on a buffer.
function setup() {
createCanvas(400, 400);
background(30)
fill(200,50,60)
msk = createGraphics(width,height)
pattern = createGraphics(width,height)
pixelDensity(1)
msk.beginShape();
// Exterior part of shape, clockwise winding
msk.vertex(0, 0);
msk.vertex(400, 0);
msk.vertex(400, 400);
msk.vertex(0, 400);
// Interior part of shape, counter-clockwise winding
msk.beginContour();
msk.vertex(20, 20);
msk.vertex(50, 220);
msk.vertex(120, 380);
msk.vertex(370, 320);
msk.vertex(240, 160);
msk.vertex(350, 40);
msk.endContour();
msk.endShape(CLOSE);
mPixs = msk.loadPixels()
}
Then create a different buffer for the pattern
for (let c=0; c<9; c++) {
for (let r=0; r<9; r++) {
pattern.circle(width/8*c,height/8*r,40)
}
}
Now load pixels from the mask and use the alpha value of each pixel on the alpha level of each corresponding pixel on the pattern.
pattern.loadPixels()
msk.loadPixels()
for (let i=0; i < pattern.pixels.length; i+=4){
pattern.pixels[i+3] = msk.pixels[i+3]
}
pattern.updatePixels()
Finally, just add the resulting buffer to your main canvas with image(pattern,0,0)
Take a look at this working example carefully coded just for you! :D
let msk, pattern, mPixs
function setup() {
createCanvas(400, 400);
background(30)
fill(200,50,60)
msk = createGraphics(width,height)
pattern = createGraphics(width,height)
pixelDensity(1)
msk.beginShape();
// Exterior part of shape, clockwise winding
msk.vertex(0, 0);
msk.vertex(400, 0);
msk.vertex(400, 400);
msk.vertex(0, 400);
// Interior part of shape, counter-clockwise winding
msk.beginContour();
msk.vertex(20, 20);
msk.vertex(50, 220);
msk.vertex(120, 380);
msk.vertex(370, 320);
msk.vertex(240, 160);
msk.vertex(350, 40);
msk.endContour();
msk.endShape(CLOSE);
mPixs = msk.loadPixels()
}
function draw() {
for (let c=0; c<9; c++) {
for (let r=0; r<9; r++) {
pattern.circle(width/8*c,height/8*r,40)
}
}
for (let c=0; c<9; c++) {
for (let r=0; r<9; r++) {
let xo=random(-5,5), yo=random(-5,5)
circle(width/8*c+xo,height/8*r+yo,50)
}
}
pattern.loadPixels()
msk.loadPixels()
for (let i=0; i < pattern.pixels.length; i+=4){
pattern.pixels[i+3] = msk.pixels[i+3]
}
pattern.updatePixels()
image(pattern,0,0)
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<main>
</main>
<script src="sketch.js"></script>
</body>
</html>

Related

how can i make all sprites move to different attraction points using p5.js /p5.play

I am creating a game and have sprites falling from the top of the canvas and destroying towers at the bottom of the canvas. I am trying to use attractionPoint on all the different towers so the sprites are moving towards the towers. I have currently gotten one sprite to move to a tower using attactionPoint but the remaining sprites continue to move in a straight path. Is there a way I am able to set multiple attraction points so the sprites are moving toward all towers?
see code below:
function createAsteroids() {
// code to spawn asteroids at random locations
for (let i = 0; i < numAsteroids; i++) {
asteroid = createSprite(random(0, 1000), random(-50, -350), 40, 40);
asteroid.maxSpeed = random(1, 3);
rock.add(asteroid);
asteroidArray.push(asteroid);
}
}
function drawAsteroid() {
// draws asteroids moving down and checking collision
rock.overlap(turret, explode);
for (let i = 0; i < asteroidArray.length; i++) {
if (asteroidArray[i].position.y > height) {
asteroidArray[i].position.y = 0;
asteroidArray[i].position.x = random(0, 800);
}
asteroidArray[i].addSpeed(3, 90);
asteroid.attractionPoint(1, 100, 740);
asteroid.attractionPoint(1, 400, 740);
asteroid.attractionPoint(1, 700, 740);
}
}
function explode(sprite, obstical) {
//code for explosion and collision
sprite.remove();
obstical.remove();
}
You're very close but there's a couple things that aren't quite correct. In drawAsteroid you're setting the attractionPoint on only the lastly created asteroid, and doing it 3 times and calling it - i assume - in the draw method so it would get updated each frame. We only really want the drawAsteroid function for its respawning logic, so let's rename this method to updateAsteroid rather than drawAsteroid because we're going to call drawSprites(rock); in the draw method (rock is the group you've created for your asteroids).
When we set the attractionPoint we only want to do that once and that's when we create the asteroid:
function createAsteroids() {
// code to spawn asteroids at random locations
for (let i = 0; i < numAsteroids; i++) {
let asteroid = createSprite(random(0, width), random(-50, -350), 40, 40);
asteroid.maxSpeed = random(1, 3);
rock.add(asteroid);
let tower = floor(random(3)) // i know there's only 3 asteroids, you could do something better here
asteroid.attractionPoint(asteroid.maxSpeed, towers[tower].position.x, towers[tower].position.y);
}
}
So we're just randomly targeting a tower from the group.
I've set up a small example that should point you in the right direction.
// Find my blog at https://codeheir.com/
// I do a lot of p5.js stuff that might interest you!
let numAsteroids = 10;
let towers;
let rock;
function setup() {
createCanvas(400, 400);
rock = Group();
towers = Group();
createTowers();
createAsteroids();
}
function draw() {
background(220);
drawSprites(rock);
drawSprites(towers);
updateAsteroids();
}
function createAsteroids() {
// code to spawn asteroids at random locations
for (let i = 0; i < numAsteroids; i++) {
let asteroid = createSprite(random(0, width), random(-50, -350), 40, 40);
asteroid.maxSpeed = random(1, 3);
rock.add(asteroid);
let tower = floor(random(3))
asteroid.attractionPoint(asteroid.maxSpeed, towers[tower].position.x, towers[tower].position.y);
}
}
function updateAsteroids() {
// draws asteroids moving down and checking collision
//rock.overlap(turret, explode);
for (let i = 0; i < rock.length; i++) {
if (rock[i].position.y > height) {
rock[i].position.y = 0;
rock[i].position.x = random(0, width);
}
}
}
function createTowers() {
towers.push(createSprite(40, height - 30, 40, 90));
towers.push(createSprite(width/2, height - 30, 40, 90));
towers.push(createSprite(width-40, height - 30, 40, 90));
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/molleindustria/p5.play/lib/p5.play.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
I've also created a p5.js sketch to point you in the right direction.

How to remove a 'bullet ' sprite after it has reached mouseX and mouseY? [P5.Play]

I'm making a game using P5.Play, where I have 3 obelisks; one on the left, one on the right and one in the middle. I have made it so if my mouse position is within a specific area of the canvas, the obelisk nearest will shoot towards my mouse position. However I am struggling to find a way to get the sprite to explode once it has reached the x and y point at which when I clicked the mouse. I won't post my full code because it's for an assignment but here is a snippet.
function mousePressed() {
if (mouseX < width / 3) {
bullet = createSprite(obelisks[0].position.x, obelisks[0].position.y - 60, 20, 20)
} else if (mouseX > width / 3 && mouseX < width - width / 3) {
bullet = createSprite(obelisks[1].position.x, obelisks[1].position.y - 60, 20, 20)
} else {
bullet = createSprite(obelisks[2].position.x, obelisks[2].position.y - 60, 20, 20)
}
// if bullet position is less than the distance between firing point and cursor position, then remove bullet??
bullet.addImage(bulletSprite);
bullet.attractionPoint(10, mouseX, mouseY);
bullet.rotateToDirection = true;
}
First of all, thank you for introducing me to p5.play!
There's a couple of things to think about here, but you're very close. From reading the documentation there's a function on the sprite object called overlapPoint(pointX, pointY) which:
Checks if the given point is inside the sprite's collider.
This returns a boolean. We can leverage this to determine if our bullet has reached its destination.
First of all, let's define the destinationX and destinationY properties on our object:
function mousePressed() {
...
bullet = createSprite(width/2, height, 10, 10);
let destinationX = mouseX;
let destinationY = mouseY;
bullet.attractionPoint(10, destinationX, destinationY);
bullet.destinationX = destinationX;
bullet.destinationY = destinationY;
}
Now, we can use these properties to determine if we've hit the overlap point:
function draw() {
background(220);
drawSprite(obelisk);
if (bullet) {
drawSprite(bullet);
if (bullet.overlapPoint(bullet.destinationX, bullet.destinationY)) {
bullet.remove();
}
}
}
Here's a working, simplified example:
let obelisk;
let bullet;
function setup() {
createCanvas(400, 400);
obelisk = createSprite(width/2, height, 50, 200);
}
function draw() {
background(220);
drawSprite(obelisk);
if (bullet) {
drawSprite(bullet);
if (bullet.overlapPoint(bullet.destinationX, bullet.destinationY)) {
bullet.remove();
}
}
}
function mousePressed() {
bullet = createSprite(width/2, height, 10, 10);
let destinationX = mouseX;
let destinationY = mouseY;
bullet.attractionPoint(10, destinationX, destinationY);
bullet.destinationX = destinationX;
bullet.destinationY = destinationY;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/molleindustria/p5.play/lib/p5.play.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
And here's a link to a p5.js sketch I created, to help you further.
Hopefully, this points you in the right direction. I might also recommend that you create an array of bullets, such: let bullets = [] and add to this array when you shoot, this would prevent the deletion of a bullet when shooting in rapid succession. Or you could maybe even look into p5.play's grouping object.

how can I use parcel.js to run p5.js code?

I've lately been using parcel.js with react apps and it's been very user friendly.
I'm following along to a Coding Train tutorial and parcel for whatever reason is not running the code.
https://github.com/CodingTrain/website/tree/master/CodingChallenges/CC_001_StarField/P5
When I run this with live server, no errors and everything runs correctly.
When I attempt to run it with parcel.js, there are no errors, it just doesn't run, nothing renders.
Any idea why this might be happening?
Thank you
I think the issue here is that Parcel.js doesn't put variables defined in src'd scripts into the global scope by default, and p5.js depends on these globals by default (setup and draw in particular). You could solve the problem by making speed, setup and draw in sketch.js, and Star in Star.js, properties of window (or, equivalently, global). See the example below, which works when built with parcel index.html.
(This method has the benefit of preserving access to the familiar (global) p5.js commands without the need for a prefix, but might give complications with globals being overwritten if you're using other libraries.)
//sketch.js
let stars = [];
window.speed = undefined;
window.setup = function setup() {
createCanvas(600, 600);
for (let i = 0; i < 800; i++) {
stars[i] = new Star();
}
}
window.draw = function draw() {
speed = map(mouseX, 0, width, 0, 50);
background(0);
translate(width / 2, height / 2);
for (let i = 0; i < stars.length; i++) {
stars[i].update();
stars[i].show();
}
}
//Star.js
window.Star = function Star() {
this.x = random(-width, width);
this.y = random(-height, height);
this.z = random(width);
this.pz = this.z;
this.update = function() {
this.z = this.z - speed;
if (this.z < 1) {
this.z = width;
this.x = random(-width, width);
this.y = random(-height, height);
this.pz = this.z;
}
};
this.show = function() {
fill(255);
noStroke();
var sx = map(this.x / this.z, 0, 1, 0, width);
var sy = map(this.y / this.z, 0, 1, 0, height);
var r = map(this.z, 0, width, 16, 0);
ellipse(sx, sy, r, r);
var px = map(this.x / this.pz, 0, 1, 0, width);
var py = map(this.y / this.pz, 0, 1, 0, height);
this.pz = this.z;
stroke(255);
line(px, py, sx, sy);
};
}
<html>
<head>
<meta charset="UTF-8" />
<title>StarField_p5.js</title>
<style>
body {
padding: 0;
margin: 0;
}
canvas {
vertical-align: top;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/p5#1.0.0/lib/p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5#1.0.0/lib/addons/p5.sound.min.js"></script>
<script src="./Star.js"></script>
<script src="./sketch.js"></script>
</head>
<body>
<main></main>
</body>
</html>
Alternatively you could use instance mode of P5.js to set things up without globals (See also thread here - the code sample of which has now had its P5.js dependency removed, but you can look at the commit history). This has the downside that the familiar P5.js / processing functions have to be prefixed with an object reference, minimally something like s.createCanvas (the methods often seem to depend on being bound properly in object orientated style). It is likely the safer approach if other libraries are involved though, especially as p5.js defines lots of globals with common names like random, map and height if it isn't used in instance mode.
Something like this:
// index.js
// I've combined sketch.js and Star.js here to make
// it easier to share variable speed
const myp5 = new p5((s) => {
const stars = [];
let speed =1;
s.setup = function setup() {
s.createCanvas(600, 600);
for (let i = 0; i < 800; i++) {
stars[i] = new Star();
}
};
s.draw = function draw() {
speed = s.map(s.mouseX, 0, s.width, 0, 50);
s.background(0);
s.translate(s.width / 2, s.height / 2);
for (let i = 0; i < stars.length; i++) {
stars[i].update();
stars[i].show();
}
};
function Star() {
this.x = s.random(-s.width, s.width);
this.y = s.random(-s.height, s.height);
this.z = s.random(s.width);
this.pz = this.z;
this.update = function () {
this.z = this.z - speed;
if (this.z < 1) {
this.z = s.width;
this.x = s.random(-s.width, s.width);
this.y = s.random(-s.height, s.height);
this.pz = this.z;
}
};
this.show = function () {
s.fill(255);
s.noStroke();
var sx = s.map(this.x / this.z, 0, 1, 0, s.width);
var sy = s.map(this.y / this.z, 0, 1, 0, s.height);
var r = s.map(this.z, 0, s.width, 16, 0);
s.ellipse(sx, sy, r, r);
var px = s.map(this.x / this.pz, 0, 1, 0, s.width);
var py = s.map(this.y / this.pz, 0, 1, 0, s.height);
this.pz = this.z;
s.stroke(255);
s.line(px, py, sx, sy);
};
}
},'sketch1');
<html>
<head>
<meta charset="UTF-8" />
<title>StarField_p5.js</title>
<style>
body {
padding: 0;
margin: 0;
}
canvas {
vertical-align: top;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/p5#1.0.0/lib/p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5#1.0.0/lib/addons/p5.sound.min.js"></script>
<script src="./index.js"></script>
</head>
<body>
<div id="sketch1"></div>
</body>
</html>

The createSlider() function creates the slider outside of the canvas. How to position the slider inside the canvas?

I'm trying to implement a slider in a canvas using p5.js. If there are no HTML elements in the body of the webpage, then the canvas and the slider are on the top-left corner of the webpage.
However, when I change the position of the canvas, the slider does not stay inside the canvas. The position method sets the position of the slider ABSOLUTE with respect to the webpage.
How do I set the position of the slider inside the canvas?
This is the bare-minimum code to replicate the issue.
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>p5.js example</title>
<style> body {padding: 0; margin: 0;} </style>
<script src="../p5.min.js"></script>
<script src="../addons/p5.dom.min.js"></script>
<script src="../addons/p5.sound.min.js"></script>
<script src="sketch.js"></script>
</head>
<body>
<div>
<p><object width="150" height="150" vspace="100"></object>
This is some text. This is some text. This is some text.</p>
</div>
</body>
</html>
The sketch.js file is:
var slider;
function setup() {
slider = createSlider(0, 255, 100);
slider.position(10, 10);
slider.style('width', '80px');
}
function draw() {
var val = slider.value();
background(val);
}
p5.js' createSlider() function creates an <input /> range HTML element which is different from the <canvas/> element it uses to render the other methods.
You could use absolute positioning in tandem with z-index to have the canvas with a lower z-index and the slider with a higher z-index (on top of the canvas), both with the absolute positions you need.
If you really do want the slider rendered inside the canvas element you could roll your own slider class using the p5.js drawing functions and mouse/touch events, but it wouldn't be as CPU efficient as using an <input /> range HTML element.
Here is the Processing > Examples > Topics > GUI > Scrollbar example ported to p5.js:
/**
* Scrollbar.
*
* Move the scrollbars left and right to change the positions of the images.
*/
let slider;
function setup() {
createCanvas(640, 360);
noStroke();
slider = new HScrollbar(0, height/2-8, width, 16, 16);
}
function draw() {
background(255);
fill(0);
text("slider position: " + slider.getPos().toFixed(2),width/2,height/3);
slider.update();
slider.display();
}
class HScrollbar {
constructor(xp, yp, sw, sh, l) {
this.swidth = sw;
this.sheight = sh;
let widthtoheight = sw - sh;
this.ratio = sw / widthtoheight;
this.xpos = xp;
this.ypos = yp-this.sheight/2;
this.spos = this.xpos + this.swidth/2 - this.sheight/2;
this.newspos = this.spos;
this.sposMin = this.xpos;
this.sposMax = this.xpos + this.swidth - this.sheight;
this.loose = l;
this.over = false;
this.locked = false;
}
update() {
if (this.overEvent()) {
this.over = true;
} else {
this.over = false;
}
if (mouseIsPressed && this.over) {
this.locked = true;
}
if (!mouseIsPressed) {
this.locked = false;
}
if (this.locked) {
this.newspos = constrain(mouseX-this.sheight/2, this.sposMin, this.sposMax);
}
if (abs(this.newspos - this.spos) > 1) {
this.spos = this.spos + (this.newspos-this.spos)/this.loose;
}
}
overEvent() {
if (mouseX > this.xpos && mouseX < this.xpos+this.swidth &&
mouseY > this.ypos && mouseY < this.ypos+this.sheight) {
return true;
} else {
return false;
}
}
display() {
noStroke();
fill(204);
rect(this.xpos, this.ypos, this.swidth, this.sheight);
if (this.over || this.locked) {
fill(0, 0, 0);
} else {
fill(102, 102, 102);
}
rect(this.spos, this.ypos, this.sheight, this.sheight);
}
getPos() {
// Convert spos to be values between
// 0 and the total width of the scrollbar
return this.spos * this.ratio;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.min.js"></script>
Please, check this:
CSlider - Canvas Slider for p5.js with scale() support, mostly supported standard p5 slider calls:
https://editor.p5js.org/Comissar/sketches/IHUYuzjR

Why is there so much time in between animation code and next animation frame?

I'm implementing a game using a canvas as the renderer, and there are some points where there seem to be excessive "waits" between render frames and I have no idea what to think of this.
I've been working on this for the past few weeks and for the most part, there haven't really been issues. However, when there are a lot of things on screen there is a "wait" for some reason.
It takes about the same amount of time that the rendering code does before "composite layers" is called. I don't know if this is something that I can prevent or if it is just something that I have to live with. I'm only getting to the point where I'm going below 60 fps in extreme situations anyway.
Another example:
index.js
window.onload = () => {
const width = 1280;
const height = 720;
const canvas = document.getElementById("game-canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
function renderLoop() {
console.time("render");
// Do render and request next frame
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < 13000; i++) {
// Not real code this is just to demonstrate the issue
ctx.fillRect(i % width, 10 * Math.floor(i / (width)), 10, 10);
}
console.timeEnd("render");
requestAnimationFrame(renderLoop);
}
requestAnimationFrame(renderLoop);
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
canvas {
border: 2px solid black;
display: inline-block;
vertical-align: top;
}
</style>
</head>
<body>
<script src="./index.js"></script>
<canvas id="game-canvas"></canvas>
</body>
</html>

Resources