Steez
Steez

Reputation: 229

Cannot read property 'x' of undefined (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>

Upvotes: 0

Views: 529

Answers (3)

Niddro
Niddro

Reputation: 1725

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.

Upvotes: 0

Lukasz M
Lukasz M

Reputation: 5723

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.

Upvotes: 2

bbill
bbill

Reputation: 2304

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.

Upvotes: 1

Related Questions