Related
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>
// Commented because does not work in Sandbox
// window.localStorage; //Ignore this line
// Where all my variables have been assigned
var c = document.getElementById("GameScreen");
var ctx = c.getContext("2d");
var charY = 220;
const gravity = 10;
var score = 0;
var time = 0;
var speed = 5;
var cloneID = 0;
var clonePos = [600];
var clonePoints = [0];
var animationBounce = 0;
var jump = 10;
var charDead = 0;
var dataCharY = [];
var dataDisObst = [];
var disObst = 1000;
var lowestLoopDis;
var jumpFactor = 0;
var disDeath;
var AIgames = 1;
var bestScoreAI = 0;
ctx.translate(c.width / 2, c.height / 2);
// Was going to use this for background trees but haven't done it yet
new obj(50, 50, 30, 30);
// Runs most functions
function runAll() {
if (charDead == 0) {
clearAll(); //This function runs most of the code
updateChar();
createGround();
updateObj();
groundDetect();
updateScore();
hitDetect();
addData();
testBetterAI();
getDisObst();
jumpAI();
removeUnusedObst();
}
}
// Was going to use this for trees but haven't yet
function obj(x, y, width, height) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
ctx.beginPath();
ctx = c.getContext("2d");
ctx.fillStyle = "brown";
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.fillStyle = "green";
ctx.arc(-293, 150, 50, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
this.cloneID = cloneID;
}
new obj(-293, 212, 0, 2 * Math.PI);
// Creates the floor (IKR)
function createGround() {
ctx.fillStyle = "green";
ctx.fillRect(-635, 250, c.width, 50);
}
// Creates the character every milisecond (or 10, I can't remember)
function updateChar() {
ctx.fillStyle = "blue";
ctx.fillRect(-300, charY - animationBounce, 15, 30);
ctx.fillStyle = "pink";
ctx.beginPath();
ctx.arc(-293, charY - animationBounce - 15, 15, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
// Removes everything in order to be redrawn in new position
function clearAll() {
ctx.clearRect(-700, -700, 2000, 2000);
}
// Redraws every square / object
function updateObj() {
for (var i = 0; i != clonePos.length; i++) {
ctx.fillStyle = "green";
ctx.fillRect(clonePos[i], 220, 30, 30);
}
}
// Creates new square (I also decided to rename them half way through with obstacle instead of object)
function createObst() {
clonePos.push(600);
cloneID++;
}
// Changes the squares / obstacles position relative to the movement
function moveObst() {
for (var ii = 0; ii != clonePos.length; ii++) {
clonePos[ii] -= speed;
}
}
// Tests to see if the character is on the ground
function groundDetect() {
if (charY > 220) {
charY = 220;
}
}
// Makes gravity actually work
function charGravity() {
if (charY < 220) {
charY += gravity;
}
}
// Updates the score counter text
function updateScore() {
document.getElementById("scoreText").innerHTML = score;
}
// Gives the character a little bounce when moving
function charBounce() {
setTimeout(function() {
animationBounce++;
}, 100);
setTimeout(function() {
animationBounce++;
}, 200);
setTimeout(function() {
animationBounce++;
}, 300);
setTimeout(function() {
animationBounce--;
}, 400);
setTimeout(function() {
animationBounce--;
}, 500);
setTimeout(function() {
animationBounce--;
}, 600);
}
// Makes the character jump
function charJump() {
if (charY == 220) {
jump = 4;
setTimeout(function() {
charY -= jump;
}, 20);
jump = 8;
setTimeout(function() {
charY -= jump;
}, 40);
jump = 12;
setTimeout(function() {
charY -= jump;
}, 60);
jump = 16;
setTimeout(function() {
charY -= jump;
}, 80);
setTimeout(function() {
charY -= jump;
}, 100);
setTimeout(function() {
charY -= jump;
}, 120);
}
}
// Detects when the character has a hit a square
function hitDetect() {
for (var iB = 0; iB != clonePos.length; iB++) {
if (clonePos[iB] > -320 && clonePos[iB] < -280 && charY > 200) {
charDied();
}
}
}
// Runs when character dies
function charDied() {
disDeath = disObst;
charDead = 1;
charRevive();
testBetterAI();
decideAdjustments();
}
// Adds score very interval
function addingScore() {
if (charDead == 0) {
score += 100;
}
}
// Adds to an array that I will use later
function addData() {
dataCharY.push(charY);
dataDisObst.push(disObst);
}
// Test to see if one of my AI's (which hasn't been made yet) scores is better than the previous best
function testBetterAI() {
// Commented because does not work in Sandbox
// if (score > localStorage.getItem("bestScore")) {
// }
}
// Calculates the distance to the nearest square / obstacle
function getDisObst() {
lowestLoopDis = 1000;
for (var iiA = 0; iiA != clonePos.length; iiA++) {
if (clonePos[iiA] > -320) {
if (clonePos[iiA] > 0) {
if (Math.abs(clonePos[iiA]) < lowestLoopDis) {
lowestLoopDis = Math.abs(clonePos[iiA]);
}
} else {
if (Math.abs(clonePos[iiA]) < lowestLoopDis) {
lowestLoopDis = Math.abs(clonePos[iiA]);
}
}
}
}
if (lowestLoopDis < disObst) {
disObst = lowestLoopDis;
}
}
// Increments the speed of the obstacles / squares and the character
function addSpeed() {
if (speed < 25) {
speed++;
}
}
// Restarts the game
function charRevive() {
clonePos = [600];
charDead = 0;
score = 0;
time = 0;
speed = 5;
AIgames++;
}
// I accidently did this twice, whoops
function testBetterAI() {
if (score > bestScoreAI) {
bestScoreAI = score;
}
}
// Makes the unfinished AI jump when it wants to
function jumpAI() {
if (disObst <= disDeath + jumpFactor) {
charJump();
}
}
// What changes need to be made in order to improve the AI
function decideAdjustments() {
jumpFactor += Math.floor(Math.random() * 10) - 5;
if (jumpFactor < 0) {
jumpFactor = 0;
}
}
// Removing blocks that are off the screen
function removeUnusedObst() {
if (clonePos[0] < -650) {
clonePos.shift();
}
}
// Intervals here
setInterval(function() {
time++;
}, 1000);
setInterval(function() {
runAll();
}, 10);
setInterval(function() {
moveObst();
}, 50);
setInterval(function() {
charGravity();
}, 25);
setInterval(function() {
createObst();
}, 3000);
setInterval(function() {
charBounce();
}, 650);
setInterval(function() {
addingScore();
}, 3500);
setInterval(function() {
addSpeed();
}, 25000);
#GameScreen {
background-color: CornflowerBlue;
}
#scoreText {
text-align: center;
font-size: 35px;
}
<div id="scoreText"></div>
<canvas id="GameScreen" width="1270px" height="550px"></canvas>
What happens
In your hitDetect function:
function hitDetect() {
for (var iB = 0; iB != clonePos.length; iB++) {
if (clonePos[iB] > -320 && clonePos[iB] < -280 && charY > 200) {
charDied();
}
}
}
You loop over the clonePos array, until iB is equal to the length of that array. If a condition is met (collision), you execute charDied, which, in turn, executes charRevive:
function charRevive() {
clonePos = [600];
// ...
}
Meanwhile, the hitDetect loop continues. And at one point (seemingly when you start to increase the speed), it happens that iB is now above 1, which is the new length of clonePos. Now, what happens? Well, you are trapped in an infinite loop:
Lesson to be learned
If you're looping on an Array which can mutate during the loop (and change its length), never use this type of condition:
i != myArr.length
Always prefer a stricter condition if you want your loop to end at some point:
i < myArr.length
And, even better, if looping makes no sense after a condition is met, end this loop by returning. eg:
function hitDetect() {
for (var iB = 0; iB < clonePos.length; iB++) {
if (clonePos[iB] > -320 && clonePos[iB] < -280 && charY > 200) {
return charDied();
// Alternatively, if there is other stuff to do after the loop, you can use:
// break;
}
}
}
Fixed code
// Commented because does not work in Sandbox
// window.localStorage; //Ignore this line
// Where all my variables have been assigned
var c = document.getElementById("GameScreen");
var ctx = c.getContext("2d");
var charY = 220;
const gravity = 10;
var score = 0;
var time = 0;
var speed = 5;
var cloneID = 0;
var clonePos = [600];
var clonePoints = [0];
var animationBounce = 0;
var jump = 10;
var charDead = 0;
var dataCharY = [];
var dataDisObst = [];
var disObst = 1000;
var lowestLoopDis;
var jumpFactor = 0;
var disDeath;
var AIgames = 1;
var bestScoreAI = 0;
ctx.translate(c.width / 2, c.height / 2);
// Was going to use this for background trees but haven't done it yet
new obj(50, 50, 30, 30);
// Runs most functions
function runAll() {
if (charDead == 0) {
clearAll(); //This function runs most of the code
updateChar();
createGround();
updateObj();
groundDetect();
updateScore();
hitDetect();
addData();
testBetterAI();
getDisObst();
jumpAI();
removeUnusedObst();
}
}
// Was going to use this for trees but haven't yet
function obj(x, y, width, height) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
ctx.beginPath();
ctx = c.getContext("2d");
ctx.fillStyle = "brown";
ctx.fillRect(this.x, this.y, this.width, this.height);
ctx.fillStyle = "green";
ctx.arc(-293, 150, 50, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
this.cloneID = cloneID;
}
new obj(-293, 212, 0, 2 * Math.PI);
// Creates the floor (IKR)
function createGround() {
ctx.fillStyle = "green";
ctx.fillRect(-635, 250, c.width, 50);
}
// Creates the character every milisecond (or 10, I can't remember)
function updateChar() {
ctx.fillStyle = "blue";
ctx.fillRect(-300, charY - animationBounce, 15, 30);
ctx.fillStyle = "pink";
ctx.beginPath();
ctx.arc(-293, charY - animationBounce - 15, 15, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
// Removes everything in order to be redrawn in new position
function clearAll() {
ctx.clearRect(-700, -700, 2000, 2000);
}
// Redraws every square / object
function updateObj() {
for (var i = 0; i < clonePos.length; i++) {
ctx.fillStyle = "green";
ctx.fillRect(clonePos[i], 220, 30, 30);
}
}
// Creates new square (I also decided to rename them half way through with obstacle instead of object)
function createObst() {
clonePos.push(600);
cloneID++;
}
// Changes the squares / obstacles position relative to the movement
function moveObst() {
for (var ii = 0; ii < clonePos.length; ii++) {
clonePos[ii] -= speed;
}
}
// Tests to see if the character is on the ground
function groundDetect() {
if (charY > 220) {
charY = 220;
}
}
// Makes gravity actually work
function charGravity() {
if (charY < 220) {
charY += gravity;
}
}
// Updates the score counter text
function updateScore() {
document.getElementById("scoreText").innerHTML = score;
}
// Gives the character a little bounce when moving
function charBounce() {
setTimeout(function() {
animationBounce++;
}, 100);
setTimeout(function() {
animationBounce++;
}, 200);
setTimeout(function() {
animationBounce++;
}, 300);
setTimeout(function() {
animationBounce--;
}, 400);
setTimeout(function() {
animationBounce--;
}, 500);
setTimeout(function() {
animationBounce--;
}, 600);
}
// Makes the character jump
function charJump() {
if (charY == 220) {
jump = 4;
setTimeout(function() {
charY -= jump;
}, 20);
jump = 8;
setTimeout(function() {
charY -= jump;
}, 40);
jump = 12;
setTimeout(function() {
charY -= jump;
}, 60);
jump = 16;
setTimeout(function() {
charY -= jump;
}, 80);
setTimeout(function() {
charY -= jump;
}, 100);
setTimeout(function() {
charY -= jump;
}, 120);
}
}
// Detects when the character has a hit a square
function hitDetect() {
for (var iB = 0; iB < clonePos.length; iB++) {
if (clonePos[iB] > -320 && clonePos[iB] < -280 && charY > 200) {
return charDied();
}
}
}
// Runs when character dies
function charDied() {
disDeath = disObst;
charDead = 1;
charRevive();
testBetterAI();
decideAdjustments();
}
// Adds score very interval
function addingScore() {
if (charDead == 0) {
score += 100;
}
}
// Adds to an array that I will use later
function addData() {
dataCharY.push(charY);
dataDisObst.push(disObst);
}
// Test to see if one of my AI's (which hasn't been made yet) scores is better than the previous best
function testBetterAI() {
// Commented because does not work in Sandbox
// if (score > localStorage.getItem("bestScore")) {
// }
}
// Calculates the distance to the nearest square / obstacle
function getDisObst() {
lowestLoopDis = 1000;
for (var iiA = 0; iiA < clonePos.length; iiA++) {
if (clonePos[iiA] > -320) {
if (clonePos[iiA] > 0) {
if (Math.abs(clonePos[iiA]) < lowestLoopDis) {
lowestLoopDis = Math.abs(clonePos[iiA]);
}
} else {
if (Math.abs(clonePos[iiA]) < lowestLoopDis) {
lowestLoopDis = Math.abs(clonePos[iiA]);
}
}
}
}
if (lowestLoopDis < disObst) {
disObst = lowestLoopDis;
}
}
// Increments the speed of the obstacles / squares and the character
function addSpeed() {
if (speed < 25) {
speed++;
}
}
// Restarts the game
function charRevive() {
clonePos = [600];
charDead = 0;
score = 0;
time = 0;
speed = 5;
AIgames++;
}
// I accidently did this twice, whoops
function testBetterAI() {
if (score > bestScoreAI) {
bestScoreAI = score;
}
}
// Makes the unfinished AI jump when it wants to
function jumpAI() {
if (disObst <= disDeath + jumpFactor) {
charJump();
}
}
// What changes need to be made in order to improve the AI
function decideAdjustments() {
jumpFactor += Math.floor(Math.random() * 10) - 5;
if (jumpFactor < 0) {
jumpFactor = 0;
}
}
// Removing blocks that are off the screen
function removeUnusedObst() {
if (clonePos[0] < -650) {
clonePos.shift();
}
}
// Intervals here
setInterval(function() {
time++;
}, 1000);
setInterval(function() {
runAll();
}, 10);
setInterval(function() {
moveObst();
}, 50);
setInterval(function() {
charGravity();
}, 25);
setInterval(function() {
createObst();
}, 3000);
setInterval(function() {
charBounce();
}, 650);
setInterval(function() {
addingScore();
}, 3500);
setInterval(function() {
addSpeed();
}, 25000);
body { text-align: center; }
#GameScreen {
background-color: CornflowerBlue;
width: 350px; height: 150px;
}
<div id="scoreText"></div>
<canvas id="GameScreen" width="1270px" height="550px"></canvas>
As of now, all I know to be certain is to set the global variable of 'paused' to be false. Adding an eventlistener and updating my loop function, etc. is where I am uncertain on implementation, otherwise the game is "finished" by all means!
var canvas = document.getElementById('canvas');
canvas.width = 1280;
canvas.height = 700;
var ctx = canvas.getContext('2d');
let obstacles = [];
var cancelMe = '';
let difficulty = 10;
let id;
let dis = 0;
let miles = 0;
let paused = false;
function getScore() {
let highScore = localStorage.getItem('highscore');
console.log('highscore is ', highScore);
document.getElementById('score').innerHTML = 'Highscore: ' + highScore + ' ft.';
}
getScore();
function saveScore(score) {
let highScore;
if (!isNaN(localStorage.getItem('highscore'))) {
highScore = localStorage.getItem('highscore');
} else {
highScore = 0;
}
console.log(highScore);
highScore = Math.max(score, highScore);
localStorage.setItem('highscore', highScore);
}
var img = new Image();
img.src = './images/background.jpg';
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
var durianImg = new Image();
durianImg.src = './images/durian.png';
durianImg.onload;
var backgroundImage = {
img: img,
x: 0,
speed: -1.5,
move: function() {
backgroundImage.x += this.speed;
backgroundImage.x %= canvas.width;
sprite.distance += 0.2;
},
draw: function() {
ctx.drawImage(this.img, this.x, 0);
if (this.speed < 0) {
ctx.drawImage(this.img, this.x + canvas.width, 0);
} else {
ctx.drawImage(this.img, this.x - this.img.width, 0);
}
}
};
var timeFalling = 0;
function clamp(num, min, max) {
return num <= min ? min : num >= max ? max : num;
}
var sprite = {
name: 'Mr. Sprite',
x: 2,
y: 528,
distance: 0,
int: null,
moveLeft: function() {
sprite.x -= 30;
sprite.x = clamp(this.x, 0, 1280);
sprite.distance -= 30;
},
moveRight: function() {
sprite.x += 30;
sprite.x = clamp(this.x, 0, 1230);
sprite.distance += 30;
},
moveUp: function() {
if ((sprite.y = 528)) {
sprite.y -= 70;
this.beginFall();
}
},
beginFall: function() {
clearInterval(this.int);
timeFalling = 0;
this.int = setInterval(function() {
timeFalling = timeFalling + 1;
}, 10);
},
draw: function() {
spriteImg = new Image();
spriteImg.src = './images/sprite.png';
ctx.drawImage(spriteImg, sprite.x, sprite.y, 50, 60);
},
fall: function() {
if (this.y < 528) {
this.y += 9.8 * timeFalling / 150;
} else {
clearInterval(this.int);
this.y = 528;
}
}
};
class obstacle {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
fall() {
if (this.y < 528) {
this.y++;
}
}
}
document.onkeydown = function(e) {
switch (e.keyCode) {
case 37:
case 65:
sprite.moveLeft();
console.log('left', sprite);
break;
case 38:
case 87:
case 32:
sprite.moveUp();
console.log('right', sprite);
break;
case 39:
case 68:
sprite.moveRight();
console.log('right', sprite);
break;
}
};
function checkCollision(obstacle) {
if (obstacle.y + 60 > sprite.y && obstacle.y < sprite.y + 60) {
if (obstacle.x + 50 < sprite.x + 50 && obstacle.x + 50 > sprite.x) {
console.log('Collision');
gameOver();
} else if (obstacle.x < sprite.x + 41 && obstacle.x > sprite.x) {
console.log('Collision');
gameOver();
}
}
}
function updateCanvas() {
backgroundImage.move();
ctx.clearRect(0, 0, canvas.width, canvas.height);
backgroundImage.draw();
dis++;
if (dis % 20 == 0) {
miles++;
}
ctx.fillStyle = '#606060';
ctx.fillText('Distance Traversed: ' + miles + ' ft.', 540, 40);
sprite.draw();
sprite.fall();
if (dis % 40 == 0) {
obstacles.push(randomObstacle());
}
for (let i = 0; i < obstacles.length; i++) {
obstacles[i].draw();
obstacles[i].fall();
checkCollision(obstacles[i]);
if (obstacles[i].y > 520) obstacles.splice(i, 1);
}
}
function startGame() {
difficulty = Number(document.querySelector('#diffSelect').value);
id = setInterval(updateCanvas, difficulty);
startGameButton.disabled = true;
init();
loop();
}
var startGameButton = document.getElementById('startGameButton');
startGameButton.onclick = startGame;
function restartGame() {
clearInterval(id);
obstacles = [];
var timeFalling = 0;
sprite.x = 2;
sprite.y = 528;
sprite.distance = 0;
sprite.int = null;
dis = 0;
miles = 0;
fallSpeed = 1.0005;
startGameButton.disabled = false;
ctx.fillStyle = 'white';
ctx.font = '18px serif';
ctx.clearRect(0, 0, canvas.width, canvas.height);
backgroundImage.draw();
audio.pause();
location.reload();
}
var retryGameButton = document.getElementById('retryGameButton');
retryGameButton.onlick = restartGame;
ctx.fillStyle = 'white';
ctx.font = '18px serif';
function randomObstacle() {
let x = Math.random() * canvas.width;
let y = 0;
return new Durian(x, y);
}
let fallSpeed = 1.0003;
setInterval(function() {
fallSpeed += 0.0008; // tweak this to change how quickly it increases in difficulty
// console.log(fallSpeed);
}, 8000); // timer at which it gets harder
class Durian {
constructor(x, y) {
this.x = x;
this.y = y;
}
draw() {
ctx.drawImage(durianImg, this.x, this.y, 50, 60);
}
fall() {
if (this.y < 528) this.y = (this.y + 1) ** fallSpeed;
this.x -= 1.5;
}
}
var hotbod = document.querySelector('body');
function doStuff() {
hotbod.className += ' animate';
}
window.onload = function() {
doStuff();
};
function gameOver() {
clearInterval(id);
ctx.fillStyle = '#606060';
ctx.font = '70px Anton';
ctx.fillText('GAME OVER', 430, 300);
console.log('save ', miles);
saveScore(miles);
audio.pause();
new Audio('sounds/game_over.wav').play();
}
function init() {
audio = document.getElementById('audio');
// add listener function to loop on end
audio.addEventListener('ended', loop, false);
// set animation on perpetual loop
setInterval(animate);
}
function loop() {
audio.play();
}
And for my index.html where the buttons are displayed:
<div id="menu">
<div class="custom-select instruct">
<select id="diffSelect">
<option value="6.5">Easy</option>
<option value="5.5">Medium</option>
<option value="4.5">Hard</option>
<option value="3">Extreme</option>
</select>
</div>
<input id="startGameButton" type="button" class="instruct" onclick="startGame()" value="Start" />
<input id="retryGameButton" type="button" class="instruct" onclick="restartGame()" value="Reset" />
<p class="disclaimer">DISCLAIMER: Once you wipe out, hit reset & change to a higher difficulty if you dare, then hit start to play again!</p>
</div>
Try the following
btw any ... represents your code I didn't include to save space
...
let gameRunning = false;
...
document.onkeydown = function(e) {
switch (e.keyCode) {
case 80:
if(gameRunning) {
paused = !paused;
startGameButton.value = paused ? "Un-Pause" : "Pause";
}
break;
...
}
};
...
function startGame() {
if(gameRunning){
paused = !paused;
startGameButton.value = paused ? "Un-Pause" : "Pause";
}
else {
difficulty = Number(document.querySelector('#diffSelect').value);
id = setInterval(updateCanvas, difficulty);
gameRunning = true;
startGameButton.value = "Pause";
init();
loop();
}
}
...
Im trying to make a function that when hitting a enemy makes that bullet in the array disappear.
Ive tried useing pop, slice, shift but i cant get those to work.
the closest thing Ive found is to just make the array empty but it really should be the array -1 or minus that bullet.
js fiddle: https://jsfiddle.net/tmanrocks999/64thbvm3/309/
code:
var myGamePiece;
var endGoalPiece;
var myEnemy1;
var bullets = [];
var myEnemy1Hp = 10;
var myEnemy1Armor = 0;
var damage = 1;
var playerExp = 0;
var playerMaxExp = 10;
function startGame() {
myGameArea.start();
myGamePiece = new component(30, 30, 'red', 0, 240);
endGoalPiece = new component(30, 30, 'black', 450, 240);
myEnemy1 = new component(30, 30, 'green', 200, 240);
}
var myGameArea = {
canvas: document.createElement('canvas'),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext('2d');
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function(e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function(e) {
myGameArea.key = false;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
};
function component(width, height, color, x, y) {
this.gamearea = myGameArea;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
//this.gravity = 0.05;
//this.gravitySpeed = 0;
this.x = x;
this.y = y;
this.color = color;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
this.y += this.speedY; //+ this.gravitySpeed;
this.hitBottom();
this.hitTop();
this.hitRight();
this.hitLeft();
this.hitObject();
}
this.hitBottom = function() {
var rockbottom = myGameArea.canvas.height - this.height;
if (this.y > rockbottom) {
this.y = rockbottom;
}
}
this.hitTop = function() {
var rockTop = 0;
if (this.y < rockTop) {
this.y = rockTop;
}
}
this.hitRight = function() {
var rockRight = myGameArea.canvas.width - this.width;
if (this.x > rockRight) {
this.x = rockRight;
}
}
this.hitLeft = function() {
var rockLeft = 0;
if (this.x < rockLeft) {
this.x = rockLeft;
}
}
function enemyRespawn() {
myEnemy1 = new component(30, 30, "green", 200, 240);
myEnemy1Hp = 10;
document.getElementById('myEnemy1Hp').innerHTML = myEnemy1Hp;
}
this.hitObject = function() {
myGamePiece.update();
var enemy = myEnemy1.x - 11;
if (this.x == enemy) {
myEnemy1Hp = myEnemy1Hp - (damage - myEnemy1Armor);
bullets = [];
document.getElementById('myEnemy1Hp').innerHTML = myEnemy1Hp;
if (myEnemy1Hp <=0) {
myEnemy1Hp = 0;
document.getElementById('myEnemy1Hp').innerHTML = myEnemy1Hp;
playerExp = playerExp+1;
document.getElementById('playerExp').innerHTML = playerExp;
if (playerExp >= playerMaxExp) {
playerExp = 0;
playerMaxExp = playerMaxExp * 1.5;
damage = damage + 1;
document.getElementById('playerExp').innerHTML = playerExp;
document.getElementById('playerMaxExp').innerHTML = playerMaxExp;
}
myEnemy1 = new component(0, 0, 'green', 0, 0);
myEnemy1.update();
setTimeout(enemyRespawn, 5000);
}
}
}
}
function shootGun() {
let bullet = new component(11, 5, 'blue', myGamePiece.x + 27, myGamePiece.y + 13);
bullet.newPos();
bullet.speedX = 1;
bullets.push(bullet);
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.key && myGameArea.key == 37) {
myGamePiece.speedX = -1;
} //left
if (myGameArea.key && myGameArea.key == 39) {
myGamePiece.speedX = 1;
} //right
if (myGameArea.key && myGameArea.key == 38) {
myGamePiece.gravitySpeed = -1;
} //jump
if (myGameArea.key && myGameArea.key == 32) {
shootGun()
} //shoot gun
//if (myGameArea.key && myGameArea.key == 40) {myGamePiece.speedY = 1; }// down
myEnemy1.update();
endGoalPiece.update();
myGamePiece.newPos();
myGamePiece.update();
bullets.forEach((bullet) => {
bullet.newPos()
bullet.update();
});
// bullet.newPos();
// bullet.update();
}
startGame();
canvas {
border: 4px solid #d3d3d3;
background-color: #f1f1f1;
}
<p>use the arrow keys on you keyboard to move the red square.</p>
<span id="myEnemy1Hp">10</span> <br>
<span id="playerExp">0</span> / <span id="playerMaxExp">10</span> 🐺
I expect when the bullet hit the enemy for it to disappear then enemy takes 1 damage. but at the moment this works but if u have more then 1 bullet on the screen all the bullets disappear when 1 hits the enemy. How do i make it current bullet or array = array -1
I believe you are looking for array.splice()
In the context of your hitObject method, the code would look like this:
bullets.splice(bullets.indexOf(this), 1);
Also, there are a ton of notes in my answer to a similar question which you may find useful.
...and here's your updated game:
var myGamePiece;
var endGoalPiece;
var myEnemy1;
var bullets = [];
var myEnemy1Hp = 10;
var myEnemy1Armor = 0;
var damage = 1;
var playerExp = 0;
var playerMaxExp = 10;
function startGame() {
myGameArea.start();
myGamePiece = new component(30, 30, 'red', 0, 240);
endGoalPiece = new component(30, 30, 'black', 450, 240);
myEnemy1 = new component(30, 30, 'green', 200, 240);
}
var myGameArea = {
canvas: document.createElement('canvas'),
start: function() {
this.canvas.width = 480;
this.canvas.height = 270;
this.context = this.canvas.getContext('2d');
document.body.insertBefore(this.canvas, document.body.childNodes[0]);
this.interval = setInterval(updateGameArea, 20);
window.addEventListener('keydown', function(e) {
myGameArea.key = e.keyCode;
})
window.addEventListener('keyup', function(e) {
myGameArea.key = false;
})
},
clear: function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
};
function component(width, height, color, x, y) {
this.gamearea = myGameArea;
this.width = width;
this.height = height;
this.speedX = 0;
this.speedY = 0;
//this.gravity = 0.05;
//this.gravitySpeed = 0;
this.x = x;
this.y = y;
this.color = color;
this.update = function() {
ctx = myGameArea.context;
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
}
this.newPos = function() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
this.y += this.speedY; //+ this.gravitySpeed;
this.hitBottom();
this.hitTop();
this.hitRight();
this.hitLeft();
this.hitObject();
}
this.hitBottom = function() {
var rockbottom = myGameArea.canvas.height - this.height;
if (this.y > rockbottom) {
this.y = rockbottom;
}
}
this.hitTop = function() {
var rockTop = 0;
if (this.y < rockTop) {
this.y = rockTop;
}
}
this.hitRight = function() {
var rockRight = myGameArea.canvas.width - this.width;
if (this.x > rockRight) {
this.x = rockRight;
}
}
this.hitLeft = function() {
var rockLeft = 0;
if (this.x < rockLeft) {
this.x = rockLeft;
}
}
function enemyRespawn() {
myEnemy1 = new component(30, 30, 'green', 200, 240);
myEnemy1Hp = 10;
document.getElementById('myEnemy1Hp').innerHTML = myEnemy1Hp;
}
this.hitObject = function() {
myGamePiece.update();
var enemy = myEnemy1.x - 11;
if (this.x == enemy) {
myEnemy1Hp = myEnemy1Hp - (damage - myEnemy1Armor);
// bullets = []; // replaces all bullets
const index = bullets.indexOf(this)
bullets.splice(index, 1)
document.getElementById('myEnemy1Hp').innerHTML = myEnemy1Hp;
if (myEnemy1Hp <= 0) {
myEnemy1Hp = 0;
document.getElementById('myEnemy1Hp').innerHTML = myEnemy1Hp;
playerExp = playerExp + 1;
document.getElementById('playerExp').innerHTML = playerExp;
if (playerExp >= playerMaxExp) {
playerExp = 0;
playerMaxExp = playerMaxExp * 1.5;
damage = damage + 1;
document.getElementById('playerExp').innerHTML = playerExp;
document.getElementById('playerMaxExp').innerHTML = playerMaxExp;
}
myEnemy1 = new component(0, 0, 'green', 0, 0);
myEnemy1.update();
setTimeout(enemyRespawn, 5000);
}
}
}
}
function shootGun() {
let bullet = new component(11, 5, 'blue', myGamePiece.x + 27, myGamePiece.y + 13);
bullet.newPos();
bullet.speedX = 1;
bullets.push(bullet);
}
function updateGameArea() {
myGameArea.clear();
myGamePiece.speedX = 0;
myGamePiece.speedY = 0;
if (myGameArea.key && myGameArea.key == 37) {
myGamePiece.speedX = -1;
} //left
if (myGameArea.key && myGameArea.key == 39) {
myGamePiece.speedX = 1;
} //right
if (myGameArea.key && myGameArea.key == 38) {
myGamePiece.gravitySpeed = -1;
} //jump
if (myGameArea.key && myGameArea.key == 32) {
shootGun()
} //shoot gun
//if (myGameArea.key && myGameArea.key == 40) {myGamePiece.speedY = 1; }// down
myEnemy1.update();
endGoalPiece.update();
myGamePiece.newPos();
myGamePiece.update();
bullets.forEach((bullet) => {
bullet.newPos(); // update and check for collisions
bullet.update();
});
// bullet.newPos();
// bullet.update();
}
startGame();
canvas {
border: 4px solid #d3d3d3;
background-color: #f1f1f1;
}
<p>use the arrow keys on you keyboard to move the red square.</p>
<span id="myEnemy1Hp">10</span> <br>
<span id="playerExp">0</span> / <span id="playerMaxExp">10</span> 🐺
I have been following The Coding Train's coding challenge where he creates a flocking algorithm using cohesion, separation, and alignment. I challenged myself to see if I could rotate each triangle object so they are looking where they are going, however, I have struggled to implement this into my code.
My Boid class is where all the algorithms are located
const flock = [];
let alignSlider, cohesionSlider, separationSlider;
function setup() {
createCanvas(900, 600);
for (i = 0; i < 50; i++) {
flock.push(new Boid());
}
alignSlider = createSlider(0, 5, 1, 0.1);
cohesionSlider = createSlider(0, 5, 1, 0.1);
separationSlider = createSlider(0, 5, 2, 0.1);
}
function draw() {
background(51);
for (let boid of flock) {
boid.show();
boid.edges();
boid.flock(flock);
boid.update();
}
}
class Boid {
constructor() {
this.position = createVector(random(width), random(height));
this.velocity = p5.Vector.random2D();
this.velocity.setMag(random(4, 6));
this.acceleration = createVector();
this.maxForce = 0.2;
this.maxSpeed = 4;
}
edges() {
if (this.position.x > width) {
this.position.x = 0;
}
if (this.position.x < 0) {
this.position.x = width;
}
if (this.position.y > height) {
this.position.y = 0;
}
if (this.position.y < 0) {
this.position.y = height;
}
}
align(boids) {
let perceptionRadius = 100;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.position.x,
this.position.y,
other.position.x,
other.position.y
);
if (other != this && d < perceptionRadius) {
steering.add(other.velocity);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
cohesion(boids) {
let perceptionRadius = 100;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.position.x,
this.position.y,
other.position.x,
other.position.y
);
if (other != this && d < perceptionRadius) {
steering.add(other.position);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.sub(this.position);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
separation(boids) {
let perceptionRadius = 50;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.position.x,
this.position.y,
other.position.x,
other.position.y
);
if (other != this && d < perceptionRadius) {
let diff = p5.Vector.sub(this.position, other.position);
diff.div(d);
steering.add(diff);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
flock(boids) {
let alignment = this.align(boids);
let cohesion = this.cohesion(boids);
let separation = this.separation(boids);
alignment.mult(alignSlider.value());
cohesion.mult(cohesionSlider.value());
separation.mult(separationSlider.value());
this.acceleration.add(alignment);
this.acceleration.add(cohesion);
this.acceleration.add(separation);
}
update() {
this.position.add(this.velocity);
this.velocity.add(this.acceleration);
this.velocity.limit(this.maxSpeed);
this.acceleration.mult(0);
}
show() {
//this is where i create my triangle and this is where i would like to be able to rotate it
let triangleSize = 12;
let x = this.position.x;
let y = this.position.y;
fill(51);
stroke(255);
// ellipseMode(CENTER);
// rotate(this.velocity.heading());
triangle(
x,
y,
x + triangleSize,
y,
x + triangleSize / 2,
y + triangleSize * 1.2
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/addons/p5.dom.min.js"></script>
I modified your code so that the boids will point in the direction that they are moving by using push, translate, rotate and pop.
The change is all within Boid.show()
push();
translate(x, y);
rotate(this.velocity.heading() - radians(90));
triangle(0,0,triangleSize,0,triangleSize / 2,triangleSize * 1.2);
pop()
Push allows us to just apply the translate and rotation to one boid at a time as pop will undo the changes. I translate to x, y and then position the triangle at 0, 0 so the rotation will only rotate the boid triangle.
I also had to subtract 90 degrees or 1/2 pi from the velocity heading to get the boid to point in the direction of movement.
const flock = [];
let alignSlider, cohesionSlider, separationSlider;
var theta = 0.1;
function setup() {
createCanvas(900, 600);
for (i = 0; i < 50; i++) {
flock.push(new Boid());
}
alignSlider = createSlider(0, 5, 1, 0.1);
cohesionSlider = createSlider(0, 5, 1, 0.1);
separationSlider = createSlider(0, 5, 2, 0.1);
}
function draw() {
theta += .01;
background(51);
for (let boid of flock) {
boid.show();
boid.edges();
boid.flock(flock);
boid.update();
}
}
class Boid {
constructor() {
this.position = createVector(random(width), random(height));
this.velocity = p5.Vector.random2D();
this.velocity.setMag(random(4, 6));
this.acceleration = createVector();
this.maxForce = 0.2;
this.maxSpeed = 4;
}
edges() {
if (this.position.x > width) {
this.position.x = 0;
}
if (this.position.x < 0) {
this.position.x = width;
}
if (this.position.y > height) {
this.position.y = 0;
}
if (this.position.y < 0) {
this.position.y = height;
}
}
align(boids) {
let perceptionRadius = 100;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.position.x,
this.position.y,
other.position.x,
other.position.y
);
if (other != this && d < perceptionRadius) {
steering.add(other.velocity);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
cohesion(boids) {
let perceptionRadius = 100;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.position.x,
this.position.y,
other.position.x,
other.position.y
);
if (other != this && d < perceptionRadius) {
steering.add(other.position);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.sub(this.position);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
separation(boids) {
let perceptionRadius = 50;
let steering = createVector();
let total = 0;
for (let other of boids) {
let d = dist(
this.position.x,
this.position.y,
other.position.x,
other.position.y
);
if (other != this && d < perceptionRadius) {
let diff = p5.Vector.sub(this.position, other.position);
diff.div(d);
steering.add(diff);
total++;
}
}
if (total > 0) {
steering.div(total);
steering.setMag(this.maxSpeed);
steering.sub(this.velocity);
steering.limit(this.maxForce);
}
return steering;
}
flock(boids) {
let alignment = this.align(boids);
let cohesion = this.cohesion(boids);
let separation = this.separation(boids);
alignment.mult(alignSlider.value());
cohesion.mult(cohesionSlider.value());
separation.mult(separationSlider.value());
this.acceleration.add(alignment);
this.acceleration.add(cohesion);
this.acceleration.add(separation);
}
update() {
this.position.add(this.velocity);
this.velocity.add(this.acceleration);
this.velocity.limit(this.maxSpeed);
this.acceleration.mult(0);
}
show() {
//this is where i create my triangle and this is where i would like to be able to rotate it
let triangleSize = 12;
let x = this.position.x;
let y = this.position.y;
fill(51);
stroke(255);
push();
translate(x, y);
rotate(this.velocity.heading() - radians(90));
triangle(
0,
0,
triangleSize,
0,
triangleSize / 2,
triangleSize * 1.2
);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/addons/p5.dom.min.js"></script>