I'm currently in a Computer Science class at my high school and am writing a program to simulate Conway's Game of Life. I am writing the program in the Code Studio "App Lab" which uses JavaScript and is what we have been learning in. It has a smartphone on the left which you design for.
It's been going all fine and dandy so far, but I am working on trying to draw the cells on the screen and my program refuses to enter into the for loop which will draw the cells (which are represented as buttons). The function which draws the CellBoard, is called drawBoard and is a method inside of the CellBoard object.
function Cell(x, y, id) {
//base unit for the program, can be either dead or alive based on Conway's
//Game of Life Rules
this.xPos = x;
this.yPos = y;
this.id = "cell" + id;
this.alive = false;
this.aliveNextTurn = false;
this.aliveNeighbors = 0;
this.age = 0;
this.swapState = function(){
if(this.alive){
this.alive = false;
}
else{
this.alive = true;
}
};
}
function CellBoard(width, height){
//the board of cells, this object will house all the methods for the rule
//checking and state setting
this.board = [];
var count = 0;
for(var x = 0; x<width; x++){
var boardY =[];
for(var y = 0; y<height; y++){
boardY.push(new Cell(x,y,count));
count++;
}
this.board.push(boardY);
}
this.drawBoard = function(){
//draws the board of cells on the screen as buttons so that the user can
//initially set them
setScreen("simulationScreen");
//console.log("screen set");
//console.log("starting button making");
for(var i = 0; i<this.width; i++){ //<----the problem is here
//console.log("starting loop");
for(var j = 0; j<this.height; j++){
//console.log("making button");
button(this.board[i][j].id, "test");
setPosition(this.board[i][j].id, 20+(280/i), 20+(280/j), 280/i, 280/j);
setProperty(this.board[i][j].id, "background-color", rgb(0,0,0)); //black background by default
//console.log(getProperty(this.board[i][j].id, "x"));
}
}
//console.log("done drawing board");
};
}
var testBoard = new CellBoard(3, 3);
testBoard.drawBoard();
Any help is greatly appreciated, thanks!
Here are the console logs from the function that has the problem:
screen set
starting button making
done drawing board
looks like in your for loop in the drawBoard function, you use this.width and this.height. However you never set this.width and this.height. In the initialization code in the CellBoard class, you should be setting this.width = width. It might be skipping the for loop because this.width is undefined which doesn't satisfy the for loop condition.
As well, you use the this keyword in your drawBoard function. Within a function like that, this will refer to the function and not the object. Instead in the initialization code, you may want to create a variable to hold this. You can do cell_board = this in the initialization code and then use cell_board.width instead in the drawBoard function.
Related
I have been having a problem. I am trying to make Conway's Game of Life with javascript using one big class for the game. However, I get this error when I run my finished product despite using the function in the exact same way in another function. I declared the array here:
class Game {
constructor() {
this.cellSize = 5;
this.aliveColor = "#FF756B";
this.deadColor = "#181818";
this.rows = Math.floor(canvas.height / this.cellSize);
this.columns = Math.floor(canvas.width = this.cellSize);
//Variables for two lifecycles, active and past iteration
this.inactiveArray = [];
this.activeArray = [];
And call on the array in these two methods:
this.drawCells = () => {
for (let i=0; i<this.rows; i++) {
for (let j=0; j<this.columns; j++) {
let color;
if (this.activeArray[i][j] === 1) {
color = this.aliveColor;
} else {
color = this.deadColor;
}
ctx.fillStyle = color;
ctx.fillRect(i * this.cellSize, j * this.cellSize, this.cellSize, this.cellSize);
}
}
}
this.arraysInit = () => {
for(let i=0; i<this.rows; i++) {
this.activeArray[i] = [];
for(let j=0; j<this.columns; j++) {
this.activeArray[i][j] = 0;
}
}
this.inactiveArray = this.activeArray;
}
(Sorry for the bad indentation, this is my first post) The arraysInit function runs with no errors for sure as I have tried debugging all of it (to no prevail). An error is thrown in drawCells() on the line "if (this.activeArray[i][j] === 1) {" and I have no clue why, as I have clearly defined the array and used it.
One thing I noticed, Cidom10, is that your line this.columns = Math.floor(canvas.width = this.cellSize); has an = sign where it looks like it should be a division / sign. Not sure if that is just a typo or what.
But, I believe the real problem lies with the line this.inactiveArray = this.activeArray; which sets this.inactiveArray to be a reference to this.activeArray. It doesn't copy the array, which I believe you are trying to do with that line.
To copy the activeArray, you can use something like Object.assign:
this.inactiveArray = Object.assign([], this.activeArray);
My bet is that you are modifying this.inactiveArray at some point which is also modifying this.activeArray and causing some indexing issues and that's where those errors stem from.
Without more code to go off of, though, this is all in theory.
I am working on an application where I'd like to provide overlays of different animations onto a range of videos using p5js. I'm looking to organize my classes of animation types so that each animation has a similar structure to update and destroy objects during each loop. My plan is to have an array of animations that are currently "active" update them each iteration of the loop and then destroy them when they are completed. I built a class to fade text in this manner but I'm getting some weird flashy behavior that seems to occur every time a new animation is triggered in the middle of another animation. I've been trying to debug it but have been unsuccessful. Do you have any suggestions as to:
(1) if this is due to my code structure? (and maybe you have a suggestion of a better way),
or
(2) I'm doing something else incorrectly?
Here is the code:
// create an array of currently executing animations to update
// each animation class needs to have one function and one attribute:
// (1) update() -- function to move the objects where ever they need to be moved
// (2) done -- attribute to determine if they should be spliced out of the array
var animations = [];
//////////////////////////////////////////
// Global Variables for Animations //
//////////////////////////////////////////
let start = false;
let count = 0;
function setup(){
let canv = createCanvas(1920, 1080);
canv.id = "myP5canvas";
background(0);
}
function draw(){
background(0);
// Check things to see if we should be adding any animations to the picture
var drawText = random(100);
if (drawText > 98) {
//if (start == false) {
let r = 255;
let g = 204;
let b = 0;
let x = random(width-10);
let y = random(height-10);
animations.push(new TextFader("Wowwwzers!", 100, 'Georgia', r, g, b, x, y, count));
start = true;
count += 1;
}
// Update animations that exist!
for (var i=0; i < animations.length; i++) {
// update the position/attributes of the animation
animations[i].update();
// check if the animation is done and should be removed from the array
if (animations[i].done) {
console.log("SPLICE: " + animations[i].id);
animations.splice(i, 1);
}
}
}
// EXAMPLE ANIMATION
// TEXT FADE
let TextFader = function(words, size, font, red, green, blue, xloc, yloc, id) {
this.id = id;
console.log("create fader: " + this.id);
// translating inputs to variables
this.text = words;
this.size = size;
this.font = font;
// To Do: separating out each of the values until I figure out how to fade separately from the color constructor
this.red = red;
this.green = green;
this.blue = blue;
this.xloc = xloc;
this.yloc = yloc;
// Maybe add customization in the future for fading...
this.fade = 255;
this.fadeTime = 3; // in seconds
this.fadeIncrement = 5;
// Variables to use for destruction
this.createTime = millis();
this.done = false;
}
TextFader.prototype.update = function() {
// Update the fade
// If the fade is below zero don't update and set to be destroyed
this.fade -= this.fadeIncrement;
if (this.fade <= 0) {
this.done = true;
} else {
this.show();
}
}
TextFader.prototype.show = function() {
textFont(this.font);
textSize(this.size);
fill(this.red, this.green, this.blue, this.fade);
text(this.text, this.xloc, this.yloc);
console.log("Drawing: " + this.id + " fade: " + this.fade + " done: " + this.done);
}
Yay, I've got you an answer! It works like expected when you reverse the for loop that loops over the animations.
Because you splice elements of the same array inside the loop, some elements are skipped. For example; animations[0].done = true and gets removed. That means that animations[1] is now in the spot of animations[0] and animations[2] is now in the spot of animations[1].
The i variable is incremented to 1, so on the next loop, you update animations[1] (and skip the animation that is now in animation[0]).
When you reverse the loop, everything before the element you splice stays the same and nothing is skipped.
For example; animations[2].done = true and gets removed. That means that animations[1] is still in the spot of animations[1].
The i variable is decremented to 1, so on the next loop, you update animations[1] and don't skip any elements.
// Update animations that exist!
for (var i = animations.length - 1; i >= 0; i--) {
// update the position/attributes of the animation
animations[i].update();
// check if the animation is done and should be removed from the array
if (animations[i].done) {
//console.log("SPLICE: " + animations[i].id);
animations.splice(i, 1);
}
}
I'm trying to make bomberman using vanilla JS, for my examination project.
I am a little stuck right now with how to take out the bombs in the array and push them into the array again after they explode.
They need to explode after 2 seconds.
My code for bombs:
function bombPlayerOne() {
let ss = new createjs.SpriteSheet(game.q.getResult('bomb'))
let temp = new createjs.Sprite(ss, "bombIt");
temp.x = playerOne.x;
temp.y = playerOne.y;
game.stage.addChild(temp);
powerUps.bombs.push(temp);
console.log("player one placed a bomb");
for (var i = powerUps.bombs.length - 1; i > 0; i--) {
powerUps.bombs.splice;
// TODO : tween bomber ud...
powerUps.bombs.push;
}
}
function bombPlayerTwo() {
let ss = new createjs.SpriteSheet(game.q.getResult('bomb'))
let temp = new createjs.Sprite(ss, "bombIt");
temp.x = playerTwo.x;
temp.y = playerTwo.y;
game.stage.addChild(temp);
powerUps.bombs.push(temp);
console.log("player two placed a bomb");
for (var i = powerUps.bombs.length - 1; i > 0; i--) {
powerUps.bombs.splice;
// TODO : tween bomber ud...
powerUps.bombs.push;
}
}
So you have a few options, and FYI this isn't necessarily a javascript question so much as how do you handle game logic/code design type of question.
1) A bomb when placed contains a reference back to it's owner. ie
bomb.owner = playerOne
2) You have a manager that controls the state of a level, which keeps track of bombs
LevelManager.player1Bombs = ....
3) You have an array of bombs placed belonging to each player, which you then update during your logic update calls.
function gameUpdate(long milliSecondsSinceLastFrame){
for(bomb in playerOne.placedBombs){
if(bomb.isExploded){
//do other cleanup
playerOne.availableBombs ++;
}
}
//... do same for player 2 etc
}
All of them have their own advantages/disadvantages.
I am attempting to create an app in Javascript/HTML/CSS to run board games, so I can teacher my 11th and 12th grades students to do the same. Mostly I've got it working, including a dice roll, but I have a counter that determines whose turn it is that is returning NaN for one of the turns. It does return all other turns, but adds in the NaN as well.
I'm not using a for() loop because it is inside a given function that starts a player turn. All of the answers I've found online and here at StackOverflow refer to issues in counters using the for() loop.
Here's my code:
var p1="Name1";
var p2="Name2";
var p3="Name3";
var playerList=new Array(p1, p2, p3);
var pTurn=0;
var currentPlayer=playerList[pTurn];
function nextPlayer() {
pTurn++;
if(pTurn<playerList.length) {
pTurn=0;
}
currentPlayer=playerList[pTurn];
/* the rest of the function sends the data to the html page and works */
}
You need to reset pTurn when it is too large for the list. That is:
if (pTurn >= playerList.length) {
pTurn = 0;
}
var p1 = "Name1";
var p2 = "Name2";
var p3 = "Name3";
var playerList = new Array(p1, p2, p3);
var pTurn = 0;
var currentPlayer = playerList[pTurn];
function nextPlayer() {
pTurn++;
if (pTurn >= playerList.length) {
pTurn = 0;
}
currentPlayer = playerList[pTurn];
}
for (var i = 0; i < 10; ++i) {
console.log(currentPlayer);
nextPlayer();
}
I have a mighty strange JavaScript problem. I have made an object oriented maze generator, which works well, but only if I call "this" (or the alias "self") right before the generator.
See code below:
// Constructor for a maze
function Maze(mazeWidth, mazeHeight) {
// Always working reference to this
var self = this;
// Has the maze been generated?
var generated = false;
// Default dimensions
var width = 20;
var height = 20;
// Check if dimensions are given
if (!isNaN(mazeWidth) && mazeWidth >= 1) {
width = parseInt(mazeWidth);
}
if (!isNaN(mazeHeight) && mazeHeight >= 1) {
height = parseInt(mazeHeight);
}
// The maze itself
var maze = {};
// Populate the maze
for (var y = 0; y < height; y++) {
maze[y] = {};
for (var x = 0; x < width; x++) {
maze[y][x] = new MazeCell(x, y);
}
}
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
}
// For some mighty strange reason "self" (or "this") needs to be called here for the code below to work
self;
// Generate the maze
(function generateMaze() {
// Map directions to its reverse
var directionMap = {};
directionMap[Maze.prototype.N] = Maze.prototype.S;
directionMap[Maze.prototype.E] = Maze.prototype.W;
directionMap[Maze.prototype.S] = Maze.prototype.N;
directionMap[Maze.prototype.W] = Maze.prototype.E;
// Depth-first search to generate the maze
(function DFS(cell, entryDirection) {
// Set the cell as discovered and open the entry direction
cell._setDiscovered();
cell._open(entryDirection);
// Find the neighbour cells
var neighbours = {};
neighbours[Maze.prototype.N] = cell.getNeighbourCell(Maze.prototype.N);
neighbours[Maze.prototype.E] = cell.getNeighbourCell(Maze.prototype.E);
neighbours[Maze.prototype.S] = cell.getNeighbourCell(Maze.prototype.S);
neighbours[Maze.prototype.W] = cell.getNeighbourCell(Maze.prototype.W);
// Check the neighbour cells in random order
for (var i = 0; i < 4; i++) {
var direction = (function() {
var result;
var count = 0;
for (var direction in neighbours) {
if (Math.random() < 1/++count)
result = direction;
}
return result;
})();
var nextCell = neighbours[direction];
delete neighbours[direction];
if (nextCell == false)
continue;
if (nextCell._isDiscovered())
continue;
// Set exit opening of this cell
cell._open(direction);
// Process next cell
DFS(nextCell, directionMap[direction]);
}
})(self.getCell(Math.floor(Math.random()*width), Math.floor(Math.random()*height)), null); // This line is the problem
})();
// ......
If I don't call "self" above the generation code, this.getCell will be called, but the first parameter will be a reference to the generateMaze-function itself. The second parameter will be unset.
It also work if I change the dummy line from "self" to "this".
Just writing "self" (or "this") on an otherwise empty line doesn't really do anything, does it? Why is it needed?
You should add semicolons after assigning a value to an attribute or variable, even if the value is a function, like here:
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
}
It should look like this:
// Function to get a cell
this.getCell = function(x, y) {
return maze[y][x];
};
I think that might be the cause of your problem.