Cannot read property 'x' of undefined (JavaScript) - javascript

I am currently trying to build a snake game from a tutorial in JavaScript and Canvas. Currently I am a receiving an error on this line: grid.set(FOOD, randPos.x, randPos.y); which is part of the setFood function towards the bottom. I dont understand where the .x is coming from because I dont see a X or Y defined within that scope. Would really appreciate it if someone could point me in the right direction so I can finish my first game!!! Thanks.
<script>
//CONSTANTS
var COLS=26;
var ROWS=26;
//ID's
var EMPTY=0;
var SNAKE=1;
var FOOD=2;
//Direction
var LEFT=0;
var UP=1;
var RIGHT=2;
var DOWN=3;
var grid = {
width: null,
height: null,
_grid: null,
init: function(d,c,r){
this.width = c;
this.height = r;
this._grid = [];
for(var x=0; x<c; x++){
this._grid.push([]);
for(var y=0; y < r; y++){
this._grid[x].push(d);
}
}
},
set: function(val,x,y){
this._grid[x][y] = val;
},
get: function(x,y){
this._grid[x][y];
}
}
var snake = {
direction: null,
last: null,
_queue: null,
init: function(d,x,y){
this.direction = d;
this._queue = [];
this.insert(x,y);
},
insert: function(x,y){
this._queue.unshift({x:x, y:y});
this.last = this._queue[0];
},
remove: function(){
return this._queue.pop();
}
}
function setFood(){
var empty = [];
for(var x=0; x<grid.width; x++){
for(var y=0; y<grid.height; y++){
if(grid.get(x,y)===EMPTY){
empty.push({x:x, y:y});
}
}
}
var randPos = empty[Math.floor(Math.random()*empty.length)];
console.log("working");
grid.set(FOOD, randPos.x, randPos.y);
console.log("working???")
}
//Game objects
var canvas, ctx, keystate, frames;
function main(){
canvas = document.createElement('canvas');
canvas.width = COLS*20;
canvas.height = ROWS*20;
ctx = canvas.getContext('2D');
document.body.appendChild(canvas);
frames = 0;
keystate = {};
init();
loop();
}
function init(){
grid.init(EMPTY, COLS, ROWS);
var sp = {x:Math.floor(COLS/2), y:ROWS-1};
snake.init(UP, sp.x, sp.y);
grid.set(SNAKE, sp.x, sp.y);
setFood();
}
function loop(){
update();
draw();
window.requestAnimationFrame(loop, canvas);
}
function update(){
frames++;
}
function draw(){
var tw = canvas.width/grid.width;
var th = canvas.height/grid.height;
for(var x=0; x<grid.width; x++){
for(var y=0; y<grid.height; y++){
switch(grid.get(x,y)){
case EMPTY:
ctx.fillStyle = "#fff";
break;
case SNAKE:
ctx.fillStyle = "#0ff";
break;
case FOOD:
ctx.fillStyle = "#f00";
break;
}
ctx.fillRect(x*tw, y*th, tw, th);
}
}
}
main();
</script>

Take a look at the setFood function. Few lines above the line where You get the error, there is a loop that fills the empty collection with objects containing both x and y fields. One of those objects is than assigned to randPos variable.
The error is caused probably by invalid assignment of the value to randPos variable. It's undefined, so it does not contain required x field.
UPDATE
It seems that You just missed the return keyword in Your grid.get(x,y) function, so it should look like this:
get: function(x,y){
return this._grid[x][y];
}
Please verify if adding it solves Your issue.

Let me help you debug:
grid.set(FOOD, randPos.x, randPos.y); is giving you an error saying you're trying to get the x property from something that's undefined.
Okay, so randPos is undefined.
randPos comes from empty[Math.floor(Math.random()*empty.length)];.
empty is an array created inside a nested for loop: empty.push({x:x, y:y});. Do you see where x is coming from?
As Lukasz points out, there's only a specific case where an array dereference like that is undefined. What if you try doing something like
var a = [];
console.log(a[0]);
?
And the reason empty is empty? No grid cell === EMPTY.
get: function(x,y){
this._grid[x][y];
}
Because of a missing return.

New answer. Check out your function
get: function(x,y){
this._grid[x][y];
}
and compare it with the one in the video you linked
get: function(x,y){
return this._grid[x][y];
}
You need to add the return part, otherwise the function will return undefinedand the array called empty won't get any content so your random number generator will suggest you use empty[0], which is undefined.
Last answer was replacing randPos.x with randPos["x"] but as informed by bbill in the comments below, that's not it.

