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.
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 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>
I can't for the life of me work out how to reset the function createAliens below so I can begin a new game - I'm basically looking for a way to clear the sprites off the canvas so I can reset the game.
Any help would be seriously appreciated :)
const BORDER = 60;
const SPEED = 3;
let score = 0;
let numAliens = 3;
let alienSpeed = 5;
let aliensArray = [];
let ship;
let laser;
let lasers = [];
function preload() {
}
function setup() {
createCanvas(1000, 700);
aliens = new Group();
ships = new Group();
lasers = new Group();
createAliens(numAliens);
ship = createSprite(width/2, height/1.15, 40, 60);
ship.setCollider('rectangle', 0, 0, 40, 60);
ships.add(ship);
}
function draw() {
background(0);
if(aliens == 0) {
createAliens(numAliens + 2);
}
aliens.bounce(aliens); // p5 game methods - collision etc using callbacks
aliens.overlap(lasers, scoreTotal);
aliens.collide(lasers, die);
ship.bounce(aliens, reset);
keyPressed();
drawSprites();
for (let i = 0; i < allSprites.length; i++) { // game borderless
let all = allSprites[i];
if (all.position.x < - BORDER) {
all.position.x = width + BORDER;
}
if (all.position.x > width + BORDER) {
all.position.x = - BORDER;
}
if (all.position.y < - BORDER) {
all.position.y = height + BORDER;
}
if (all.position.y > height + BORDER) {
all.position.y = - BORDER;
}
}
}
function createAliens(numAliens) { // spawn some aliens
for (let j = 0; j < numAliens; j++) {
alien = createSprite(random(width), 0, 50, 60);
alien.shapeColor = color('red');
alien.setSpeed(random(0.3, alienSpeed), random(45, 150));
aliensArray.push(alien);
aliens.add(alien);
}
}
function die(alien) { // kill the alien
alien.remove();
}
function scoreTotal() {
score += 10;
}
function reset() {
console.log('RESET FIRING');
}
function keyPressed() {
if (keyDown(LEFT_ARROW)) {
ship.setSpeed(SPEED, 180);
ship.friction = 0.01;
}
else if (keyDown(RIGHT_ARROW)) {
ship.setSpeed(SPEED, 0);
ship.friction = 0.01;
}
else if (keyDown(UP_ARROW)) {
ship.setSpeed(SPEED, 270);
ship.friction = 0.01;
}
else if (keyDown(DOWN_ARROW)) {
ship.setSpeed(SPEED, 90);
ship.friction = 0.01;
}
if(keyWentDown(' ')) {
let laser = createSprite(ship.position.x, ship.position.y, 5, 5);
laser.setSpeed(10, 270);
laser.shapeColor = color('red');
laser.life = 30;
lasers.add(laser);
}
}
I have got the aliens spawning with the simple conditional logic:
if(aliens == 0) {
createAliens(numAliens + 2);
}
I need to run functions sequentially in order to animate in the proper order, but I am running into some issues. The code basically checks some conditions and stores values in arrays (not shown). These values are passed as parameters to certain functions, which need to run sequentially. I am using promises to achieve this
This code stores the needed functions (and their parameters) in an array func_call
var func_call = [];
for (var i = 0; i < s_list.length; i++){
//lower
if (!isUpper) {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
//upper
} else {
canvas = document.getElementById("myCanvasUpr");
context = canvas.getContext("2d");
}
func_call.push(function () {get_path(context, s_list[i], d_list[i], final_arr);});
func_call.push(function () {draw_marker(context, s_list[i], d_list[i], isFirst[i], isLast[i]);});
}
func_call.push(function() {wrap(final_arr)});
func_call.reduce((cur, next) => cur.then(next()), Promise.resolve());
get_path is here
function get_path(context, source, desti, arr){
return new Promise(function(resolve) {
var starting_points = string_to_point[source];
var ending_points = string_to_point[desti];
console.log("get path");
console.log(starting_points);
/*
* Define the variables
* i -> iterator count
* seg_1 -> points of starting exhibit and closest point on the path
* seg_2 -> points of ending exhibit and closest point on path
* main_path -> the points along the main path
* reverse_flag -> denotes whether the direction is forward or reverse
*/
var i;
var seg_1 = [];
var seg_2 = [];
var main_path = [];
var secondary_path = [];
var reverse_flag = false;
var secondary_flag = false;
var primary_flag = false;
var starting_point = starting_points[0];
var ending_point = ending_points[0];
var vertices = [];
var pre = [];
var secondary_vertices = [];
var points = [];
var secondary_points = [];
/*
* Add the first two segments
*/
if(starting_points.length == 3){
for(var j = starting_points[0]; j <= starting_points[1]; j++){
seg_1.push({x:point_coord[j][0] , y:point_coord[j][1]});
}
starting_point = starting_points[2];
}
if(ending_points.length == 3){
for(var j = ending_points[0]; j <= ending_points[1]; j++){
seg_2.push({x:point_coord[j][0] , y:point_coord[j][1]});
}
ending_point = ending_points[2];
}
if(starting_point == 260 && ending_point == 260){
ending_point = -1;
} else if (starting_point == 260){
for(var j = 260; j <= 267; j++){
seg_1.push({x:point_coord[j][0] , y:point_coord[j][1]});
}
starting_point = 72;
} else if (ending_point == 260){
for(var j = 260; j <= 267; j++){
seg_2.push({x:point_coord[j][0] , y:point_coord[j][1]});
}
ending_point = 72;
}
/*
* Handles reverse direction. Switches starting and end points and sets the reverse_flag
*/
if (ending_point != -1 && starting_point > ending_point){
temp = starting_point;
starting_point = ending_point;
ending_point = temp;
reverse_flag = true;
}
/*
* Add points to main_path
*/
for (i = starting_point; i <= ending_point; i++){
var b = point_coord[i];
/*if(i >= 122){
secondary_flag = true;
secondary_path.push({x:b[0],y:b[1]});
} else {
primary_flag = true;
main_path.push({x:b[0],y:b[1]});
}*/
primary_flag = true;
main_path.push({x:b[0] , y:b[1]});
}
/*
* Creates the full path -> combination of seg_1, seg_2, and main_path
*/
if(reverse_flag){
if(primary_flag){
if(secondary_flag){
vertices = seg_1.concat(secondary_path.reverse());
secondary_vertices = main_path.reverse().concat(seg_2);
context.beginPath()
context.arc(pathways[121][0], pathways[121][1], 8, 0, 2 * Math.PI);
context.fillStyle = 'green';
context.fill();
context.beginPath();
context.arc(pathways[122][0], pathways[122][1], 6, 0, 2 * Math.PI);
context.fillStyle = 'red';
context.fill();
} else {
vertices = seg_1.concat(main_path.reverse(),seg_2.reverse());
}
} else {
if(secondary_flag){
vertices = seg_1.concat(secondary_path.reverse(),seg_2);
} else {
}
}
} else {
if(primary_flag){
if(secondary_flag){
vertices = seg_1.concat(main_path);
secondary_vertices = secondary_path.concat(seg_2);
context.beginPath();
context.arc(pathways[122][0], pathways[122][1], 8, 0, 2 * Math.PI);
context.fillStyle = 'green';
context.fill();
context.beginPath()
context.arc(pathways[121][0], pathways[121][1], 6, 0, 2 * Math.PI);
context.fillStyle = 'red';
context.fill();
} else {
vertices = seg_1.concat(main_path,seg_2.reverse());
}
} else {
if(secondary_flag){
vertices = seg_1.concat(secondary_path,seg_2);
} else {
vertices = seg_1.concat(seg_2);
}
}
}
/*
* Calculate the extra points for animation, and draw the animation
*/
if(secondary_vertices.length == 0){
points = calcWaypoints(vertices);
pre.push(points);
} else {
points = calcWaypoints(vertices);
secondary_points = calcWaypoints(secondary_vertices);
pre.push(points, secondary_points);
}
arr.push([context,pre]);
console.log(arr);
resolve();
});
}
draw_marker is here
function draw_marker(context, source, desti, isFirst, isLast) {
return new Promise(function(resolve) {
/*
* Get the point number of the point on the path that the source and destination connect to
*/
var start = string_to_point[source];
var finish = string_to_point[desti];
/*
* Marker
*/
if (isFirst) {
var marker1 = new Image();
marker1.onload = function(){
marker1._x = point_coord[start[0]][0]-1;
marker1._y = point_coord[start[0]][1]-44;
context.drawImage(marker1, marker1._x, marker1._y,marker1.width,marker1.height);
};
marker1.src = "images/map_pin.png";
} else {
context.fillStyle = 'green';
context.beginPath();
context.arc(point_coord[start[0]][0], point_coord[start[0]][1], 8, 0, 2 * Math.PI);
context.strokeStyle = "green";
context.stroke();
context.fill();
}
if (isLast) {
/*var marker2 = new Image();
marker2.onload = function(){
marker2._x = point_coord[finish[0]][0]-15;
marker2._y = point_coord[finish[0]][1]-22;
context.drawImage(marker2, marker2._x, marker2._y,marker2.width,marker2.height);
};
marker2.src = "images/x_marks.png";*/
} else {
context.fillStyle = 'red';
context.beginPath();
context.arc(point_coord[finish[0]][0], point_coord[finish[0]][1], 6, 0, 2 * Math.PI);
context.strokeStyle = "#ff0000";
context.stroke();
context.fill();
}
resolve();
});
}
wrap is here
function wrap(arr){
console.log("in wrap");
var getAnimation = function(context, lines){
console.log("Get animation");
console.log(lines);
return new Promise(function(resolve) {
context.beginPath();
lines.reduce((a, c) => a.then(() => animate(context,c)), Promise.resolve());
resolve();
});
};
arr.reduce((a,c) => a.then(() => getAnimation(c[0],c[1])),Promise.resolve());
}
animate is here
var animate = function(context, p){
return new Promise(function(resolve) {
console.log("in animate");
var t = 1;
context.lineCap = "round";
context.lineWidth = 5;
//context.strokeStyle = "#ff0000";
context.strokeStyle = "#ff3c3c";
//context.strokeStyle = "#f38f1d";
var runAnimation = function(){
if(t<p.length){
console.log("running animation");
context.beginPath();
context.moveTo(p[t-1].x,p[t-1].y);
context.lineTo(p[t].x,p[t].y);
context.stroke();
t++;
requestAnimationFrame(function(){runAnimation()});
} else {
console.log("run animation resolved");
resolve()
}
};
runAnimation();
});
}
The goal is to run however many combos of get_path and draw_markers as needed, then run wrap, which in turn calls animate. Each animate needs to finish before the next one starts as well
How do I accomplish this?
Thank you. If any more details are needed, please let me know
You could use async/await if the target JavaScript environment supports it. If not you could implement a compiler like Babel and use it that way.
If you don't want to go that route there is Bluebird, a Promise library that allows you to call Promise.map with a { concurrency: 1 } option. This will call the promises in sequential order.
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())
// ^^