I have made a grid and need to check whether a cell has a or several bombs around it, but I'm a bit confused on how to do it now. I have tried this code;
function placeNumbers() {
for (let x = -ColumnRow; x < ColumnRow * 3; x += ColumnRow) {
for (let y = -ColumnRow; y < ColumnRow * 3; y += ColumnRow) {
if (cells[x].bomb == false) {
//do something
}
}
}
}
but then it says that I can't use .bomb. What can I do? I have made a class Cell which has a bomb feature and if a bomb is on the cell then the cell should have bomb = true. So then i have to check wether the neighbour of a cell does has the bomb true or false?
Does anyone have any tips or know what to do here?
Here's a sample of the code:
const canvas = document.getElementById("myCanvas")
const ctx = canvas.getContext("2d")
class Cell {
constructor(x, y, w) {
this.x = x
this.y = y
this.w = w
this.bomb = false
this.revealed = false
}
show() {
const cell = new Path2D();
cell.rect(this.x, this.y, this.w, this.w);
ctx.stroke(cell);
this.cell = cell;
}
}
const w = canvas.width
const h = canvas.height
const ColumnRow = w / 15
const cells = []
const bombs = 10
let checked = true
let bombPosition = []
function setup() {
for (let x = 0; x < w - 1; x += ColumnRow) {
for (let y = 0; y < h - 1; y += ColumnRow) {
cells.push(new Cell(x, y, ColumnRow))
}
}
}
function drawCells() {
for (let c of cells) {
c.show()
}
}
function numOfBombs() {
for (let i = 0; i < bombs; i++) {
randomX = Math.floor(Math.random() * w / ColumnRow) * ColumnRow
randomY = Math.floor(Math.random() * h / ColumnRow) * ColumnRow
bombPosition.push({ x: randomX, y: randomY });
}
}
function drawBomb() {
let img = new Image();
img.onload = function () {
for (let i = 0; i < bombPosition.length; i++) {
ctx.drawImage(img, bombPosition[i].x, bombPosition[i].y, ColumnRow, ColumnRow)
}
};
img.src = "https://raw.githubusercontent.com/americosp/Minesweeper/master/Minesweeper/images/mine.png";
}
function bombCollision(cell) {
for (let i = 0; i < bombPosition.length; i++) {
if (cell.x == bombPosition[i].x && cell.y == bombPosition[i].y) {
console.log("same position");
}
}
}
canvas.addEventListener('click', function (e) {
for (const cell of cells) {
if (ctx.isPointInPath(cell.cell, e.offsetX, e.offsetY)) {
ctx.clearRect(cell.x, cell.y, cell.w, cell.w);
checked = true
cell.revealed = true
bombCollision(cell)
} else {
/* ctx.clearRect(cell.x, cell.y, cell.w, cell.w); */
}
}
});
function update() {
drawCells()
if (checked) {
drawBomb()
// coverCell()
checked = false
}
requestAnimationFrame(update)
}
function update2() {
numOfBombs()
setup()
}
update2()
update()
<canvas id="myCanvas" width="600" height="600"></canvas>
Related
I've been trying to follow this tutorial: https://www.youtube.com/watch?v=aKYlikFAV4k&t=1848s&ab_channel=TheCodingTrain
However, I'm using vanilla Javascript. I'm struggling to get the neighboring cells for each cell in my grid. I'm pretty new to coding so help would be very much appreciated!
Here is my code so far:
//GLOBAL VARIABLES
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const wh = 600;
const cellSize = 30;
const rows = 20;
const cols = 20;
const grid = new Array(rows);
const open = [];
const closed = [];
let start;
let end;
//FUNCTIONS
//Immediately-invoked function expression
//Runs code immediately when the page loads and keeps it out of the global scope (avoids naming conflicts)
(function() {
setup();
})();
function Cell(x, y) { //Constructor function for each cell in the array
this.x = 0;
this.y = 0;
this.f = 0;
this.g = 0;
this.h = 0;
this.show = function(color) { //Function to show cell on grid
ctx.fillStyle = color;
ctx.fillRect(this.x, this.y, cellSize, cellSize);
ctx.strokeStyle = 'white';
ctx.strokeRect(this.x, this.y, cellSize, cellSize);
}
}
//Function to setup the canvas
function setup() {
let interval = setInterval(update, 120);
canvas.setAttribute('width', wh);
canvas.setAttribute('height', wh);
document.body.insertBefore(canvas, document.body.childNodes[0]); //Inserts canvas before the first element in body
createGrid();
setStartEnd();
}
//Function to create grid
function createGrid() {
for (let i = 0; i < rows; i++) { //Creating 2D array
grid[i] = new Array(cols);
}
let x = 0;
let y = 0;
for (let i = 0; i < rows; i++) { //Creating a new cell for each spot in the array
for (let j = 0; j < cols; j++) {
grid[i][j] = new Cell();
grid[i][j].x = x;
grid[i][j].y = y;
grid[i][j].show();
x = x + 1 * 30;
}
x = 0;
y = y + 1 * 30;
}
}
//Function that defines the start and end points
function setStartEnd() {
start = grid[0][0];
end = grid[cols - 1][rows - 1];
open.push(start);
}
//Function to remove a node from an array
function removeArray(arr, e) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === e) {
arr.splice(i, 1);
}
}
}
//Main function
function update() {
//nodes part of "open" array are green
for (let i = 0; i < open.length; i++) {
open[i].show('green');
}
//nodes part of "closed" array are red
for (let i = 0; i < closed.length; i++) {
closed[i].show('red');
}
}
You've made it a bit hard for yourself by having Cell not store its own x,y position in the grid.
If you move some logic from your nested i,j for loop to your Cell class, it gets easier. I modified Cell to store its x and y grid coordinate rather than pixel coordinate. You can then, in update, do something like this:
const nextOpenSet = new Set();
open.forEach(cell => {
const above = grid[cell.y - 1]?.[cell.x];
if (above) nextOpenSet.add(above);
const below = grid[cell.y + 1]?.[cell.x];
if (below) nextOpenSet.add(below);
const left = grid[cell.y][cell.x - 1];
if (left) nextOpenSet.add(left);
const right = grid[cell.y][cell.x + 1];
if (right) nextOpenSet.add(right);
});
open = Array.from(nextOpenSet);
Here's a runnable example:
//GLOBAL VARIABLES
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const wh = 600;
const cellSize = 30;
const rows = 20;
const cols = 20;
const grid = new Array(rows);
let open = [];
const closed = [];
let start;
let end;
//FUNCTIONS
//Immediately-invoked function expression
//Runs code immediately when the page loads and keeps it out of the global scope (avoids naming conflicts)
(function() {
setup();
})();
function Cell(x, y) { //Constructor function for each cell in the array
this.x = x;
this.y = y;
this.show = function(color) { //Function to show cell on grid
ctx.fillStyle = color;
ctx.fillRect(this.x * cellSize, this.y * cellSize, cellSize, cellSize);
ctx.strokeStyle = 'white';
ctx.strokeRect(this.x * cellSize, this.y * cellSize, cellSize, cellSize);
}
}
//Function to setup the canvas
function setup() {
let interval = setInterval(update, 120);
canvas.setAttribute('width', wh);
canvas.setAttribute('height', wh);
document.body.insertBefore(canvas, document.body.childNodes[0]); //Inserts canvas before the first element in body
createGrid();
setStartEnd();
}
//Function to create grid
function createGrid() {
for (let i = 0; i < rows; i++) { //Creating 2D array
grid[i] = new Array(cols);
}
for (let i = 0; i < rows; i++) { //Creating a new cell for each spot in the array
for (let j = 0; j < cols; j++) {
grid[i][j] = new Cell(i, j);
grid[i][j].show();
}
}
}
//Function that defines the start and end points
function setStartEnd() {
start = grid[0][0];
end = grid[cols - 1][rows - 1];
open.push(start);
}
//Function to remove a node from an array
function removeArray(arr, e) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === e) {
arr.splice(i, 1);
}
}
}
//Main function
function update() {
//nodes part of "open" array are green
for (let i = 0; i < open.length; i++) {
open[i].show('green');
}
//nodes part of "closed" array are red
for (let i = 0; i < closed.length; i++) {
closed[i].show('red');
}
const nextOpenSet = new Set();
open.forEach(cell => {
const above = grid[cell.y - 1]?.[cell.x];
if (above) nextOpenSet.add(above);
const below = grid[cell.y + 1]?.[cell.x];
if (below) nextOpenSet.add(below);
const left = grid[cell.y][cell.x - 1];
if (left) nextOpenSet.add(left);
const right = grid[cell.y][cell.x + 1];
if (right) nextOpenSet.add(right);
});
open = Array.from(nextOpenSet);
}
i am a begginer in javascript and i am trying to make a maze game, i followed the instructions on yt to generate random maze map. but when we came to the end and i tried to generete it. it wouldnt work. then i then i checked it in validator and console if there are any problems with it and i couldnt find any of it. so i here i am asking anyone could help me.
this is my code:
let maze = document.querySelector(".maze");
let ctx = maze.getContext("2d");
let current;
class Maze{
constructor(size,rows,columns){
this.size = size;
this.rows = rows;
this.columns = columns;
this.grid = [];
this.stack = [];
}
setup(){
for(let r = 0; r < this.rows; r++){
let row = [];
for (let c = 0; c < this.columns; c++){
let cell = new Cell(r,c, this.grid,this.size);
row.push(cell);
}
this.grid.push(row);
}
current = this.grid[0][0];
}
draw(){
maze.width=this.size;
maze.height=this.size;
maze.style.background = "black";
current.visited = true;
for (let r = 0; r < this.rows; r++) {
for (let c = 0; c < this.columns; c++) {
let grid = this.grid;
grid[r][c].show(this.size, this.rows, this.columns);
}
}
let next = current.checkNeighbours();
if (next) {
next.visited = true;
this.stack.push(current);
current.highlight(this.columns);
current.removeWalls(current, next);
current = next;
}else if (this.stack.length > 0) {
let cell = this.stack.pop();
current = cell;
current.highlight(this.columns);
}
if(this.stack.length == 0){
return;
}
window.requestAnimationFrame(() => {
this.draw();
});
}
}
class Cell{
constructor(rowNum,colNum,parentGrid,parentSize){
this.rowNum = rowNum;
this.colNum = colNum;
this.parentGrid = parentGrid;
this.parentSize = parentSize;
this.visited = false;
this.walls = {
topWall : true,
rightWall : true,
bottomWall : true,
leftwall : true,
};
}
checkNeighbours() {
let grid = this.parentGrid;
let row = this.rowNum;
let col = this.colNum;
let neighbours = [];
let top = row !== 0 ? grid[row-1][col] : undefined;
let right = col !== grid.length-1 ? grid[row][col+1] : undefined;
let bottom = row !== grid.length-1 ? grid[row+1][col] : undefined;
let left = col !== 0 ? grid[row][col-1] : undefined;
if(top && !top.visited) neighbours.push(top);
if(right && !right.visited) neighbours.push(right);
if(bottom && !bottom.visited) neighbours.push(bottom);
if(left && !left.visited) neighbours.push(left);
if(neighbours,length !== 0){
let random = Math.floor(Math.random() * neighbours.length);
return neighbours[random];
}else{
return undefined;
}
}
drawTopWall(x,y,size,columns){
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + size / columns, y);
ctx.stroke();
}
drawRightWall(x,y,size,columns,rows){
ctx.beginPath();
ctx.moveTo(x + size / columns, y);
ctx.lineTo(x + size / columns, y + size / rows);
ctx.stroke();
}
drawBottomWall(x,y,size,columns,rows){
ctx.beginPath();
ctx.moveTo(x, y + size / rows);
ctx.lineTo(x + size / columns, y + size / rows);
ctx.stroke();
}
drawLeftWall(x,y,size,columns,rows){
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x, y + size / rows);
ctx.stroke();
}
highlight(columns){
let x = (this.colNum * this.parentSize) / columns + 1;
let y = (this.rowNum * this.parentSize) / columns + 1;
ctx.fillStyle = "purple";
ctx.fillRect(
x,
y,
this.parentSize / columns - 3,
this.parentSize / columns - 3
);
}
removeWall(cell1,cell2){
let x = cell1.colNum - cell2.colNum;
if (x === 1) {
cell1.walls.leftWall = false;
cell2.walls.rightWall = false;
} else if (x === -1) {
cell1.walls.rightWall = false;
cell2.walls.leftWall = false;
}
let y = cell1.rowNum - cell2.rowNum;
if (y === 1) {
cell1.walls.topWall = false;
cell2.walls.bottomWall = false;
} else if (y === -1) {
cell1.walls.bottomWall = false;
cell2.walls.topWall = false;
}
}
show(size,rows,columns){
let x = (this.colNum * size) / columns;
let y = (this.rowNum * size) / rows;
ctx.strokeStyle = "White";
ctx.fillStyle = "black";
ctx.lineWidth = 2;
if (this.walls.topWall) this.drawTopWall(x, y, size, columns, rows);
if (this.walls.rightWall) this.drawRightWall(x, y, size, columns, rows);
if (this.walls.bottomWall) this.drawBottomWall(x, y, size, columns, rows);
if (this.walls.leftwall) this.drawLeftWall(x, y, size, columns, rows);
if(this.visited){
ctx.fillRect(x + 1, y + 1, size / columns - 2, size / rows - 2);
}
}
}
let newMaze = new Maze(700,20,20);
newMaze.setup();
newMaze.draw();
<canvas class="maze"></canvas>
This line
if(neighbours,length !== 0){
...
}
doesn't look right. Maybe you want if(neighbours.length !== 0){ ... } (note the dot instead of the comma).
Also,
current.removeWalls() should be current.removeWall() (without the "s")
I am trying to build a maze generator for a personal project. I have a recursive depth-first search function that recursively goes through each cell in the grid, checks if it has unvisited neighbors, then calls the recursive function again with the next neighbor. It is able to generate the maze just fine but I want to add a delay between each call to the recursive function so I can animate the creation of the maze as it visits each cell. Using the chrome debugger, it seems to do the 1s delay for the first iteration and then it stops waiting and jumps from the await delay back to the beginning of the function over and over without ever moving on. What am I doing wrong?
Here is the recursive function and delay function:
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
});
}
and here is the full javascript code:
"use strict"
// declare globals
const numCols = 10;
const numRows = 10;
const cellSize = 50;
var grid = [];
// create canvas
var canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.width = numCols * cellSize;
canvas.height = numRows * cellSize;
var body = document.getElementsByTagName("body")[0];
body.appendChild(canvas);
var context = canvas.getContext('2d');
function setup() {
createGrid();
const start = grid[0][0]; // start at top left cell
const end = grid[1][1];
recursiveDFS(start);
}
class Cell {
constructor(col, row) {
this.col = col;
this.row = row;
this.neighbors = {};
this.walls = {
top: true,
right: true,
bottom: true,
left: true
};
this.visited = false;
}
setNeighbors() {
//top
if(this.row - 1 >= 0) {
this.neighbors.top = grid[this.col][this.row - 1];
}
//right
if (this.col + 1 < numCols) {
this.neighbors.right = grid[this.col + 1][this.row];
}
//bottom
if (this.row + 1 < numRows) {
this.neighbors.bottom = grid[this.col][this.row + 1];
}
//left
if (this.col - 1 >= 0) {
this.neighbors.left = grid[this.col - 1][this.row];
}
}
}
// create 2d array of Cell objects
// indexing as grid[col][row]
// grid = [[(0,0), (1,0)],
// [(0,1), (1,1)]]
function createGrid() {
for (var col = 0; col < numCols; col++) {
var colArr = []
for (var row = 0; row < numRows; row++) {
var cell = new Cell(col, row);
colArr.push(cell);
drawGridLines(cell);
}
grid.push(colArr);
}
for (var row = 0; row < numRows; row++) {
for (var col = 0; col < numCols; col++) {
grid[col][row].setNeighbors();
}
}
}
// return single neighbor randomized from all possible neighbors
function getNextNeighbor(cell) {
if (cell.neighbors) {
var neighbors = [];
for (var neighbor in cell.neighbors) {
if (cell.neighbors[neighbor].visited === false){
neighbors.push([cell.neighbors[neighbor], neighbor]);
}
}
}
if(neighbors.length > 0) {
return neighbors[Math.floor(Math.random() * neighbors.length)];
} else {
return [undefined, undefined];
}
}
function delay(ms) {
return new Promise(resolve => {
console.log("waiting...");
setTimeout(resolve, ms)
});
}
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
function highlightCell(cell) {
context.globalCompositeOperation='destination-over'; // fill rect under existing grid
const topLeft = [(cell.col) * cellSize, (cell.row) * cellSize];
context.fillStyle = '#FF0000';
context.fillRect(topLeft[0], topLeft[1], cellSize, cellSize);
}
function removeWall(cell1, cell2, direction) {
switch (direction) {
case 'top':
cell1.walls.top = false;
cell2.walls.bottom = false;
break;
case 'right':
cell1.walls.right = false;
cell2.walls.left = false;
break;
case 'bottom':
cell1.walls.bottom = false;
cell2.walls.top = false;
break;
case 'left':
cell1.walls.left = false;
cell2.walls.right = false;
break;
}
redrawGrid();
}
function redrawGrid() {
context.clearRect(0, 0, numCols * cellSize, numRows * cellSize); // clear canvas
for (var col = 0; col < numCols; col++) {
for (var row = 0; row < numRows; row++) {
drawGridLines(grid[col][row]);
}
}
}
function drawGridLines(cell) {
const topLeft = [ cell.col * cellSize, cell.row * cellSize];
const topRight = [(cell.col + 1) * cellSize, cell.row * cellSize];
const bottomLeft = [ cell.col * cellSize, (cell.row + 1) * cellSize];
const bottomRight = [(cell.col + 1) * cellSize, (cell.row + 1) * cellSize];
context.lineWidth = 2;
//draw top line
if(cell.walls.top){
context.beginPath();
context.moveTo(topLeft[0], topLeft[1]);
context.lineTo(topRight[0], topRight[1]);
context.stroke();
}
//draw right line
if(cell.walls.right) {
context.beginPath();
context.moveTo(topRight[0], topRight[1]);
context.lineTo(bottomRight[0], bottomRight[1]);
context.stroke();
}
//draw bottom line
if(cell.walls.bottom) {
context.beginPath();
context.moveTo(bottomRight[0], bottomRight[1]);
context.lineTo(bottomLeft[0], bottomLeft[1]);
context.stroke();
}
//draw left line
if(cell.walls.left) {
context.beginPath();
context.moveTo(bottomLeft[0], bottomLeft[1]);
context.lineTo(topLeft[0], topLeft[1]);
context.stroke();
}
}
setup();
async function recursiveDFS(currentCell) {
await delay(1000);
highlightCell(currentCell);
currentCell.visited = true;
var [next, direction] = getNextNeighbor(currentCell);
while(typeof(next) != 'undefined') {
removeWall(currentCell, next, direction);
highlightCell(next);
await recursiveDFS(next);
[next, direction] = getNextNeighbor(currentCell);
}
}
Add await when u call recursiveDFS(next); so that it will wait for the function to be done before going to the next step as you have set the function as async.
This is a question from javascript.
if (game.ship.livesPool.getPool[0].getDamaged())
I got an error on this getDamaged() function as undefined inside another function.
game = new Game();
game is defined outside this function as a global variable.
this.ship = new Ship();
ship is defined inside Game class.
this.livesPool = new Pool();
var pool = [];
this.getPool = function(){
return pool;
}
livesPool is defined inside Pool class. pool is an array defined in Pool class.
getPool function will return this array.
pool[i] = new Lives();
each of the pool[i] will be assigned Lives object in Pool class.
this.getDamaged = function(){
return this.damaged;
}
getDamaged() function is defined this way inside Lives class.
Why does it show me that this function is undefined?
Game class
function Game() {
this.init = function () {
// Obtain the canvas from HTML
this.bgCanvas = document.getElementById('background');
this.shipCanvas = document.getElementById('ship');
this.mainCanvas = document.getElementById('main');
// Load the context
/* Just one of them passing to conditional statement is enough to test
* the availability
*/
if (this.bgCanvas.getContext) {
this.bgContext = this.bgCanvas.getContext('2d');
this.shipContext = this.shipCanvas.getContext('2d');
this.mainContext = this.mainCanvas.getContext('2d');
Background.prototype.context = this.bgContext;
Background.prototype.canvasHeight = this.bgCanvas.height;
Background.prototype.canvasWidth = this.bgCanvas.width;
Ship.prototype.context = this.shipContext;
Ship.prototype.canvasHeight = this.shipCanvas.height;
Ship.prototype.canvasWidth = this.shipCanvas.width;
Lives.prototype.context = this.shipContext;
Lives.prototype.canvasHeight = this.shipCanvas.height;
Lives.prototype.canvasWidth = this.shipCanvas.width;
Bullet.prototype.context = this.mainContext;
Bullet.prototype.canvasHeight = this.mainCanvas.height;
Bullet.prototype.canvasWidth = this.mainCanvas.width;
Bullet.prototype.bossContext = this.shipContext;
Enemy.prototype.context = this.mainContext;
Enemy.prototype.canvasHeight = this.mainCanvas.height;
Enemy.prototype.canvasWidth = this.mainCanvas.width;
Boss.prototype.context = this.shipContext;
Boss.prototype.canvasHeight = this.shipCanvas.height;
Boss.prototype.canvasWidth = this.shipCanvas.width;
// Define background in the game
this.background = new Background();
this.background.init(0, 0, imageRepository.background.width, imageRepository.background.height);
// Define ship in the game
this.ship = new Ship();
var shipStartX = this.shipCanvas.width / 2 - imageRepository.ship.width / 2;
var shipStartY = this.shipCanvas.height / 4 * 3 + imageRepository.ship.height / 2;
this.ship.init(shipStartX, shipStartY, imageRepository.ship.width, imageRepository.ship.height);
this.ship.type = "ship";
this.ship.hasExplored = false;
// Define enemy pools in the game
this.enemyPool = new Pool(10);
this.enemyPool.init("enemy");
this.boss = new Boss();
return true;
}
else {
return false;
}
};
this.runGame = function () {
this.ship.draw();
animate();
};
this.restart = function () {
this.bgContext.clearRect(0, 0, this.bgCanvas.width, this.bgCanvas.height);
this.mainContext.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);
this.shipContext.clearRect(0, 0, this.shipCanvas.width, this.shipCanvas.height);
this.enemyPool.init("enemy");
var shipStartX = this.shipCanvas.width / 2 - imageRepository.ship.width / 2;
var shipStartY = this.shipCanvas.height / 4 * 3 + imageRepository.ship.height / 2;
this.ship.x = shipStartX;
this.ship.y = shipStartY;
this.ship.hasExplored = false;
this.ship.bulletPool.init("bullet");
score = 0;
displayedScore = 0;
bossExist = false;
this.boss.life = 100;
this.boss.bulletPool.init("boss_bullet");
this.boss.fireRate = 0;
while(this.ship.livesPool.getSize() < 3){
this.ship.livesPool.increaseLives();
}
document.getElementById('game-over').style.display = "none";
this.runGame();
};
this.start = function () {
gameStarted = true;
document.getElementById('main-menu').style.display = 'none';
document.getElementById('score-board').style.display = 'block';
};
this.backHome = function () {
gameStarted = false;
document.getElementById('game-over').style.display = 'none';
document.getElementById('score-board').style.display = 'none';
document.getElementById('main-menu').style.display = 'block';
this.restart();
};
}
Ship class.
function Ship() {
this.speed = 5;
this.bulletPool = new Pool(maxNumOfBullets);
this.bulletPool.init("bullet");
this.bulletSoundPool = new SoundPool(maxNumOfBullets);
this.bulletSoundPool.init("bullet");
this.livesPool = new Pool(3);
this.livesPool.init("lives");
this.hasExplored = false;
this.life = 3;
var fireRate = 15;
var counter = 0;
this.draw = function () {
if (this.livesPool.getSize() <= 0) {
this.context.clearRect(this.x, this.y, this.width, this.height);
}
else {
this.context.drawImage(imageRepository.ship, this.x, this.y);
}
}
this.move = function () {
counter++;
// Determine if the action is move action
if (KEY_STATUS.left || KEY_STATUS.right ||
KEY_STATUS.down || KEY_STATUS.up) {
// The ship moved, so erase it's current image so it can
// be redrawn in it's new location
this.context.clearRect(this.x, this.y, this.width, this.height);
// Update x and y according to the direction to move and
// redraw the ship. Change the else if's to if statements
// to have diagonal movement.
if (KEY_STATUS.left) {
this.x -= this.speed
if (this.x <= 0) // Keep player within the screen
this.x = 0;
} else if (KEY_STATUS.right) {
this.x += this.speed
if (this.x >= this.canvasWidth - this.width)
this.x = this.canvasWidth - this.width;
} else if (KEY_STATUS.up) {
this.y -= this.speed
if (this.y <= this.canvasHeight / 4 * 3)
this.y = this.canvasHeight / 4 * 3;
} else if (KEY_STATUS.down) {
this.y += this.speed
if (this.y >= this.canvasHeight - this.height)
this.y = this.canvasHeight - this.height;
}
}
this.draw();
if (KEY_STATUS.space && counter >= fireRate) {
this.fire();
counter = 0;
}
};
this.fire = function () {
this.bulletPool.getTwo(this.x + imageRepository.ship.width / 10, this.y, 3);
this.bulletSoundPool.get();
};
}
Pool class.
function Pool(maxSize) {
var size = maxSize;
var pool = [];
var type = "";
// This design enables us to not need to create an object each loop
this.init = function (obj) {
if (obj === "bullet") {
type = "bullet";
for (var i = 0; i < size; i++) {
var bullet = new Bullet("bullet");
bullet.init(0, 0, imageRepository.bullet.width, imageRepository.bullet.height);
bullet.collidableWith = "enemy";
bullet.type = "bullet";
pool[i] = bullet;
}
}
else if (obj === "enemy") {
type = "enemy";
for (var i = 0; i < size; i++) {
var enemy = null;
var rand = Math.floor(Math.random() * 10);
if (rand < 8) {
enemy = new Enemy("enemy", 0, 10);
enemy.init(0, 0, imageRepository.enemy.width, imageRepository.enemy.height);
}
else {
enemy = new Enemy("enemy2", 2, 15);
enemy.init(0, 0, imageRepository.enemy2.width, imageRepository.enemy2.height);
}
enemy.collidableWith = "ship";
enemy.type = "enemy";
pool[i] = enemy;
}
}
else if (obj === "boss_bullet") {
type = "boss_bullet";
for (var i = 0; i < size; i++) {
var bullet = new Bullet("boss_bullet");
bullet.init(0, 0, imageRepository.boss_bullet.width, imageRepository.boss_bullet.height);
bullet.collidableWith = "ship";
bullet.type = "bullet";
pool[i] = bullet;
}
}
else if (obj === "lives") {
type = "lives";
for (var i = 0; i < size; i++) {
var lives = new Lives();
lives.init(imageRepository.background.width - ((i + 1) * imageRepository.lives.width) - 10,
imageRepository.background.height - imageRepository.lives.height - 10,
imageRepository.lives.width, imageRepository.lives.height);
pool[i] = lives;
}
}
};
// Return pool attribute for usage in checking collision
this.getPool = function () {
var res = [];
for (var i = 0; i < pool.length; i++) {
if (pool[i].alive) {
res.push(pool[i]);
}
}
return res;
};
this.get = function (x, y, speed) {
if (pool[size - 1] instanceof Bullet) {
if (!pool[size - 1].alive) {
pool[size - 1].spawn(x, y, speed);
pool.unshift(pool.pop());
}
}
else if (pool[size - 1] instanceof Enemy) {
if (!pool[size - 1].alive) {
if (!(pool[0].alive && pool[0].y <= pool[0].height)) {
pool[size - 1].spawn(x, y, speed);
pool.unshift(pool.pop());
}
}
}
};
this.getTwo = function (x, y, speed) {
if (type === "bullet") {
if (!pool[size - 1].alive && !pool[size - 2].alive) {
this.get(x, y, speed);
this.get(x + (imageRepository.ship.width * 8) / 10
- imageRepository.bullet.width, y, speed);
}
}
else if (type === "boss_bullet") {
if (!pool[size - 1].alive && !pool[size - 2].alive) {
this.get(x, y, speed);
// This will have the center of boss as the center between two bullets
// x + 2 * (imageRepository.boss.width / 2 - x) - imageRepository.boss_bullet.width
this.get(x + imageRepository.boss.width * 3 / 5 - imageRepository.boss_bullet.width,
y, speed);
console.log(x);
console.log(x + imageRepository.boss.width * 3 / 5 - imageRepository.boss_bullet.width);
}
}
};
this.animate = function () {
for (var i = 0; i < size; i++) {
if (pool[i].alive) {
if (pool[i].draw()) {
pool[i].clear();
pool.push((pool.splice(i, 1))[0]);
}
}
else
break;
}
};
this.getSize = function () {
return size;
};
this.setSize = function (input) {
size = input;
};
this.decreaseLives = function () {
if (size >= 1) {
if (pool[size - 1] instanceof Lives) {
pool[size - 1].setDamaged(true);
size--;
}
}
};
this.increaseLives = function(){
if (pool[size - 1] instanceof Lives){
pool[size - 1].setDamaged(true);
size++;
}
};
}
Lives class.
function Lives() {
this.alive = true;
this.damaged = false;
this.draw = function () {
this.context.clearRect(this.x, this.y, this.width, this.height);
this.context.drawImage(imageRepository.lives, this.x, this.y);
if (this.damaged)
return true;
}
this.clear = function () {
alive = false;
this.x = -1 * this.width;
this.y = -1 * this.height;
}
this.getDamaged = function(){
return this.damaged;
}
this.setDamaged = function(input){
this.damaged = input;
}
}
getPool is a function, you need to call it:
if (game.ship.livesPool.getPool()[0].getDamaged())
// ^^
I need some expert help. When I make canvas background transparent the line colors the whole canvas (shown in code below).
How do I stop/fix this? I want the line stay as a single line that doesn't color the canvas.
Math.clamp = function(x, min, max) {
return x < min ? min : (x > max ? max : x);
};
// canvas settings
var viewWidth = window.innerWidth,
viewHeight = window.innerHeight,
drawingCanvas = document.getElementById("drawing_canvas"),
ctx,
timeStep = (10 / 100),
time = 0;
var lineTension = 0.067,
lineDamping = 0.068,
waveSpreadFactor = 0.1;
var previousMousePosition = {
x: 0,
y: 0
},
currentMousePosition = {
x: 0,
y: 0
},
actualMousePosition = {
x: 0,
y: 0
};
var line,
lineSegmentCount = 64,
lineMaxForce = 32,
lineStrokeGradient;
var audioCtx,
nodeCount = 64,
oscillatorNodes = [],
gainNodes = [];
var segmentsPerNode = lineSegmentCount / nodeCount;
function initGui() {
}
function goBananas() {
lineTension = 0.2;
line.anchors[Math.floor(Math.random() * line.anchors.length)].
vel = lineMaxForce;
}
function resetLine() {
line.reset();
for (var i = 0; i < nodeCount; i++) {
oscillatorNodes[i].detune.value = 100;
gainNodes[i].gain.value = 0;
}
lineTension = 0.0025;
lineDamping = 0.05;
waveSpreadFactor = 0.1;
}
function initDrawingCanvas() {
drawingCanvas.width = viewWidth;
drawingCanvas.height = viewHeight;
window.addEventListener('resize', resizeHandler);
window.addEventListener('mousemove', mouseMoveHandler);
setInterval(updateMousePosition, (1000 / 30));
ctx = drawingCanvas.getContext('2d');
ctx.lineWidth = 5;
line = new Line(0, viewHeight * 0.5, viewWidth, lineSegmentCount);
// line.anchors[0].vel = viewHeight * 0.25;
lineStrokeGradient = ctx.createLinearGradient(0, 0, 0, viewHeight);
lineStrokeGradient.addColorStop(0, '#0ff');
}
function initWebAudio() {
audioCtx = new(window.AudioContext || window.webkitAudioContext)();
for (var i = 0; i < nodeCount; i++) {
var oscillatorNode = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
oscillatorNode.connect(gainNode);
gainNode.connect(audioCtx.destination);
gainNode.gain.value = 0;
oscillatorNode.type = 'saw';
oscillatorNode.detune.value = 200;
oscillatorNode.frequency.value = 1200 * (i / 60);
oscillatorNode.start();
oscillatorNodes[i] = oscillatorNode;
gainNodes[i] = gainNode;
}
}
function resizeHandler() {
drawingCanvas.width = viewWidth = window.innerWidth;
drawingCanvas.height = viewHeight = window.innerHeight;
if (ctx) {
ctx.lineWidth = 4;
line.resize(viewWidth, viewHeight * 0.5);
}
}
function mouseMoveHandler(event) {
actualMousePosition.x = Math.floor(event.clientX);
actualMousePosition.y = Math.floor(event.clientY);
}
function updateMousePosition() {
previousMousePosition.x = currentMousePosition.x;
previousMousePosition.y = currentMousePosition.y;
currentMousePosition.x = actualMousePosition.x;
currentMousePosition.y = actualMousePosition.y;
}
function update() {
var px = Math.min(previousMousePosition.x, currentMousePosition.x),
py = Math.min(previousMousePosition.y, currentMousePosition.y),
pw = Math.max(1, Math.abs(previousMousePosition.x - currentMousePosition.x)),
ph = Math.max(1, Math.abs(previousMousePosition.y - currentMousePosition.y)),
force = Math.clamp(currentMousePosition.y -
previousMousePosition.y, -lineMaxForce, lineMaxForce);
var pixels = ctx.getImageData(px, py, pw, ph),
data = pixels.data;
for (var i = 0; i < data.length; i += 3) {
var r = data[i + 0],
g = data[i + 1],
b = data[i + 2],
x = (i % ph) + px;
if (r + g + b > 0) {
line.ripple(x, force);
}
}
line.update();
for (var j = 0; j < gainNodes.length; j++) {
var anchor = line.anchors[j * segmentsPerNode],
gain = Math.clamp(Math.abs(anchor.vel) / viewHeight * 0.5, 0, 3),
detune = Math.clamp(anchor.pos / viewHeight * 100, 0, 300);
gainNodes[j].gain.value = gain;
oscillatorNodes[j].detune.value = detune;
}
}
function draw() {
ctx.strokeStyle = lineStrokeGradient;
line.draw();
}
window.onload = function() {
initDrawingCanvas();
initWebAudio();
initGui();
requestAnimationFrame(loop);
};
function loop() {
update();
draw();
time += timeStep;
requestAnimationFrame(loop);
}
Line = function(x, y, length, segments) {
this.x = x;
this.y = y;
this.length = length;
this.segments = segments;
this.segmentLength = this.length / this.segments;
this.anchors = [];
for (var i = 0; i <= this.segments; i++) {
this.anchors[i] = {
target: this.y,
pos: this.y,
vel: 0,
update: function() {
var dy = this.pos - this.target,
acc = -lineTension * dy - lineDamping * this.vel;
this.pos += this.vel;
this.vel += acc;
},
reset: function() {
this.pos = this.target;
this.vel = 0;
}
};
}
};
Line.prototype = {
resize: function(length, targetY) {
this.length = length;
this.segmentLength = this.length / this.segments;
for (var i = 0; i <= this.segments; i++) {
this.anchors[i].target = targetY;
}
},
reset: function() {
for (var i = 0; i <= this.segments; i++) {
this.anchors[i].reset();
}
},
ripple: function(origin, amplitude) {
var i = Math.floor((origin - this.x) / this.segmentLength);
if (i >= 0 && i <= this.segments) {
this.anchors[i].vel = amplitude;
}
},
update: function() {
var lDeltas = [],
rDeltas = [],
i;
for (i = 0; i <= this.segments; i++) {
this.anchors[i].update();
}
for (i = 0; i <= this.segments; i++) {
if (i > 0) {
lDeltas[i] = waveSpreadFactor * (this.anchors[i].pos - this.anchors[i - 1].pos);
this.anchors[i - 1].vel += lDeltas[i];
}
if (i < this.segments) {
rDeltas[i] = waveSpreadFactor * (this.anchors[i].pos - this.anchors[i + 1].pos);
this.anchors[i + 1].vel += rDeltas[i];
}
}
for (i = 0; i <= this.segments; i++) {
if (i > 0) {
this.anchors[i - 1].pos += lDeltas[i];
}
if (i < this.segments) {
this.anchors[i + 1].pos += rDeltas[i];
}
}
},
draw: function() {
ctx.beginPath();
for (var i = 0; i <= this.segments; i++) {
ctx.lineTo(
this.x + this.segmentLength * i,
this.anchors[i].pos
);
}
ctx.stroke();
}
};
From the code you posted, the problem seems to be a missing
ctx.clearRect(0, 0, viewWidth, viewHeight)
at the beginning of your "draw" function.
See a working example here