Related

Changing stroke colour using recursion

I am struggling with the error of which throw me once I am trying to recursively change a colour. The error is: “Uncaught TypeError Cannot read property ‘map’ of undefined (sketch: line 18)” and reference to this piece of code: this.color.levels.map(x => x * 0.9) .
I supposed that’s because of a recursive and problem with “this” context. The “right” function which I’ve created executing just one time until above error is thrown.
Any ideas how to make this work or how to recursively change the colour referencing to the same object which I’ve been created?
My code: https://editor.p5js.org/grzegorz.kuguar#gmail.com/sketches/Syc1qQmnQ
<code> class Branch {
constructor(begin, end, strokeW, color, angle) {
this.begin = begin;
this.end = end;
this.angle = angle;
this.strokeW = strokeW;
this.color = color;
}
display() {
stroke(this.color);
strokeWeight(this.strokeW);
line(this.begin.x, this.begin.y, this.end.x, this.end.y);
}
right(angle) {
let direction = p5.Vector.sub(this.end, this.begin);
direction.rotate(angle);
let nextPoint = p5.Vector.add(direction, this.end);
let right = new Branch(this.end, nextPoint, this.strokeW*0.7, this.color.levels.map(x => x * 0.9)); //this line of code throw an error once I am trying to manipulate on the array
return right;
}
}
let tree = [];
let trunk;
let something; //just for check how looks like a p5.color object
function setup() {
createCanvas(400, 400);
background(20);
something = color(100, 230, 100);
console.log(something);
let x = createVector(width/2, height);
let y = createVector(width/2, height-100);
trunk = new Branch(x,y, 7, color(255,100, 100));
tree[0] = trunk;
tree.push(trunk);
}
function draw() {
for(let i = 0; i < tree.length; i++) {
tree[i].display();
}
}
function mousePressed() {
for(let i = tree.length-1; i >= 0; i--) {
tree.push(tree[i].right(Math.PI/4, 0.66));
}
}
The problem is in your use of map. You are doing:
this.col.levels.map(x => x * 0.9)
which returns an array, not a p5.Color object.
To create the new color object, instead use:
color.apply(this, this.col.levels.map(x => x * 0.9))
You can see the full sketch here

How to set rectangles as objects to array in JS?

I am a beginer. I want to create a pixel art site. For this I try to develope my javascript code. Now I am on the way to simplify the code by setting the different rectangles using var as object and array to avoid to type milles of lines. Than I think to create at the second part an array constructor with defining coords(other x, other y) for every single rectangle in 2D array.
At the moment I don't relise why the first part of the code is not working. Can you please suggest your mind? Thanks a lot in advance.
Here is my code (link on JS Bin):
var canvas;
var ctx;
var x = 0;
var y = 0;
var w = 10; // Width=10px
var h = w; // Heigth=10px
function init() {
canvas = document.querySelector('#myCanvas');
ctx = canvas.getContext('2d');
draw();
}
// Create a rect by path method for restoring the buffer
var rect;
function draw(){
ctx.beginPath();
ctx.rect(x,y,w,h);
}
var c = ['#66757F', '#F7F7F7', '#CCD6DD']; // Setting a color palette as an array
for (var i=0; i<c.length; i++){
c[i]=ctx.fillStyle();
}
// Define colored rectangles as the Objects
var r1 = {rect;[0]}
var r2 = {rect;[1]}
var r3 = {rect;[2]}
ctx.fill();
// Setting three rectangle by diagonal
var r=[r1,r2,r3];// Array of setted rectangles
function draw(){
for (var j=0; j<r.length; j++){
r[j]=ctx.moveTo(x+w*j,y+h*j);
}
}
for (var j=0; j<r.length; i++){
r[j]=ctx.moveTo(x+w*j,y+h*j);
}
You typed 'i++' when using the letter 'j'.
Not sure whether this solves the problem.
Why do you use Math.abs in
var w = Math.abs(-10); // Width=10px
Isn't it easier to set 'var w' to 10 ?
var w = 10;
Is what you're looking for how to create classes and make objects from that class?
If so this is how you would create a class and make objects.
//This will hold all of your objects.
var listOfObjects = [];
//This is a class. You can create objects with it.
function myClass() {
//location
this.X = 0;
this.Y = 0;
//size
this.width = 5;
this.height = 5;
}
function CreateNewObject() {
//This will create and add an object of myClass to your array.
//Now you can loop through the array and modify the values as you wish.
listOfObjects.push(new myClass());
}

