p5.js mousePressed line animation - javascript

In p5.js I'm trying to create an animation where the bottom point of the lines smoothly (5px per frame) move to the bottom left corner of the canvas when the mouse is pressed. I can get them to the point they need to get to with lines.align but they instantly move there instead of animating.
let lines = []
function setup() {
createCanvas(600, 400);
for(let i = 0; i < 150; i++) {
lines[i] = new Line(random(600), 0, random(600), 400, random(149,212), random(89, 146), 1);
}
}
function draw() {
background(32, 44, 57);
for(let i = 0; i < lines.length; i++){
lines[i].show();
}
}
function mousePressed() {
for(let i = 0; i < lines.length; i++){
lines[i].align();
}
}
class Line {
constructor(xStart, yStart, xFinish, yFinish, g, b, w) {
this.xStart = xStart;
this.yStart = yStart;
this.xFinish = xFinish;
this.yFinish = yFinish;
this.g = g;
this.b = b;
this.w = w;
}
show() {
stroke(242, this.g, this.b);
strokeWeight(this.w);
line(this.xStart, this.yStart, this.xFinish, this.yFinish);
}
align() {
while (this.xFinish > 0) {
this.xFinish = this.xFinish - 5;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>

You're using a blocking while loop which is why visually you see a jump from the lines' start to end positions.
One option is to use a condition and increment the value in draw():
let lines = []
function setup() {
createCanvas(600, 400);
for(let i = 0; i < 150; i++) {
lines[i] = new Line(random(600), 0, random(600), 400, random(149,212), random(89, 146), 1);
}
}
function draw() {
background(32, 44, 57);
for(let i = 0; i < lines.length; i++){
lines[i].align();
lines[i].show();
}
}
class Line {
constructor(xStart, yStart, xFinish, yFinish, g, b, w) {
this.xStart = xStart;
this.yStart = yStart;
this.xFinish = xFinish;
this.yFinish = yFinish;
this.g = g;
this.b = b;
this.w = w;
}
show() {
stroke(242, this.g, this.b);
strokeWeight(this.w);
line(this.xStart, this.yStart, this.xFinish, this.yFinish);
}
align() {
if (this.xFinish > 0) {
this.xFinish = this.xFinish - 5;
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js"></script>
You might want to also check out lerp() to interpolation between start and end positions.
Additionally, if you need more control over timing/easing/etc you can use something like TweenLite to do the interpolation for you.
(It is a good exercise though to get a hang of incrementing values/interpolating/tweening manually first through)

Related

When spawning many iterations of a class object, how do I take an object out of the array using a if statement inside of the Class?

I'm a beginner using p5js and I'm trying to work with classes. I'm making a game where you have to find and click a 'wanted man', from a crowd.
So basically, a randomizer picks between 7 different types of 'civilians', and it's supposed to remove one of the types from the 'civilians' that have been spawned. After removing the 'wanted man', I want to add one wanted man so that there is only one 'wanted man'.
So the code spawns a bunch of random 'civilians', then it will delete all 'wanted man' types in the array, and add only one of them. I think there is a better way to do this though.
My basic desire is to have a crowd of 'civilians' that run around, - one of which is a 'wanted man' - and you would have to find and click that 'wanted man' (kind of like a hunting/assassination game).
This is the code for the sketch.js file:
var civilians = [];
var page = 0;
var man1img;
var man2img;
var man3img;
var man4img;
var man5img;
var man6img;
var aliemanimg;
var w;
var h;
var spawnCount = 14;
var wantedMan;
var randCiv;
function preload() {
man1img = loadImage("man1.png");
man2img = loadImage("man2.png");
man3img = loadImage("man3.png");
man4img = loadImage("man4.png");
man5img = loadImage("man5.png");
man6img = loadImage("man6.png");
aliemanimg = loadImage("alieman.png");
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
function setup() {
createCanvas(windowWidth, windowHeight);
imageMode(CENTER);
// wantedMan = round(random(0, 6));
wantedMan = 0;
for (var i = 0; i < spawnCount; i++) {
randCiv = round(random(0, 6));
w = random(windowWidth);
h = random(windowHeight);
civilians.push(new Civilian(w, h, wantedMan, randCiv));
console.log(wantedMan);
if (civilians[i].isWantedMan()) {
//OVER HERE \/
civilians.splice(i, 1);
}
}
civilians.push(new Civilian(w, h, wantedMan, wantedMan));
}
// page setup
// page 1 : main screen (play, settings, and those stuff)
// page 2 : show chosen civilian
// page 3 : playing
// page 4 : lose
// page 5 : options
function draw() {
background(220, 80, 80);
for (var i = civilians.length - 1; i >= 0; i--) {
civilians[i].update();
civilians[i].show(mouseX, mouseY);
if (civilians[i].clickedOn(mouseX, mouseY)) {
// detect if is right person
console.log("clicked on boi");
if (civilians[i].isWantedMan()) {
console.log("HES WANTED");
} else {
console.log("HES NOT WANTED");
}
}
}
text(round(frameRate()), 20, 20);
//show wanted man
var tempImg = man1img;
if (wantedMan == 1) {
tempImg = man2img;
} else if (wantedMan == 2) {
tempImg = man3img;
} else if (wantedMan == 3) {
tempImg = man4img;
}
if (wantedMan == 4) {
tempImg = man5img;
} else if (wantedMan == 5) {
tempImg = man6img;
} else if (wantedMan == 6) {
tempImg = aliemanimg;
}
image(tempImg, 50, 70, 70, 90);
}
This is the code for the class:
class Civilian {
constructor(x, y, wantedMan, type) {
this.x = x;
this.y = y;
this.w = 47;
this.h = 60;
this.t = {
x: x,
y: y,
};
this.size = 47;
this.moveSpeed = 0.01;
this.moveDist = 20;
this.wantedMan = wantedMan;
this.civilian = type
this.civilianImg = man1img
this.wantedMan = wantedMan
}
update() {
//move target to random position
this.t.x = random(this.t.x - this.moveDist, this.t.x + this.moveDist);
this.t.y = random(this.t.y - this.moveDist, this.t.y + this.moveDist);
//edge detect
if (this.t.x < 0) {
this.t.x += 5;
}
if (this.t.x > width) {
this.t.x -= 5;
}
if (this.t.y < 0) {
this.t.y += 5;
}
if (this.t.y > height) {
this.t.y -= 5;
}
//images position follows target but with easing
this.x += (this.t.x - this.x) * this.moveSpeed;
this.y += (this.t.y - this.y) * this.moveSpeed;
}
show(ex, ey) {
var d = dist(ex, ey, this.x, this.y);
if (d > this.size / 2) {
tint(255, 255, 255);
} else {
tint(0, 255, 0);
}
if(this.civilian == 1) {
this.civilianImg = man2img
} else if(this.civilian == 2) {
this.civilianImg = man3img
} else if(this.civilian ==3) {
this.civilianImg = man4img
} if(this.civilian == 4) {
this.civilianImg = man5img
} else if(this.civilian == 5) {
this.civilianImg = man6img
} else if(this.civilian == 6) {
this.civilianImg = aliemanimg
}
image(this.civilianImg, this.x, this.y, 47, 60);
}
clickedOn(ex, ey) {
var d = dist(ex, ey, this.x, this.y);
return d < this.size / 2 && mouseIsPressed;
}
isWantedMan() {
return this.civilian == this.wantedMan;
}
}
However, whenever I add a .splice(i,1) under the 'for' loop in setup function - to remove the 'wanted man', it shows this error:
"TypeError: Cannot read properties of undefined (reading
'isWantedMan') at /sketch.js:41:22".
isWantedMan() is a function in the Civilian Class, that returns true if the current 'civilian' is wanted. The .splice is supposed to remove a object from the array, when it is a 'wanted man'.
I don't know why this happens. When I replace the .splice code with a console.log() code, then there is no error.
Also there were probably a lot of things that I could have done better in the code.

How to scale co-ordinates to fit on canvas?

I'm having trouble scale x and y co-ordinates that are being loaded in from a .txt files to my canvas size so the circles and lines I've drawn don't extend beyond the canvas, any suggestions?
let dots = [];
let dotsData;
let orderData;
function preload() {
dotsData = loadStrings('./dots.txt');
orderData = loadStrings('./orderFile.txt');
}
function setup() {
createCanvas(750, 750);
createDots(dotsData);
}
function draw() {
background(220);
fill(255, 100, 200);
for (let i = 0; i < dots.length; i++) {
circle(dots[i].x, dots[i].y, 20);
let goalDot = dots.find(e => e.id == orderData[i]);
if (goalDot) {
line(dots[i].x, dots[i].y, goalDot.x, goalDot.y);
}
}
}
function createDots(filename) {
const probData = new Array(filename.length);
for (let i = 0; i < probData.length; i++) {
dotsData[i] = splitTokens(filename[i]);
dots.push({
id: dotsData[i][0],
x: dotsData[i][1],
y: dotsData[i][2]
})
}
}
The dotsData file looks like
1 565.0 575.0
2 25.0 185.0
3 345.0 750.0
4 945.0 685.0
5 845.0 655.0
The orderData file looks like this
5
1
4
3
2
Compute the maximum coordinates of the geometry (points), after reading from file:
let max_x = 0;
let max_y = 0;
function createDots(filename) {
const probData = new Array(filename.length);
for (let i = 0; i < probData.length; i++) {
dotsData[i] = splitTokens(filename[i]);
dots.push({
id: dotsData[i][0],
x: dotsData[i][1],
y: dotsData[i][2]
})
max_x = max(max_x, dotsData[i][1]);
max_y = max(max_y, dotsData[i][2]);
}
}
Before drawing, set a scale() depending on the width and the height of the canvas:
function draw() {
background(220);
fill(255, 100, 200);
scale(width / (max_x * 1.1), height / (max_y * 1.1));
for (let i = 0; i < dots.length; i++) {
circle(dots[i].x, dots[i].y, 20);
let goalDot = dots.find(e => e.id == orderData[i]);
if (goalDot) {
line(dots[i].x, dots[i].y, goalDot.x, goalDot.y);
}
}
}

P5js: why is my bg framerate not refreshing properly?

I have 2 sets of code in here. One is the simple bounce off code. The other is a function. I've tried to make it a function but it doesn't seem to be working properly.
The bg framerate doesn't clear in the sense that a string of balls show rather than a ball bouncing and animating.
if(this.y_pos > 400) this condition doesn't seem to be working even tho it works when it is drawn in the draw function.
var sketch = function (p) {
with(p) {
p.setup = function() {
createCanvas(800, 600);
// x_pos = 799;
// y_pos = 100;
// spdx = -random(5,10);
// spdy = random(12,17);
};
p.draw = function() {
background(0);
// fill(255);
// ellipse(x_pos,y_pos,50);
// x_pos += spdx;
// y_pos += spdy;
// if(y_pos > 400)
// {
// spdy *= -1;
// }
// for( var i = 0; i < 10; i++)
avalance(799, 100, random(5,10), random(12,17));
};
function avalance(x, y, spdx, spdy)
{
this.x_pos = x;
this.y_pos = y;
this.spdx = spdx;
this.spdy = spdy;
this.x_pos = 799;
this.y_pos = 100;
this.spdx = 1;
this.spdy = 1;
this.movement = function()
{
this.x_pos += -spdx;
this.y_pos += spdy;
if(this.y_pos > 400)
{
this.spdy *= -1;
}
}
this.draw = function()
{
this.movement();
this.drawnRox();
}
this.drawnRox = function()
{
fill(255);
ellipse(this.x_pos,this.y_pos,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>
Let's address both issues:
The draw() function is called for each new frame and in it you call avalance which creates a new ball. To fix that you need to
Create a global variable let ball; out of setup() and draw() so that you can reuse that later;
In setup create a new ball and assign it to your ball variable: ball = new avalance(799, 100, random(5,10), random(12,17));
In draw() you want to update the ball and that's what its own draw() function does (I would advise renaming it update() for example, to avoid confusion with the p5 specific draw() function). So you just need to call ball.draw() in draw().
This creates a ball which moves but still don't respect your 400px limit.
The issue is that in movement() you add spdx and spdy to the position but when the ball crosses the limit you update this.spdy, so you need to update the function with this code:
this.x_pos += -this.spdx;
this.y_pos += this.spdy;
And you should be good! Here is a code pend with your code working as you intend.
Also as a bonus advise: You probably want to use some p5.Vector objects to store positions, speeds and accelerations it really makes your code easier to read and to use. You could also rename your function Avalance (capital A) to show that you actually use a class and that this function shouldn't be called without new.
As #statox suggested, do new avalance(799, 100, random(5,10), random(12,17)) in the setup() section and call draw of the ball in draw() section.
You can test the code below by clicking "Run code snippet".
var sketch = function (p) {
with(p) {
var ball;
p.setup = function() {
createCanvas(800, 600);
ball = new avalance(799, 100, random(5,10), random(12,17));
};
p.draw = function() {
background(0);
ball.draw();
};
function avalance(x, y, spdx, spdy)
{
function movement ()
{
x += -spdx;
y += spdy;
if (y > height || y < 0)
{
spdy *= -1;
}
if (x > width || x < 0)
{
spdx *= -1;
}
}
this.draw = function()
{
movement();
drawnRox();
}
function drawnRox ()
{
fill(255);
ellipse(x,y,50);
}
}
}
};
let node = document.createElement('div');
window.document.getElementById('p5-container').appendChild(node);
new p5(sketch, node);
body {
background-color:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
<div id="p5-container"></div>

I am having a bug with the enemies and when they reach your character in a small game I made

The bug happens when you shoot and die at the same time, and in the console it pops up as "Uncaught TypeError: Cannot read property 'draw' of undefined. Line 65"
The thing is, the bug started happening when I included the function that kills the player, which starts on line 143, and the thing that breaks it is the single line
enemy = [];
Here's the enemy function
function Enemy(x, y) {
this.x = x;
this.y = y;
this.draw = function() {
noStroke();
fill(255, 0, 0);
rect(this.x, this.y, 20, 20);
}
this.move = function() {
this.x -= movement; }
this.offscreen = function() {
if(this.x < 0) {
return true;
} else {
return false;
}
}
this.contact = function() {
for(let i = 0; i < enemy.length; i++) {
var d = dist(playerx, playery, enemy[i].x, enemy[i].y);
if(d <= 20) {
this.kill();
}
}
}
this.kill = function() {
var prevScore = score;
playerx = width / 10;
playery = height / 2;
alert("You died! your score was: " + prevScore);
fire = [];
enemy = [];
score = 0
}
}
Here's where it says it's getting the type error (which worked perfectly before)
This is lines 55 - 74
for(let i = fire.length - 1; i > 0; i--) {
fire[i].draw();
fire[i].move();
fire[i].check();
if(fire[i].offscreen()) {
fire.splice(i, 1);
}
}
for(let i = enemy.length - 1; i > 0; i--) {
enemy[i].draw();
enemy[i].move();
if(enemy[i].offscreen()) {
enemy.splice(i, 1);
}
if(enemy[i].contact()) {
enemy[i].kill();
}
}
The entire code is here: https://code.sololearn.com/Wtza5vElEZ9d/?ref=app
(It's less than 200 lines so not really big)
As well as that bug, I am wanting to find out how it would be possible to make the game gradually get faster. I tried having the frameCount be divided/modulos by a variable (that I had called spawnRate) but when I altered the variable in any way, it just stopped spawning the squares all together.
It's also made with p5.js.
Your enemy.contact() function is used in if statement so you should return a boolean with that function.
this.contact = function() {
return dist(playerx, playery, this.x, this.y) <= 20;
}
It would be better if you make function death() in player object.
It will not be so confusing.

HTML5 spin circle

I am trying to spin a circle on a canvas, i think i got most of the code but i don't know whats wrong with the code, the circle is not spinning. Do i need to create another function in order to spin the circle?
any suggestions
http://jsfiddle.net/qY85C/1/
var angle = 0;
function convertToRadians(degree) {
return degree*(Math.PI/180);
}
function incrementAngle() {
angle++;
if(angle > 360) {
angle = 0;
}
}
var myColor = ["#ECD078","#D95B43"];
var myData = [50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50];
function getTotal(){
var myTotal = 0;
for (var j = 0; j < myData.length; j++) {
myTotal += myData[j];
}
return myTotal;
}
function drawColoredCircle() {
var lastend = 0;
var myTotal = getTotal();//160
for (var i = 0; i < myData.length; i++) {
ctxBg.fillStyle = myColor[i%2];
ctxBg.translate(canvasBg.width/2, canvasBg.width/2);
ctxBg.rotate(convertToRadians(angle));
ctxBg.translate(-canvasBg.width/2, -canvasBg.width/2);
ctxBg.beginPath();
ctxBg.moveTo(canvasBg.width/2,canvasBg.width/2);
ctxBg.arc(canvasBg.width/2,canvasBg.height/2,500,lastend,lastend+(Math.PI*2*(myData[i]/myTotal)),false);
ctxBg.lineTo(canvasBg.width/2,canvasBg.height/2);
ctxBg.fill();
lastend += Math.PI*2*(myData[i]/myTotal);
}
}
function loop(){
drawColoredCircle();
requestAnimFrame(loop);
}
You need to change the angle inside your loop
function loop(){
angle+=Math.PI/18000;
drawColoredCircle();
requestAnimFrame(loop);
}
Also, do you want to clear the canvas before drawing the circle?
function loop(){
ctxBg.clearRect(0,0,canvasBg.width,canvasBg.height);
angle+=Math.PI/18000;
drawColoredCircle();
requestAnimFrame(loop);
}

Categories