P5.js ellipse not following mouseX and Y - javascript

I am not sure why but the "invisible circle" is not align with the mouseX and Y and i ended up hard coding it. Anyone know why this happens?
If i just wrote (mouseX,mouseY) the ellipse will be around 300 units away from the cusor.
Is there any way fix it/ improve it?
Without setting exact value such as mouseX-300?
Thanks
*not much details i can think of for now.not much details i can think of for now.not much details i can think of for now.
let cubes = [];
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
backCol = color(243, 243, 243);
//background(backCol);
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
let xPos = map(i, 0, 9, 50, width - 50);
let yPos = map(j, 0, 9, 50, height - 50);
cubes.push(new Cubes(xPos, yPos));
}
}
}
function draw() {
background(backCol);
noFill();
for (let a = 0; a < cubes.length; a++) {
cubes[a].update();
}
}
class Cubes {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 30;
this.stroke = 70;
this.shift1 = color(96);
this.shift2 = color(244);
}
update() {
this.shape();
this.shift_Color();
this.Invisible_Circle();
}
Invisible_Circle () {
push();
stroke(10);
//noFill();
// translate(mouseX,mouseY);
ellipse(mouseX - 280,mouseY - 280,200);
pop();
}
shape() {
push();
stroke(this.stroke);
translate(this.x - width / 2, this.y - height / 2, 0);
this.magnetic()
box(this.size);
pop();
}
shift_Color() {
let distance = dist(mouseX, mouseY, this.x, this.y);
let shiftX = map(mouseX, 0, this.a, 0, 1.0);
let change = lerpColor(this.shift1, this.shift2, shiftX);
if (distance < this.a) {
fill(change);
} else {
noFill();
}
}
magnetic() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.size * 3) {
this.a = atan2(mouseY - this.y, mouseX - this.x);
rotateX(this.a);
rotateY(this.a);
} else {
rotateX(millis() / 1000);
rotateY(millis() / 1000);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

In WEBGL mode (createCanvas()) the center of the screen is by default (0, 0). Anyway, the top left coordinate of the window is still (0, 0). Hence you have to translate the mouse position, by (-width/2, -height/2):
ellipse(mouseX - width/2, mouseY - height/2, 200);
Furthermore Invisible_Circle should not a method of Cubes:
function Invisible_Circle () {
stroke(10);
//noFill();
ellipse(mouseX - width/2, mouseY - height/2, 200);
}
class Cubes {
constructor(x, y) {
// [...]
And has to be called after drawing the boxes:
function draw() {
background(backCol);
Invisible_Circle();
noFill();
for (let a = 0; a < cubes.length; a++) {
cubes[a].update();
}
Invisible_Circle();
}
See the example:
let cubes = [];
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
backCol = color(243, 243, 243);
//background(backCol);
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
let xPos = map(i, 0, 9, 50, width - 50);
let yPos = map(j, 0, 9, 50, height - 50);
cubes.push(new Cubes(xPos, yPos));
}
}
}
function draw() {
background(backCol);
Invisible_Circle();
noFill();
for (let a = 0; a < cubes.length; a++) {
cubes[a].update();
}
Invisible_Circle();
}
function Invisible_Circle () {
stroke(10);
//noFill();
ellipse(mouseX - width/2, mouseY - height/2, 200);
}
class Cubes {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 30;
this.stroke = 70;
this.shift1 = color(96);
this.shift2 = color(244);
}
update() {
this.shape();
this.shift_Color();
}
shape() {
push();
stroke(this.stroke);
translate(this.x - width / 2, this.y - height / 2, 0);
this.magnetic()
box(this.size);
pop();
}
shift_Color() {
let distance = dist(mouseX, mouseY, this.x, this.y);
let shiftX = map(mouseX, 0, this.a, 0, 1.0);
let change = lerpColor(this.shift1, this.shift2, shiftX);
if (distance < this.a) {
fill(change);
} else {
noFill();
}
}
magnetic() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.size * 3) {
this.a = atan2(mouseY - this.y, mouseX - this.x);
rotateX(this.a);
rotateY(this.a);
} else {
rotateX(millis() / 1000);
rotateY(millis() / 1000);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>

Related

p5: Using Sine wave in WEBGL to change y-axis

I have some spheres that I want to move straight on the x axis with only their y-axis changing based on the sine wave (you know the up and down yo-yo pattern). It is pretty simple in 2d but for some reason, in WEBGL, the angle changes in the sin function is not working for this.y.
let particles = [];
let quantity = 20;
function setup() {
createCanvas(300, 300, WEBGL);
for (let i = 0; i < quantity; i++) {
let particle = new Particle();
particles.push(particle);
}
}
function draw() {
background(15);
orbitControl();
directionalLight([255], createVector(0, 0, -1));
translate(-width / 2, 0);
particles.forEach(particle => {
particle.update();
particle.edges();
particle.show();
});
};
// particle.js
function Particle() {
this.angle = 0;
this.r = 10;
this.x = random(width);
this.y = sin(this.angle) * 200; // I am changing this angle later but it's not working
this.z = random(-50, 50);
this.pos = createVector(this.x, 0, 0);
this.vel = createVector(2, this.y, 0);
this.update = function() {
this.pos.add(this.vel);
this.vel.limit(4);
this.angle += 3;
// print(this.pos);
}
this.edges = function() {
if (this.pos.x >= width) {
this.pos.x = 0;
}
}
this.show = function() {
noStroke();
push();
translate(this.pos.x, this.pos.y, 0);
sphere(this.r);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
You only call sin one time in the construtor. You probably meant to apply it to this.y on every frame based on this.angle, or some other incrementing value. Also, there are duplicate vectors; pick either an x/y pair or use a vector x/y, but not both.
let particles = [];
let quantity = 20;
function setup() {
createCanvas(300, 300, WEBGL);
for (let i = 0; i < quantity; i++) {
let particle = new Particle();
particles.push(particle);
}
}
function draw() {
background(15);
orbitControl();
directionalLight([255], createVector(0, 0, -1));
translate(-width / 2, 0);
particles.forEach(particle => {
particle.update();
particle.edges();
particle.show();
});
};
// particle.js
function Particle() {
this.angle = 0;
this.r = 10;
this.x = random(width);
this.y = 0;
this.update = function() {
this.angle += 0.05;
this.y = sin(this.angle) * 100;
}
this.edges = function() {
if (this.x >= width) {
this.x = 0;
}
}
this.show = function() {
noStroke();
push();
translate(this.x, this.y, 0);
sphere(this.r);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>

How to reset CreateCanvas() in JS after being called

When I was writing the code I saw that the code would not reset the canvas to (400, 400) after being changed to (600, 600). It would disorientate the canvas and stretch all the shapes with it in evaporation(). When going through all the screens and trying to go back to reset back.
//Variables
let screen = 0;
let bugs = [];
let amt = 1000;
let xpos=0;
function setup() {
colorMode(HSB);
//Changes Colormode to HSB(Hue-Saturation-Brightness)
//Creates the particles for the evaporation screen
//For loop with increasement that states more of the smoke is created
for (let i = 0; i < amt; i++) {
let x = 200;
let y = 300;
let rad = random(10, 50);
let b = new Bug(x, y, rad);
bugs[i] = b;
}
}
//Says if the value of screen changes so does the screen
function draw() {
if(screen == 0) {
evaporationscreen();
} else if(screen == 1) {
condensation();
} else if(screen == 2) {
presipitation();
}
}
//Evaporation
function evaporationscreen() {
background('#d2b48c');
createCanvas(400, 400);
//NOT RGB, HSB COLOR PALLETE(DO NOT MIX UP);
//HTML Color pallete('e928320');
noStroke();
fill('#3895d3') //Lake
circle(400, 400, 600)
fill('#18bdf0') //Sky
rect(0, 0, 400, 200);
fill('#ffff00'); //Sun
circle(300, 60, 70);
fill('#e9bd15'); //Darkspots on Sun
circle(315, 75, 10);//Forms the Sun and Darkspots
circle(280, 52, 8);//Forms the Sun and Darkspots
circle(299, 60, 13);//Forms the Sun and Darkspots
circle(320, 48, 9);//Forms the Sun and Darkspots
circle(295, 30, 6);//Forms the Sun and Darkspots
circle(284, 79, 9);//Forms the Sun and Darkspots
fill(240, 100, 50);//Forms the Sun and Darkspots
rect(0, 200, 400, 400); //Beach
console.log("EVAPORATION");
//Lots of variables
let stoprip = 45;
let lake = int(dist(400, 400, mouseX, mouseY));
var starx = random(0, 400);
var stary = random (10, 190);
var sun = int(dist(300, 60, mouseX, mouseY));
//If Statement that creates ripples
if (mouseIsPressed && lake < 300) {
if (mouseY >= 200) {
noFill();
stroke('#3895d3');
strokeWeight(4);
circle(mouseX, mouseY, ripple);
ripple = ripple + 5;
console.log('lake rippling');
if (ripple >= stoprip) {
ripple = 10
}
} else {
ripple = 10
}
} else if (mouseIsPressed && sun <=35) {
fill('#fee12b');
circle(starx, stary, 10);
console.log('stars');
}
background(50, 0.25);
//For loop that creates the smoke effect with an increasement
for (let i = 0; i < bugs.length; i++) {
bugs[i].show();
bugs[i].move();
if( bugs[i].radius > 100 ) {
bugs.splice(i, 1);
}
}
let x = 200;
let y = 300;
let rad = random(10, 50);
let b = new Bug(x, y, rad);
bugs.push(b);
}
//Particles for evaporation
class Bug {
constructor(tempX, tempY, tempRadius) {
this.x = tempX;
this.y = tempY;
this.radius = tempRadius;
this.color = color(random(80,100), 0.05);
}
//Creates the sketch for it
show() {
noStroke();
fill(this.color);
ellipse(this.x, this.y, this.radius);
}
//Moves it up at a random pace
move() {
this.x = this.x + random(-5, 5);
this.y = this.y - random(2);
this.radius = this.radius + 0.4;
}
}
function condensation() {
//Changes the canvas from 400,400 to 600,600
createCanvas(600, 600);
//Changes background to grey to imitate storm,
background(50);
console.log("CONDENSATION");
//Colors the cloud and fills it with white
noStroke();
fill(255);
//States where cloud spawns
cloud(xpos,50,1);
cloud(xpos,50,2);
cloud(xpos,80,0.75);
xpos++;
//Has cloud with an increasement
if (xpos>600) {
xpos=0;
}
}
function presipitation() {
createCanvas(600, 600);
ellipseMode(RADIUS);
noFill();
background(211, 69, 93);
console.log("PRESIPITATION")
//If statement that create a for loop that spawns new particles(rain)
if (particles.length < 200) particles.push(new Particle());
for (var i = 0; i < particles.length; i++) {
particles[i].update();
particles[i].display();
}
}
var particles = [];
//Variable
//Rain drop / Particle
class Particle {
constructor() {
this.reset();
}
//Resets Particle after update
reset() {
this.x = random(width);
this.y = random(-150, 0);
this.vy = random(0.1, 2);
this.maxy = this.y + height;
this.r = 0;
this.tr = 50;
this.w = random(0.1, 2);
}
//If statement that creates the gravity for the particle
update() {
if (this.y < this.maxy) {
this.y += this.vy;
} else {
this.r++;
}
if (this.r > this.tr) this.reset();
}
//If statement(says it will remain the raindrop form or change into the puddle
display() {
strokeWeight(this.w);
if (this.y < this.maxy) {
stroke(255);
push();
translate(this.x,this.y);
beginShape();
strokeWeight(1);
vertex(0,-5);
quadraticVertex(3, 0, 0, 1);
quadraticVertex(-3,0, 0, -5);
endShape(CLOSE);
pop();
} else {
stroke(255, map(this.r, 0, this.tr, 255, 0));
ellipse(this.x, this.y, this.r, this.r*.5);
}
}
}
//Changes Screen from evaporation, condensation, precipitation
function mousePressed(){
if(screen==0){
screen=1;
}else if(screen==1){
screen = 2;
} else if(screen == 2) {
screen = 0;
}
}
//Makes cloud
function cloud(x,y,sc){
push()
scale(sc);
ellipse(x,y,50,30);
ellipse(x,y+20,80,30);
ellipse(x+20,y,40,30);
ellipse(x+30,y+10,70,40);
pop()
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sketch</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="libraries/p5.min.js"></script>
<script src="libraries/p5.sound.min.js"></script>
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
From the documentation for createCanvas:
Creates a canvas element in the document, and sets the dimensions of it in pixels. This method should be called only once at the start of setup. Calling createCanvas more than once in a sketch will result in very unpredictable behavior.
Instead of calling createCanvas repeatedly in your drawing functions, you should use resizeCanvas once when transitioning from one screen to another.
I couldn't actually reproduce whatever issue you were describing (partly because I could not make sense of your description). However I did also notice an issue with the variable ripple not being declared anywhere, so I fixed that, and now the sketch appears to be working correctly.
//Variables
let previousScreen = 0;
let screen = 0;
let bugs = [];
let amt = 1000;
let xpos = 0;
let ripple = 0;
function setup() {
createCanvas(400, 400);
colorMode(HSB);
//Changes Colormode to HSB(Hue-Saturation-Brightness)
//Creates the particles for the evaporation screen
//For loop with increasement that states more of the smoke is created
for (let i = 0; i < amt; i++) {
let x = 200;
let y = 300;
let rad = random(10, 50);
let b = new Bug(x, y, rad);
bugs[i] = b;
}
}
//Says if the value of screen changes so does the screen
function draw() {
if (screen == 0) {
if (previousScreen !== screen) {
resizeCanvas(400, 400);
previousScreen = screen;
}
evaporationscreen();
} else if (screen == 1) {
if (previousScreen !== screen) {
resizeCanvas(600, 600);
previousScreen = screen;
}
condensation();
} else if (screen == 2) {
if (previousScreen !== screen) {
resizeCanvas(600, 600);
previousScreen = screen;
}
presipitation();
}
}
//Evaporation
function evaporationscreen() {
background('#d2b48c');
//NOT RGB, HSB COLOR PALLETE(DO NOT MIX UP);
//HTML Color pallete('e928320');
noStroke();
fill('#3895d3') //Lake
circle(400, 400, 600)
fill('#18bdf0') //Sky
rect(0, 0, 400, 200);
fill('#ffff00'); //Sun
circle(300, 60, 70);
fill('#e9bd15'); //Darkspots on Sun
circle(315, 75, 10); //Forms the Sun and Darkspots
circle(280, 52, 8); //Forms the Sun and Darkspots
circle(299, 60, 13); //Forms the Sun and Darkspots
circle(320, 48, 9); //Forms the Sun and Darkspots
circle(295, 30, 6); //Forms the Sun and Darkspots
circle(284, 79, 9); //Forms the Sun and Darkspots
fill(240, 100, 50); //Forms the Sun and Darkspots
rect(0, 200, 400, 400); //Beach
console.log("EVAPORATION");
//Lots of variables
let stoprip = 45;
let lake = int(dist(400, 400, mouseX, mouseY));
var starx = random(0, 400);
var stary = random(10, 190);
var sun = int(dist(300, 60, mouseX, mouseY));
//If Statement that creates ripples
if (mouseIsPressed && lake < 300) {
if (mouseY >= 200) {
noFill();
stroke('#3895d3');
strokeWeight(4);
circle(mouseX, mouseY, ripple);
ripple = ripple + 5;
console.log('lake rippling');
if (ripple >= stoprip) {
ripple = 10
}
} else {
ripple = 10
}
} else if (mouseIsPressed && sun <= 35) {
fill('#fee12b');
circle(starx, stary, 10);
console.log('stars');
}
background(50, 0.25);
//For loop that creates the smoke effect with an increasement
for (let i = 0; i < bugs.length; i++) {
bugs[i].show();
bugs[i].move();
if (bugs[i].radius > 100) {
bugs.splice(i, 1);
}
}
let x = 200;
let y = 300;
let rad = random(10, 50);
let b = new Bug(x, y, rad);
bugs.push(b);
}
//Particles for evaporation
class Bug {
constructor(tempX, tempY, tempRadius) {
this.x = tempX;
this.y = tempY;
this.radius = tempRadius;
this.color = color(random(80, 100), 0.05);
}
//Creates the sketch for it
show() {
noStroke();
fill(this.color);
ellipse(this.x, this.y, this.radius);
}
//Moves it up at a random pace
move() {
this.x = this.x + random(-5, 5);
this.y = this.y - random(2);
this.radius = this.radius + 0.4;
}
}
function condensation() {
//Changes background to grey to imitate storm,
background(50);
console.log("CONDENSATION");
//Colors the cloud and fills it with white
noStroke();
fill(255);
//States where cloud spawns
cloud(xpos, 50, 1);
cloud(xpos, 50, 2);
cloud(xpos, 80, 0.75);
xpos++;
//Has cloud with an increasement
if (xpos > 600) {
xpos = 0;
}
}
function presipitation() {
ellipseMode(RADIUS);
noFill();
background(211, 69, 93);
console.log("PRESIPITATION")
//If statement that create a for loop that spawns new particles(rain)
if (particles.length < 200) particles.push(new Particle());
for (var i = 0; i < particles.length; i++) {
particles[i].update();
particles[i].display();
}
}
var particles = [];
//Variable
//Rain drop / Particle
class Particle {
constructor() {
this.reset();
}
//Resets Particle after update
reset() {
this.x = random(width);
this.y = random(-150, 0);
this.vy = random(0.1, 2);
this.maxy = this.y + height;
this.r = 0;
this.tr = 50;
this.w = random(0.1, 2);
}
//If statement that creates the gravity for the particle
update() {
if (this.y < this.maxy) {
this.y += this.vy;
} else {
this.r++;
}
if (this.r > this.tr) this.reset();
}
//If statement(says it will remain the raindrop form or change into the puddle
display() {
strokeWeight(this.w);
if (this.y < this.maxy) {
stroke(255);
push();
translate(this.x, this.y);
beginShape();
strokeWeight(1);
vertex(0, -5);
quadraticVertex(3, 0, 0, 1);
quadraticVertex(-3, 0, 0, -5);
endShape(CLOSE);
pop();
} else {
stroke(255, map(this.r, 0, this.tr, 255, 0));
ellipse(this.x, this.y, this.r, this.r * .5);
}
}
}
//Changes Screen from evaporation, condensation, precipitation
function mousePressed() {
if (screen == 0) {
screen = 1;
} else if (screen == 1) {
screen = 2;
} else if (screen == 2) {
screen = 0;
}
}
//Makes cloud
function cloud(x, y, sc) {
push();
scale(sc);
ellipse(x, y, 50, 30);
ellipse(x, y + 20, 80, 30);
ellipse(x + 20, y, 40, 30);
ellipse(x + 30, y + 10, 70, 40);
pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>

Matter.js collision not detecting after setting velocity

var Engine = Matter.Engine,
World = Matter.World,
Bodies = Matter.Bodies,
Body = Matter.Body;
var ground;
var engine;
var player = [];
function setup() {
createCanvas(400, 400);
engine = Engine.create();
world = engine.world;
Engine.run(engine)
var options = {
isStatic: true
}
engine.world.gravity.y = 0
my = new Cell(200, 200, 32)
ground = Bodies.rectangle(200, height, width, 20, options)
World.add(world, ground)
// engine.world.gravity.y = 0;
console.log(player)
}
function keyPressed() {
player.push(new Cell(mouseX, mouseY, 32));
}
function draw() {
background(0);
my.show();
for (var i = 0; i < player.length; i++) {
player[i].show();
}
}
function Cell(x, y, r) {
this.body = Matter.Bodies.circle(x, y, r, r);
// World.add(world,this.body);
this.r = r;
World.add(world, this.body)
// player[player.length] = this;
this.show = function() {
var pos = this.body.position;
Body.setVelocity(this.body, {
x: mouseX - pos.x,
y: mouseY - pos.y
})
push();
translate(pos.x, pos.y)
// noStroke()
ellipseMode(CENTER);
ellipse(0, 0, this.r * 2, this.r * 2)
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.12.0/matter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
If I comment out Body.setVelocity, then collision works fine but not when using Body.setVelocity. Code above is working, please help me with broken collision detection. You can check collision problem by pressing keys more than 4 times.
It is a bit unclear what you try to achieve. But the code
Body.setVelocity(this.body, {
x: mouseX - pos.x,
y: mouseY - pos.y
})
in show, sets a velocity to the body, which is equal the distance of the mouse position to the body, repeatedly in every frame. This causes that each body moves too the mouse position immediately and breaks down the collision detection.
You have to modify the actual velocity of the body, by a certain value, witch depends on the distance of the bod to the mouse position. e.g.:
vx = this.body.velocity.x + (mouseX - pos.x) * 0.001
vy = this.body.velocity.y + (mouseY - pos.y) * 0.001
Body.setVelocity(this.body, {x: vx, y: vy } )
var Engine = Matter.Engine,
World = Matter.World,
Bodies = Matter.Bodies,
Body = Matter.Body;
var ground;
var engine;
var player = [];
function setup() {
createCanvas(400, 400);
engine = Engine.create();
world = engine.world;
Engine.run(engine)
var options = {
isStatic: true
}
engine.world.gravity.y = 0
my = new Cell(200, 200, 32)
ground = Bodies.rectangle(200, height, width, 20, options)
World.add(world, ground)
// engine.world.gravity.y = 0;
console.log(player)
}
function keyPressed() {
player.push(new Cell(mouseX, mouseY, 32));
}
function draw() {
background(0);
my.show();
for (var i = 0; i < player.length; i++) {
player[i].show();
}
}
function Cell(x, y, r) {
this.body = Matter.Bodies.circle(x, y, r, r);
// World.add(world,this.body);
this.r = r;
World.add(world, this.body)
// player[player.length] = this;
this.show = function() {
var pos = this.body.position;
vx = this.body.velocity.x + (mouseX - pos.x) * 0.001
vy = this.body.velocity.y + (mouseY - pos.y) * 0.001
Body.setVelocity(this.body, {x: vx, y: vy } )
push();
translate(pos.x, pos.y)
// noStroke()
ellipseMode(CENTER);
ellipse(0, 0, this.r * 2, this.r * 2)
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.12.0/matter.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>

p5.js How to stop rotation of certain elements

I am drawing some rotating rectangles in p5.js, and it is all working fine. Then I need some text to be displayed on top of rectangles and I want it to be motionless. However, the text is spinning as well. I have tried some solutions like setting rotate(0) or value negative to the rotation of last drawn rectangle, but nothing works.
class Rectangle {
constructor(size, colorR, colorG, colorB, angle) {
this.size = size;
this.colorR = colorR;
this.colorG = colorG;
this.colorB = colorB;
this.angle = angle;
this.actualAngle = this.angle;
}
draw() {
stroke(0);
strokeWeight(2);
fill(this.colorR, this.colorG, this.colorB);
rotate(this.actualAngle);
rect(0, 0, this.size, this.size);
}
increaseAngle() {
this.actualAngle += this.angle;
}
getActualAngle(){
return this.actualAngle;
}
}
var rectangles = [];
function setup() {
var canvas1 = createCanvas(800, 700);
canvas1.parent("regLog");
var size = 12;
for (var i = 0; i < 10; i++) {
let ranR = random(150);
let ranG = random(150);
let ranB = random(150);
let angle = random(-0.005, 0.005);
rectangles.push(new Rectangle(size, ranR, ranG, ranB, angle));
size += 50;
}
}
function draw() {
background(255);
rectMode(CENTER);
translate(width / 2, height / 2);
for (var i = rectangles.length - 1; i > 0; i--) {
rectangles[i].draw();
}
if (second() % 1 == 0) {
for (let i = 0; i < rectangles.length; i++) {
rectangles[i].increaseAngle();
}
}
rotate(0);
noStroke();
fill(255, 255, 255, 0);
rect(0, 0, 50, 50);
textAlign(CENTER);
fill(255);
textSize(60);
text("Coming soon!", 0, 0);
}
I used push() to "save" the current rotation settings and then pop() to restore them after each time you draw a rectangle. When you use rotate(), it rotates the all of the future shapes you draw, so the changes are "permanent" unless you're very careful to reset.
You can see it work in this jsfiddle -> https://jsfiddle.net/ozsLtxhb/237/
i commented out the part where you assign a parent just for the jsfiddle to work so uncomment it before you copy-paste it into anything
class Rectangle {
constructor(size, colorR, colorG, colorB, angle) {
this.size = size;
this.colorR = colorR;
this.colorG = colorG;
this.colorB = colorB;
this.angle = angle;
this.actualAngle = this.angle;
}
draw() {
/*push here*/
push();
stroke(0);
strokeWeight(2);
fill(this.colorR, this.colorG, this.colorB);
rotate(this.actualAngle);
rect(0, 0, this.size, this.size);
/*pop here*/
pop();
}
increaseAngle() {
this.actualAngle += this.angle;
}
getActualAngle(){
return this.actualAngle;
}
}
var rectangles = [];
function setup() {
var canvas1 = createCanvas(800, 700);
/* canvas1.parent("regLog"); */
var size = 12;
for (var i = 0; i < 10; i++) {
let ranR = random(150);
let ranG = random(150);
let ranB = random(150);
let angle = random(-0.005, 0.005);
rectangles.push(new Rectangle(size, ranR, ranG, ranB, angle));
size += 50;
}
}
function draw() {
background(255);
rectMode(CENTER);
translate(width / 2, height / 2);
for (var i = rectangles.length - 1; i > 0; i--) {
rectangles[i].draw();
}
if (second() % 1 == 0) {
for (let i = 0; i < rectangles.length; i++) {
rectangles[i].increaseAngle();
}
}
rotate(0);
noStroke();
fill(255, 255, 255, 0);
rect(0, 0, 50, 50);
textAlign(CENTER);
fill(255);
textSize(60);
text("Coming soon!", 0, 0);
}

Why does the ball in pong get stuck at the bottom?

I recently made a JS Pong game. It works well, but the ball rarely gets stuck at the bottom or top. It looks like it is halfway through the wall and constantly bouncing. Video of the issue happening. You can try the game here. I do not know why this issue is happening because the logic seems right and works 90% of the time correctly. Here are the main two functions of my program:
function moveAll() {
if (showingWinScreen) {
return;
}
computerMovement();
ballX += ballSpeedX;
ballY += ballSpeedY;
if (ballY <= 10) {
ballSpeedY = -ballSpeedY;
} else if (ballY >= HEIGHT - 10) {
ballSpeedY = -ballSpeedY;
}
if (ballX >= WIDTH - 10) {
if ((ballY > paddleY) && (ballY < paddleY + 100)) {
ballSpeedX = -ballSpeedX;
var deltaY = ballY - paddleY - 50;
ballSpeedY = deltaY / 5;
} else {
player1Score++;
ballReset();
}
} else if (ballX <= 10) {
if ((ballY > mouseY - 50) && (ballY < mouseY + 50)) {
ballSpeedX = -ballSpeedX;
deltaY = ballY - mouseY;
ballSpeedY = deltaY / 6;
} else {
player2Score++;
ballReset();
}
}
}
function drawAll() {
if (showingWinScreen) {
colorRect(0, 0, WIDTH, HEIGHT, "black");
canvas.fillStyle = "yellow";
canvas.fillText("Click to continue!", 300, 300);
if (player1Score == WINNING_SCORE) {
canvas.fillText("You won!", 360, 500);
} else if (player2Score == WINNING_SCORE) {
canvas.fillText("The computer beat you!", 280, 500);
}
return;
}
colorRect(0, 0, WIDTH, HEIGHT, "black");
drawNet();
makeCircle(ballX, ballY, 10, 0, Math.PI * 2, "red");
colorRect(790, paddleY, 10, 100, "cyan");
colorRect(0, mouseY - 50, 10, 100, "yellow");
canvas.fillStyle = "white";
canvas.fillText(player1Score + " " + player2Score, 360, 100);
}
Thank you for your help!
I think there's only one case in which this could happen: when, in a colliding frame, you decrease the speed.
When the speed remains the same, no matter what, your ball will always bounce back to the previous' frames position:
var cvs = document.querySelector("canvas");
var ctx = cvs.getContext("2d");
var balls = [
Ball(50, 50, 0, 5, 5, "red"),
Ball(100, 50, 0, 5, 10, "blue"),
Ball(150, 50, 0, 5, 15, "green"),
Ball(200, 50, 0, 5, 20, "yellow")
];
var next = () => {
updateFrame(balls);
drawFrame(balls);
}
var loop = () => {
requestAnimationFrame(() => {
next();
loop();
});
}
next();
function Ball(x, y, vx, vy, r, color) {
return {
x: x,
y: y,
vx: vx,
vy: vy,
r: r,
color: color
}
};
function updateBall(b) {
b.x += b.vx;
b.y += b.vy;
if (b.y <= b.r ||
b.y >= cvs.height - b.r) {
b.vy *= -1;
}
};
function drawBall(b) {
ctx.beginPath();
ctx.fillStyle = b.color;
ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI, false);
ctx.fill();
}
function updateFrame(balls) {
balls.forEach(updateBall);
}
function drawFrame(balls) {
ctx.clearRect(0, 0, cvs.width, cvs.height);
balls.forEach(drawBall);
};
<canvas width="300" height="150" style="background: #454545"></canvas>
<button onclick="next()">next</button>
<button onclick="loop()">run</button>
But when the speed changes, things get stuck:
var cvs = document.querySelector("canvas");
var ctx = cvs.getContext("2d");
var balls = [
Ball(50, 50, 0, 10, 5, "red"),
Ball(100, 50, 0, 10, 10, "blue"),
Ball(150, 50, 0, 10, 15, "green"),
Ball(200, 50, 0, 10, 20, "yellow")
];
var next = () => {
updateFrame(balls);
drawFrame(balls);
}
var loop = () => {
requestAnimationFrame(() => {
next();
loop();
});
}
next();
function Ball(x, y, vx, vy, r, color) {
return {
x: x,
y: y,
vx: vx,
vy: vy,
r: r,
color: color
}
};
function updateBall(b) {
b.x += b.vx;
b.y += b.vy;
if (b.y <= b.r ||
b.y >= cvs.height - b.r) {
b.vy *= -0.5;
}
};
function drawBall(b) {
ctx.beginPath();
ctx.fillStyle = b.color;
ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI, false);
ctx.fill();
}
function updateFrame(balls) {
balls.forEach(updateBall);
}
function drawFrame(balls) {
ctx.clearRect(0, 0, cvs.width, cvs.height);
balls.forEach(drawBall);
};
<canvas width="300" height="150" style="background: #454545"></canvas>
<button onclick="next()">next</button>
<button onclick="loop()">run</button>
In your case, I'm thinking this can only happen when there's a paddle collision AND a wall collision simultaneously.
A quick-to-implement solution would be to check if the new position is valid before translating the ball position. If you don't want the precise location, you can place the ball at the point of collision. Note that this will produce a slightly off frame.
E.g.:
var newY = ballY + ballSpeedY;
// Top wall
if(newY <= 10) {
ballY = 10;
ballSpeedY = -ballSpeedY;
}
// Bottom wall
else if(newY >= HEIGHT-10){
ballY = HEIGHT - 10;
ballSpeedY = -ballSpeedY;
}
// No collision
else {
ballY = newY;
}
Update: a more detailed description of what can happen
Let's say your ball collides with the top border of your canvas and with your paddle in the same frame.
First, you move the ball to the colliding position: ballY += ballSpeedY; Say your ballY is 4, and your ballSpeedY is -5, you'll position the ball to -1, inside the wall.
If this were to be the only collision, you should be okay. You flip the speed (ballSpeedY = -ballSpeedY), so in the next frame, your ball should be back at -1 + 5 = 4, so ballY will be 4 again, and your ball will move towards 4 + 5 = 9 in the next frame.
Now a problem arises, when in the -1 positioned frame, you collide with the paddle as well! When the paddle hits the ball, you modify the ballspeed: ballSpeedY = deltaY / 5;. If this turns out to be < 1, your ball won't be able to exit the wall in the next frame. Instead of -1 + 5 = 4, your ball will, for example, move to: -1 + 0.5 = -0.5.
Now, your ball won't be able to get back in to play, since the next frame will, again, calculate a collision and flip the speed. This results in the bouncy, trembling effect you see when the ball gets stuck.
A naive but pretty decent solution, is to only update the position of the ball to a valid position. I.e.: never to a colliding coordinate.
var animate = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / 60)
};
var canvas = document.createElement("canvas");
var width = 400;
var height = 600;
canvas.width = width;
canvas.height = height;
var context = canvas.getContext('2d');
var player = new Player();
var computer = new Computer();
var ball = new Ball(200, 300);
var keysDown = {};
var render = function () {
context.fillStyle = "#FF00FF";
context.fillRect(0, 0, width, height);
player.render();
computer.render();
ball.render();
};
var update = function () {
player.update();
computer.update(ball);
ball.update(player.paddle, computer.paddle);
};
var step = function () {
update();
render();
animate(step);
};
function Paddle(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.x_speed = 0;
this.y_speed = 0;
}
Paddle.prototype.render = function () {
context.fillStyle = "#0000FF";
context.fillRect(this.x, this.y, this.width, this.height);
};
Paddle.prototype.move = function (x, y) {
this.x += x;
this.y += y;
this.x_speed = x;
this.y_speed = y;
if (this.x < 0) {
this.x = 0;
this.x_speed = 0;
} else if (this.x + this.width > 400) {
this.x = 400 - this.width;
this.x_speed = 0;
}
};
function Computer() {
this.paddle = new Paddle(175, 10, 50, 10);
}
Computer.prototype.render = function () {
this.paddle.render();
};
Computer.prototype.update = function (ball) {
var x_pos = ball.x;
var diff = -((this.paddle.x + (this.paddle.width / 2)) - x_pos);
if (diff < 0 && diff < -4) {
diff = -5;
} else if (diff > 0 && diff > 4) {
diff = 5;
}
this.paddle.move(diff, 0);
if (this.paddle.x < 0) {
this.paddle.x = 0;
} else if (this.paddle.x + this.paddle.width > 400) {
this.paddle.x = 400 - this.paddle.width;
}
};
function Player() {
this.paddle = new Paddle(175, 580, 50, 10);
}
Player.prototype.render = function () {
this.paddle.render();
};
Player.prototype.update = function () {
for (var key in keysDown) {
var value = Number(key);
if (value == 37) {
this.paddle.move(-4, 0);
} else if (value == 39) {
this.paddle.move(4, 0);
} else {
this.paddle.move(0, 0);
}
}
};
function Ball(x, y) {
this.x = x;
this.y = y;
this.x_speed = 0;
this.y_speed = 3;
}
Ball.prototype.render = function () {
context.beginPath();
context.arc(this.x, this.y, 5, 2 * Math.PI, false);
context.fillStyle = "#000000";
context.fill();
};
Ball.prototype.update = function (paddle1, paddle2) {
this.x += this.x_speed;
this.y += this.y_speed;
var top_x = this.x - 5;
var top_y = this.y - 5;
var bottom_x = this.x + 5;
var bottom_y = this.y + 5;
if (this.x - 5 < 0) {
this.x = 5;
this.x_speed = -this.x_speed;
} else if (this.x + 5 > 400) {
this.x = 395;
this.x_speed = -this.x_speed;
}
if (this.y < 0 || this.y > 600) {
this.x_speed = 0;
this.y_speed = 3;
this.x = 200;
this.y = 300;
}
if (top_y > 300) {
if (top_y < (paddle1.y + paddle1.height) && bottom_y > paddle1.y && top_x < (paddle1.x + paddle1.width) && bottom_x > paddle1.x) {
this.y_speed = -3;
this.x_speed += (paddle1.x_speed / 2);
this.y += this.y_speed;
}
} else {
if (top_y < (paddle2.y + paddle2.height) && bottom_y > paddle2.y && top_x < (paddle2.x + paddle2.width) && bottom_x > paddle2.x) {
this.y_speed = 3;
this.x_speed += (paddle2.x_speed / 2);
this.y += this.y_speed;
}
}
};
document.body.appendChild(canvas);
animate(step);
window.addEventListener("keydown", function (event) {
keysDown[event.keyCode] = true;
});
window.addEventListener("keyup", function (event) {
delete keysDown[event.keyCode];
});
http://jsfiddle.net/kHJr6/2/

Categories