maximum call stack size exceeded - no apparent recursion

I've spent about 12 hours looking through this code, and fiddling with it, trying to find out where there's a recursion problem because I'm getting the, "maximum call stack size exceeded," error, and haven't found it. Someone smarter than me please help me!
so far, all I found was that when I make the object, spot, a circle, object, the problem disappears, but when I make it a, 'pip', I get this stack overflow error. I've gone over the pip class with a friggin' microscope, and still have no idea why this is happening!
var canvas = document.getElementById('myCanvas');
//-------------------------------------------------------------------------------------
// Classes
//-------------------------------------------------------------------------------------
//=====================================================================================
//CLASS - point
function point(x,y){
this.x = x;
this.y = y;
}
//=====================================================================================
// CLASS - drawableItem
function drawableItem() {
var size = 0;
this.center = new point(0,0);
this.lineWidth = 1;
this.dependentDrawableItems = new Array();
}
//returns the size
drawableItem.prototype.getSize = function getSize(){
return this.size;
}
// changes the size of this item and the relative size of all dependents
drawableItem.prototype.changeSize = function(newSize){
var relativeItemSizes = new Array;
relativeItemSizes.length = this.dependentDrawableItems.length;
// get the relative size of all dependent items
for (var i = 0; i < this.dependentDrawableItems.length; i++){
relativeItemSizes[i] = this.dependentDrawableItems[i].getSize() / this.size;
}
// change the size
this.size = newSize;
// apply the ratio of change back to all dependent items
for (var i = 0; i < relativeItemSizes.length; i++){
this.dependentDrawableItems[i].changeSize(relativeItemSizes[i] * newSize);
}
}
//moves all the vertices and every dependent to an absolute point based on center
drawableItem.prototype.moveTo = function(moveX,moveY){
//record relative coordinates
var relativeItems = new Array;
relativeItems.length = this.dependentDrawableItems.length;
for (var i = 0; i < relativeItems.length; i++){
relativeItems[i] = new point;
relativeItems[i].x = this.dependentDrawableItems[i].center.x - this.center.x;
relativeItems[i].y = this.dependentDrawableItems[i].center.y - this.center.y;
}
//move the center
this.center.x = moveX;
this.center.y = moveY;
//move all the items relative to the center
for (var i = 0; i < relativeItems.length; i++){
this.dependentDrawableItems[i].moveItemTo(this.center.x + relativeItems[i].x,
this.center.y + relativeItems[i].y);
}
}
// draws every object in dependentDrawableItems
drawableItem.prototype.draw = function(ctx){
for (var i = 0; i < this.dependentDrawableItems.length; i++) {
this.dependentDrawableItems[i].draw(ctx);
}
}
//=====================================================================================
//CLASS - circle
function circle(isFilledCircle){
drawableItem.call(this);
this.isFilled = isFilledCircle
}
circle.prototype = new drawableItem();
circle.prototype.parent = drawableItem.prototype;
circle.prototype.constructor = circle;
circle.prototype.draw = function(ctx){
ctx.moveTo(this.center.x,this.center.y);
ctx.beginPath();
ctx.arc(this.center.x, this.center.y, this.size, 0, 2*Math.PI);
ctx.closePath();
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.outlineColor;
if (this.isFilled === true){
ctx.fill();
}else {
ctx.stroke();
}
this.parent.draw.call(this,ctx);
}
//=====================================================================================
//CLASS - pip
function pip(size){
circle.call(this,true);
}
pip.prototype = new circle(false);
pip.prototype.parent = circle.prototype;
pip.prototype.constructor = pip;
//----------------------------------------------------------------------
// Objects/variables - top layer is last (except drawable area is first)
//----------------------------------------------------------------------
var drawableArea = new drawableItem();
var spot = new pip();
spot.changeSize(20);
drawableArea.dependentDrawableItems[drawableArea.dependentDrawableItems.length] = spot;
//------------------------------------------
// Draw loop
//------------------------------------------
function drawScreen() {
var context = canvas.getContext('2d');
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
spot.moveTo(context.canvas.width/2, context.canvas.height/2);
drawableArea.draw(context);
}
window.addEventListener('resize', drawScreen);
Here's the demo: http://jsfiddle.net/DSU8w/
this.parent.draw.call(this,ctx);
is your problem. On a pip object, the parent will be circle.prototype. So when you now call spot.draw(), it will call spot.parent.draw.call(spot), where this.parent is still the circle.prototype…
You will need to explicitly invoke drawableItem.prototype.draw.call(this) from circle.prototype.draw. Btw, you should not use new for the prototype chain.
Why would you write code like that? It's so difficult to understand and debug. When I'm creating lots of classes I usually use augment to structure my code. This is how I would rewrite your code:
var Point = Object.augment(function () {
this.constructor = function (x, y) {
this.x = x;
this.y = y;
};
});
Using augment you can create classes cleanly. For example your drawableItem class could be restructured as follows:
var DrawableItem = Object.augment(function () {
this.constructor = function () {
this.size = 0;
this.lineWidth = 1;
this.dependencies = [];
this.center = new Point(0, 0);
};
this.changeSize = function (toSize) {
var fromSize = this.size;
var ratio = toSize / fromSize;
this.size = toSize;
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) {
var dependency = dependencies[index++];
dependency.changeSize(dependency.size * ratio);
}
};
this.moveTo = function (x, y) {
var center = this.center;
var dx = x - center.x;
var dy = y - center.y;
center.x = x;
center.y = y;
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) {
var dependency = dependencies[index++];
var center = dependency.center;
dependency.moveTo(center.x + dx, center.y + dy);
}
};
this.draw = function (context) {
var dependencies = this.dependencies;
var length = dependencies.length;
var index = 0;
while (index < length) dependencies[index++].draw(context);
};
});
Inheritance is also very simple. For example you can restructure your circle and pip classes as follows:
var Circle = DrawableItem.augment(function (base) {
this.constructor = function (filled) {
base.constructor.call(this);
this.filled = filled;
};
this.draw = function (context) {
var center = this.center;
var x = center.x;
var y = center.y;
context.moveTo(x, y);
context.beginPath();
context.arc(x, y, this.size, 0, 2 * Math.PI);
context.closePath();
context.lineWidth = this.lineWidth;
context[this.filled ? "fill" : "stroke"]();
base.draw.call(this, context);
};
});
var Pip = Circle.augment(function (base) {
this.constructor = function () {
base.constructor.call(this, true);
};
});
Now that you've created all your classes you can finally get down to the drawing:
window.addEventListener("DOMContentLoaded", function () {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var drawableArea = new DrawableItem;
var spot = new Pip;
spot.changeSize(20);
drawableArea.dependencies.push(spot);
window.addEventListener("resize", drawScreen, false);
drawScreen();
function drawScreen() {
var width = canvas.width = window.innerWidth;
var height = canvas.height = window.innerHeight;
spot.moveTo(width / 2, height / 2);
drawableArea.draw(context);
}
}, false);
We're done. See the demo for yourself: http://jsfiddle.net/b5vNk/
Not only have we made your code more readable, understandable and maintainable but we have also solved your recursion problem.
As Bergi mentioned the problem was with the statement this.parent.draw.call(this,ctx) in the circle.prototype.draw function. Since spot.parent is circle.prototype the this.parent.draw.call(this,ctx) statement is equivalent to circle.prototype.draw.call(this,ctx). As you can see the circle.prototype.draw function now calls itself recursively until it exceeds the maximum recursion depth and throws an error.
The augment library solves this problem elegantly. Instead of having to create a parent property on every prototype when you augment a class augment provides you the prototype of that class as a argument (we call it base):
var DerivedClass = BaseClass.augment(function (base) {
console.log(base === BaseClass.prototype); // true
});
The base argument should be treated as a constant. Because it's a constant base.draw.call(this, context) in the Circle class above will always be equivalent to DrawableItem.prototype.draw.call(this, context). Hence you will never have unwanted recursion. Unlike this.parent the base argument will alway point to the correct prototype.
Bergi's answer is correct, if you don't want to hard code the parent name multiple times you could use a helper function to set up inheritance:
function inherits(Child,Parent){
Child.prototype=Object.create(Parent.prototype);
Child.parent=Parent.prototype;
Child.prototype.constructor=Child;
};
function DrawableItem() {
this.name="DrawableItem";
}
DrawableItem.prototype.changeSize = function(newSize){
console.log("changeSize from DrawableItem");
console.log("invoking object is:",this.name);
}
function Circle(isFilledCircle){
Circle.parent.constructor.call(this);
this.name="Circle";//override name
}
inherits(Circle,DrawableItem);
Circle.prototype.changeSize = function(newSize){
Circle.parent.changeSize.call(this);
console.log("and some more from circle");
};
function Pip(size){
Pip.parent.constructor.call(this,true);
this.name="Pip";
}
inherits(Pip,Circle);
var spot = new Pip();
spot.changeSize();
For a polyfill on Object.create look here.

