When a user clicks on any particle I want it to expand and fade and upon collision with any other particle and that particle will also expand and fade. Now my problem is that I want to know if there is a way in which I can get those particles (made with constructor in this case) to effect each other when they get collide. Link to Codepen
var bubbles = [];
function setup() {
frameRate(25);
// Creates Canvas
createCanvas(windowWidth, windowHeight);
//Genrates 100 Particles with random a & y
for (var i = 0; i < 80; i++) {
var x = random(width);
var y = random(height);
bubbles[i] = new Bubble(x, y);
}
}
function mousePressed() {
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].clicked();
}
}
function draw() {
clear();
//Adds color and motion
for (var bubble of bubbles) {
fill(bubble.color.red, bubble.color.green, bubble.color.blue);
bubble.move();
bubble.display();
}
}
function Bubble(x, y) {
this.x = x;
this.y = y;
this.wh = 15;
this.speedX = random(1, 5);
this.speedY = random(1, 5);
//Individual Particle Creation
this.display = function() {
noStroke();
ellipse(this.x, this.y, this.wh, this.wh);
};
//Interactivity
this.clicked = function() {
var d = dist(this.x, this.y, mouseX, mouseY);
if (d < 8) {
this.wh = 100;
}
};
//Randomizes colors
this.color = {
red: random(255),
green: random(255),
blue: random(255)
};
//Particle Motion
this.move = function() {
//Motion in X direction
this.x += this.speedX;
//Bouncing back on X-axis
if (this.x > windowWidth || this.x < 0) {
this.speedX = -this.speedX;
}
//Motion in Y Direction
this.y += this.speedY;
//Bouncing back on Y-axis
if (this.y > windowHeight || this.y < 0) {
this.speedY = -this.speedY;
}
};
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
::-webkit-scrollbar{
display:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.10/p5.js"></script>
Use a nested for loop.
Step 1: Loop over the bubbles. Do this with a for loop.
Step 2: For each bubble, loop over the rest of the bubbles (if you're on bubble 4, start with bubble 5). Do this with another for loop inside the first one.
Step 3: Now that you have two bubbles, do the collision between them.
If you're having trouble getting that working, then please start smaller. Start with a simpler program that just shows two hard-coded bubbles and does collision detection between them.
Related
I have made a simple sketch where 4 circles are moving at a given speed. I want to change the fill color when any of the circles touches another circle.
I think the problem is that I am initializing the x and y values for ellipse in my constructor object which I need to update as the circle moves, but I am not sure how to do that.
// Declare objects
let bubble1;
let bubble2;
let bubble3;
let bubble4;
var centDistance;
function setup() {
createCanvas(windowWidth, windowHeight);
ellipseMode(CENTER);
// Create objects
bubble1 = new Bubble(50, 0, 2);
bubble2 = new Bubble(50, 2, 0);
bubble3 = new Bubble(50, -2, 0);
bubble4 = new Bubble(50, 0, -2);
}
function draw() {
background(0);
bubble1.display();
bubble2.display();
bubble1.move();
bubble2.move();
bubble3.display();
bubble3.move();
bubble4.display();
bubble4.move();
var d = dist(bubble1.x, bubble1.y, bubble2.x, bubble2.y);
if (d < bubble1.r + bubble2.r) {
bubble1.changeCol();
bubble2.changeCol();
}
}
class Bubble {
constructor(r, xSpeed, ySpeed) {
this.x = width / 2;
this.y = height / 2;
this.r = r;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.fillColor = color(255);
this.move = function() {
if (this.x > width / 2 + 200 || this.x < width / 2 - 200) {
this.xSpeed = -this.xSpeed;
}
if (this.y > height / 2 + 200 || this.y < height / 2 - 200) {
this.ySpeed = -this.ySpeed;
}
this.x += this.xSpeed;
this.y += this.ySpeed;
}
this.display = function() {
fill(this.fillColor);
stroke(255);
ellipse(this.x, this.y, 2 * this.r);
}
this.changeCol = function() {
this.fillColor = color(0);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js"></script>
The problem is that when the animation starts both circles are colliding, bubble1 and bubble2 in this example.
then you change the color to black inside your changeCol function and they both turn black and then stay that way indefinitely.
Your code is working the problem is that this.fillColor will not be re-initialized every re-render since you've instanciated your objects inside the setup function, so you have to re-paint the circles when they're not touching one another.
if (d < bubble1.r+bubble2.r) {
bubble1.changeCol(255);
bubble2.changeCol(255);
} else {
bubble1.changeCol(0);
bubble2.changeCol(0);
}
this.changeCol = function (col) {
this.fillColor = color(col);
}
I'm working my way through the Khan Academy advanced JS and I've encountered what I feel should be a simple problem, but I've just gotten entirely stuck on it. I'm working on a memory tile game, trying to add some extra features.
I'm trying to change the stroke colour of an object when the mouse is over it - I've included this as a prototype in each 'tile' object and this check is performed during the draw function - so every frame.
The stroke colour does change as I wanted it to, but stops working once I 'flip up' a couple of the tiles and they flip back down, the hover effect stops working. It now works only when a single tile is flipped up.
I can't figure out why the 'face up' variable in the tile object would be affecting the hover check - I feel I'm missing something obvious but for the life of me can't see it.
I've looked at some similar projects and their button highlighting, they seem to do exactly the same thing as me.
The Tile Object -
var Tile = function(x, y, face) {
this.x = x;
this.y = y;
this.size = 70;
this.face = face;
this.isFaceUp = false;
this.isMatch = false;
};
Tile.prototype.draw = function() {
this.hover();
fill(214, 247, 202);
strokeWeight(2);
rect(this.x, this.y, this.size, this.size, 10);
if (this.isFaceUp) {
image(this.face, this.x, this.y, this.size, this.size);
} else {
image(getImage("avatars/leaf-green"), this.x, this.y, this.size, this.size);
}
};
//Mouse Hover code - only works one time for some reason. After flipping back this stops working for some reason.
Tile.prototype.hover = function() {
stroke(0, 0, 0);
if (this.isMouseInside()){
stroke(7, 122, 44);
}
};
Tile.prototype.isUnderMouse = function(x, y) {
return x >= this.x && x <= this.x + this.size &&
y >= this.y && y <= this.y + this.size;
};
//check if mouse cursor is inside the tile
Tile.prototype.isMouseInside = function() {
return mouseX > this.x &&
mouseX < (this.x + this.size) &&
mouseY > this.y &&
mouseY < (this.y + this.size) &&
this.isFaceUp === false;
};
The Mouse Clicked Function - prewritten by KA
mouseClicked = function() {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (tile.isUnderMouse(mouseX, mouseY)) {
if (flippedTiles.length < 2 && !tile.isFaceUp) {
tile.isFaceUp = true;
flippedTiles.push(tile);
if (flippedTiles.length === 2) {
numTries++;
if (flippedTiles[0].face === flippedTiles[1].face) {
flippedTiles[0].isMatch = true;
flippedTiles[1].isMatch = true;
flippedTiles.length = 0;
numMatches++;
}
delayStartFC = frameCount;
}
}
loop();
}
}
};
The Draw function - prewritten by KA
draw = function() {
background(255, 255, 255);
if (delayStartFC && (frameCount - delayStartFC) > 30) {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
if (!tile.isMatch) {
tile.isFaceUp = false;
}
}
flippedTiles = [];
delayStartFC = null;
noLoop();
}
for (var i = 0; i < tiles.length; i++) {
tiles[i].draw();
}
if (numMatches === tiles.length/2) {
fill(0, 0, 0);
textSize(20);
text("You found them all in " + numTries + " tries!", 20, 375);
}
};
noLoop();
It drove me nuts trying to work out a function for mouse over then it hit me, my solution to change the stroke weight on rollover. in the Tile.prototype.draw function add:
if (this.isUnderMouse(mouseX, mouseY)){
strokeWeight(5);
}
else {
strokeWeight(2);
}
I'm trying to have my Particle object collide and reflect off my Slate object.
If I wanted to use an ellipse, it would be simple because I could just create a radius variable - can't do that with a rectangle.
It's something to do with the distance variable, I just can't figure it out.
var div;
var movers;
function setup() {
createCanvas(windowWidth,windowHeight);
background("#FDEDEB");
div = new Slate();
movers = new Particle();
}
function draw() {
background("#FDEDEB");
div.display();
movers.display();
movers.update();
movers.move();
if (movers.hits(div)) {
console.log("hit");
}
}
function Slate() {
this.x = 30;
this.y = height/2;
this.display = function() {
noStroke();
fill("#DF4655");
rect(this.x,this.y, 700, 200);
}
}
function Particle() {
this.pos = createVector(10,0);
this.vel = createVector(0,0);
this.acc = createVector(0.01,0.01);
this.history = [];
this.display = function() {
fill("#DF4655");
point(this.pos.x,this.pos.y);
//beginShape();
for(var j = 0; j < this.history.length; j++) {
var pos = this.history[j];
ellipse(pos.x,pos.y, 5, 3);
}
//endShape();
}
this.update = function() {
var v = createVector(this.pos.x,this.pos.y);
this.history.push(v);
if (this.history.length > 10) {
this.history.splice(0,1);
}
}
this.hits = function(div) {
// BOUNCE OFF SLATE
var d = dist(this.pos.x,this.pos.y,div.x,div.y);
if (d < 0) {
console.log('hits');
}
}
this.move = function() {
this.pos.add(this.vel);
this.vel.add(this.acc);
this.vel.limit(10);
for (var i = 0; i < this.history.length; i++) {
this.history[i].x += random(-2,2);
this.history[i].y += random(-2,2);
}
}
}
If the particle is a point (or can be represented as a point), you need to use point-rectangle collision detection. Basically you would need to check whether the point is between the left and right edges and the top and bottom edges of the rectangle.
if(pointX > rectX && pointX < rectX + rectWidth && pointY > rectY && pointY < rectY + rectHeight){
//the point is inside the rectangle
}
If the particle is an ellipse and you need to factor in the radius of that ellipse, then you're better off representing the particle as a rectangle, just for collision purposes. Then you can use rectangle-rectangle collision detection. This is also called a bounding box, and it's one of the most common ways to handle collision detection.
if(rectOneRight > rectTwoLeft && rectOneLeft < rectTwoRight && rectOneBottom > rectTwoTop && rectOneTop < rectTwoBottom){
//the rectangles are colliding
}
Shameless self-promotion: I wrote a tutorial on collision detection available here. It's for Processing, but everything is the same for P5.js.
I created a collision detection between Snake and BasicEnemy. I created a for loop to make five different enemies but the collision detection doesn't get called on any of the enemies that were created from the for loop. The collision only works with the one BasicEnemy object. Why isn't collision function being called for all of the enemies inside the array? Thank you.
Sketch.js
var snake;
var food;
var basicEnemy;
var scl = 20;
var enemies = [];
function setup() {
createCanvas(600, 500);
snake = new Snake();
basicEnemy = new BasicEnemy();
//** CREATE FIVE ENEMIES **
for (var i = 0; i < 5; i++) {
enemies[i] = new BasicEnemy();
}
}
// **FUNCTION WHEN SNAKE HITS ENEMY**
function collision() {
console.log("hit!");
}
function draw() {
background(51);
//Draw snake
snake.update();
snake.show();
//Draw basicEnemy
basicEnemy.update();
basicEnemy.show();
//** LOOP THROUGH ENEMIES AND UPDATE AND SHOW **
for (var i = 0; i < enemies.length; i++) {
enemies[i].show();
enemies[i].update();
if (enemies[i].hits(snake)) {
collision();
}
}
}
function keyPressed() {
if (keyCode === UP_ARROW){
snake.dir(0, -1);
} else if (keyCode === DOWN_ARROW) {
snake.dir(0, 1);
} else if (keyCode === LEFT_ARROW) {
snake.dir(-1 , 0);
} else if (keyCode === RIGHT_ARROW) {
snake.dir(1 , 0);
}
}
BasicEnemy.js
function BasicEnemy() {
this.x = random(700);
this.y = random(700);
this.velX = 15;
this.velY = 15;
}
//** FUNCTION TO CHECK IF ENEMY AND SNAKE ARE IN THE SAME LOCATION **
this.hits = function (pos) {
var = d = dist(this.x, this.y, pos.x, pos.y);
if(d < 1) {
return true;
} else {
return false;
}
}
this.show = function () {
fill(255, 0, 100);
rect(this.x, this.y, scl, scl);
}
Snake.js
function Snake() {
this.x = 0;
this.y = 0;
this.xspeed = 1;
this.yspeed = 0;
this.update = function() {
this.x = this.x + this.xspeed * scl;
this.y = this.y + this.yspeed * scl;
this.x = constrain(this.x, 0, width - scl);
this.y = constrain(this.y, 0, height - scl);
}
this.show = function() {
fill(255);
rect(this.x, this.y, scl, scl);
}
this.dir = function (x , y) {
this.xspeed = x;
this.yspeed = y;
}
}
Because you're essentially checking for the distance between the top left corners of the snake and the enemy, this'll only return true, if they completely overlap.
Use an AABB collision detection instead:
return this.x + scl >= pos.x && this.x <= pos.x + scl && this.y + scl >= pos.y && this.y <= pos.y + scl;
This returns true, if the first rectangle contains the second rectangle.
MDN says:
One of the simpler forms of collision detection is between two rectangles that are axis aligned — meaning no rotation. The algorithm works by ensuring there is no gap between any of the 4 sides of the rectangles. Any gap means a collision does not exist.
I'm trying to achieve multiple animations in a game that I am creating using Canvas (it is a simple ping-pong game). This is my first game and I am new to canvas but have created a few experiments before so I have a good knowledge about how canvas work.
First, take a look at the game here.
The problem is, when the ball hits the paddle, I want a burst of n particles at the point of contact but that doesn't came right. Even if I set the particles number to 1, they just keep coming from the point of contact and then hides automatically after some time.
Also, I want to have the burst on every collision but it occurs on first collision only. I am pasting the code here:
//Initialize canvas
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
W = window.innerWidth,
H = window.innerHeight,
particles = [],
ball = {},
paddles = [2],
mouse = {},
points = 0,
fps = 60,
particlesCount = 50,
flag = 0,
particlePos = {};
canvas.addEventListener("mousemove", trackPosition, true);
//Set it's height and width to full screen
canvas.width = W;
canvas.height = H;
//Function to paint canvas
function paintCanvas() {
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, W, H);
}
//Create two paddles
function createPaddle(pos) {
//Height and width
this.h = 10;
this.w = 100;
this.x = W/2 - this.w/2;
this.y = (pos == "top") ? 0 : H - this.h;
}
//Push two paddles into the paddles array
paddles.push(new createPaddle("bottom"));
paddles.push(new createPaddle("top"));
//Setting up the parameters of ball
ball = {
x: 2,
y: 2,
r: 5,
c: "white",
vx: 4,
vy: 8,
draw: function() {
ctx.beginPath();
ctx.fillStyle = this.c;
ctx.arc(this.x, this.y, this.r, 0, Math.PI*2, false);
ctx.fill();
}
};
//Function for creating particles
function createParticles(x, y) {
this.x = x || 0;
this.y = y || 0;
this.radius = 0.8;
this.vx = -1.5 + Math.random()*3;
this.vy = -1.5 + Math.random()*3;
}
//Draw everything on canvas
function draw() {
paintCanvas();
for(var i = 0; i < paddles.length; i++) {
p = paddles[i];
ctx.fillStyle = "white";
ctx.fillRect(p.x, p.y, p.w, p.h);
}
ball.draw();
update();
}
//Mouse Position track
function trackPosition(e) {
mouse.x = e.pageX;
mouse.y = e.pageY;
}
//function to increase speed after every 5 points
function increaseSpd() {
if(points % 4 == 0) {
ball.vx += (ball.vx < 0) ? -1 : 1;
ball.vy += (ball.vy < 0) ? -2 : 2;
}
}
//function to update positions
function update() {
//Move the paddles on mouse move
if(mouse.x && mouse.y) {
for(var i = 1; i < paddles.length; i++) {
p = paddles[i];
p.x = mouse.x - p.w/2;
}
}
//Move the ball
ball.x += ball.vx;
ball.y += ball.vy;
//Collision with paddles
p1 = paddles[1];
p2 = paddles[2];
if(ball.y >= p1.y - p1.h) {
if(ball.x >= p1.x && ball.x <= (p1.x - 2) + (p1.w + 2)){
ball.vy = -ball.vy;
points++;
increaseSpd();
particlePos.x = ball.x,
particlePos.y = ball.y;
flag = 1;
}
}
else if(ball.y <= p2.y + 2*p2.h) {
if(ball.x >= p2.x && ball.x <= (p2.x - 2) + (p2.w + 2)){
ball.vy = -ball.vy;
points++;
increaseSpd();
particlePos.x = ball.x,
particlePos.y = ball.y;
flag = 1;
}
}
//Collide with walls
if(ball.x >= W || ball.x <= 0)
ball.vx = -ball.vx;
if(ball.y > H || ball.y < 0) {
clearInterval(int);
}
if(flag == 1) {
setInterval(emitParticles(particlePos.x, particlePos.y), 1000/fps);
}
}
function emitParticles(x, y) {
for(var k = 0; k < particlesCount; k++) {
particles.push(new createParticles(x, y));
}
counter = particles.length;
for(var j = 0; j < particles.length; j++) {
par = particles[j];
ctx.beginPath();
ctx.fillStyle = "white";
ctx.arc(par.x, par.y, par.radius, 0, Math.PI*2, false);
ctx.fill();
par.x += par.vx;
par.y += par.vy;
par.radius -= 0.02;
if(par.radius < 0) {
counter--;
if(counter < 0) particles = [];
}
}
}
var int = setInterval(draw, 1000/fps);
Now, my function for emitting particles is on line 156, and I have called this function on line 151. The problem here can be because of I am not resetting the flag variable but I tried doing that and got more weird results. You can check that out here.
By resetting the flag variable, the problem of infinite particles gets resolved but now they only animate and appear when the ball collides with the paddles. So, I am now out of any solution.
I can see 2 problems here.
your main short term problem is your use of setinterval is incorrect, its first parameter is a function.
setInterval(emitParticles(particlePos.x, particlePos.y), 1000/fps);
should be
setInterval(function() {
emitParticles(particlePos.x, particlePos.y);
}, 1000/fps);
Second to this, once you start an interval it runs forever - you don't want every collision event to leave a background timer running like this.
Have an array of particles to be updated, and update this list once per frame. When you make new particles, push additional ones into it.