Having trouble using instance mode - javascript

I am getting errors when I used instance mode in my code. My mini game isn't showing up I am not sure where the error is coming from. It works fine without the instance mode but I need to use the instance mode so that I can reference this code in another file. I'm using this link as a reference. http://p5js.org/examples/instance-mode-instantiation.html
let sketch = function(p) {
let blob;
let blobs = [];
let zoom = 1;
let timer = 20;
let hits = false;
let score = 0;
p.setup = function() {
p.createCanvas(600, 600);
blob = new Blob(0, 0, 64);
for (let i = 0; i < 300; i++) {
let x = p.random(-p.width,p.width);
let y = p.random(-p.height,p.height);
blobs[i] = new Blob(x, y, 15);
}
};
p.draw = function() {
p.background(0);
p.translate(width/2, height/2);
let newzoom = 64 / blob.r;
p.zoom = p.lerp(zoom, newzoom, 0.1);
p.scale(zoom);
p.translate(-blob.pos.x, -blob.pos.y);
for (var i = blobs.length-1; i >=0; i--) {
blobs[i].show();
if (blob.eats(blobs[i])) {
blobs.splice(i, 1);
}
}
if (frameCount % 60 == 0 && timer > 0) { // if the frameCount is divisible by 60, then a second has passed. it will stop at 0
p.timer --;
}
if (timer == 0 && score >=250) {
p.text("You Win", 0, 0);
p.noLoop();
}
if (blob.eats){
p.console.log("hit");
}
if (timer == 0 && score <= 250){
p.text("You Lose", 0, 0);
p.textSize(200);
p.noLoop();
}
blob.show();
blob.update();
};
};
let myp5 = new p5(sketch);
class Blob {
constructor(x, y, r) {
this.pos = createVector(x, y);
this.r = r;
this.vel = createVector(0,0);
}
show(p) {
p.ellipse(this.pos.x, this.pos.y, this.r, this.r);
}
eats(other) {
let d = p5.Vector.dist(this.pos, other.pos);
if (d < this.r + other.r) {
let sum = PI * this.r * this.r + PI * other.r * other.r;
score ++;
this.r = sqrt(sum / PI);
//this.r += other.r;
return true;
} else {
return false;
}
}

While width, height and frameCount are properties of the canvas, console is it not.
p.translate(width/2, height/2);
p.translate(p.width/2, p.height/2);
if (frameCount % 60 == 0 && timer > 0) {
if (p.frameCount % 60 == 0 && timer > 0) {
console.log("hit");
console.log("hit");
I don't know the implementation of Blob. but you have to passe the canvas object (p) to the show method and the let constructor.
The variable score can't be accessed in the class Blob
See the example, where I applied the suggested changes to your original code:
class Blob {
constructor(p, x, y, r) {
this.pos = p.createVector(x, y);
this.r = r;
this.vel = p.createVector(0,0);
}
show(p) {
p.ellipse(this.pos.x, this.pos.y, this.r, this.r);
}
eats(other) {
let d = this.pos.dist(other.pos);
if (d < this.r + other.r) {
let sum = Math.pi * this.r * this.r + Math.pi * other.r * other.r;
this.r = Math.sqrt(sum / Math.pi);
//this.r += other.r;
return true;
} else {
return false;
}
}
update() {
// ...
}
}
let sketch = function(p) {
let blob;
let blobs = [];
let zoom = 1;
let timer = 20;
let hits = false;
let score = 0;
p.setup = function() {
p.createCanvas(600, 600);
blob = new Blob(p, 0, 0, 64);
for (let i = 0; i < 300; i++) {
let x = p.random(-p.width,p.width);
let y = p.random(-p.height,p.height);
blobs[i] = new Blob(p, x, y, 15);
}
};
p.draw = function() {
p.background(0);
p.translate(p.width/2, p.height/2);
let newzoom = 64 / blob.r;
p.zoom = p.lerp(zoom, newzoom, 0.1);
p.scale(zoom);
p.translate(-blob.pos.x, -blob.pos.y);
for (var i = blobs.length-1; i >=0; i--) {
blobs[i].show(p);
if (blob.eats(blobs[i])) {
score ++;
blobs.splice(i, 1);
}
}
if (p.frameCount % 60 == 0 && timer > 0) {
p.timer --;
}
if (timer == 0 && score >=250) {
p.text("You Win", 0, 0);
p.noLoop();
}
if (blob.eats){
console.log("hit");
}
if (timer == 0 && score <= 250){
p.text("You Lose", 0, 0);
p.textSize(200);
p.noLoop();
}
blob.show(p);
blob.update();
};
};
let myp5 = new p5(sketch)
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>

Related

Small issues with an interactive project in p5.js / javascript

I need help with an interactive project in p5.js.
Here are the things that are still missing:
the clock hands should rotate
when the puzzle is solved "won" should be displayed and the timer should stop (Here I have a code part in my program but it doesn´t work)
the "moves" don´t count up when you click somewhere - only at the specific field
at "Game Over" the picture and the clock should stop
Here is a picture of the puzzle: Slide Puzzle
I hope someone can help me. Thank you very much.
Here is the code:
let w = window.innerWidth/2;
let h = window.innerHeight/1.5;
let ground;
let plate = [];
let tiles = [];
let col = 4;
let row = 4;
let bubbles = [];
let timerValue = 160;
let moves = 0;
function setup() {
canvas = createCanvas(w, h);
ground = createGraphics(w, h);
w = width / col;
h = height / row;
textAlign(CENTER, CENTER);
setInterval(timeIt, 1000);
imag = loadImage('Mauszeiger.png');
for (let i = 0; i < col; i++) {
for (let j = 0; j < row; j++) {
let img = createImage(w, h);
let index = i + j * col;
plate.push(index);
let teil = new Tile(index, img);
tiles[index] = teil;
}
}
tiles.pop();
plate.pop();
plate.push(-1);
startViz();
simpleShuffle(plate);
}
function draw() {
background(100);
push();
scale(1, 0.9);
drawViz();
updateTiles();
for (let i = 0; i < col; i++) {
for (let j = 0; j < row; j++) {
let index = i + j * col;
let x = i * w;
let y = j * h;
let teileIndex = plate[index];
if (teileIndex > -1) {
let img = tiles[teileIndex].img;
image(img, x, y, w, h);
}
strokeWeight(5);
noFill();
rect(x, y, w, h);
}
}
pop();
if (isSolved()) {
console.log('SOLVED');
timerValue = 0;
text('Won', width/2, height/2);
}
if (timerValue < 10 || timerValue >= 10) {
textSize(15);
fill(255);
text(timerValue, width-30, height*0.95);
}
textSize(45);
if (timerValue == 0) {
text('Game Over', width/2, height/2);
draw = function(){}
}
textSize(15);
text('moves: ' + moves, width/2, height*0.95);
push();
noFill();
stroke(255);
ellipse(27, height*0.95, 40, 40);
imageMode(CENTER);
image(imag, 30, height*0.95, imag.width / 100, imag.height / 100);
pop();
}
function mousePressed() {
let i = floor(mouseX / w);
let j = floor(mouseY / h);
move(i, j, plate);
if (mousePressed) {
moves++;
}
}
function windowResized() {
w = window.innerWidth/2;
h = window.innerHeight/1.5;
resizeCanvas(w, h);
}
function timeIt() {
if (timerValue > 0) {
timerValue--;
}
}
function isSolved() {
for (let i = 0; i < plate.length - 1; i++) {
if (plate[i] !== tiles[i].index) {
return false;
}
}
return true;
}
function updateTiles() {
for (let i = 0; i < col; i++) {
for (let j = 0; j < row; j++) {
let x = j * w;
let y = i * h;
let index = i + j * col;
if (tiles[index]) tiles[index].img.copy(ground, x, y, w, h, 0, 0, w, h);
}
}
}
function swap(i, j, arr) {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function randomMove(arr) {
let r1 = floor(random(col));
let r2 = floor(random(row));
move(r1, r2, arr);
}
function simpleShuffle(arr) {
for (let i = 0; i < 1000; i++) {
randomMove(arr);
}
}
function move(i, j, arr) {
let blank = findBlank();
let blankCol = blank % col;
let blankRow = floor(blank / row);
if (isNeighbor(i, j, blankCol, blankRow)) {
swap(blank, i + j * col, arr);
}
}
function isNeighbor(i, j, x, y) {
if (i !== x && j !== y) {
return false;
}
if (abs(i - x) == 1 || abs(j - y) == 1) {
return true;
}
return false;
}
function findBlank() {
for (let i = 0; i < plate.length; i++) {
if (plate[i] == -1) return i;
}
}
function startViz() {
for (let i = 0; i < 5; i++) {
bubbles.push(new Bubble());
}
}
function drawViz() {
ground.background(30);
for (let b of bubbles) {
b.update();
b.show();
b.show1();
}
}
class Bubble {
constructor() {
this.r = random(50, 80);
this.x = random(this.r, width - this.r);
this.y = random(this.r, height - this.r);
this.vx = random(-1 * (w*0.005), 3 * (w*0.005));
this.vy = random(-1 * (w*0.005), 3 * (w*0.005));
this.color = color(random(255), random(255), random(255));
}
show() {
ground.noFill();
ground.stroke(255);
ground.fill(this.color);
ground.strokeWeight(3);
ground.circle(this.x, this.y, this.r * 2 * (w*0.005));
}
show1() {
ground.line(this.x, this.y, this.x + this.r / 2 * (w*0.005), this.y + this.r / 2 * (w*0.005));
ground.line(this.x, this.y, this.x - this.r / 2 * (w*0.005), this.y + this.r / 2 * (w*0.005));
}
update() {
this.x += this.vx;
this.y += this.vy;
if (this.x > width - this.r || this.x < this.r) {
this.vx *= -1;
}
if (this.y > height - this.r || this.y < this.r) {
this.vy *= -1;
}
}
}
class Tile {
constructor(i, img) {
this.index = i;
this.img = img;
}
}

P5.js game crashes due to TypeError for a missing element in an array

OK, so I run into this problem where my program regularly crashes mid-game due to a TypeError: Uncaught TypeError: bullets[i] is undefined
The error occurs in the function where I check for every variable in the bullets and the enemies array if there is a collision.
This is the Source Code of this small school project I created, and I've been trying to troubleshoot this particular bug for hours at a time now. If anyone were able to help me that would be amazing as I can't quite grasp what the problem could be other than their occurring a collision when an element has already been spliced. But since the draw() function in P5.js runs 60 times per second that shouldn't really happen as regularly as it does, so I don't understand how this even can be an issue.
function setup() {
createCanvas(displayWidth - 20, displayHeight - 20);
angleMode(DEGREES);
rectMode(CENTER);
ellipseMode(CENTER);
playerX = width / 2;
playerY = height / 2;
}
let playerX = 0;
let playerY = 0;
let angle = 0;
let timer = 120;
let time = 0;
let v = 2;
let cooldown = 20;
let color = 50;
let hit = 0;
var bullets = [];
var enemies_easy = [];
var enemies_hard = [];
var score = 0;
var highscore = 0;
var gameover = false;
var gamestarted = false;
var nexthard = 5;
var currentenemy = 0;
function draw() {
if (!gamestarted) {
push();
textAlign(CENTER);
background('black');
translate(displayWidth / 2, displayHeight / 2);
textSize(width / 50);
fill('white');
text('Welcome!', 0, -100);
text('Controls:', 0, 0);
text('Fullscreen: F Moving: W, A, S, D Restart: R Shooting: Left Mouse Button Start: Space Bar', 0, 100);
pop();
if (keyIsDown(32)) {
bullets = [];
gamestarted = true;
}
} else {
if (!gameover) {
//calculates shot enemies_easy
bulletcollision();
//shoots if weapon cooldown is expired
background('black');
cooldown--;
activategun();
//draw hero
angle = atan2(mouseY - playerY, mouseX - playerX) + 90;
push();
fill('blue');
translate(playerX, playerY);
rotate(angle);
ellipse(0, 0, 40, 40);
//draw gun
fill('grey');
rect(0, 0 - 25, 10, 20);
pop();
//move hero
move();
//creates enemies with increasing difficulty
time++;
difficulty();
spawnenemy_easy();
spawnenemy_hard();
//shows score on screen
showscore();
//draw crosshair
noCursor();
push();
fill('white');
stroke('white');
line(mouseX, mouseY, mouseX + 20, mouseY);
line(mouseX, mouseY, mouseX - 20, mouseY);
line(mouseX, mouseY, mouseX, mouseY + 20);
line(mouseX, mouseY, mouseX, mouseY - 20);
pop();
//checks for game over
playercollision();
} else {
if (keyIsDown(82)) {
bullets = [];
enemies_easy = [];
enemies_hard = [];
timer = 120;
time = 0;
cooldown = 20;
score = 0;
playerX = width / 2;
playerY = height / 2;
v = 2;
gameover = false;
}
}
}
}
class bullet {
constructor() {
this.x = playerX;
this.y = playerY;
this.angle = createVector(mouseX - playerX, mouseY - playerY);
this.angle.normalize();
}
drawbullet() {
push();
fill('white');
ellipse(this.x, this.y, 10, 10);
pop();
this.y = this.y + 10 * this.angle.y;
this.x = this.x + 10 * this.angle.x;
}
}
class enemy_easy {
constructor() {
this.x = random(-1000, width + 1000);
if (this.x > width || this.x < 0) {
if (this.x > width) {
this.x = width;
this.y = random(0, height + 1);
}
if (this.x < 0) {
this.x = 0;
this.y = random(0, height + 1);
} else {}
} else {
let i = floor(random(0, 2));
this.y = i * height;
}
}
drawenemy_easy() {
push();
this.angle = createVector(this.x - playerX, this.y - playerY);
this.angle.normalize();
fill('red');
ellipse(this.x, this.y, 30, 30);
rotate(angle);
pop();
this.x = this.x - v * this.angle.x; // * random(0, 5);
this.y = this.y - v * this.angle.y; // * random(0, 5);
}
}
class enemy_hard {
constructor() {
this.x = random(-1000, width + 1000);
if (this.x > width || this.x < 0) {
if (this.x > width) {
this.x = width;
this.y = random(0, height + 1);
}
if (this.x < 0) {
this.x = 0;
this.y = random(0, height + 1);
} else {}
} else {
let i = floor(random(0, 2));
this.y = i * height;
}
}
drawenemy_hard() {
push();
this.angle = createVector(this.x - playerX, this.y - playerY);
this.angle.normalize();
fill('purple');
ellipse(this.x, this.y, 30, 30);
rotate(angle);
pop();
this.x = this.x - v * this.angle.x; // * random(0, 5);
this.y = this.y - v * this.angle.y; // * random(0, 5);
}
}
function keyPressed() {
//fullscreen Taste F: https://www.geeksforgeeks.org/p5-js-fullscreen-function/
if (keyCode === 70) {
let fs = fullscreen();
fullscreen(!fs);
}
}
function move() {
if (keyIsDown(83) && playerY <= height - 22) {
playerY = playerY + 4;
}
if (keyIsDown(87) && playerY >= 22) {
playerY = playerY - 4;
}
if (keyIsDown(68) && playerX <= width - 22) {
playerX = playerX + 4;
}
if (keyIsDown(65) && playerX >= 22) {
playerX = playerX - 4;
}
}
function activategun() {
for (var i = 0; i < bullets.length; i++) {
bullets[i].drawbullet();
if (bullets[i].x <= 0 || bullets[i].y <= 0 || bullets[i].x >= width || bullets[i].y >= height) {
bullets.splice(i, 1);
}
}
}
function mousePressed() {
if (cooldown < 0) {
bullets.push(new bullet());
cooldown = 20;
}
}
function spawnenemy_easy() {
for (var i = 0; i < enemies_easy.length; i++) {
enemies_easy[i].drawenemy_easy();
}
}
function spawnenemy_hard() {
for (var i = 0; i < enemies_hard.length; i++) {
enemies_hard[i].drawenemy_hard();
}
}
function newenemy() {
if (currentenemy < nexthard) {
enemies_easy.push(new enemy_easy());
currentenemy++;
} else {
enemies_hard.push(new enemy_hard());
currentenemy = 0;
nexthard = random(2, 6);
}
}
function bulletcollision() {
for (var i = 0; i < bullets.length; i++) {
for (var e = 0; e < enemies_easy.length; e++) {
var distance = createVector(bullets[i].x - enemies_easy[e].x, bullets[i].y - enemies_easy[e].y);
if (distance.mag() <= 18) {
bullets.splice(i, 1);
enemies_easy.splice(e, 1);
score++;
if (score > highscore) {
highscore++;
}
}
}
for (var e = 0; e < enemies_hard.length; e++) {
var distance = createVector(bullets[i].x - enemies_hard[e].x, bullets[i].y - enemies_hard[e].y);
if (distance.mag() <= 18) {
bullets.splice(i, 1);
hit++;
if (hit == 2) {
enemies_hard.splice(e, 1);
score++;
if (score > highscore) {
highscore++;
}
hit = 0;
}
}
}
}
}
function playercollision() {
for (var i = 0; i < enemies_easy.length; i++) {
var distance = createVector(enemies_easy[i].x - playerX, enemies_easy[i].y - playerY);
if (distance.mag() <= 25) {
push();
background(abs(255 * sin(random(0, 255))), 255 * sin(random(0, 255)), 255 * sin(random(0, 255)));
fill('white');
textAlign(CENTER);
textSize(width / 40);
translate(width / 2, height / 2);
text("Game Over!", 0, -100);
text("Score: " + score, 0, 0);
text("High Score: " + highscore, 0, 100);
pop();
return gameover = true;
}
}
for (var i = 0; i < enemies_hard.length; i++) {
var distance = createVector(enemies_hard[i].x - playerX, enemies_hard[i].y - playerY);
if (distance.mag() <= 25) {
push();
background(abs(255 * sin(random(0, 255))), 255 * sin(random(0, 255)), 255 * sin(random(0, 255)));
fill('white');
textAlign(CENTER);
textSize(width / 40);
translate(width / 2, height / 2);
text("Game Over!", 0, -100);
text("Score: " + score, 0, 0);
text("High Score: " + highscore, 0, 100);
pop();
return gameover = true;
}
}
}
function difficulty() {
if (time >= timer) {
newenemy();
time = 0;
timer = timer * 0.99;
v = v * 1.02;
}
}
function showscore() {
push();
fill('white');
textSize(width / 55);
textAlign(CENTER);
text('Score: ' + score, 100, 100);
pop();
}
Does anyone know how I could fix this issue or if I even can?
Any help is appreciated!
The problem is that you are removing bullets from the array as you iterate through the array. Use while-loop instead of the for-loop:
function bulletcollision() {
let i = 0;
while (i < bullets.length) {
let bullet_hit = false;
for (var e = 0; e < enemies_easy.length; e++) {
var distance = createVector(bullets[i].x - enemies_easy[e].x, bullets[i].y - enemies_easy[e].y);
if (distance.mag() <= 18) {
bullet_hit = true;
destroy_enemy(enemies_easy, e);
}
}
for (var e = 0; e < enemies_hard.length; e++) {
var distance = createVector(bullets[i].x - enemies_hard[e].x, bullets[i].y - enemies_hard[e].y);
if (distance.mag() <= 18) {
bullet_hit = true;
hit++;
if (hit == 2) {
destroy_enemy(enemies_hard, e);
hit = 0;
}
}
}
if (bullet_hit) {
bullets.splice(i, 1);
} else {
i ++;
}
}
}
function destroy_enemy(enemies, e) {
enemies.splice(e, 1);
score++;
highscore = Math.max(highscore, score);
}
See also Looping through array and removing items, without breaking for loop.

Why the nextButton/startButton does not work?

I am working on a project on Khan Academy in which I have to create a game with at least 3 levels. I have developed most of the game but when I tried to proceed from one level to next the game somehow stops.
Here is the full project:
Project Link
/**
* Contains 3 levels
*
*
* Changed Ground
* Brown rectangle is replaced with Dirt Block.
*
* Scoring system changed
* Collecting Good sticks gets 1 point.
* Collecting Bad sticks gets -1 point. (i.e. loses point).
* Hitting rocks will lose 1 point.
*
**/
var level = 0;
var nosOfSticks = 5;
var target = 0;
var speed = 1;
var endLevel = false;
var buttonClicked = false;
var levelButtonEnabled = false;
var startButtonEnabled = true;
var Beaver = function(x, y) { // Beaver Constructor
this.x = x;
this.y = y;
this.img = getImage("creatures/Hopper-Happy");
this.sticks = 0;
};
Beaver.prototype.draw = function() { // Draw function to draw beaver
fill(255, 0, 0);
this.x = constrain(this.x, 0, width-40);
this.y = constrain(this.y, 0, height-50);
image(this.img, this.x, this.y, 40, 40);
};
Beaver.prototype.hop = function() { // Hop function to make beaver hop
this.img = getImage("creatures/Hopper-Jumping");
this.y -= speed * 5;
};
Beaver.prototype.hopLeft = function() {
this.img = getImage("creatures/Hopper-Jumping");
this.x -= speed * 5;
};
Beaver.prototype.hopRight = function() {
this.img = getImage("creatures/Hopper-Jumping");
this.x += speed * 5;
};
Beaver.prototype.fall = function() { // fall function makes beaver fall on the ground
this.img = getImage("creatures/Hopper-Happy");
this.y += speed * 5;
};
Beaver.prototype.checkForStickGrab = function(stick) { // function that checks sticks grab
if ((stick.x >= this.x && stick.x <= (this.x + 40)) &&
(stick.y >= this.y && stick.y <= (this.y + 40))) {
stick.y = -400;
this.sticks++;
}
};
Beaver.prototype.checkForBadStickGrab = function(badstick) { // function that checks badsticks grab
if ((badstick.x >= this.x && badstick.x <= (this.x + 40)) &&
(badstick.y >= this.y && badstick.y <= (this.y + 40))) {
badstick.y = -400;
this.sticks--;
}
};
Beaver.prototype.checkForRockHit = function(rock) { // function that checks rocks hit
if ((rock.x >= this.x - 40 && rock.x <= (this.x + 40)) &&
(rock.y >= this.y - 30 && rock.y <= (this.y + 40))) {
rock.x = -400;
this.sticks--;
}
};
// Drawing Sticks
var Stick = function(x, y) { // Stick constructor
this.x = x;
this.y = y;
};
Stick.prototype.draw = function() { // Draw function to draw sticks
fill(0, 0, 0);
rectMode(CENTER);
rect(this.x, this.y, 5, 40);
};
var Badstick = function(x, y) { // Bad Sticks constructor
Stick.call(this, x, y);
};
//Badstick.prototype = Object.create(Stick);
Badstick.prototype.draw = function() { //Draw function to draw badsticks
fill(255, 0, 13);
rectMode(CENTER);
rect(this.x, this.y, 5, 40);
};
// Drawings Rocks
var Rock = function(x, y) { // rocks constructor
this.x = x;
this.y = y;
this.img = getImage("cute/Rock");
};
Rock.prototype.draw = function(x, y) { // function to draw rocks
fill(0, 0, 0);
image(this.img, this.x, this.y, 40, 40);
};
var beaver = new Beaver(200, 300);
var sticks = [];
for (var i = 0; i < nosOfSticks; i++) {
sticks.push(new Stick(i * 100 + 400, random(20, 260)));
}
var badSticks = [];
for (var i = 0; i < nosOfSticks/2; i++) {
badSticks.push(new Badstick(i * 200 + 400, random(20, 270)));
}
var rocks = [];
for ( var i = 0; i < nosOfSticks * 0.375; i++) {
rocks.push(new Rock(random(0, 375), i * random() - (i * 100)));
}
var grassXs = [];
for (var i = 0; i < 25; i++) {
grassXs.push(i*20);
}
var blockXs = [];
for (var i = 0; i < 25; i++) {
blockXs.push(i*20);
}
var Button = function (x, y, w, h, color, text, size, font, textcolor, best) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = color;
this.text = text;
this.size = size;
this.font = font;
this.textcolor = textcolor;
this.best = best;
};
Button.prototype.draw = function() {
rectMode(CORNER);
fill(this.color);
rect(this.x, this.y, this.w, this.h);
fill(this.textcolor);
stroke(this.textcolor);
textFont(this.font, this.size);
text(this.text, this.x + (this.w/2 - this.w/2.5), this.y + (this.h/2 + this.size/2.5));
/*textFont(this.font, this.size / 2);
text("Best : " + this.best, this.x + 10, this.y + 90);*/
};
Button.prototype.clicked = function() {
if(mouseIsPressed && mouseX >= this.x && mouseX <= this.x + this.w && mouseY >= this.y && mouseY <= this.y + this.h ) {
return true;
}
};
var nextButton = new Button(315, 360, 75, 30, color(0, 255, 0), "Next Level", 12, "Aerial Bold", color(0, 0, 0));
var startButton = new Button(315, 360, 75, 30, color(0, 255, 0), "Start Again", 12, "Aerial Bold", color(0, 0, 0));
var playButton = new Button(140, 250, 120, 50, color(0, 0, 0), "PLAY", 40, "Aerial Bold", color(255, 255, 255));
var level1Button = new Button(30, 120, 100, 100, color(0, 0, 0), "Level 1", 25, "Aerial Bold", color(255, 255, 255));
var level2Button = new Button(140, 120, 100, 100, color(0, 0, 0), "Level 2", 25, "Aerial Bold", color(255, 255, 255));
var level3Button = new Button(250, 120, 100, 100, color(0, 0, 0), "Level 3", 25, "Aerial Bold", color(255, 255, 255));
var drawWin = function() {
fill(255, 0, 0);
textSize(36);
text("YOU WIN!!!!", 100, 200);
nextButton.draw();
};
var drawLoss = function() {
fill(255, 0, 0);
textSize(36);
text("YOU LOSE!!!!", 100, 200);
startButton.draw();
};
var movement = function() {
if (keyIsPressed) {
if(keyCode === UP) {
beaver.hop();
} /*else if(keyCode === LEFT) {
beaver.hopLeft();
} else if(keyCode === RIGHT) {
beaver.hopRight();
} */
} else { beaver.fall();}
};
var drawScore = function() {
fill(0, 255, 0);
textSize(18);
text("Score: " + beaver.sticks, 10, 390);
};
var isWin = function() {
if(beaver.sticks >= target) {
drawWin();
speed = 1;
return true;
}
};
var isLoss = function() {
if (beaver.sticks < target ) {
speed = 1;
drawLoss();
return true;
}
};
var drawBackground = function() {
//static
speed = 1;
background(227, 254, 255);
stroke(0, 0, 0);
rectMode(CORNER);
rect(0, height*0.90, width, height*0.10);
for (var i = 0; i < grassXs.length; i++) {
image(getImage("cute/GrassBlock"), grassXs[i], height*0.85, 35, 20);
image(getImage("cute/DirtBlock"), grassXs[i], height*0.85, 35, 60);
grassXs[i] -= speed;
if (grassXs[i] <= - 20) {
grassXs[i] = width;
}
}
};
var drawSticks = function() {
for (var i = 0; i < sticks.length; i++) {
sticks[i].draw();
beaver.checkForStickGrab(sticks[i]);
sticks[i].x -= speed;
}
};
var drawBadSticks = function() {
for (var i = 0; i < badSticks.length; i++) {
badSticks[i].draw();
beaver.checkForBadStickGrab(badSticks[i]);
badSticks[i].x -= speed;
}
};
var drawRocks = function() {
for (var i = 0; i < rocks.length; i++) {
rocks[i].draw();
beaver.checkForRockHit(rocks[i]);
rocks[i].y += speed;
}
};
var drawLevel = function() {
speed = 1;
drawBackground();
if (level === 1) {
target = 1;
drawSticks();
}
if (level === 2) {
target = 1;
drawSticks();
drawBadSticks();
}
if (level === 3) {
target = 1;
drawBadSticks();
drawSticks();
drawRocks();
}
beaver.draw();
movement();
drawScore();
if (sticks[nosOfSticks - 1].x < -5) {
isWin();
isLoss();
}
};
var drawLevels = function() {
level = "l";
background(0, 0, 0);
level1Button.draw();
level2Button.draw();
level3Button.draw();
if (level1Button.clicked() && level === "l") {
level = 1;
drawLevel();
} else if (level2Button.clicked() && level === "l") {
level = 2;
drawLevel();
} else if (level3Button.clicked() && level === "l") {
level = 3;
drawLevel();
}
};
var drawStart = function() {
level = 0;
background(0);
text("Hoppy Beaver", 75, 50);
text("Extreme", 120, 100);
playButton.draw();
if (playButton.clicked() && level === 0) {
levelButtonEnabled = false;
drawLevels();
}
};
//drawStart();
mouseClicked = function() {
if (nextButton.clicked() || startButton.clicked()) {
if (beaver.sticks >= 1) {
if (level === 0) {
level = 1;
sticks = [];
draw();
isWin = false;
}
if (level === 1) {
level = 2;
sticks = [];
draw();
isWin = false;
}
if (level === 2) {
level = 3;
sticks = [];
draw();
isWin = false;
}
if (level === 3) {
level = 1;
sticks = [];
isWin = false;
draw();
}
} else if (beaver.sticks < 1) {
if (level === 1) {
level = 1;
sticks = [];
drawLevel();
isLoss = false;
}
if (level === 2) {
level = 2;
sticks = [];
drawLevel();
isLoss = false;
}
if (level === 3) {
level = 3;
sticks = [];
drawLevel();
isLoss = false;
}
}
}
};
draw = function() {
speed = 1;
if (level === 1) {
drawLevel();
} else if (level === 2) {
drawLevel();
} else if (level === 3) {
drawLevel();
} else if (level === "l") {
drawLevels();
} else { drawStart(); }
};
welcome to stackoverflow. The problem with your code is this bit right here in the drawLevel function.
if (sticks[nosOfSticks - 1].x < -5) {
isWin();
isLoss();
}
At the start of your program you initialize the sticks array with some stick objects in line 124. When level 1 ends and the next button is clicked, you set the sticks array to an empty array sticks=[] in the mouseClicked function.However, you never re-add anything into the sticks array. Thus, when that block of code runs, the element at position nosOfSticks-1 is undefined, leading to your problem.My suggestion is to make a for loop after sticks=[] to refill the sticks array just like in line 124.
Good Luck!
Also, take a look at this guide for debugging help, how to debug small programs.

HTML5 canvas particle explosion

I'm trying to get this particle explosion working. It's working but it looks like some frames does not get rendered. If I click many times to call several explosions it starts to uhm.. "lag/stutter". Is there something I have forgotten to do? It may look like the browser hangs when I click many times. Is it too much to have 2 for loops inside each other?
Attached my code so you can see.
Just try to click many times, and you will see the problem visually.
// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
// Canvas
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
// Set full-screen
c.width = window.innerWidth;
c.height = window.innerHeight;
// Options
var background = '#333'; // Background color
var particlesPerExplosion = 20;
var particlesMinSpeed = 3;
var particlesMaxSpeed = 6;
var particlesMinSize = 1;
var particlesMaxSize = 3;
var explosions = [];
var fps = 60;
var now, delta;
var then = Date.now();
var interval = 1000 / fps;
// Optimization for mobile devices
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
fps = 29;
}
// Draw
function draw() {
// Loop
requestAnimationFrame(draw);
// Set NOW and DELTA
now = Date.now();
delta = now - then;
// New frame
if (delta > interval) {
// Update THEN
then = now - (delta % interval);
// Our animation
drawBackground();
drawExplosion();
}
}
// Draw explosion(s)
function drawExplosion() {
if (explosions.length == 0) {
return;
}
for (var i = 0; i < explosions.length; i++) {
var explosion = explosions[i];
var particles = explosion.particles;
if (particles.length == 0) {
explosions.splice(i, 1);
return;
}
for (var ii = 0; ii < particles.length; ii++) {
var particle = particles[ii];
// Check particle size
// If 0, remove
if (particle.size < 0) {
particles.splice(ii, 1);
return;
}
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, Math.PI * 2, 0, false);
ctx.closePath();
ctx.fillStyle = 'rgb(' + particle.r + ',' + particle.g + ',' + particle.b + ')';
ctx.fill();
// Update
particle.x += particle.xv;
particle.y += particle.yv;
particle.size -= .1;
}
}
}
// Draw the background
function drawBackground() {
ctx.fillStyle = background;
ctx.fillRect(0, 0, c.width, c.height);
}
// Clicked
function clicked(e) {
var xPos, yPos;
if (e.offsetX) {
xPos = e.offsetX;
yPos = e.offsetY;
} else if (e.layerX) {
xPos = e.layerX;
yPos = e.layerY;
}
explosions.push(new explosion(xPos, yPos));
}
// Explosion
function explosion(x, y) {
this.particles = [];
for (var i = 0; i < particlesPerExplosion; i++) {
this.particles.push(new particle(x, y));
}
}
// Particle
function particle(x, y) {
this.x = x;
this.y = y;
this.xv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.yv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.size = randInt(particlesMinSize, particlesMaxSize, true);
this.r = randInt(113, 222);
this.g = '00';
this.b = randInt(105, 255);
}
// Returns an random integer, positive or negative
// between the given value
function randInt(min, max, positive) {
if (positive == false) {
var num = Math.floor(Math.random() * max) - min;
num *= Math.floor(Math.random() * 2) == 1 ? 1 : -1;
} else {
var num = Math.floor(Math.random() * max) + min;
}
return num;
}
// On-click
$('canvas').on('click', function(e) {
clicked(e);
});
draw();
<!DOCTYPE html>
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</html>
You are returning from iterating over the particles if one is too small. This causes the other particles of that explosion to render only in the next frame.
I have a working version:
// Request animation frame
const requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
// Canvas
const c = document.getElementById('canvas');
const ctx = c.getContext('2d');
// Set full-screen
c.width = window.innerWidth;
c.height = window.innerHeight;
// Options
const background = '#333'; // Background color
const particlesPerExplosion = 20;
const particlesMinSpeed = 3;
const particlesMaxSpeed = 6;
const particlesMinSize = 1;
const particlesMaxSize = 3;
const explosions = [];
let fps = 60;
const interval = 1000 / fps;
let now, delta;
let then = Date.now();
// Optimization for mobile devices
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
fps = 29;
}
// Draw
function draw() {
// Loop
requestAnimationFrame(draw);
// Set NOW and DELTA
now = Date.now();
delta = now - then;
// New frame
if (delta > interval) {
// Update THEN
then = now - (delta % interval);
// Our animation
drawBackground();
drawExplosion();
}
}
// Draw explosion(s)
function drawExplosion() {
if (explosions.length === 0) {
return;
}
for (let i = 0; i < explosions.length; i++) {
const explosion = explosions[i];
const particles = explosion.particles;
if (particles.length === 0) {
explosions.splice(i, 1);
return;
}
const particlesAfterRemoval = particles.slice();
for (let ii = 0; ii < particles.length; ii++) {
const particle = particles[ii];
// Check particle size
// If 0, remove
if (particle.size <= 0) {
particlesAfterRemoval.splice(ii, 1);
continue;
}
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, Math.PI * 2, 0, false);
ctx.closePath();
ctx.fillStyle = 'rgb(' + particle.r + ',' + particle.g + ',' + particle.b + ')';
ctx.fill();
// Update
particle.x += particle.xv;
particle.y += particle.yv;
particle.size -= .1;
}
explosion.particles = particlesAfterRemoval;
}
}
// Draw the background
function drawBackground() {
ctx.fillStyle = background;
ctx.fillRect(0, 0, c.width, c.height);
}
// Clicked
function clicked(e) {
let xPos, yPos;
if (e.offsetX) {
xPos = e.offsetX;
yPos = e.offsetY;
} else if (e.layerX) {
xPos = e.layerX;
yPos = e.layerY;
}
explosions.push(
new explosion(xPos, yPos)
);
}
// Explosion
function explosion(x, y) {
this.particles = [];
for (let i = 0; i < particlesPerExplosion; i++) {
this.particles.push(
new particle(x, y)
);
}
}
// Particle
function particle(x, y) {
this.x = x;
this.y = y;
this.xv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.yv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.size = randInt(particlesMinSize, particlesMaxSize, true);
this.r = randInt(113, 222);
this.g = '00';
this.b = randInt(105, 255);
}
// Returns an random integer, positive or negative
// between the given value
function randInt(min, max, positive) {
let num;
if (positive === false) {
num = Math.floor(Math.random() * max) - min;
num *= Math.floor(Math.random() * 2) === 1 ? 1 : -1;
} else {
num = Math.floor(Math.random() * max) + min;
}
return num;
}
// On-click
$('canvas').on('click', function (e) {
clicked(e);
});
draw();
<!DOCTYPE html>
<html>
<head>
<style>* {margin:0;padding:0;overflow:hidden;}</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</html>
Loops, break and continue.
The problem was caused when you checked for empty particle arrays and when you found a particle to remove.
The bugs
The following two statements and blocks caused the problem
if (particles.length == 0) {
explosions.splice(i, 1);
return;
}
and
if (particles.size < 0) {
explosions.splice(ii, 1);
return;
}
The returns stopped the rendering of particles, so you would sometimes return before drawing a single particle was rendered just because the first explosion was empty or first particle was too small.
Continue and break
You can use the continue token in javascript to skip the rest of a for, while, do loop
for(i = 0; i < 100; i++){
if(test(i)){
// need to skip this iteration
continue;
}
// more code
// more code
// continue skips all the code upto the closing }
} << continues to here and if i < 100 the loop continues on.
Or you can completely break out of the loop with break
for(i = 0; i < 100; i++){
if(test(i)){
// need to exit the for loop
break;
}
// more code
// more code
// break skips all the code to the first line after the closing }
}
<< breaks to here and if i remains the value it was when break was encountered
The fix
if (particles.length == 0) {
explosions.splice(i, 1);
continue;
}
and
if (particles.size < 0) {
explosions.splice(ii, 1);
continue;
}
Your example with the fix
Your code with the fix. Befor I found it I started changing stuff.
Minor stuff.
requestAnimationFrame passes a time in milliseconds so to an accuracy of micro seconds.
You were setting then incorrectly and would have been losing frames. I changed the timing to use the argument time and then is just set to the time when a frame is drawn.
There are some other issues, nothing major and more of a coding style thing. You should capitalise objects created with new
function Particle(...
not
function particle(...
and your random is a overly complex
function randInt(min, max = min - (min = 0)) {
return Math.floor(Math.random() * (max - min) + min);
}
or
function randInt(min,max){
max = max === undefined ? min - (min = 0) : max;
return Math.floor(Math.random() * (max - min) + min);
}
randInt(100); // int 0 - 100
randInt(10,20); // int 10-20
randInt(-100); // int -100 to 0
randInt(-10,20); // int -10 to 20
this.xv = randInt(-particlesMinSpeed, particlesMaxSpeed);
this.yv = randInt(-particlesMinSpeed, particlesMaxSpeed);
this.size = randInt(particlesMinSize, particlesMaxSize);
And if you are using the same name in variables a good sign to create an object
var particlesPerExplosion = 20;
var particlesMinSpeed = 3;
var particlesMaxSpeed = 6;
var particlesMinSize = 1;
var particlesMaxSize = 3;
Could be
const settings = {
particles : {
speed : {min : 3, max : 6 },
size : {min : 1 : max : 3 },
explosionCount : 20,
},
background : "#000",
}
Anyways your code.
var c = canvas;
var ctx = c.getContext('2d');
// Set full-screen
c.width = innerWidth;
c.height = innerHeight;
// Options
var background = '#333'; // Background color
var particlesPerExplosion = 20;
var particlesMinSpeed = 3;
var particlesMaxSpeed = 6;
var particlesMinSize = 1;
var particlesMaxSize = 3;
var explosions = [];
var fps = 60;
var now, delta;
var then = 0; // Zero start time
var interval = 1000 / fps;
// Optimization for mobile devices
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
fps = 29;
}
// Draw
// as time is passed you need to start with requestAnimationFrame
requestAnimationFrame(draw);
function draw(time) { //requestAnimationFrame frame passes the time
requestAnimationFrame(draw);
delta = time - then;
if (delta > interval) {
then = time
drawBackground();
drawExplosion();
}
}
// Draw explosion(s)
function drawExplosion() {
if (explosions.length == 0) {
return;
}
for (var i = 0; i < explosions.length; i++) {
var explosion = explosions[i];
var particles = explosion.particles;
if (particles.length == 0) {
explosions.splice(i, 1);
//return;
continue;
}
for (var ii = 0; ii < particles.length; ii++) {
var particle = particles[ii];
// Check particle size
// If 0, remove
if (particle.size < 0) {
particles.splice(ii, 1);
// return;
continue;
}
ctx.beginPath();
ctx.arc(particle.x, particle.y, particle.size, Math.PI * 2, 0, false);
ctx.closePath();
ctx.fillStyle = 'rgb(' + particle.r + ',' + particle.g + ',' + particle.b + ')';
ctx.fill();
// Update
particle.x += particle.xv;
particle.y += particle.yv;
particle.size -= .1;
}
}
}
// Draw the background
function drawBackground() {
ctx.fillStyle = background;
ctx.fillRect(0, 0, c.width, c.height);
}
// Clicked
function clicked(e) {
var xPos, yPos;
if (e.offsetX) {
xPos = e.offsetX;
yPos = e.offsetY;
} else if (e.layerX) {
xPos = e.layerX;
yPos = e.layerY;
}
explosions.push(new explosion(xPos, yPos));
}
// Explosion
function explosion(x, y) {
this.particles = [];
for (var i = 0; i < particlesPerExplosion; i++) {
this.particles.push(new particle(x, y));
}
}
// Particle
function particle(x, y) {
this.x = x;
this.y = y;
this.xv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.yv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
this.size = randInt(particlesMinSize, particlesMaxSize, true);
this.r = randInt(113, 222);
this.g = '00';
this.b = randInt(105, 255);
}
// Returns an random integer, positive or negative
// between the given value
function randInt(min, max, positive) {
if (positive == false) {
var num = Math.floor(Math.random() * max) - min;
num *= Math.floor(Math.random() * 2) == 1 ? 1 : -1;
} else {
var num = Math.floor(Math.random() * max) + min;
}
return num;
}
// On-click
$('canvas').on('click', function(e) {
clicked(e);
});
<!DOCTYPE html>
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</html>

Delete or hide the image in previous frame - html 5 canvas

I'm trying to edit a pacman implementation (source) using htlm5 and js. The problem is when I make the background transparent, the ghosts and pacman images holding the previews frames. This is an example about my problem.
My js code:
Pacman.FPS = 30;
Pacman.Ghost = function (game, map, colour) {
function getColour() {
if (eatable) {
if (secondsAgo(eatable) > 5) {
return game.getTick() % 20 > 10 ? "rgba(0, 0, 0, 0.1)" : "rgba(0, 0, 0, 0.1)";
} else {
return "rgba(0, 0, 0, 0.1)";
}
} else if(eaten) {
return "#222";
}
return colour;
};
function draw(ctx) {
var s = map.blockSize,
top = (position.y/10) * s,
left = (position.x/10) * s;
if (eatable && secondsAgo(eatable) > 8) {
eatable = null;
}
if (eaten && secondsAgo(eaten) > 3) {
eaten = null;
}
var tl = left + s;
var base = top + s - 3;
var inc = s / 10;
var high = game.getTick() % 10 > 5 ? 3 : -3;
var low = game.getTick() % 10 > 5 ? -3 : 3;
ctx.fillStyle = getColour();
ctx.beginPath();
ctx.moveTo(left, base);
ctx.quadraticCurveTo(left, top, left + (s/2), top);
ctx.quadraticCurveTo(left + s, top, left+s, base);
// Wavy things at the bottom
ctx.quadraticCurveTo(tl-(inc*1), base+high, tl - (inc * 2), base);
ctx.quadraticCurveTo(tl-(inc*3), base+low, tl - (inc * 4), base);
ctx.quadraticCurveTo(tl-(inc*5), base+high, tl - (inc * 6), base);
ctx.quadraticCurveTo(tl-(inc*7), base+low, tl - (inc * 8), base);
ctx.quadraticCurveTo(tl-(inc*9), base+high, tl - (inc * 10), base);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.fillStyle = "#FFF";
ctx.arc(left + 6,top + 6, s / 6, 0, 300, false);
ctx.arc((left + s) - 6,top + 6, s / 6, 0, 300, false);
ctx.closePath();
ctx.fill();
var f = s / 12;
var off = {};
off[RIGHT] = [f, 0];
off[LEFT] = [-f, 0];
off[UP] = [0, -f];
off[DOWN] = [0, f];
ctx.beginPath();
ctx.fillStyle = "rgba(0, 0, 0, 0)";
//ctx.fillStyle = "#000";
ctx.arc(left+6+off[direction][0], top+6+off[direction][3],
s / 15, 0, 300, false);
ctx.arc((left+s)-6+off[direction][0], top+6+off[direction][4],
s / 15, 0, 300, false);
ctx.closePath();
ctx.fill();
};
function pane(pos) {
if (pos.y === 100 && pos.x >= 190 && direction === RIGHT) {
return {"y": 100, "x": -10};
}
if (pos.y === 100 && pos.x <= -10 && direction === LEFT) {
return position = {"y": 100, "x": 190};
}
return false;
};
function move(ctx) {
var oldPos = position,
onGrid = onGridSquare(position),
npos = null;
if (due !== direction) {
npos = getNewCoord(due, position);
if (onGrid &&
map.isFloorSpace({
"y":pointToCoord(nextSquare(npos.y, due)),
"x":pointToCoord(nextSquare(npos.x, due))})) {
direction = due;
} else {
npos = null;
}
}
if (npos === null) {
npos = getNewCoord(direction, position);
}
if (onGrid &&
map.isWallSpace({
"y" : pointToCoord(nextSquare(npos.y, direction)),
"x" : pointToCoord(nextSquare(npos.x, direction))
})) {
due = getRandomDirection();
return move(ctx);
}
position = npos;
var tmp = pane(position);
if (tmp) {
position = tmp;
}
due = getRandomDirection();
return {
"new" : position,
"old" : oldPos
};
};
return {
"eat" : eat,
"isVunerable" : isVunerable,
"isDangerous" : isDangerous,
"makeEatable" : makeEatable,
"reset" : reset,
"move" : move,
"draw" : draw
};
};
Pacman.User = function (game, map) {
var position = null,
direction = null,
eaten = null,
due = null,
lives = null,
score = 5,
keyMap = {};
keyMap[KEY.ARROW_LEFT] = LEFT;
keyMap[KEY.ARROW_UP] = UP;
keyMap[KEY.ARROW_RIGHT] = RIGHT;
keyMap[KEY.ARROW_DOWN] = DOWN;
function addScore(nScore) {
score += nScore;
if (score >= 10000 && score - nScore < 10000) {
lives += 1;
}
};
function theScore() {
return score;
};
function loseLife() {
lives -= 1;
};
function getLives() {
return lives;
};
function initUser() {
score = 0;
lives = 3;
newLevel();
}
function newLevel() {
resetPosition();
eaten = 0;
};
function resetPosition() {
position = {"x": 90, "y": 120};
direction = LEFT;
due = LEFT;
};
function reset() {
initUser();
resetPosition();
};
function keyDown(e) {
if (typeof keyMap[e.keyCode] !== "undefined") {
due = keyMap[e.keyCode];
e.preventDefault();
e.stopPropagation();
return false;
}
return true;
};
function getNewCoord(dir, current) {
return {
"x": current.x + (dir === LEFT && -2 || dir === RIGHT && 2 || 0),
"y": current.y + (dir === DOWN && 2 || dir === UP && -2 || 0)
};
};
function onWholeSquare(x) {
return x % 10 === 0;
};
function pointToCoord(x) {
return Math.round(x/10);
};
function nextSquare(x, dir) {
var rem = x % 10;
if (rem === 0) {
return x;
} else if (dir === RIGHT || dir === DOWN) {
return x + (10 - rem);
} else {
return x - rem;
}
};
function next(pos, dir) {
return {
"y" : pointToCoord(nextSquare(pos.y, dir)),
"x" : pointToCoord(nextSquare(pos.x, dir)),
};
};
function onGridSquare(pos) {
return onWholeSquare(pos.y) && onWholeSquare(pos.x);
};
function isOnSamePlane(due, dir) {
return ((due === LEFT || due === RIGHT) &&
(dir === LEFT || dir === RIGHT)) ||
((due === UP || due === DOWN) &&
(dir === UP || dir === DOWN));
};
function move(ctx) {
var npos = null,
nextWhole = null,
oldPosition = position,
block = null;
if (due !== direction) {
npos = getNewCoord(due, position);
if (isOnSamePlane(due, direction) ||
(onGridSquare(position) &&
map.isFloorSpace(next(npos, due)))) {
direction = due;
} else {
npos = null;
}
}
if (npos === null) {
npos = getNewCoord(direction, position);
}
if (onGridSquare(position) && map.isWallSpace(next(npos, direction))) {
direction = NONE;
}
if (direction === NONE) {
return {"new" : position, "old" : position};
}
if (npos.y === 100 && npos.x >= 190 && direction === RIGHT) {
npos = {"y": 100, "x": -10};
}
if (npos.y === 100 && npos.x <= -12 && direction === LEFT) {
npos = {"y": 100, "x": 190};
}
position = npos;
nextWhole = next(position, direction);
block = map.block(nextWhole);
if ((isMidSquare(position.y) || isMidSquare(position.x)) &&
block === Pacman.BISCUIT || block === Pacman.PILL) {
map.setBlock(nextWhole, Pacman.EMPTY);
addScore((block === Pacman.BISCUIT) ? 10 : 50);
eaten += 1;
if (eaten === 182) {
game.completedLevel();
}
if (block === Pacman.PILL) {
game.eatenPill();
}
}
return {
"new" : position,
"old" : oldPosition
};
};
function isMidSquare(x) {
var rem = x % 10;
return rem > 3 || rem < 7;
};
function calcAngle(dir, pos) {
if (dir == RIGHT && (pos.x % 10 < 5)) {
return {"start":0.25, "end":1.75, "direction": false};
} else if (dir === DOWN && (pos.y % 10 < 5)) {
return {"start":0.75, "end":2.25, "direction": false};
} else if (dir === UP && (pos.y % 10 < 5)) {
return {"start":1.25, "end":1.75, "direction": true};
} else if (dir === LEFT && (pos.x % 10 < 5)) {
return {"start":0.75, "end":1.25, "direction": true};
}
return {"start":0, "end":2, "direction": false};
};
function drawDead(ctx, amount) {
var size = map.blockSize,
half = size / 2;
if (amount >= 1) {
return;
}
ctx.fillStyle = "#FFFF00";
ctx.beginPath();
ctx.moveTo(((position.x/10) * size) + half,
((position.y/10) * size) + half);
ctx.arc(((position.x/10) * size) + half,
((position.y/10) * size) + half,
half, 0, Math.PI * 2 * amount, true);
ctx.fill();
};
function draw(ctx) {
var s = map.blockSize,
angle = calcAngle(direction, position);
ctx.fillStyle = "#FFFF00";
ctx.beginPath();
ctx.moveTo(((position.x/10) * s) + s / 2,
((position.y/10) * s) + s / 2);
ctx.arc(((position.x/10) * s) + s / 2,
((position.y/10) * s) + s / 2,
s / 2, Math.PI * angle.start,
Math.PI * angle.end, angle.direction);
ctx.fill();
};
initUser();
return {
"draw" : draw,
"drawDead" : drawDead,
"loseLife" : loseLife,
"getLives" : getLives,
"score" : score,
"addScore" : addScore,
"theScore" : theScore,
"keyDown" : keyDown,
"move" : move,
"newLevel" : newLevel,
"reset" : reset,
"resetPosition" : resetPosition
};
};
Pacman.Map = function (size) {
var height = null,
width = null,
blockSize = size,
pillSize = 0,
map = null;
function withinBounds(y, x) {
return y >= 0 && y < height && x >= 0 && x < width;
}
function isWall(pos) {
return withinBounds(pos.y, pos.x) && map[pos.y][pos.x] === Pacman.WALL;
}
function isFloorSpace(pos) {
if (!withinBounds(pos.y, pos.x)) {
return false;
}
var peice = map[pos.y][pos.x];
return peice === Pacman.EMPTY ||
peice === Pacman.BISCUIT ||
peice === Pacman.PILL;
}
function drawWall(ctx) {
var i, j, p, line;
ctx.strokeStyle = "#fFF";
ctx.lineWidth = 5;
ctx.lineCap = "round";
for (i = 0; i < Pacman.WALLS.length; i += 1) {
line = Pacman.WALLS[i];
ctx.beginPath();
for (j = 0; j < line.length; j += 1) {
p = line[j];
if (p.move) {
ctx.moveTo(p.move[0] * blockSize, p.move[1] * blockSize);
} else if (p.line) {
ctx.lineTo(p.line[0] * blockSize, p.line[1] * blockSize);
} else if (p.curve) {
ctx.quadraticCurveTo(p.curve[0] * blockSize,
p.curve[1] * blockSize,
p.curve[2] * blockSize,
p.curve[3] * blockSize);
}
}
ctx.stroke();
}
}
function reset() {
map = Pacman.MAP.clone();
height = map.length;
width = map[0].length;
};
function block(pos) {
return map[pos.y][pos.x];
};
function setBlock(pos, type) {
map[pos.y][pos.x] = type;
};
function drawPills(ctx) {
if (++pillSize > 30) {
pillSize = 0;
}
for (i = 0; i < height; i += 1) {
for (j = 0; j < width; j += 1) {
if (map[i][j] === Pacman.PILL) {
ctx.beginPath();
ctx.fillStyle = "rgba(0, 0, 0, 0)";
//ctx.fillStyle = "#000";
ctx.fillRect((j * blockSize), (i * blockSize),
blockSize, blockSize);
ctx.fillStyle = "#FFF";
ctx.arc((j * blockSize) + blockSize / 2,
(i * blockSize) + blockSize / 2,
Math.abs(5 - (pillSize/3)),
0,
Math.PI * 2, false);
ctx.fill();
ctx.closePath();
}
}
}
};
function draw(ctx) {
var i, j, size = blockSize;
ctx.fillStyle = "rgba(0, 0, 0, 0)";
//ctx.fillStyle = "#000";
ctx.fillRect(0, 0, width * size, height * size);
drawWall(ctx);
for (i = 0; i < height; i += 1) {
for (j = 0; j < width; j += 1) {
drawBlock(i, j, ctx);
}
}
};
function drawBlock(y, x, ctx) {
var layout = map[y][x];
if (layout === Pacman.PILL) {
return;
}
ctx.beginPath();
if (layout === Pacman.EMPTY || layout === Pacman.BLOCK ||
layout === Pacman.BISCUIT) {
ctx.fillStyle = "rgba(0, 0, 0, 0)";
//ctx.fillStyle = "#000";
ctx.fillRect((x * blockSize), (y * blockSize),
blockSize, blockSize);
if (layout === Pacman.BISCUIT) {
ctx.fillStyle = "#FFF";
ctx.fillRect((x * blockSize) + (blockSize / 2.5),
(y * blockSize) + (blockSize / 2.5),
blockSize / 6, blockSize / 6);
}
}
ctx.closePath();
};
reset();
return {
"draw" : draw,
"drawBlock" : drawBlock,
"drawPills" : drawPills,
"block" : block,
"setBlock" : setBlock,
"reset" : reset,
"isWallSpace" : isWall,
"isFloorSpace" : isFloorSpace,
"height" : height,
"width" : width,
"blockSize" : blockSize
};
};
Pacman.Audio = function(game) {
var files = [],
endEvents = [],
progressEvents = [],
playing = [];
function load(name, path, cb) {
var f = files[name] = document.createElement("audio");
progressEvents[name] = function(event) { progress(event, name, cb); };
f.addEventListener("canplaythrough", progressEvents[name], true);
f.setAttribute("preload", "true");
f.setAttribute("autobuffer", "true");
f.setAttribute("src", path);
f.pause();
};
function progress(event, name, callback) {
if (event.loaded === event.total && typeof callback === "function") {
callback();
files[name].removeEventListener("canplaythrough",
progressEvents[name], true);
}
};
function disableSound() {
for (var i = 0; i < playing.length; i++) {
files[playing[i]].pause();
files[playing[i]].currentTime = 0;
}
playing = [];
};
function ended(name) {
var i, tmp = [], found = false;
files[name].removeEventListener("ended", endEvents[name], true);
for (i = 0; i < playing.length; i++) {
if (!found && playing[i]) {
found = true;
} else {
tmp.push(playing[i]);
}
}
playing = tmp;
};
return {
"disableSound" : disableSound,
"load" : load,
"play" : play,
"pause" : pause,
"resume" : resume
};
};
var PACMAN = (function () {
var state = WAITING,
audio = null,
ghosts = [],
ghostSpecs = ["#00FFDE", "#FF0000", "#FFB8DE", "#FFB847"],
eatenCount = 0,
level = 0,
tick = 0,
ghostPos, userPos,
stateChanged = true,
timerStart = null,
lastTime = 0,
ctx = null,
timer = null,
map = null,
user = null,
stored = null;
function getTick() {
return tick;
};
function collided(user, ghost) {
return (Math.sqrt(Math.pow(ghost.x - user.x, 2) +
Math.pow(ghost.y - user.y, 2))) < 10;
};
function drawFooter() {
var topLeft = (map.height * map.blockSize),
textBase = topLeft + 17;
ctx.fillStyle = "rgba(0, 0, 0, 0)";
//ctx.fillStyle = "#000000";
ctx.fillRect(0, topLeft, (map.width * map.blockSize), 30);
ctx.fillStyle = "rgba(0, 0, 0, 0)";
//ctx.fillStyle = "#FFFF00";
for (var i = 0, len = user.getLives(); i < len; i++) {
ctx.fillStyle = "rgba(0, 0, 0, 0)";
//ctx.fillStyle = "#FFFF00";
ctx.beginPath();
ctx.moveTo(150 + (25 * i) + map.blockSize / 2,
(topLeft+1) + map.blockSize / 2);
ctx.arc(150 + (25 * i) + map.blockSize / 2,
(topLeft+1) + map.blockSize / 2,
map.blockSize / 2, Math.PI * 0.25, Math.PI * 1.75, false);
ctx.fill();
}
ctx.fillStyle = !soundDisabled() ? "#00FF00" : "#FF0000";
ctx.font = "bold 16px sans-serif";
//ctx.fillText("♪", 10, textBase);
ctx.fillText("s", 10, textBase);
ctx.fillStyle = "#FFF";
ctx.font = "14px BDCartoonShoutRegular";
ctx.fillText("Score: " + user.theScore(), 30, textBase);
ctx.fillText("Level: " + level, 260, textBase);
}
function redrawBlock(pos) {
map.drawBlock(Math.floor(pos.y/10), Math.floor(pos.x/10), ctx);
map.drawBlock(Math.ceil(pos.y/10), Math.ceil(pos.x/10), ctx);
}
function mainDraw() {
var diff, u, i, len, nScore;
ghostPos = [];
for (i = 0, len = ghosts.length; i < len; i += 1) {
ghostPos.push(ghosts[i].move(ctx));
}
u = user.move(ctx);
for (i = 0, len = ghosts.length; i < len; i += 1) {
redrawBlock(ghostPos[i].old);
}
redrawBlock(u.old);
for (i = 0, len = ghosts.length; i < len; i += 1) {
ghosts[i].draw(ctx);
}
user.draw(ctx);
userPos = u["new"];
for (i = 0, len = ghosts.length; i < len; i += 1) {
ctx.fillStyle = "rgba(0, 0, 0, 0)";
if (collided(userPos, ghostPos[i]["new"])) {
if (ghosts[i].isVunerable()) {
audio.play("eatghost");
ghosts[i].eat();
eatenCount += 1;
nScore = eatenCount * 50;
drawScore(nScore, ghostPos[i]);
user.addScore(nScore);
setState(EATEN_PAUSE);
timerStart = tick;
} else if (ghosts[i].isDangerous()) {
audio.play("die");
setState(DYING);
timerStart = tick;
}
}
}
};
function mainLoop() {
var diff;
if (state !== PAUSE) {
++tick;
}
map.drawPills(ctx);
if (state === PLAYING) {
mainDraw();
} else if (state === WAITING && stateChanged) {
stateChanged = false;
map.draw(ctx);
dialog("Press N to start a New game");
} else if (state === EATEN_PAUSE &&
(tick - timerStart) > (Pacman.FPS / 3)) {
map.draw(ctx);
setState(PLAYING);
} else if (state === DYING) {
if (tick - timerStart > (Pacman.FPS * 2)) {
loseLife();
} else {
redrawBlock(userPos);
for (i = 0, len = ghosts.length; i < len; i += 1) {
redrawBlock(ghostPos[i].old);
//ctx.fillStyle = "rgba(0, 0, 0, 0)";
ghostPos.push(ghosts[i].draw(ctx));
}
user.drawDead(ctx, (tick - timerStart) / (Pacman.FPS * 2));
}
} else if (state === COUNTDOWN) {
diff = 5 + Math.floor((timerStart - tick) / Pacman.FPS);
if (diff === 0) {
map.draw(ctx);
setState(PLAYING);
} else {
if (diff !== lastTime) {
lastTime = diff;
map.draw(ctx);
dialog("Starting in: " + diff);
}
}
}
drawFooter();
}
}());
For every game you want to make, your gameLoop should contain game logic like this (and in advance: GameAlchemist is right, everything should be redrawn every frame):
Clear the whole canvas
Draw background elements (don't use expensive calls, simply draw an image. If you have a lot of drawn elements, such as shapes, lines, etc., make sure to buffer this first on f.e. another, hidden canvas)
Draw more static (background) elements if you want to, that don't change position (f.e. walls)
Draw dynamic elements (your Hero, enemies, bullets, etc.)
Think of all these steps as layers (as in f.e. Photoshop) and make sure to do it in the right order.
This cheat sheet is very helpful.
Also, instead of setInterval, start using requestAnimationFrame. See f.e. this link.
PS Please don't beg for code, in the way you do. Experiment, try, fail, and try again. Then you will learn. Not by asking for gift wrapped pieces of code.

Categories

Resources