I'm having trouble with how I remove bricks. The bricks are part of an array, and the ball continuously runs a for loop checking to see if it's hit any bricks. If it does, it splices that brick from the array. But the bricks don't disappear!
//Helper Functions
function AABBIntersect(ax, ay, aw, ah, bx, by, bw, bh) {
return ax < bx+bw && ay < by+bh && bx < ax+aw && by < ay+ah;
}
var ball = {
//A few of the basic variables called by the upcoming function
update: function() {
this.x += this.vel.x;
this.y += this.vel.y;
if (0 > this.y) {
var offset = 0 - this.y;
this.y += 2*offset;
this.vel.y *= -1;
}
if (this.y+this.height > HEIGHT) {
this.serve();
}
if (0 > this.x || this.x+this.size > WIDTH) {
var offset = this.vel.x < 0 ? 0 - this.x : WIDTH - (this.x+this.size);
this.x += 2*offset;
this.vel.x *= -1;
}
if (AABBIntersect(this.x, this.y, this.size, this.size, player.x, player.y, player.width, player.height)) {
var offset = player.y - (this.y+this.size);
this.y += 2*offset;
var n = (this.x+this.size - player.x)/(player.width+this.size);
var phi = 0.25*pi*(2*n - 1);
var smash = Math.abs(phi) > 0.2*pi ? 1.5 : 1;
this.vel.x = smash*this.speed*Math.sin(phi);
this.vel.y = smash*-1*this.speed*Math.cos(phi);
}
for (var i = 0; i < bricks.length; i++) {
var b = bricks[i];
if (AABBIntersect(this.x, this.y, this.width, this.height, b.x, b.y, b.width, b.height)) {
bricks.splice(i, 1);
i--;
bricks.length--;
continue;
}
}
}
}
var bricks = [];
function main() {
canvas = document.createElement("canvas");
canvas.width = WIDTH;
canvas.height = HEIGHT;
ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
keystate = {};
document.addEventListener("keydown", function(evt) {
keystate[evt.keyCode] = true;
});
document.addEventListener("keyup", function(evt) {
delete keystate[evt.keyCode];
} );
init();
var loop = function() {
update();
draw();
window.requestAnimationFrame(loop, canvas);
};
window.requestAnimationFrame(loop, canvas);
}
function init() {
var cols = WIDTH / 40;
player.x = (WIDTH - player.width) / 2;
player.y = HEIGHT - (player.height * 2);
ball.x = (WIDTH - ball.size) / 2;
ball.y = player.y - ball.size;
ball.width = ball.size;
ball.height = ball.size;
ball.serve();
for (var i = 0; i < 7; i++) {
for (var j = 0; j < cols; j++) {
bricks.push({
color: "#f00",
x: 2 + j*40,
y: 2 + i*20,
w: 36,
h: 16
});
}
}
}
function update() {
frames++;
player.update();
ball.update();
}
function draw() {
ctx.fillRect(0, 0, WIDTH, HEIGHT);
ctx.save();
ctx.fillStyle = "#fff";
player.draw();
ball.draw();
for (var i = 0; i < bricks.length; i++) {
ctx.fillStyle = bricks[i].color;
ctx.fillRect(bricks[i].x, bricks[i].y, bricks[i].w, bricks[i].h);
}
ctx.restore();
}
main();
A few issues:
The brick objects do not have width or height properties, but w and h;
The splice should not happen on a brick element (b), but on the array of bricks (bricks);
The length of bricks should not be decremented after the splice, as that operation already reduces the length.
So use this loop:
for (var i = 0; i < bricks.length; i++) {
var b = bricks[i];
if (AABBIntersect(this.x, this.y, this.width, this.height, b.x, b.y, b.w, b.h)) {
bricks.splice(i, 1);
i--;
}
}
Related
Could someone explain to me why this translation doesn't work?
Since I'm super new to both processing and p5.js, I keep getting error messages...
This is the processing code that I'd like to change.
Particle[] particles;
float alpha;
void setup() {
size(900, 500);
background(0);
noStroke();
setParticles();
}
void draw() {
frameRate(30);
alpha = map(height, 0, width, 5, 35);
fill(4, 255);
rect(0, 0, width, height);
background(0);
loadPixels();
for (Particle p : particles) {
p.move();
}
updatePixels();
}
void setParticles() {
particles = new Particle[10000];
for (int i = 0; i < 10000; i++) {
float x = random(width);
float y = random(height);
float adj = map(y, 0, height, 255, 0);
int c = color(60, adj, 255);
particles[i]= new Particle(x, y, c);
}
}
void mousePressed() {
setParticles();
}
class Particle {
float posX, posY, incr, theta;
color c;
Particle(float xIn, float yIn, color cIn) {
posX = xIn;
posY = yIn;
c = cIn;
}
public void move() {
update();
wrap();
display();
}
void update() {
incr += .008;
theta = noise(posX * .006, posY * .008, incr) * TWO_PI;
posX += 2 * tan(theta);
posY += 2 * sin(theta);
}
void display() {
if (posX > 0 && posX < width && posY > 0 && posY < height) {
pixels[(int)posX + (int)posY * width] = c;
}
}
void wrap() {
if (posX < 0) posX = width;
if (posX > width ) posX = 0;
if (posY < 0 ) posY = height;
if (posY > height) posY = 0;
}
}
And this is the p5.js translation which doesn't work.
var particles;
let particle = [];
const num = 100;
function setup() {
createCanvas(900, 500);
background(0);
noStroke();
setParticles();
}
function draw() {
frameRate(30);
let alpha = map(height, 0, width, 5, 35);
fill(0, alpha);
rect(0, 0, width, height);
loadPixels();
for (var i = 0; i < num; i++) {
particles[i].update();
particles[i].display();
particles[i].wrap();
}
updatePixels();
}
function setParticles() {
particles = new Array(10000);
for (var i = 0; i < 10000; i++) {
var x = random(width);
var y = random(height);
var adj = map(y, 0, height, 255, 0);
let c = color(60, adj, 255);
particles[i] = new Particle(x, y, c);
}
}
function mousePressed() {
setParticles();
}
function Particle (xln, yln, cln) {
this.posX = xln;
this.posY = yln;
this.c = cln;
this.update = function() {
this.incr += .008;
this.theta = noise(this.posX * .006, this.posY * .008, this.incr) * TWO_PI;
this.posX += 2 * tan(this.theta);
this.posY += 2 * sin(this.theta);
}
this.display = function() {
if (this.posX > 0 && this.posX < width && this.posY > 0 && this.posY < height) {
pixels[int(this.posX) + int(this.posY) * width] = c;
}
}
this.wrap = function() {
if (this.posX < 0)
this.posX = width;
if (this.posX > width)
this.posX = 0;
if (this.posY < 0)
this.posY = height;
if (this.posY > height)
this.posY = 0;
}
}
I'll be a great help if anyone with experience could help me!
Cool project! You're very close there were just a few things that needed tweaking.
If you plonk a console.log(this.posX) in the display() function you'll see that it's NaN, if you trace your steps you'll notice that it's caused by:
this.theta = noise(this.posX * 0.006, this.posY * 0.008, this.incr) * TWO_PI;
this.posX += 2 * sin(this.theta);
The problem here being that this.incr wasn't defined, so adding this.incr = 0 to the constructor fixes that.
The following line in the display function is missing a this. for c:
pixels[int(this.posX) + int(this.posY) * width] = c; // should be this.c
You're also not looping through all of the particles, which may be a concious decision but it looks pretty awesome when you do:
for (var i = 0; i < particles.length; i++) {
particles[i].update();
particles[i].display();
particles[i].wrap();
}
When dealing with pixels you have to consider that there are 4 elements in the pixels array which represent the R, G, B, A values in order of each pixel. So to get the index of a given X and Y:
let index = (int(this.posX) + int(this.posY) * width) * 4;
And lastly, you'll probably need to set pixelDensity(1) so that it covers the whole screen, more information can be found in the p5.js docs.
I've used es6 classes rather than functions, but the premise is the same:
var particles = [];
let particle = [];
const num = 100;
function setup() {
createCanvas(900, 400);
pixelDensity(1);
background(0);
noStroke();
setParticles();
}
function draw() {
frameRate(30);
let alpha = map(height, 0, width, 5, 35);
fill(0, alpha);
rect(0, 0, width, height);
loadPixels();
for (var i = 0; i < particles.length; i++) {
particles[i].update();
particles[i].display();
particles[i].wrap();
}
updatePixels();
}
function setParticles() {
particles = new Array(10000);
for (var i = 0; i < 10000; i++) {
var x = random(width);
var y = random(height);
var adj = map(y, 0, height, 255, 0);
let c = color(60, adj, 255);
particles[i] = new Particle(x, y, c);
}
}
function mousePressed() {
setParticles();
}
class Particle {
constructor(xln, yln, cln) {
this.posX = xln;
this.posY = yln;
this.c = cln;
this.incr = 0;
}
update() {
this.incr += 0.008;
this.theta = noise(this.posX * 0.006, this.posY * 0.008, this.incr) * TWO_PI;
this.posX += 2 * tan(this.theta);
this.posY += 2 * sin(this.theta);
}
display() {
if (
this.posX > 0 &&
this.posX < width &&
this.posY > 0 &&
this.posY < height
) {
let index = (int(this.posX) + int(this.posY) * width) * 4;
pixels[index] = this.c;
pixels[index+1] = this.c;
pixels[index+2] = this.c;
pixels[index+3] = this.c;
}
}
wrap() {
if (this.posX < 0) this.posX = width;
if (this.posX > width) this.posX = 0;
if (this.posY < 0) this.posY = height;
if (this.posY > height) this.posY = 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>
And here's a link to the p5.js editor sketch.
OK, so I run into this problem where my program regularly crashes mid-game due to a TypeError: Uncaught TypeError: bullets[i] is undefined
The error occurs in the function where I check for every variable in the bullets and the enemies array if there is a collision.
This is the Source Code of this small school project I created, and I've been trying to troubleshoot this particular bug for hours at a time now. If anyone were able to help me that would be amazing as I can't quite grasp what the problem could be other than their occurring a collision when an element has already been spliced. But since the draw() function in P5.js runs 60 times per second that shouldn't really happen as regularly as it does, so I don't understand how this even can be an issue.
function setup() {
createCanvas(displayWidth - 20, displayHeight - 20);
angleMode(DEGREES);
rectMode(CENTER);
ellipseMode(CENTER);
playerX = width / 2;
playerY = height / 2;
}
let playerX = 0;
let playerY = 0;
let angle = 0;
let timer = 120;
let time = 0;
let v = 2;
let cooldown = 20;
let color = 50;
let hit = 0;
var bullets = [];
var enemies_easy = [];
var enemies_hard = [];
var score = 0;
var highscore = 0;
var gameover = false;
var gamestarted = false;
var nexthard = 5;
var currentenemy = 0;
function draw() {
if (!gamestarted) {
push();
textAlign(CENTER);
background('black');
translate(displayWidth / 2, displayHeight / 2);
textSize(width / 50);
fill('white');
text('Welcome!', 0, -100);
text('Controls:', 0, 0);
text('Fullscreen: F Moving: W, A, S, D Restart: R Shooting: Left Mouse Button Start: Space Bar', 0, 100);
pop();
if (keyIsDown(32)) {
bullets = [];
gamestarted = true;
}
} else {
if (!gameover) {
//calculates shot enemies_easy
bulletcollision();
//shoots if weapon cooldown is expired
background('black');
cooldown--;
activategun();
//draw hero
angle = atan2(mouseY - playerY, mouseX - playerX) + 90;
push();
fill('blue');
translate(playerX, playerY);
rotate(angle);
ellipse(0, 0, 40, 40);
//draw gun
fill('grey');
rect(0, 0 - 25, 10, 20);
pop();
//move hero
move();
//creates enemies with increasing difficulty
time++;
difficulty();
spawnenemy_easy();
spawnenemy_hard();
//shows score on screen
showscore();
//draw crosshair
noCursor();
push();
fill('white');
stroke('white');
line(mouseX, mouseY, mouseX + 20, mouseY);
line(mouseX, mouseY, mouseX - 20, mouseY);
line(mouseX, mouseY, mouseX, mouseY + 20);
line(mouseX, mouseY, mouseX, mouseY - 20);
pop();
//checks for game over
playercollision();
} else {
if (keyIsDown(82)) {
bullets = [];
enemies_easy = [];
enemies_hard = [];
timer = 120;
time = 0;
cooldown = 20;
score = 0;
playerX = width / 2;
playerY = height / 2;
v = 2;
gameover = false;
}
}
}
}
class bullet {
constructor() {
this.x = playerX;
this.y = playerY;
this.angle = createVector(mouseX - playerX, mouseY - playerY);
this.angle.normalize();
}
drawbullet() {
push();
fill('white');
ellipse(this.x, this.y, 10, 10);
pop();
this.y = this.y + 10 * this.angle.y;
this.x = this.x + 10 * this.angle.x;
}
}
class enemy_easy {
constructor() {
this.x = random(-1000, width + 1000);
if (this.x > width || this.x < 0) {
if (this.x > width) {
this.x = width;
this.y = random(0, height + 1);
}
if (this.x < 0) {
this.x = 0;
this.y = random(0, height + 1);
} else {}
} else {
let i = floor(random(0, 2));
this.y = i * height;
}
}
drawenemy_easy() {
push();
this.angle = createVector(this.x - playerX, this.y - playerY);
this.angle.normalize();
fill('red');
ellipse(this.x, this.y, 30, 30);
rotate(angle);
pop();
this.x = this.x - v * this.angle.x; // * random(0, 5);
this.y = this.y - v * this.angle.y; // * random(0, 5);
}
}
class enemy_hard {
constructor() {
this.x = random(-1000, width + 1000);
if (this.x > width || this.x < 0) {
if (this.x > width) {
this.x = width;
this.y = random(0, height + 1);
}
if (this.x < 0) {
this.x = 0;
this.y = random(0, height + 1);
} else {}
} else {
let i = floor(random(0, 2));
this.y = i * height;
}
}
drawenemy_hard() {
push();
this.angle = createVector(this.x - playerX, this.y - playerY);
this.angle.normalize();
fill('purple');
ellipse(this.x, this.y, 30, 30);
rotate(angle);
pop();
this.x = this.x - v * this.angle.x; // * random(0, 5);
this.y = this.y - v * this.angle.y; // * random(0, 5);
}
}
function keyPressed() {
//fullscreen Taste F: https://www.geeksforgeeks.org/p5-js-fullscreen-function/
if (keyCode === 70) {
let fs = fullscreen();
fullscreen(!fs);
}
}
function move() {
if (keyIsDown(83) && playerY <= height - 22) {
playerY = playerY + 4;
}
if (keyIsDown(87) && playerY >= 22) {
playerY = playerY - 4;
}
if (keyIsDown(68) && playerX <= width - 22) {
playerX = playerX + 4;
}
if (keyIsDown(65) && playerX >= 22) {
playerX = playerX - 4;
}
}
function activategun() {
for (var i = 0; i < bullets.length; i++) {
bullets[i].drawbullet();
if (bullets[i].x <= 0 || bullets[i].y <= 0 || bullets[i].x >= width || bullets[i].y >= height) {
bullets.splice(i, 1);
}
}
}
function mousePressed() {
if (cooldown < 0) {
bullets.push(new bullet());
cooldown = 20;
}
}
function spawnenemy_easy() {
for (var i = 0; i < enemies_easy.length; i++) {
enemies_easy[i].drawenemy_easy();
}
}
function spawnenemy_hard() {
for (var i = 0; i < enemies_hard.length; i++) {
enemies_hard[i].drawenemy_hard();
}
}
function newenemy() {
if (currentenemy < nexthard) {
enemies_easy.push(new enemy_easy());
currentenemy++;
} else {
enemies_hard.push(new enemy_hard());
currentenemy = 0;
nexthard = random(2, 6);
}
}
function bulletcollision() {
for (var i = 0; i < bullets.length; i++) {
for (var e = 0; e < enemies_easy.length; e++) {
var distance = createVector(bullets[i].x - enemies_easy[e].x, bullets[i].y - enemies_easy[e].y);
if (distance.mag() <= 18) {
bullets.splice(i, 1);
enemies_easy.splice(e, 1);
score++;
if (score > highscore) {
highscore++;
}
}
}
for (var e = 0; e < enemies_hard.length; e++) {
var distance = createVector(bullets[i].x - enemies_hard[e].x, bullets[i].y - enemies_hard[e].y);
if (distance.mag() <= 18) {
bullets.splice(i, 1);
hit++;
if (hit == 2) {
enemies_hard.splice(e, 1);
score++;
if (score > highscore) {
highscore++;
}
hit = 0;
}
}
}
}
}
function playercollision() {
for (var i = 0; i < enemies_easy.length; i++) {
var distance = createVector(enemies_easy[i].x - playerX, enemies_easy[i].y - playerY);
if (distance.mag() <= 25) {
push();
background(abs(255 * sin(random(0, 255))), 255 * sin(random(0, 255)), 255 * sin(random(0, 255)));
fill('white');
textAlign(CENTER);
textSize(width / 40);
translate(width / 2, height / 2);
text("Game Over!", 0, -100);
text("Score: " + score, 0, 0);
text("High Score: " + highscore, 0, 100);
pop();
return gameover = true;
}
}
for (var i = 0; i < enemies_hard.length; i++) {
var distance = createVector(enemies_hard[i].x - playerX, enemies_hard[i].y - playerY);
if (distance.mag() <= 25) {
push();
background(abs(255 * sin(random(0, 255))), 255 * sin(random(0, 255)), 255 * sin(random(0, 255)));
fill('white');
textAlign(CENTER);
textSize(width / 40);
translate(width / 2, height / 2);
text("Game Over!", 0, -100);
text("Score: " + score, 0, 0);
text("High Score: " + highscore, 0, 100);
pop();
return gameover = true;
}
}
}
function difficulty() {
if (time >= timer) {
newenemy();
time = 0;
timer = timer * 0.99;
v = v * 1.02;
}
}
function showscore() {
push();
fill('white');
textSize(width / 55);
textAlign(CENTER);
text('Score: ' + score, 100, 100);
pop();
}
Does anyone know how I could fix this issue or if I even can?
Any help is appreciated!
The problem is that you are removing bullets from the array as you iterate through the array. Use while-loop instead of the for-loop:
function bulletcollision() {
let i = 0;
while (i < bullets.length) {
let bullet_hit = false;
for (var e = 0; e < enemies_easy.length; e++) {
var distance = createVector(bullets[i].x - enemies_easy[e].x, bullets[i].y - enemies_easy[e].y);
if (distance.mag() <= 18) {
bullet_hit = true;
destroy_enemy(enemies_easy, e);
}
}
for (var e = 0; e < enemies_hard.length; e++) {
var distance = createVector(bullets[i].x - enemies_hard[e].x, bullets[i].y - enemies_hard[e].y);
if (distance.mag() <= 18) {
bullet_hit = true;
hit++;
if (hit == 2) {
destroy_enemy(enemies_hard, e);
hit = 0;
}
}
}
if (bullet_hit) {
bullets.splice(i, 1);
} else {
i ++;
}
}
}
function destroy_enemy(enemies, e) {
enemies.splice(e, 1);
score++;
highscore = Math.max(highscore, score);
}
See also Looping through array and removing items, without breaking for loop.
A last issue i have in this code is that i am not sure how to create color change effect as 8 directional.
I can only map a mouseX to make it lerpColor horizontally or vertically .
But how do i make it work through moving both mouseX and mouseY?
I had it in shift_Color method within the class. I tried to state that within a certain dist(), lerpColor. But now it only showing black color rather than the changing color effect.
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.gap = 110
this.shift1 = color(96);
this.shift2 = color(244);
}
update() {
this.shape();
this.shift_Color();
}
shape() {
push();
stroke(this.stroke);
//fill(this.shift1);
this.shift_Color();
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);
if (distance < this.gap) {
this.shift1 = color(96);
this.shift2 = color(244);
this.shiftX = map(mouseX - width/2,this.gap,distance,0,1.0);
this.change = lerpColor(this.shift1, this.shift2, this.shiftX);
fill(this.change);
} else {
fill(this.shift1);
}
}
magnetic() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.gap) {
this.attract = atan2(mouseY - this.y, mouseX - this.x);
rotateX(this.attract);
rotateY(this.attract);
} else {
rotateX(millis() / 1000);
rotateY(millis() / 1000);
}
}
}
If I understood your question properly, there are two problems in your code.
The first is the fact that because you are trying to map the distance between mouse and the cube to number between 0 and 1, you should write lerpColor(this.shift2, this.shift1, this.shiftX) instead of lerpColor(this.shift1, this.shift2, this.shiftX), since this.shift2 is the lighter color and will be the inner color.
Another problem is when mapping the change variable, change should be calculated based on the distance between mouse and the cube (which is the distance variable), not mouseX or mouseY. Solution is to simply do map(distance, 0, this.gap, 0, 1);.
let cubes = [];
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
backCol = color(243, 243, 243);
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 cube of cubes) {
cube.update();
}
}
class Cubes {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 30;
this.stroke = 70;
this.gap = 110
this.shift1 = color(96);
this.shift2 = color(244);
}
update() {
this.shape();
this.shift_Color();
}
shape() {
push();
stroke(this.stroke);
//fill(this.shift1);
this.shift_Color();
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);
if (distance < this.gap) {
this.shiftX = map(distance, 0, this.gap, 0, 1);
// this.shiftX = map(mouseX - width/2,this.gap,distance,0,1.0);
this.change = lerpColor(this.shift2, this.shift1, this.shiftX);
// this.change = lerpColor(this.shift1, this.shift2, this.shiftX);
fill(this.change);
} else {
fill(this.shift1);
}
}
magnetic() {
let distance = dist(mouseX, mouseY, this.x, this.y);
if (distance < this.gap) {
this.attract = atan2(mouseY - this.y, mouseX - this.x);
rotateX(this.attract);
rotateY(this.attract);
} else {
rotateX(millis() / 1000);
rotateY(millis() / 1000);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
I also modified some of your code. Like the line
this.shift1 = color(96);
this.shift2 = color(244);
which is not needed currently since neither of these variable changes during execution.
Following the collision between a ball from an array and an object (rectangle), the ball doesn't seem to have the same bounce affect as it has when it hits the ground.
When coming into contact with the object, it seems to pick up speed and suddenly glitches through and comes to rest on the ground.
Questions:
Why does it seem to want to rest on the ground and not on the object itself?
How can I make the ball have the same bounce affect when coming into contact with the object as it has when coming into contact with the ground?
Code:
var balls = [];
var obstacle;
function setup() {
createCanvas(400, 400);
obstacle = new Obstacle();
}
function draw() {
background(75);
obstacle.display();
for (var i = 0; i < balls.length; i++) {
balls[i].display();
balls[i].update();
balls[i].edges();
RectCircleColliding(balls[i], obstacle);
//console.log(RectCircleColliding(balls[i], obstacle));
}
}
function mousePressed() {
balls.push(new Ball(mouseX, mouseY));
}
function Ball(x, y) {
this.x = x;
this.y = y;
this.r = 15;
this.gravity = 0.5;
this.velocity = 0;
this.display = function() {
fill(255, 0, 100);
stroke(255);
ellipse(this.x, this.y, this.r * 2);
}
this.update = function() {
this.velocity += this.gravity;
this.y += this.velocity;
}
this.edges = function() {
if (this.y >= height - this.r) {
this.y = height - this.r;
this.velocity = this.velocity * -1;
this.gravity = this.gravity * 1.1;
}
}
}
function Obstacle() {
this.x = width - width;
this.y = height / 2;
this.w = 200;
this.h = 25;
this.display = function() {
fill(0);
stroke(255);
rect(this.x, this.y, this.w, this.h);
}
}
function RectCircleColliding(Ball, Obstacle) {
// define obstacle borders
var oRight = Obstacle.x + Obstacle.w;
var oLeft = Obstacle.x;
var oTop = Obstacle.y;
var oBottom = Obstacle.y + Obstacle.h;
//compare ball's position (acounting for radius) with the obstacle's border
if (Ball.x + Ball.r > oLeft) {
if (Ball.x - Ball.r < oRight) {
if (Ball.y + Ball.r > oTop) {
if (Ball.y - Ball.r < oBottom) {
Ball.y = Obstacle.y - Ball.r * 2;
Ball.velocity = Ball.velocity * -1;
Ball.gravity = Ball.gravity * 1.1;
Ball.velocity += Ball.gravity;
Ball.y += Ball.velocity;
return (true);
}
}
}
}
return false;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
The main issue with this question's code is that we need to check for collisions and only allow updates when the ball is not colliding. Another issue was that when collisions occur we must cap the gravity to prevent the ball from plunging all the way to the ground.
Here is the corrected code:
var balls = [];
var obstacle;
function setup() {
createCanvas(400, 400);
obstacle = new Obstacle();
}
function draw() {
background(75);
obstacle.display();
for (var i = 0; i < balls.length; i++) {
balls[i].display();
if (!RectCircleColliding(balls[i], obstacle)){
balls[i].update();
balls[i].edges();
}
//console.log(RectCircleColliding(balls[i], obstacle));
}
}
function mousePressed() {
balls.push(new Ball(mouseX, mouseY));
}
function Ball(x, y) {
this.x = x;
this.y = y;
this.r = 15;
this.gravity = 0.5;
this.velocity = 0;
this.display = function() {
fill(255, 0, 100);
stroke(255);
ellipse(this.x, this.y, this.r * 2);
}
this.update = function() {
this.velocity += this.gravity;
this.y += this.velocity;
}
this.edges = function() {
if (this.y >= height - this.r) {
this.y = height - this.r;
this.velocity = this.velocity * -1;
this.gravity = this.gravity * 1.1;
}
}
}
function Obstacle() {
this.x = width - width;
this.y = height / 2;
this.w = 200;
this.h = 25;
this.display = function() {
fill(0);
stroke(255);
rect(this.x, this.y, this.w, this.h);
}
}
function RectCircleColliding(Ball, Obstacle) {
// define obstacle borders
var oRight = Obstacle.x + Obstacle.w;
var oLeft = Obstacle.x;
var oTop = Obstacle.y;
var oBottom = Obstacle.y + Obstacle.h;
//compare ball's position (acounting for radius) with the obstacle's border
if (Ball.x + Ball.r > oLeft) {
if (Ball.x - Ball.r < oRight) {
if (Ball.y + Ball.r > oTop) {
if (Ball.y - Ball.r < oBottom) {
let oldY = Ball.y;
Ball.y = oTop - Ball.r;
Ball.velocity = Ball.velocity * -1;
if (Ball.gravity < 2.0){
Ball.gravity = Ball.gravity * 1.1;
} else {
Ball.velocity = 0;
Ball.y = oldY;
}
return (true);
}
}
}
}
return false;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
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.