I am making this random little sketch where you can click and drop coins and there is a count of how many coins you have in the top left.
The problem I am running into is, whenever you run this you will notice that the amount label just continuously stacks on top of itself whenever it updates. I have read other posts that say that we need to redraw the background to clear the text but when I do this, it also removes any coins that have been generated on the canvas. How can I update this to keep the coins visible but remove and redraw the amount?
var moneyCount = 0;
function setup() {
createCanvas(windowWidth, windowHeight);
background(100);
}
function draw() {
if (mouseIsPressed){
dropCoins();
displayCount();
}
}
function displayCount() {
textSize(80);
text('$' + moneyCount, 80, 80);
}
function dropCoins() {
var maxSize = 40;
var xLoc = mouseX;
var yLoc = mouseY;
makeStacks(xLoc, yLoc, maxSize);
}
function makeStacks(x, y, size){
fill(255,215,0);
ellipse(x, y, size);
for (i = 0; i < size; i++){
let r1 = random(100);
let r2 = random(100);
if (r1 < 50){
x = x + 2
} else {
x = x - 2;
}
if (r2 < 50){
y = y + 2;
} else {
y = y - 2;
}
moneyCount++;
ellipse(x, y, size);
}
}
You have a couple of options available:
You store where the coins are so you can redraw them after you clear the background
You use multiple "layers" using createGraphics() so you can clear the background, but not the coins PGraphics
For option 1 you can do something like this:
var moneyCount = 0;
var coins = [];
function setup() {
createCanvas(windowWidth, windowHeight);
textSize(80);
}
function draw() {
background(100);
if (mouseIsPressed){
dropCoins();
}
displayCoins();
displayCount();
}
function displayCount() {
text('$' + moneyCount, 80, 80);
}
function dropCoins() {
var maxSize = 40;
var xLoc = mouseX;
var yLoc = mouseY;
makeStacks(xLoc, yLoc, maxSize);
}
function makeStacks(x, y, size){
for (i = 0; i < size; i++){
let r1 = random(100);
let r2 = random(100);
if (r1 < 50){
x = x + 2
} else {
x = x - 2;
}
if (r2 < 50){
y = y + 2;
} else {
y = y - 2;
}
moneyCount++;
coins.push({x:x,y:y,size:size});
}
}
function displayCoins(){
fill(255,215,0);
for(var i = 0 ; i < coins.length; i++){
ellipse(coins[i].x,coins[i].y,coins[i].size);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
The idea is you would initialise an array to store the data you would need to redraw the coins (x,y, and size (if it varies)).
Bare in mind the more coins you'd add, the more memory you'd use.
If you simply need the rendered image and don't need the coin position data, option 2 will be more efficient.
For option 2 the main idea, as you can see in the documentation, is that you can have another graphics layer to draw into. Once initialised simply use dot notion on the instance and use the typical p5 drawing calls on it. To render it use image()
Here's a demo for the PGraphics option:
var moneyCount = 0;
var coinsLayer;
function setup() {
createCanvas(windowWidth, windowHeight);
textSize(80);
coinsLayer = createGraphics(windowWidth, windowHeight);
}
function draw() {
background(100);
// render coins layer
image(coinsLayer,0,0);
if (mouseIsPressed){
dropCoins(coinsLayer);
}
displayCount();
}
function displayCount() {
text('$' + moneyCount, 80, 80);
}
function dropCoins(coinsLayer) {
var maxSize = 40;
var xLoc = mouseX;
var yLoc = mouseY;
makeStacks(coinsLayer, xLoc, yLoc, maxSize);
}
function makeStacks(layer, x, y, size){
layer.fill(255,215,0);
layer.ellipse(x, y, size);
for (i = 0; i < size; i++){
let r1 = random(100);
let r2 = random(100);
if (r1 < 50){
x = x + 2
} else {
x = x - 2;
}
if (r2 < 50){
y = y + 2;
} else {
y = y - 2;
}
moneyCount++;
layer.ellipse(x, y, size);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
Related
Im playing around with the prime spiral code from Dan Shiffman:
https://www.youtube.com/watch?v=a35KWEjRvc0&t=716s&ab_channel=TheCodingTrain
I need to use this code as a background to another sketch, but due to the moving nature of the spiral its impossible that the spiral will not draw ontop of the other sketch, even with p5.layers.
I am therefore searching for a way to refactor this code into something that draw all the figures at once so use it as a background.
My idea is to create some kind of for loop in setup, but i am afraid its to big of nut to crack for me.
let x, y;
let step = 1;
let stepSize = 20;
let numSteps = 1;
let state = 0;
let turnCounter = 1;
let totalSteps;
let offset = 0;
function isPrime(value) {
if (value == 1) return false;
for (let i = 2; i <= sqrt(value); i++) {
if (value % i == 0) {
return false;
}
}
return true;
}
function setup() {
createCanvas(500, 500);
const cols = width / stepSize;
const rows = height / stepSize;
totalSteps = cols * rows;
x = width / 2;
y = height / 2;
background(0);
}
function draw() {
primeSpiral(20, 1)
primeSpiral(30, 200)
incrementStep();
noStroke();
}
function incrementStep()
{
switch (state) {
case 0:
x += stepSize;
break;
case 1:
y -= stepSize;
break;
case 2:
x -= stepSize;
break;
case 3:
y += stepSize;
break;
}
if (step % numSteps == 0) {
state = (state + 1) % 4;
turnCounter++;
if (turnCounter % 2 == 0) {
numSteps++;
}
}
step++;
if (step > totalSteps) {
noLoop();
}
}
function primeSpiral(offset, color){
if (!isPrime(step+offset)) {
//might put something here
} else {
let r = stepSize * 0.5;
fill(color, 99, 164);
push();
translate(x, y);
rotate(-PI / 4);
triangle(-r, +r, 0, -r, +r, +r);
pop();
}
}
You can move the code from draw() to the loop in setup(), here is example:
...
function setup() {
createCanvas(500, 500);
const cols = width / stepSize;
const rows = height / stepSize;
totalSteps = cols * rows;
x = width / 2;
y = height / 2;
background(0);
for (let i = 0; i<100; i++) { // 100 is the number of processed frames, you can change it as you need
primeSpiral(20, 1)
primeSpiral(30, 200)
incrementStep();
noStroke();
}
}
function draw() { // now draw is empty and you can place new code here
}
...
I'm new to p5js and I'm trying to plot incoming x y data by drawing circles which initially expand and then shrink becoming 'fixed' , but the speed at which the circles expand slows down over time - and I can't work out why. I have one array which stores the animated expanding / shrinking circle and another which stores the final state of the fixed circle.
let circles = [];
let circlesStatic = [];
function setup() {
createCanvas(640, 480);
frameRate(60);
smooth();
// e1 = new Ellipse(320,240);
// e2 = new Ellipse(20,20);
background(0);
}
function mouseClicked() {
let e = new Ellipse(mouseX, mouseY);
circles.push(e);
}
function draw() {
for (let i = 0; i < circles.length; i++) {
circles[i].render();
}
for (let i = 0; i < circlesStatic.length; i += 2) {
// noFill();
fill('rgba(50,50,50, 0.1)');
strokeWeight(1);
stroke('rgba(100,100,100, 0.25)');
circle(circlesStatic[i], circlesStatic[i + 1], 20);
}
}
class Ellipse {
// constructor
constructor(x, y) {
this.x = x; // copy x argument value to the instance (this) x property
this.y = y; // copy x argument value to the instance (this) x property
// current size - continuously updated
this.size = 10;
// minimum size
this.minSize = 10;
// maximum size
this.maxSize = random(30, 35);
// change speed for size (how much will the size increase/decrease each frame)
this.sizeSpeed = 3;
// internal frameCount replacement
this.tick = 0;
// this.fill=(255,0,0);
}
render() {
// if the size is either too small, or too big, flip the size speed sign (if it was positive (growing) - make it negative (shrink) - and vice versa)
if (this.size < this.minSize || this.size > this.maxSize) {
this.sizeSpeed *= -1;
}
// increment the size with the size speed (be it positive or negative)
this.size += this.sizeSpeed;
console.log(this.sizeSpeed);
if (this.size < this.minSize) {
this.sizeSpeed = 0;
circlesStatic.push(this.x);
circlesStatic.push(this.y);
}
background(0);
// noStroke();
fill(200, 50, 0);
ellipse(this.x, this.y, this.size, this.size);
// this.tick++;
// this.size = map(sin(this.tick * this.sizeSpeed),-1.0,1.0,this.minSize,this.maxSize);
// fill(random(255), random(255), random(255));
// ellipse(this.x,this.y, this.size,this.size);
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
The problem is that every time you render a circle it checks if it should become "static," and if it should it adds new values to circlesStatic. However, there is nothing to stop the animating circle to continue being rendered on subsequent frames. This results in massive numbers of entries being added to circlesStatic which means longer and longer render times. I've implemented one possible fix below. I also made it so that if you hold the shift key down you will see the previous behavior (watch the length of circlesStatic skyrocket after a few clicks). As soon as you release the shift key the fix will run and the number of entries in circlesStatic will plateau.
let circles = [];
let circlesStatic = [];
function setup() {
createCanvas(640, 480);
frameRate(60);
smooth();
// e1 = new Ellipse(320,240);
// e2 = new Ellipse(20,20);
textSize(16);
}
function mouseClicked() {
let e = new Ellipse(mouseX, mouseY);
circles.push(e);
}
function draw() {
background(0);
push();
noStroke();
fill('red')
text(`circles: ${circles.length}; circlesStatic: ${circlesStatic.length}`, 20, 20);
pop();
for (let i = 0; i < circles.length; i++) {
circles[i].render();
if (!keyIsDown(SHIFT)) {
if (circles[i].isStatic()) {
circles.splice(i, 1);
i--;
}
}
}
for (let i = 0; i < circlesStatic.length; i += 2) {
// noFill();
fill('rgba(50,50,50, 1)');
strokeWeight(1);
stroke('rgba(100,100,100, 1)');
circle(circlesStatic[i], circlesStatic[i + 1], 20);
}
}
class Ellipse {
// constructor
constructor(x, y) {
this.x = x; // copy x argument value to the instance (this) x property
this.y = y; // copy x argument value to the instance (this) x property
// current size - continuously updated
this.size = 10;
// minimum size
this.minSize = 10;
// maximum size
this.maxSize = random(30, 35);
// change speed for size (how much will the size increase/decrease each frame)
this.sizeSpeed = 3;
// internal frameCount replacement
this.tick = 0;
// this.fill=(255,0,0);
}
isStatic() {
return this.sizeSpeed === 0;
}
render() {
// if the size is either too small, or too big, flip the size speed sign (if it was positive (growing) - make it negative (shrink) - and vice versa)
if (this.size < this.minSize || this.size > this.maxSize) {
this.sizeSpeed *= -1;
}
// increment the size with the size speed (be it positive or negative)
this.size += this.sizeSpeed;
console.log(this.sizeSpeed);
if (this.size < this.minSize) {
this.sizeSpeed = 0;
circlesStatic.push(this.x);
circlesStatic.push(this.y);
}
// background(0);
// noStroke();
fill(200, 50, 0);
ellipse(this.x, this.y, this.size, this.size);
// this.tick++;
// this.size = map(sin(this.tick * this.sizeSpeed),-1.0,1.0,this.minSize,this.maxSize);
// fill(random(255), random(255), random(255));
// ellipse(this.x,this.y, this.size,this.size);
}
}
<script src="https://cdn.jsdelivr.net/npm/p5#1.4.1/lib/p5.js"></script>
I've made a square grid on top of the canvas and also a cruve (that is meant to have a fading trail). I made them seperately and tried combining them so the curve would appear on top of the grid. However, it doesn't show the curve.
I've commented out the grid so it's easier to see the curve.
How do I get this to work?
var cols = 10;
var rows = 10;
var t = 0;
var particleArray = [];
function setup() {
createCanvas(600, 600);
background(0);
fill(100);
rect(0, 0, 550, 550, 25);
}
// blue grid
function draw() {
/*for (var c = 0; c < cols; c++) {
for (var r = 0; r < rows; r++) {
var XO = 25 + c * 50;
var YO = 25 + r * 50;
stroke(0);
fill(100,149,237);
rect(XO, YO, 50, 50);
noLoop();
// :(
}
}
*/
//curve
y = width / 2 + 270 * sin(3 * t + PI / 2) - 25;
x = height / 2 + 270 * sin(1 * t) - 25;
particleArray.push(new Particle(x, y, t));
for (i=0; i<particleArray.length; i++) {
particleArray[i].show(t);
}
if (particleArray.length > 700) {
particleArray.shift();
}
t += .01;
}
function Particle(x, y, t) {
this.x = x;
this.y = y;
this.t = t;
this.show = function(currentT) {
var _ratio = t / currentT;
_alpha = map(_ratio, 0, 1, 0, 255); //points will fade out as time elaps
fill(255, 255, 255, _alpha);
ellipse(x, y, 5, 5);
}
}
I don't know if this was intentional but you called noLoop() function where you're drawing the grid. If you comment that out it works.
I'm trying to make a circle move between two body keypoints – i.e between left and right shoulder. I started with this code to make an object following some position:
var rectLocation;
var x = 50;
var y = 50;
function setup() {
createCanvas(640,400);
rectLocation = createVector(width/2,height/2);
}
function draw() {
background(255);
var target = createVector(x,y);
var distance = target.dist(rectLocation);
var mappedDistance = map(distance, 100, 0, 1.5, 0.5);
target.sub(rectLocation);
target.normalize();
target.mult(mappedDistance);
rectLocation.add(target);
text('hello',rectLocation.x, rectLocation.y);
x = x + 1 ;
if (x > width) {
x = 50;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>
So with poseNet I converted x and y values of each keypoint into p5 Vectors and made one location follow another, but it doesn't seem to move and I can't get why. Sketch: https://editor.p5js.org/sebyakin/sketches/LQ7gqwGZH and my code here:
let video;
let poseNet;
let noseX = 0;
let noseY = 0;
let eyelX = 0;
let eyelY = 0;
let shoulderX = 0;
let shoulderY = 0;
let firstLocation;
let target;
function setup() {
createCanvas(640, 480);
video = createCapture(VIDEO);
video.hide();
poseNet = ml5.poseNet(video, modelReady);
poseNet.on('pose', gotPoses);
// creating empty vectors
firstLocation = createVector(0,0);
target = createVector(0,0);
}
function gotPoses(poses) {
if (poses.length > 0) {
let nX = poses[0].pose.keypoints[0].position.x;
let nY = poses[0].pose.keypoints[0].position.y;
let eX = poses[0].pose.keypoints[1].position.x;
let eY = poses[0].pose.keypoints[1].position.y;
let sX = poses[0].pose.keypoints[5].position.x;
let sY = poses[0].pose.keypoints[5].position.y;
noseX = nX;
noseY = nY;
eyelX = eX;
eyelY = eY;
shoulderX = sX;
shoulderY = sY;
}
}
function modelReady() {
console.log('model ready');
}
function draw() {
image(video, 0, 0);
fill(0,0,255);
// set vectors to keypoints values
firstLocation.set(noseX, noseY);
target.set(shoulderX, shoulderY)
// make a circle follow target loaction
let distance = target.dist(firstLocation);
let mappedDistance = map(distance, 100, 0, 1.5, 0.5);
target.sub(firstLocation);
// target.normalize();
// target.mult(mappedDistance);
firstLocation.add(target);
ellipse(firstLocation.x, firstLocation.y, 50);
console.log(target.x);
}
Where did I go wrong?
I would like to do the following in p5.js. Say I have a canvas roughly like this:
Let's say I have given those red squares some interactivity using the function mouseClicked() and their respective coordinates in the canvas (as in, if I click on a square, change its color).
Now I'd like to use that blue "i"-button to display some sort of info box, and it should look approximately like this:
I want that "info dialog" to go away if the user clicks on that "OK-button" (whcih is not really a button, but also just a square in a p5 canvas).
Question: Is there an elegant way of deactivating the "square interactivity" and activating that "OK button interactivity" and to have the interactivity the other way around whenever the info box is not being displayed?
The only way I can think of to achieve this goes like this:
function mouseClicked(){
if(infoBoxIsBeingDisplayed){
do something
}else{
do something else
}
}
However, this seems a little convoluted.
I'd appreciate any suggestions on how to do this better.
Your solution seems fine, and it also seems much less "convoluted" than any of the other options. Keep it simple.
You might be able to clean up your code by splitting up your logic into smaller utility functions. Something like this:
function mouseClicked(){
if(infoBoxIsBeingDisplayed){
mouseClickedInfoBox();
}else{
mouseClickedSquaresCanvas();
}
}
Then your logic would be in those utility functions, specific to each screen. You could further split it up to generalize your bounds-checking, but the idea is the same.
I've run into a similar problem to this before, the best way I came up with to solve this (which is a fairly patchy way to do it) is to move the squares outside of the canvas. sorry my code is probably really messy but look at the function game and you will see what i did
If you run the game make it full screen.
example:(to play the game use A and D or use the arrow keys but i suggest A and D because the way the code snippet is set up the game doesn't fit on the page and the page moves around when you press the arrow keys)
var bs = [];
var speed;
var ship1;
var num = 40;
var d;
var gscore = 0;
var highscore = 0;
var cap = 0;
function setup() {
createCanvas(windowWidth,windowHeight- 4);
for(var i = 0; i < num; i++) {
bs[i] = new Box(random(0, width), random(-600,-30));
}
ship1 = new ship();
button1 = new button();
}
function draw() {
background(0);
if(cap == 0){
gscore = gscore + 0.1;
}
if(highscore < gscore){
highscore = gscore;
}
speed = map(gscore,4,100,4,5);
ship1.show();
ship1.update();
for(var i = 0; i < num; i++) {
bs[i].update();
bs[i].show();
if(bs[i].y >= height){
bs[i].x = random(0, width);
bs[i].y = random(-600,-30);
}
for(var j = 0; j < num; j++) {
if(bs[i].touch(bs[j])){
if(bs[i] != bs[j]){
bs[j].x = random(0, width);
bs[j].y = random(-600,-30);
}
}
}
if(bs[i].touch(ship1)){
game();
}
}
push();
fill(255,0,0);
textSize(36);
text("score: "+ floor(gscore),0,36);
text("highscore: "+floor(highscore),0,72);
pop();
}
function Box(x, y) {
this.x = x;
this.y = y;
this.show = function() {
fill(255);
noStroke();
rect(this.x, this.y, 30, 30);
}
this.update = function() {
this.y = this.y + speed;
}
this.touch = function(other){
d = dist(this.x, this.y, other.x, other.y);
if(d < 15/*half of the squares*/+15/*the total area of the ship*/){
return true;
}else {
return false;
}
}
}
function game(){//look here, game is the end game screen
for(var i = 0; i < num; i++) {
bs[i].x = -200;//making all squares x value -200
bs[i].y = -200;//making all squares y value -200
}
ship1.x = -200;//making ship x value -200
ship1.y = -200;//making ship y value -200
cap = 1;//cap is a variable made to stop the score from increasing when the end game screen is shown its "capping" the score
push();
fill(255,0,0);
textAlign(CENTER);
textSize(64);
text("You lose", width/2, height/2);
fill(255);
text("Try again?", width/2,height/2+64);
button1.touch();//touch checks if the mouse is over the button(the button sucks ass btw the hitbox for it is a square not a rectangle)
button1.show();//showing the button
button1.update();//updates the text and button color when highlighted
fill(texthover);
textSize(48);
text("Yes",width/2, height/2+145);
pop();
}
function button(){
this.x = width/2;
this.y = height/2+128;
this.d;
this.update = function() {
this.x = width/2;
this.y = height/2+128;
}
this.show = function(){
push();
rectMode(CENTER);
fill(hover);
rect(this.x, this.y, 128, 64, 50);
pop();
}
this.touch = function(){
this.d = dist(this.x, this.y, mouseX, mouseY);
if(this.d <32){
hover = 51;
texthover = 255;
if(mouseIsPressed){
for(var i = 0; i < num; i++) {
bs[i].x = random(0, width);
bs[i].y = random(-600,-30);
}
ship1.x = width/2;
ship1.y = 450;
gscore = 0;
cap = 0;
}
}else {
hover = 200;
texthover = 0;
}
}
}
function ship() {
this.x = width/2;
this.y = 450;
this.update = function() {
if(keyIsDown(LEFT_ARROW) || keyIsDown(65)) {
if(this.x>14){
this.x = this.x - map(gscore,2,100,2,3);
}
}
if(keyIsDown(RIGHT_ARROW) || keyIsDown(68)) {
if(this.x<width- 15){
this.x = this.x + map(gscore,2,100,2,3);
}
}
}
this.show = function() {
push();
rectMode(CENTER);
fill(200,200,0);
rect(this.x+15, this.y+5, 5, 30);
fill(150,100,200);
rect(this.x+15, this.y + 15,30, 15)
pop();
}
}
function windowResized() {
createCanvas(windowWidth,windowHeight- 4);
button1.update();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/addons/p5.dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.14/p5.js"></script>