javascript - iterating over bidimensional arrays

I am trying to write a javascript program that renders an 8x8 grid of dirt tiles on an HTML5 canvas. However, when I run this code it throws up error messages when running the draw_terrain() function and it appears to be a problem with the blockArray.length component. Can someone explain to me how to fix this problem? Thanks in advance.
//Define initial canvas variables and images
var canvas;
var ctx;
var WIDTH = 800;
var HEIGHT = 800;
var dirt = new Image();
dirt.src = 'dirt.png';
//Function called to initialise canvas variables and run draw on interval
function init(){
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
return setInterval(draw, 15);
}
//Function that generates an 8x8 Array called blockArray
function gen_terrain(){
var blockArray = new Array(8);
for(var i=0; i<blockArray.length; i++) {
blockArray[i] = new Array(8);
for(var j=0; j<blockArray[i].length; j++){
blockArray[i][j] = 0;
};
};
}
//Function that returns a random number between a min and max
function randomRange (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function draw_terrain(){
for(var i=0; i<blockArray.length; i++) {
for(var j=0; j<blockArray[i].length; j++){
ctx.drawImage(dirt,(n-1)*32,(j-1)*32);
};
};
}
function draw(){
draw_terrain();
}
gen_terrain();
init();
Your problem, as other people have explained is that the variable you are using the build the array will not exist by the time the draw occurs. Just place your array declaration outside of the function and your issue will go away.
See comment below:
function init(){
canvas = document.getElementById('myCanvas');
ctx = canvas.getContext('2d');
return setInterval(draw, 15);
}
//Function that generates an 8x8 Array called blockArray
var blockArray = []; // <== has to be global (outside function).
function gen_terrain(){
// Not here -> var blockArray = [];
for(var i=8; i--;) {
blockArray[i] = [0,0,0,0,0,0,0,0];
};
}
Example

How to create separate objects using Modular JavaScript pattern

I'm trying to update the interval value x but not succeeding. I hope to eventually have different pawn objects with internal values I can update when keypress up/down/left/right to redraw the canvas.
Code Update: Able to update x, y values now, but not sure about creating seperate objects using modular JavaScript pattern.
JavaScript using jQuery 1.5.1:
//Constructors
var pawn = (function() {
var x = 25;
var y = 25;
var getX = function() {
return x;
};
var getY = function() {
return y;
};
function _drawPawn(x,y) {
var x = x || 25;
var y = y || 25;
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var b = $('body');
var winH = b.height();
var winW = b.width();
$(canvas).attr('height',winH).attr('width',winW);
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.lineWidth="3";
ctx.arc(x, y, 10, 0, Math.PI * 2, true); // circle
ctx.stroke();
}
}
function left() {
x = 100;
y = 100;
}
return {
getX:getX,
getY:getY,
draw: function drawPawn(x,y) {
_drawPawn(x,y);
},
left:left
}
})();
//Init
$(function() {
var b = pawn;
b.left();
alert(b.getX());
var a = pawn;
alert(a.getX());
//b.draw();
});
and the html:
<canvas id="canvas" height="800px" width="600px">
Download a modern browser like Internet Explorer 9, Firefox, Safari or Chome to view this.
</canvas>
As it is currently written, your getX() function will be automatically invoked with the supplied (empty) parameter list, and return x, which is then equivalent to:
var getX = x;
which will give getX the value of x at the time it was declared.
To fix, removed the parentheses:
var getX = function() {
return x;
};
You also need to fix this function in your returned object:
draw: function drawPawn(x, y) {
_drawPawn(x, y);
}
Since the apparent intent is to call draw without parameters and use the currently bound x and y values, it should be:
draw: function drawPawn() {
_drawPawn(x, y);
}
First like alnitak said remove paranthese from getX, getY.
If u want different pawn objects you should use var b = new pawn()

Categories