Related
I am trying the simple circle filling and it works. But when I try to fill the first circle with more circles, it hangs the program immediately. Here's my draw and and class code which fills the circles inside circles:
let krr=[];
function draw() {
background(0);
print(cir.length,krr.length)
if (cir.length<=100){
let temp=new c();
cir.push(temp);
}
else if (krr.length<100){
print(1)
fr=cir[0]
if (fr.r>50){
let re=new cirgain(fr.x,fr.y,(fr.r)/2)
krr.push(re);
}
}
for (let h of krr){
h.show();
}
for (let g of cir){
g.show();
g.grow();
}
}
class cirgain{
constructor(x,y,r){
this.smr=floor(r/3);
if (krr.length==0){
while (true){
this.x=random(x-r+1,x+r-1);
this.y=random(y-r+1,y+r-1)
if (this.x*this.x+this.y*this.y<r*r-2){
break
}
}
}
else{
let flag1=1
let count1=0
while (flag){
if (count1>=500){
count1=0;
this.smr--;
}
while (true){
this.x=random(x-r+1,x+r-1);
this.y=random(y-r+1,y+r-1);
if (this.x*this.x+this.y*this.y<r*r-2)
break
}
for (let i=0;i<krr.length;i++){
if (dist(krr[i].x,krr[i].y,this.x,this.y)<r+this.smr){
flag1=1
count1++;
break;
}
flag1=0;
}
}
}
this.ccc=createVector(random(255),random(100,255),random(100,255))
}
show(){
stroke(0);
noFill();
strokeWeight(3)
stroke(this.ccc.x,this.ccc.y,this.ccc.z);
circle(this.x,this.y,this.smr)
}
}
If the whole code (including setup and class c (which at first fills the space with circles)) is needed, let me know, I will edit to include it.
Edit: Okay, here is the whole code:
let cir = [];
let maxR;
let krr = [];
function setup() {
createCanvas(windowWidth, windowHeight);
maxR = width / 4;
if (height > width)
maxR = height / 4
colorMode(HSB);
angleMode(DEGREES);
}
function draw() {
background(0);
print(cir.length, krr.length)
if (cir.length <= 100) {
let temp = new c();
cir.push(temp);
} else if (krr.length < 100) {
print(1)
fr = cir[0]
if (fr.r > 50) {
let re = new cirgain(fr.x, fr.y, (fr.r) / 2)
krr.push(re);
}
}
for (let h of krr) {
h.show();
}
for (let g of cir) {
g.show();
g.grow();
}
}
class c {
constructor() {
this.tempr = 1
if (cir.length == 0) {
this.x = random(maxR + 1, width - maxR - 1);
this.y = random(maxR + 1, height - maxR - 1)
this.r = maxR;
} else {
let flag = 1
let count = 0
while (flag) {
if (count >= 500) {
count = 0;
maxR--;
}
this.x = random(maxR / 2 + 1, width - maxR / 2 - 1);
this.y = random(maxR / 2 + 1, height - maxR / 2 - 1);
this.r = maxR;
for (let i = 0; i < cir.length; i++) {
if (dist(cir[i].x, cir[i].y, this.x, this.y) < cir[i].r / 2 + this.r / 2 + 3) {
flag = 1
count++;
break;
}
flag = 0;
}
}
}
this.cc = createVector(random(255), random(100, 255), random(100, 255))
}
show() {
stroke(0);
noFill();
strokeWeight(3)
stroke(this.cc.x, this.cc.y, this.cc.z);
circle(this.x, this.y, this.tempr)
rectMode(CENTER);
}
grow() {
if (this.tempr <= this.r)
// this.tempr+=.5
this.tempr += this.r / 100
}
}
class cirgain {
constructor(x, y, r) {
this.smr = floor(r / 3);
if (krr.length == 0) {
while (true) {
this.x = random(x - r + 1, x + r - 1);
this.y = random(y - r + 1, y + r - 1)
if (this.x * this.x + this.y * this.y < r * r - 2) {
break
}
}
} else {
let flag1 = 1
let count1 = 0
while (flag) {
if (count1 >= 500) {
count1 = 0;
this.smr--;
}
while (true) {
this.x = random(x - r + 1, x + r - 1);
this.y = random(y - r + 1, y + r - 1);
if (this.x * this.x + this.y * this.y < r * r - 2)
break
}
for (let i = 0; i < krr.length; i++) {
if (dist(krr[i].x, krr[i].y, this.x, this.y) < r + this.smr) {
flag1 = 1
count1++;
break;
}
flag1 = 0;
}
}
}
this.ccc = createVector(random(255), random(100, 255), random(100, 255))
}
show() {
stroke(0);
noFill();
strokeWeight(3)
stroke(this.ccc.x, this.ccc.y, this.ccc.z);
circle(this.x, this.y, this.smr)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Your logic for finding viable x/y values randomly in the cirgain constructor seems broken. Several of your while loops seem like they never exit.
let cir = [];
let krr = [];
let maxR;
function setup() {
createCanvas(windowWidth, windowHeight);
maxR = width / 4;
if (height > width) {
maxR = height / 4;
}
colorMode(HSB);
angleMode(DEGREES);
}
function draw() {
background(0);
print(cir.length, krr.length);
if (cir.length <= 100) {
let temp = new c();
cir.push(temp);
} else if (krr.length < 100) {
print(1)
fr = cir[0]
if (fr.r > 50) {
let re = new cirgain(fr.x, fr.y, (fr.r) / 2)
krr.push(re);
}
}
for (let h of krr) {
h.show();
}
for (let g of cir) {
g.show();
g.grow();
}
}
class c {
constructor() {
this.tempr = 1
if (cir.length == 0) {
this.x = random(maxR + 1, width - maxR - 1);
this.y = random(maxR + 1, height - maxR - 1)
this.r = maxR;
} else {
let flag = 1
let count = 0
while (flag) {
if (count >= 500) {
count = 0;
maxR--;
}
this.x = random(maxR / 2 + 1, width - maxR / 2 - 1);
this.y = random(maxR / 2 + 1, height - maxR / 2 - 1);
this.r = maxR;
for (let i = 0; i < cir.length; i++) {
if (dist(cir[i].x, cir[i].y, this.x, this.y) < cir[i].r / 2 + this.r / 2 + 3) {
flag = 1
count++;
break;
}
flag = 0;
}
}
}
this.cc = createVector(random(255), random(100, 255), random(100, 255))
}
show() {
stroke(0);
noFill();
strokeWeight(3)
stroke(this.cc.x, this.cc.y, this.cc.z);
circle(this.x, this.y, this.tempr)
rectMode(CENTER);
}
grow() {
if (this.tempr <= this.r)
// this.tempr+=.5
this.tempr += this.r / 100
}
}
class cirgain {
constructor(x, y, r) {
this.smr = floor(r / 3);
if (krr.length == 0) {
let n = 0;
while (++n < 1000) {
this.x = random(x - r + 1, x + r - 1);
this.y = random(y - r + 1, y + r - 1);
if (this.x * this.x + this.y * this.y < r * r - 2) {
break;
}
}
if (n >= 1000) {
console.warn('1. Unable to find an x/y value that satisfied the constraint');
debugger;
}
} else {
let flag1 = 1;
let count1 = 0;
let n = 0;
while (flag1 && ++n < 10000) {
if (count1 >= 500) {
count1 = 0;
this.smr--;
}
let n2 = 0;
while (++n2 < 1000) {
this.x = random(x - r + 1, x + r - 1);
this.y = random(y - r + 1, y + r - 1);
if (this.x * this.x + this.y * this.y < r * r - 2) {
break;
}
}
if (n2 >= 1000) {
console.warn('2. Unable to find an x/y value that satisfied the constraint');
debugger;
}
for (let i = 0; i < krr.length; i++) {
if (dist(krr[i].x, krr[i].y, this.x, this.y) < r + this.smr) {
flag1 = 1
count1++;
break;
}
flag1 = 0;
}
}
if (n >= 10000) {
console.warn('3. Flag never set to false');
debugger;
}
}
this.ccc = createVector(random(255), random(100, 255), random(100, 255))
}
show() {
stroke(0);
noFill();
strokeWeight(3)
stroke(this.ccc.x, this.ccc.y, this.ccc.z);
circle(this.x, this.y, this.smr);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Update
You seem to have an error in your logic checking if the randomly generated x/y values are acceptable:
// Instead of this, which checks if the square of the distance from the top left (0, 0) to the randomly generated center is less than the radius squared
if (this.x * this.x + this.y * this.y < r * r - 2) {
break;
}
// You probably meant to check if the distance from the center of the circle to the randomly generated position is less than the radius:
if (dist(x, y, this.x, this.y) < r - 2) {
break;
}
Additionally I cannot make heads or tails of this logic:
for (let i = 0; i < krr.length; i++) {
if (dist(krr[i].x, krr[i].y, this.x, this.y) < r + this.smr) {
flag1 = 1
count1++;
break;
}
flag1 = 0;
}
For each cirgain that you create you are check that for every existing cirgain the new one has a distance from the existing that is less than the containing circle's radius plus the current circle's diameter*, and if that is the case (which it almost always is!) you try to find a different random position, and only after 500 attempts you decrease the current circle's radius. I can hardly tell what your objective is, but there has to be a better way.
* You're using the default ellipse mode, which means the third argument to circle() is the diameter, which makes this code especially confusing
I have written this code to demonstrate a basic visual p5js project. In here there are 10 balls of varying sizes and colors that spawn at random positions, move around in the canvas and might collide with each other. I am not looking for elastic collision or "realistic" collision physics for that matter. I just want the balls to change to a different direction (can be random as long as it works) and work accordingly.
Here is my code :
class Ball {
//create new ball using given arguments
constructor(pos, vel, radius, color) {
this.pos = pos;
this.vel = vel;
this.radius = radius;
this.color = color;
}
//collision detection
collide(check) {
if (check == this) {
return;
}
let relative = p5.Vector.sub(check.pos, this.pos);
let dist = relative.mag() - (this.radius + check.radius);
if (dist < 0) { //HELP HERE! <--
this.vel.mult(-1);
check.vel.mult(-1);
}
}
//give life to the ball
move() {
this.pos.add(this.vel);
if (this.pos.x < this.radius) {
this.pos.x = this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.x > width - this.radius) {
this.pos.x = width - this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.y < this.radius) {
this.pos.y = this.radius;
this.vel.y = -this.vel.y;
}
if (this.pos.y > height - this.radius) {
this.pos.y = height - this.radius;
this.vel.y = -this.vel.y;
}
}
//show the ball on the canvas
render() {
fill(this.color);
noStroke();
ellipse(this.pos.x, this.pos.y, this.radius * 2);
}
}
let balls = []; //stores all the balls
function setup() {
createCanvas(window.windowWidth, window.windowHeight);
let n = 10;
//loop to create n balls
for (i = 0; i < n; i++) {
balls.push(
new Ball(
createVector(random(width), random(height)),
p5.Vector.random2D().mult(random(5)),
random(20, 50),
color(random(255), random(255), random(255))
)
);
}
}
function draw() {
background(0);
//loop to detect collision at all instances
for (let i = 0; i < balls.length; i++) {
for (let j = 0; j < i; j++) {
balls[i].collide(balls[j]);
}
}
//loop to render and move all balls
for (let i = 0; i < balls.length; i++) {
balls[i].move();
balls[i].render();
}
}
Here is a link to the project : https://editor.p5js.org/AdilBub/sketches/TNn2OREsN
All I need is the collision to change the direction of the ball to a random direction and not get stuck. Any help would be appreciated. I am teaching kids this program so I just want basic collision, doesnot have to be "realistic".
Any help is appreciated. Thank you.
The issues you are currently encountering with balls being stuck has to do with randomly generating balls that overlap such that after one iteration of movement they still overlap. When this happens both balls will simply oscillate in place repeatedly colliding with each other. You can prevent this simply by checking for collisions before adding new balls:
class Ball {
//create new ball using given arguments
constructor(pos, vel, radius, color) {
this.pos = pos;
this.vel = vel;
this.radius = radius;
this.color = color;
}
isColliding(check) {
if (check == this) {
return;
}
let relative = p5.Vector.sub(check.pos, this.pos);
let dist = relative.mag() - (this.radius + check.radius);
return dist < 0;
}
//collision detection
collide(check) {
if (this.isColliding(check)) {
this.vel.x *= -1;
this.vel.y *= -1;
check.vel.x *= -1;
check.vel.y *= -1;
}
}
//give life to the ball
move() {
this.pos.add(this.vel);
if (this.pos.x < this.radius) {
this.pos.x = this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.x > width - this.radius) {
this.pos.x = width - this.radius;
this.vel.x = -this.vel.x;
}
if (this.pos.y < this.radius) {
this.pos.y = this.radius;
this.vel.y = -this.vel.y;
}
if (this.pos.y > height - this.radius) {
this.pos.y = height - this.radius;
this.vel.y = -this.vel.y;
}
}
//show the ball on the canvas
render() {
fill(this.color);
noStroke();
ellipse(this.pos.x, this.pos.y, this.radius * 2);
}
}
let balls = []; //stores all the balls
function setup() {
createCanvas(500, 500);
let n = 10;
//loop to create n balls
for (i = 0; i < n; i++) {
let newBall =
new Ball(
createVector(random(width), random(height)),
p5.Vector.random2D().mult(random(5)),
random(20, 40),
color(random(255), random(255), random(255))
);
let isOk = true;
// check for collisions with existing balls
for (let j = 0; j < balls.length; j++) {
if (newBall.isColliding(balls[j])) {
isOk = false;
break;
}
}
if (isOk) {
balls.push(newBall);
} else {
// try again
i--;
}
}
}
function draw() {
background(0);
//loop to detect collision at all instances
for (let i = 0; i < balls.length; i++) {
for (let j = 0; j < i; j++) {
balls[i].collide(balls[j]);
}
}
//loop to render and move all balls
for (let i = 0; i < balls.length; i++) {
balls[i].move();
balls[i].render();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
That said, fully elastic collisions (which means collisions are instantaneous and involve no loss of energy due to deformation and resulting heat emission) are actually quite simple to simulate. Here's a tutorial I made on OpenProcessing demonstrating the necessary concepts using p5.js: Elastic Ball Collision Tutorial.
Here's the final version of the code from that tutorial:
const radius = 30;
const speed = 100;
let time;
let balls = []
let boundary = [];
let obstacles = [];
let paused = false;
function setup() {
createCanvas(400, 400);
angleMode(DEGREES);
ellipseMode(RADIUS);
boundary.push(createVector(60, 4));
boundary.push(createVector(width - 4, 60));
boundary.push(createVector(width - 60, height - 4));
boundary.push(createVector(4, height - 60));
obstacles.push(createVector(width / 2, height / 2));
balls.push({
pos: createVector(width * 0.25, height * 0.25),
vel: createVector(speed, 0).rotate(random(0, 360))
});
balls.push({
pos: createVector(width * 0.75, height * 0.75),
vel: createVector(speed, 0).rotate(random(0, 360))
});
balls.push({
pos: createVector(width * 0.25, height * 0.75),
vel: createVector(speed, 0).rotate(random(0, 360))
});
time = millis();
}
function keyPressed() {
if (key === "p") {
paused = !paused;
time = millis();
}
}
function draw() {
if (paused) {
return;
}
deltaT = millis() - time;
time = millis();
background('dimgray');
push();
fill('lightgray');
stroke('black');
strokeWeight(2);
beginShape();
for (let v of boundary) {
vertex(v.x, v.y);
}
endShape(CLOSE);
pop();
push();
fill('dimgray');
for (let obstacle of obstacles) {
circle(obstacle.x, obstacle.y, radius);
}
pop();
for (let i = 0; i < balls.length; i++) {
let ball = balls[i];
// update position
ball.pos = createVector(
min(max(0, ball.pos.x + ball.vel.x * (deltaT / 1000)), width),
min(max(0, ball.pos.y + ball.vel.y * (deltaT / 1000)), height)
);
// check for collisions
for (let i = 0; i < boundary.length; i++) {
checkCollision(ball, boundary[i], boundary[(i + 1) % boundary.length]);
}
for (let obstacle of obstacles) {
// Find the tangent plane that is perpendicular to a line from the obstacle to
// the moving circle
// A vector pointing in the direction of the moving object
let dirVector = p5.Vector.sub(ball.pos, obstacle).normalize().mult(radius);
// The point on the perimiter of the obstacle that is in the direction of the
// moving object
let p1 = p5.Vector.add(obstacle, dirVector);
checkCollision(ball, p1, p5.Vector.add(p1, p5.Vector.rotate(dirVector, -90)));
}
// Check for collisions with other balls
for (let j = 0; j < i; j++) {
let other = balls[j];
let distance = dist(ball.pos.x, ball.pos.y, other.pos.x, other.pos.y);
if (distance / 2 < radius) {
push();
let midPoint = p5.Vector.add(ball.pos, other.pos).div(2);
let boundaryVector = p5.Vector.sub(other.pos, ball.pos).rotate(-90);
let v1Parallel = project(ball.vel, boundaryVector);
let v2Parallel = project(other.vel, boundaryVector);
let v1Perpendicular = p5.Vector.sub(ball.vel, v1Parallel);
let v2Perpendicular = p5.Vector.sub(other.vel, v2Parallel);
ball.vel = p5.Vector.add(v1Parallel, v2Perpendicular);
other.vel = p5.Vector.add(v2Parallel, v1Perpendicular);
let bounce = min(radius, 2 * radius - distance);
ball.pos.add(p5.Vector.rotate(boundaryVector, -90).normalize().mult(bounce));
other.pos.add(p5.Vector.rotate(boundaryVector, 90).normalize().mult(bounce));
pop();
}
}
}
// Only draw balls after all position updates are complete
for (let ball of balls) {
circle(ball.pos.x, ball.pos.y, radius);
}
}
function drawLine(origin, offset) {
line(origin.x, origin.y, origin.x + offset.x, origin.y + offset.y);
}
// Handles collision with a plane given two points on the plane.
// It is assumed that given a vector from p1 to p2, roating that vector
// clockwise 90 degrees will give a vector pointing to the in-bounds side of the
// plane (i.e. a "normal").
function checkCollision(ball, p1, p2) {
let boundaryVector = p5.Vector.sub(p2, p1);
let objVector = p5.Vector.sub(ball.pos, p1);
let angle = boundaryVector.angleBetween(objVector);
let distance = objVector.mag() * sin(angle);
if (distance <= radius) {
// Collision
let vParallel = project(ball.vel, boundaryVector);
let vPerpendicular = p5.Vector.sub(ball.vel, vParallel);
ball.vel = p5.Vector.add(vParallel, p5.Vector.mult(vPerpendicular, -1));
let bounce = min(radius, (radius - distance) * 2);
// If the ball has crossed over beyond the plane we want to offset it to be on
// the in-bounds side of the plane.
let bounceOffset = p5.Vector.rotate(boundaryVector, 90).normalize().mult(bounce);
ball.pos.add(bounceOffset);
}
}
// p5.Vector helpers
function project(vect1, vect2) {
vect2 = p5.Vector.normalize(vect2);
return p5.Vector.mult(vect2, p5.Vector.dot(vect1, vect2));
}
function reject(vect1, vect2) {
return p5.Vector.sub(vect1, project(vect1, vect2));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I'm currently creating a little game to learn to code in p5.js.
The bug I'm facing is when I try to shoot missils[array of object] the Keyboard Arrow at comets that fly around [also an array of objects]. In order to understand if the missils would shoot the comet, I created a class method that compare their distance and if their distance is less than for ex 20, both the missil and the comets are getting spliced from their array.
It works randomly, sometimes it works for 20 comets and sometimes it lags at the first encounter. I suspect that this has to do with the nested for loops that I'm using.
First of all I'm creating 2 objects MOVER every 5 seconds in the Setup function and store them into an array.
for(let i=0; i<2; i++) {
{
setInterval(
function(){
movertest = new Mover(random(400, width), random(400, height));
mover.push(movertest);
},
5000)
};
}
I basically created then a method called "destroy" in my object Mover, which indicates whether or not a missil is less then 20 pixels away.
destroy(px, py) {
if( dist(this.position.x, this.position.y, px, py) <= 20) {
return true
} else {
return false;
}
}
and then when I summon those functions in the draw functions of P5.js, it is lagging.
for (i=0; i < missil.length; i++) {
missil[i].show();
missil[i].update();
for (p=0; p < planets.length; p++) {
missil[i].bounce(planets[p].position.x, planets[p].position.y, planets[p].lrect/2)
};
//if missil is in the canvas then check for collision
if (missil[i].contains(width/2,height/2)) {
for (let u = 0; u < mover.length; u++) {
if (mover[u].destroy(missil[i].position.x, missil[i].position.y)) {
mover.splice(u,1);
missil.splice(i,1);
console.log(u);
console.log(i)
};
}
} else {
missil.splice(i,1)
}
} // if missil is out of the canvas it get erased from the array.
If you would like to help me that would be super appreciated! If you want to play the game to understand the error, you can move green rectangle with your mouse, and avoid the comets and shoot them with the Keyboard arrow! thanks a lot ! :)
Here is the entire code:
let mover = [];
let x = 0;
let collision = 0; //counter to know when to end the game
let collu = 0;
let lrect = 10; //size of the ship.
let accslider, velslider;
let shoot = false; //to know if to shoot a missil.
let started = true;
let movertest = [];
//number of missil.
let missil = [];
let outofcanvas;
let a = 0;
let b = 0;
let munitions = 100;
let planets = [];
let cron;
function windowalert() {
if (confirm("Votre score est de rejouer?")) {
location.reload()
} else {
location.close();
}
}
function setup() {
background(0, 1);
cnv = createCanvas(900, 600);
for (let i = 0; i < 2; i++) {
setInterval(function() {
movertest = new Mover(random(400, width), random(400, height));
mover.push(movertest);
}, 5000)
}
for (p = 0; p < 4; p++) {
planets[p] = new Planet(100, 100, 20 + p * 20, 20 + p * 20)
}
ship = new Ship(300, 300);
accslider = createSlider(0, 255, 100);
accslider.position(width + 20, 20);
// noLoop(); // putted here since loop is when pressed the button start => function
}
function draw() {
text(munitions, 80, 20);
if (started) {
background(0, 50);
for (let i = 0; i < mover.length; i++) {
mover[i].show();
mover[i].update(x);
mover[i].edge();
// if rollover/ contains is true => then change collision +1 => collsison arrives at 255=> you are dead.
if (mover[i].contains(mouseX, mouseY)) {
collision += 1;
lrect += 0.04;
}
}
if (collision >= 255) {
clearInterval(cron);
started = false;
windowalert()
}
ship.move(mouseX, mouseY);
//ship.impact(mover[i].position.x,mover[i].position.y)
ship.show();
ship.edge();
noCursor();
x += 0.00005;
for (p = 0; p < planets.length; p++) {
planets[p].show(50 + p * 10, 180 + p * 40, 120 + p * 10);
planets[p].move();
}
for (i = 0; i < missil.length; i++) {
missil[i].show();
missil[i].update();
for (p = 0; p < planets.length; p++) {
missil[i].bounce(planets[p].position.x, planets[p].position.y, planets[p].lrect / 2)
}
//if missil is in the canvas then check for collision
if (missil[i].contains(width / 2, height / 2)) {
for (let u = 0; u < mover.length; u++) {
if (mover[u].destroy(missil[i].position.x, missil[i].position.y)) {
mover.splice(u, 1);
missil.splice(i, 1);
console.log(u);
console.log(i)
};
}
} else {
missil.splice(i, 1)
}
}
}
} // if missil is out of the canvas it get erased from the array.
setInterval(function() {
if (munitions < 100) {
munitions += 1
}
}, 1000);
function keyPressed() {
if (munitions > 0) {
if (keyIsDown(LEFT_ARROW))
{
a = -1;
munitions += -1
} // a is in the class munitions and represent the vector x. I did it like that so when we click both arrow it goes in diagonal.
if (keyIsDown(RIGHT_ARROW))
{
a = 1;
munitions += -1
}
if (keyIsDown(UP_ARROW)) {
b = -1;
munitions += -1
}
if (keyIsDown(DOWN_ARROW))
{
b = 1;
munitions += -1
}
for (let u = 0; u < 1; u++) {
let mi = new Missil(mouseX, mouseY, a, b);
missil.push(mi);
}
}
}
function keyReleased() {
if (keyCode == LEFT_ARROW) {
a = 0
} // this was implemented to reput the missil vector at the default value when released the key.
if (keyCode == RIGHT_ARROW) {
a = 0
}
if (keyCode == UP_ARROW) {
b = 0
}
if (keyCode == DOWN_ARROW) {
b = 0
}
}
class Missil {
constructor(x, y, a, b) //partira de mx,my.
{
this.position = createVector(x, y);
this.vel = createVector(a, b);
this.vel.mult(random(2, 4))
}
update() {
this.position = this.position.add(this.vel);
}
show() {
stroke(255);
noStroke();
fill(255, 0, 0, 100);
ellipse(this.position.x, this.position.y, 5);
}
contains(px, py) {
if (dist(this.position.x, this.position.y, px, py) < width / 2) {
return true
} else {
return false
}
}
bounce(px, py, dista) {
{
if (dist(this.position.x, this.position.y, px, py) < dista) {
let v = createVector(this.position.x - px, this.position.y - py);
this.vel = this.vel.mult(1.5).reflect(v)
}
}
}
}
class Planet {
constructor(x, y, lrect, lrect2) {
this.position = createVector(x, y)
this.vel = p5.Vector.random2D();
this.lrect = lrect;
this.lrect2 = lrect2;
}
show(r, g, b) {
fill(r, g, b);
noStroke();
ellipseMode(CENTER);
ellipse(this.position.x, this.position.y, this.lrect, this.lrect2)
stroke(255);
}
move() {
let center = createVector(width / 2, height / 2)
this.gravityacc = p5.Vector.sub(center, this.position);
this.gravityacc.setMag(0.004);
this.vel = this.vel.add(this.gravityacc);
this.position = this.position.add(this.vel);
this.vel.limit(1.3);
}
}
class Mover { //those are the comets
constructor(x, y) {
this.position = createVector(x, y);
this.vel = p5.Vector.random2D();
this.vel.mult(random(3));
//this.acc=p5.Vector.random2D();// acceleeration is a a random vector here.
// this.acc.setMag(0.01); // magnitude of acceleration is slow. Acceleration is a vector.
// this.vel.limit(3); // shrinks size of vector to 5, but if it smaller then 5 then it doent equal to 5 like in setMag().
}
update(speed)
{
setInterval(function() {
this.speed += 0.01
}, 3000);
let mouse = createVector(mouseX, mouseY);
this.acc = p5.Vector.sub(mouse, this.position);
this.acc.setMag(0.04)
this.acc.limit(0.1)
this.vel.add(this.acc); /// add acceleration to velocitiy.
this.position.add(this.vel);
this.vel.limit(5);
}
show() {
stroke(255, 10);
strokeWeight(0);
fill(map(this.position.x, 0, width, 0, 255), map(this.position.y, 0, height, 0, 255), 255, 255);
ellipse(this.position.x, this.position.y, 5)
}
edge() {
if (this.position.x >= width) {
let n = createVector(-1, 0);
this.vel = this.vel.reflect(n)
}
if (this.position.x <= 0) {
let n = createVector(1, 0);
this.vel = this.vel.reflect(n)
}
if (this.position.y >= height) {
let n = createVector(0, -1);
this.vel = this.vel.reflect(n)
}
if (this.position.y <= 0) {
let n = createVector(0, 1);
this.vel = this.vel.reflect(n)
}
}
contains(px, py) {
if (dist(this.position.x, this.position.y, px, py) < 5 + lrect) {
return true
} else {
return false
}
}
destroy(px, py) {
if (dist(this.position.x, this.position.y, px, py) <= 20) {
return true
} else {
return false;
}
}
}
class Ship {
constructor(x, y) {
this.position = createVector(x, y);
this.vel = createVector();
this.acc = createVector();
}
move(px, py) {
this.position.x = px;
this.position.y = py
} //if key pressed.
edge() {
if (this.position.x >= width) {
this.position.x = width
}
if (this.position.y >= height - 50) {
this.position.y = height - 50
}
}
show() {
stroke(255);
strokeWeight(0);
fill(collision * 1, 255 - collision * 2, 0, 100);
rect(this.position.x, this.position.y, lrect, lrect)
}
}
/* Without the HTML this isn't functional
"use strict";
document.form_main.start.onclick = () => start();
document.form_main.pause.onclick = () => pause();
document.form_main.reset.onclick = () => reset();
function start() {
pause();
cron = setInterval(() => {
timer();
}, 10);
started = true; // to indicate to start draw
loop(); // noLoop in fucntion setup.
}
function pause() {
clearInterval(cron);
started = false;
}
function reset() {
location.reload();
}
function timer() {
if ((millisecond += 10) == 1000) {
millisecond = 0;
second++;
}
if (second == 60) {
second = 0;
minute++;
}
if (minute == 60) {
minute = 0;
hour++;
}
document.getElementById('hour').innerText = returnData(hour);
document.getElementById('minute').innerText = returnData(minute);
document.getElementById('second').innerText = returnData(second);
document.getElementById('millisecond').innerText = returnData(millisecond);
}
function returnData(input) {
return input > 10 ? input : `0${input}`
} */
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
The problem is in the the missil/mover collision detection loop:
You are looping through all of the "missils"
For each missil you loop through all the movers
In the event of a collision you remove both the mover and the missil
But you continue looping with the current value of i, which may now be past the end of the array!
As a result you are getting an error sometimes because on a subsequent pass through the mover loop missil[i] is undefined. Any time you update an array you are currently looping over you need to be careful to update your indices and re-check your length before continuing.
// 1. You are looping through all of the "missils"
for (i = 0; i < missil.length; i++) {
// ...
// 2. For each missil you loop through all the movers
for (let u = 0; u < mover.length; u++) {
if (mover[u].destroy(missil[i].position.x, missil[i].position.y)) {
// 3. In the event of a collision you remove both the mover and the missil
mover.splice(u, 1);
missil.splice(i, 1);
console.log(u);
console.log(i)
}
// 4. But you continue looping with the current value of i, which may now be past the end of the array!
}
Here is a fixed version of your sketch:
let mover = [];
let x = 0;
let collision = 0; //counter to know when to end the game
let collu = 0;
let lrect = 10; //size of the ship.
let accslider, velslider;
let shoot = false; //to know if to shoot a missil.
let started = true;
let movertest = [];
//number of missil.
let missil = [];
let outofcanvas;
let a = 0;
let b = 0;
let munitions = 100;
let planets = [];
let cron;
function windowalert() {
if (confirm("Votre score est de rejouer?")) {
location.reload()
} else {
location.close();
}
}
function setup() {
background(0, 1);
cnv = createCanvas(900, 600);
for (let i = 0; i < 2; i++) {
setInterval(function() {
movertest = new Mover(random(400, width), random(400, height));
mover.push(movertest);
}, 5000)
}
for (p = 0; p < 4; p++) {
planets[p] = new Planet(100, 100, 20 + p * 20, 20 + p * 20)
}
ship = new Ship(300, 300);
accslider = createSlider(0, 255, 100);
accslider.position(width + 20, 20);
// noLoop(); // putted here since loop is when pressed the button start => function
}
function draw() {
text(munitions, 80, 20);
if (started) {
background(0, 50);
for (let i = 0; i < mover.length; i++) {
mover[i].show();
mover[i].update(x);
mover[i].edge();
// if rollover/ contains is true => then change collision +1 => collsison arrives at 255=> you are dead.
if (mover[i].contains(mouseX, mouseY)) {
collision += 1;
lrect += 0.04;
}
}
if (collision >= 255) {
clearInterval(cron);
started = false;
windowalert()
}
ship.move(mouseX, mouseY);
//ship.impact(mover[i].position.x,mover[i].position.y)
ship.show();
ship.edge();
noCursor();
x += 0.00005;
for (p = 0; p < planets.length; p++) {
planets[p].show(50 + p * 10, 180 + p * 40, 120 + p * 10);
planets[p].move();
}
for (i = 0; i < missil.length; i++) {
missil[i].show();
missil[i].update();
for (p = 0; p < planets.length; p++) {
missil[i].bounce(planets[p].position.x, planets[p].position.y, planets[p].lrect / 2)
}
//if missil is in the canvas then check for collision
if (missil[i].contains(width / 2, height / 2)) {
let colission = false;
for (let u = 0; u < mover.length; u++) {
if (mover[u].destroy(missil[i].position.x, missil[i].position.y)) {
mover.splice(u, 1);
missil.splice(i, 1);
// Exit the mover loop immediately
colission = true;
break;
}
}
if (colission) {
// because we've deleted the item at i, the item that was at
// i + 1 is now at i, so in order not to skip that item we
// need to decrement i before continuing
i--;
}
} else {
missil.splice(i, 1);
i--;
}
}
}
} // if missil is out of the canvas it get erased from the array.
setInterval(function() {
if (munitions < 100) {
munitions += 1
}
}, 1000);
function keyPressed() {
if (munitions > 0) {
if (keyIsDown(LEFT_ARROW))
{
a = -1;
munitions += -1
} // a is in the class munitions and represent the vector x. I did it like that so when we click both arrow it goes in diagonal.
if (keyIsDown(RIGHT_ARROW))
{
a = 1;
munitions += -1
}
if (keyIsDown(UP_ARROW)) {
b = -1;
munitions += -1
}
if (keyIsDown(DOWN_ARROW))
{
b = 1;
munitions += -1
}
for (let u = 0; u < 1; u++) {
let mi = new Missil(mouseX, mouseY, a, b);
missil.push(mi);
}
}
}
function keyReleased() {
if (keyCode == LEFT_ARROW) {
a = 0
} // this was implemented to reput the missil vector at the default value when released the key.
if (keyCode == RIGHT_ARROW) {
a = 0
}
if (keyCode == UP_ARROW) {
b = 0
}
if (keyCode == DOWN_ARROW) {
b = 0
}
}
class Missil {
constructor(x, y, a, b) //partira de mx,my.
{
this.position = createVector(x, y);
this.vel = createVector(a, b);
this.vel.mult(random(2, 4))
}
update() {
this.position = this.position.add(this.vel);
}
show() {
stroke(255);
noStroke();
fill(255, 0, 0, 100);
ellipse(this.position.x, this.position.y, 5);
}
contains(px, py) {
if (dist(this.position.x, this.position.y, px, py) < width / 2) {
return true
} else {
return false
}
}
bounce(px, py, dista) {
{
if (dist(this.position.x, this.position.y, px, py) < dista) {
let v = createVector(this.position.x - px, this.position.y - py);
this.vel = this.vel.mult(1.5).reflect(v)
}
}
}
}
class Planet {
constructor(x, y, lrect, lrect2) {
this.position = createVector(x, y)
this.vel = p5.Vector.random2D();
this.lrect = lrect;
this.lrect2 = lrect2;
}
show(r, g, b) {
fill(r, g, b);
noStroke();
ellipseMode(CENTER);
ellipse(this.position.x, this.position.y, this.lrect, this.lrect2)
stroke(255);
}
move() {
let center = createVector(width / 2, height / 2)
this.gravityacc = p5.Vector.sub(center, this.position);
this.gravityacc.setMag(0.004);
this.vel = this.vel.add(this.gravityacc);
this.position = this.position.add(this.vel);
this.vel.limit(1.3);
}
}
class Mover { //those are the comets
constructor(x, y) {
this.position = createVector(x, y);
this.vel = p5.Vector.random2D();
this.vel.mult(random(3));
//this.acc=p5.Vector.random2D();// acceleeration is a a random vector here.
// this.acc.setMag(0.01); // magnitude of acceleration is slow. Acceleration is a vector.
// this.vel.limit(3); // shrinks size of vector to 5, but if it smaller then 5 then it doent equal to 5 like in setMag().
}
update(speed)
{
setInterval(function() {
this.speed += 0.01
}, 3000);
let mouse = createVector(mouseX, mouseY);
this.acc = p5.Vector.sub(mouse, this.position);
this.acc.setMag(0.04)
this.acc.limit(0.1)
this.vel.add(this.acc); /// add acceleration to velocitiy.
this.position.add(this.vel);
this.vel.limit(5);
}
show() {
stroke(255, 10);
strokeWeight(0);
fill(map(this.position.x, 0, width, 0, 255), map(this.position.y, 0, height, 0, 255), 255, 255);
ellipse(this.position.x, this.position.y, 5)
}
edge() {
if (this.position.x >= width) {
let n = createVector(-1, 0);
this.vel = this.vel.reflect(n)
}
if (this.position.x <= 0) {
let n = createVector(1, 0);
this.vel = this.vel.reflect(n)
}
if (this.position.y >= height) {
let n = createVector(0, -1);
this.vel = this.vel.reflect(n)
}
if (this.position.y <= 0) {
let n = createVector(0, 1);
this.vel = this.vel.reflect(n)
}
}
contains(px, py) {
if (dist(this.position.x, this.position.y, px, py) < 5 + lrect) {
return true
} else {
return false
}
}
destroy(px, py) {
if (dist(this.position.x, this.position.y, px, py) <= 20) {
return true
} else {
return false;
}
}
}
class Ship {
constructor(x, y) {
this.position = createVector(x, y);
this.vel = createVector();
this.acc = createVector();
}
move(px, py) {
this.position.x = px;
this.position.y = py
} //if key pressed.
edge() {
if (this.position.x >= width) {
this.position.x = width
}
if (this.position.y >= height - 50) {
this.position.y = height - 50
}
}
show() {
stroke(255);
strokeWeight(0);
fill(collision * 1, 255 - collision * 2, 0, 100);
rect(this.position.x, this.position.y, lrect, lrect)
}
}
/* Without the HTML this isn't functional
"use strict";
document.form_main.start.onclick = () => start();
document.form_main.pause.onclick = () => pause();
document.form_main.reset.onclick = () => reset();
function start() {
pause();
cron = setInterval(() => {
timer();
}, 10);
started = true; // to indicate to start draw
loop(); // noLoop in fucntion setup.
}
function pause() {
clearInterval(cron);
started = false;
}
function reset() {
location.reload();
}
function timer() {
if ((millisecond += 10) == 1000) {
millisecond = 0;
second++;
}
if (second == 60) {
second = 0;
minute++;
}
if (minute == 60) {
minute = 0;
hour++;
}
document.getElementById('hour').innerText = returnData(hour);
document.getElementById('minute').innerText = returnData(minute);
document.getElementById('second').innerText = returnData(second);
document.getElementById('millisecond').innerText = returnData(millisecond);
}
function returnData(input) {
return input > 10 ? input : `0${input}`
} */
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
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>
I have a game in which there si a rocket. When rockets health is 0 the game over screen should appear and when any key is pressed the game should restart.
I tried using redraw and loop keywords in p5.js
let x = 240;
let x1 = 258;
let score = 0;
let health = 5;
let opelsins = [];
let ecllipseOpelsin = [];
let monsterimg;
let meteorimg;
let inGameSound;
let opelsinimg;
let gameOverSound;
let backgroundsun;
let gameoverScreen;
function collideRectangleCircle(rx, ry, rw, rh, cx, cy, cr)
{
return rx+rw > cx-cr && cx+cr > rx && ry+rh > cy-cr && cy+cr > ry;
}
function Opelsin() {
this.x = random(40,560);
this.y = random(-200,-190);
this.speed = random(3,10);
this.fall = function() {
this.y = this.y + this.speed;
if (this.y > height) {
this.y = random(-200,-100);
this.x = random(40,560);
this.speed = random(3, 10);
}
};
this.show = function() {
fill(255)
ellipse(this.x + 15,this.y + 34, 10, 10)
image(opelsinimg,this.x,this.y, 40, 40)
};
}
function opelsinmodel() {
this.x = random(0,600);
this.y = random(-300,-310);
this.speed = random(3,10);
this.show = function() { ellipse(this.x,this.y, 20, 20) };
}
function setup() {
createCanvas(600, 400);
frameRate(300)
inGameSound.setVolume(1);
inGameSound.loop();
interval = setInterval(scoreCount, 500);
}
function gameOver() {
textSize(20);
text("GAME OVER", 250, 200);
text("SCORE: " + score, 270, 220);
fill(255);
}
function newGame() {
if (keyIsPressed === true) {
//redraw();
loop();
}
}
function preload() {
soundFormats('m4a')
monsterimg = loadImage('assets/monster.png');
opelsinimg = loadImage('assets/opelsin.png');
inGameSound = loadSound('assets/spaceBotInGameMusic.m4a');
gameOverSound = loadSound('assets/gameOverInGameSound.m4a');
backgroundsun = loadImage('assets/backgroundsun.jpg');
gameoverScreen = loadImage('assets/gameoverScreen.png');
}
function scoreCount() {
score++;
}
function draw() {
// background(11, 72, 170);
image(backgroundsun, 0, 0, 700, 400);
if (score == 0) {
for (let i = 0; i < 5; i ++) {
opelsins[i] = new Opelsin();
ecllipseOpelsin[i] = new opelsinmodel();
}
} if (score == 50) {
for (let i = 0; i < 10; i ++) {
opelsins[i] = new Opelsin();
ecllipseOpelsin[i] = new opelsinmodel();
}
}
if (score == 100) {
for (let i = 0; i < 15; i ++) {
opelsins[i] = new Opelsin();
ecllipseOpelsin[i] = new opelsinmodel();
}
}
if (score == 150) {
for (let i = 0; i < 20; i ++) {
opelsins[i] = new Opelsin();
ecllipseOpelsin[i] = new opelsinmodel();
}
}
if (score == 200) {
for (let i = 0; i < 25; i ++) {
opelsins[i] = new Opelsin();
ecllipseOpelsin[i] = new opelsinmodel();
}
}
if (keyIsDown(LEFT_ARROW) && x > -14) {
x -= 5;
}
if (keyIsDown(RIGHT_ARROW) && x < 550) {
x += 5;
}
if (keyIsDown(LEFT_ARROW) && x1 > 9) {
x1 -= 5;
}
if (keyIsDown(RIGHT_ARROW) && x1 < 565) {
x1 += 5;
}
fill(255, 255, 0)
rect(x1, 345, 20, 20)
image(monsterimg,x,310,60,60)
for (let opelsin of opelsins) {
opelsin.fall();
opelsin.show();
}
textSize(20);
text("Health: " + health, 10, 20);
fill(255);
textSize(20);
text("Score: " + score, 10, 40);
fill(255);
for (let opelsin of opelsins) {
hit = collideRectCircle(x1, 335, 20, 30, opelsin.x, opelsin.y, 40);
if(hit == true) {
health -= 1;
opelsin.y = height+1;
if (health == 0) {
inGameSound.stop();
gameOverSound.setVolume(1);
gameOverSound.loop();
image(gameoverScreen, 0, 0, 600, 400);
gameOver();
noLoop();
newGame();
}
}
}
}
What I expect is when the game over screen appears and then when the random key is pressed the game should restart.
The issue is that the game loop is stopped by noLoop() when the game is over. The loop is never restarted.
Set a state (e.g waitRestart) when the game is over (instead of newGame()):
let waitRestart = false;
function draw() {
// [...]
for (let opelsin of opelsins) {
hit = collideRectCircle(x1, 335, 20, 30, opelsin.x, opelsin.y, 40);
if(hit == true) {
health -= 1;
opelsin.y = height+1;
if (health == 0) {
inGameSound.stop();
gameOverSound.setVolume(1);
gameOverSound.loop();
image(gameoverScreen, 0, 0, 600, 400);
gameOver();
noLoop();
// newGame(); <----- delete
waitRestart = true; // <----- wait for restart
}
}
}
}
Add a keyPressed() callback instead of the newGame() function, which restarts the game and set all initial states like health and score:
function keyPressed() {
if (waitRestart) {
waitRestart = false;
score = 0;
health = 5;
loop();
}
}