Sign in to follow this  
Massemassimo

undefined forEach element

Recommended Posts

Hi guys,

 

I use the following (shortened) code:

updateGroups() {            console.log(this.tiles);            this.tiles.forEach(function(tileGroup) {                                var tile = tileGroup.getAt(0);                var value = tile.val;                var valueText = tileGroup.getAt(1);                                if (value == 0) {                    this.emptyTiles.add(tileGroup);                } else {                    this.occupiedTiles.add(tileGroup);                }            }, this);        }

As I made sure (with logging it to the console) the group "tiles" has children, each themselves being a group with two children.

 

Firebug throws the error "tileGroup is undefined" for line "var tile = tileGroup.getAt(0);"

 

Why? What's going wrong here?

Share this post


Link to post
Share on other sites

So, this may be obvious, but have you run a console.log on tileGroup within the anonymous function called by forEach? What does that show? Is it actually the group it should be?

 

Without more code to reference, my guess is that the function scope is getting mixed up somehow. If 'this.tiles' is a Phaser.Group, that should run correctly. Well, 'tileGroup' should not be undefined, anyway. I can't really verify the others parts.

Share this post


Link to post
Share on other sites

Hey, thank you for your answer.

 

I tried logging each tileGroup in the forEach loop and it works fine. If I reduce the function to

updateGroups() {            console.log(this.tiles.length);            this.tiles.forEach(function(tileGroup) {                console.log(tileGroup);            }, this);        }

the program runs just fine (while obviously not doing what it's supposed to do). The console logs all 16 subgroups / tileGroups. So they do exist and are definitely not undefined. Only when I try to call .getAt(x) on them do I get the error.

 

I will just throw the biggest / most important chunk of the code out there below, maybe that helps.

 

If you'd like for me to post the javascript instead of my typescript, please tell me.

module AddEmUp {    export class Level01 extends Phaser.State {        private tiles: Phaser.Group;        private emptyTiles: Phaser.Group;        private occupiedTiles: Phaser.Group;        private valueTexts: Phaser.Group;        private count: number = 0;        private txt_count: Phaser.Text;        private txt_points: Phaser.Text;        private style = { font: "24px Arial", fill: "#ff0044", align: "left" };        create() {            this.txt_count = this.add.text(12, 512, "Moves:    ", this.style);            this.txt_count.anchor.set(0, 0);            this.txt_points = this.add.text(500, 512, "Points:    ", this.style);            this.txt_points.anchor.set(1, 0);                        this.emptyTiles = this.add.group(null, "emptyTiles");            this.occupiedTiles = this.add.group(null, "occupiedTiles");            this.valueTexts = this.add.group();            this.spawnBoard();            this.updateGroups();                              }        spawnBoard() {            this.tiles = this.add.group();            for (var i = 0; i < 4; i++) {                for (var j = 0; j < 4; j++) {                                        var posX = i * 128;                    var posY = j * 128;                    var group = this.add.group();                    var tile = this.tiles.create(posX, posY, "tile", null, true);                    tile.val = 1;                    group.add(tile);                    var text = this.add.text(posX + 64, posY + 64, tile.val, this.style, group);                    text.anchor.set(0.5, 0.5);                    this.tiles.add(group);                }            }            this.newChip();          }        newChip() {            var max = this.emptyTiles.length;            var rnd = this.game.rnd.integerInRange(0, max);            this.txt_points.setText("Points: " + rnd);            var chip = this.emptyTiles.getAt(rnd);            chip.value = 2;        }        updateGroups() {            console.log(this.tiles.length);            this.tiles.forEach(function(tileGroup) {                console.log(tileGroup);                this.emptyTiles.removeAll();                this.occupiedTiles.removeAll();                                var tile = tileGroup.getAt(0);                var value = tile.val;                var valueText = tileGroup.getAt(1);                                if (value == 0) {                    this.emptyTiles.add(tileGroup);                } else {                    this.occupiedTiles.add(tileGroup);                }            }, this);            console.log(this.emptyTiles);            console.log(this.occupiedTiles);        }        updateBoard() {            this.updateGroups();            this.occupiedTiles.forEach(function (tile) {                // draw tile.value as text on tile            }, this);        }        setPos(tile, posX, posY) {            tile.posX = posX;            tile.posY = posY;        }        setValue(tile) {            tile.value += tile.value;        }                countUp() {            this.count++;            this.txt_count.setText("Moves: " + this.count);        }        moveLeft() {            this.countUp();            // Do whatcha gotta do            this.updateBoard();        }        moveRight() {            this.countUp();        }        moveUp() {            this.countUp();        }        moveDown() {            this.countUp();        }        spawnChip() {        }        update() {            var cursors = this.game.input.keyboard.createCursorKeys();            cursors.left.onDown.addOnce(this.moveLeft, this);            cursors.right.onDown.addOnce(this.moveRight, this);            cursors.up.onDown.addOnce(this.moveUp, this);            cursors.down.onDown.addOnce(this.moveDown, this);                 }    }}

You're also welcome to point out better ways to do what I am doing, since I'm completely selftaught and probably not very talented! ;)

 

I am going for a "2048 Puzzle" here, in case you're wondering.

Share this post


Link to post
Share on other sites

the whole shebang in js (generated from typescript):

window.onload = function () {    var game = new AddEmUp.Game();};var __extends = this.__extends || function (d,  {    for (var p in  if (b.hasOwnProperty(p)) d[p] = b[p];    function __() { this.constructor = d; }    __.prototype = b.prototype;    d.prototype = new __();};var AddEmUp;(function (AddEmUp) {    var Boot = (function (_super) {        __extends(Boot, _super);        function Boot() {            _super.apply(this, arguments);        }        Boot.prototype.preload = function () {            //this.load.image('preloadBar', 'assets/images/loader.png');        };        Boot.prototype.create = function () {            //  Unless you specifically need to support multitouch I would recommend setting this to 1            this.input.maxPointers = 1;            //  Phaser will automatically pause if the browser tab the game is in loses focus. You can disable that here:            this.stage.disableVisibilityChange = true;            if (this.game.device.desktop) {                //  If you have any desktop specific settings, they can go in here                // this.stage.scale.pageAlignHorizontally = true;            } else {                //  Same goes for mobile settings.            }            this.game.state.start('state_Preloader', true, false);        };        return Boot;    })(Phaser.State);    AddEmUp.Boot = Boot;})(AddEmUp || (AddEmUp = {}));var AddEmUp;(function (AddEmUp) {    var Game = (function (_super) {        __extends(Game, _super);        function Game() {            _super.call(this, 512, 640, Phaser.AUTO, 'content');            this.state.add('state_Boot', AddEmUp.Boot, false);            this.state.add('state_Preloader', AddEmUp.Preloader, false);            this.state.add('state_MainMenu', AddEmUp.MainMenu, false);            this.state.add('state_Level01', AddEmUp.Level01, false);            this.state.start('state_Boot');        }        return Game;    })(Phaser.Game);    AddEmUp.Game = Game;})(AddEmUp || (AddEmUp = {}));var AddEmUp;(function (AddEmUp) {    var Level01 = (function (_super) {        __extends(Level01, _super);        function Level01() {            _super.apply(this, arguments);            this.count = 0;            this.tileSize = 128;            this.tilesX = 4;            this.tilesY = 4;            this.style = { font: "24px Arial", fill: "#ff0044", align: "left" };        }        Level01.prototype.create = function () {            this.txt_count = this.add.text(12, 512, "Moves:    ", this.style);            this.txt_count.anchor.set(0, 0);            this.txt_points = this.add.text(500, 512, "Points:    ", this.style);            this.txt_points.anchor.set(1, 0);            this.emptyTiles = this.add.group(null, "emptyTiles");            this.occupiedTiles = this.add.group(null, "occupiedTiles");            this.valueTexts = this.add.group();            this.spawnBoard();            this.updateGroups();        };        Level01.prototype.spawnBoard = function () {            this.tiles = this.add.group();            for (var i = 0; i < this.tilesX; i++) {                for (var j = 0; j < this.tilesY; j++) {                    var posX = i * this.tileSize;                    var posY = j * this.tileSize;                    var group = this.add.group();                    var tile = this.tiles.create(posX, posY, "tile", null, true);                    tile.val = 1;                    group.add(tile);                    var text = this.add.text(posX + this.tileSize / 2, posY + this.tileSize / 2, tile.val, this.style, group);                    text.anchor.set(0.5, 0.5);                    this.tiles.add(group);                }            }            this.newChip();        };        Level01.prototype.newChip = function () {            var max = this.emptyTiles.length;            var rnd = this.game.rnd.integerInRange(0, max);            this.txt_points.setText("Points: " + rnd);            var chip = this.emptyTiles.getAt(rnd);            chip.value = 2;        };        Level01.prototype.updateGroups = function () {            console.log(this.tiles.length);            this.tiles.forEach(function (tileGroup) {                console.log(tileGroup);                this.emptyTiles.removeAll();                this.occupiedTiles.removeAll();                var tile = tileGroup.getAt(0);                var value = tile.val;                var valueText = tileGroup.getAt(1);                if (value == 0) {                    this.emptyTiles.add(tileGroup);                } else {                    this.occupiedTiles.add(tileGroup);                }            }, this);            console.log(this.emptyTiles);            console.log(this.occupiedTiles);        };        Level01.prototype.updateBoard = function () {            this.updateGroups();            this.occupiedTiles.forEach(function (tile) {                // draw tile.value as text on tile            }, this);        };        Level01.prototype.setPos = function (tile, posX, posY) {            tile.posX = posX;            tile.posY = posY;        };        Level01.prototype.setValue = function (tile) {            tile.value += tile.value;        };        Level01.prototype.countUp = function () {            this.count++;            this.txt_count.setText("Moves: " + this.count);        };        Level01.prototype.moveLeft = function () {            this.countUp();            // Do whatcha gotta do            this.updateBoard();        };        Level01.prototype.moveRight = function () {            this.countUp();        };        Level01.prototype.moveUp = function () {            this.countUp();        };        Level01.prototype.moveDown = function () {            this.countUp();        };        Level01.prototype.spawnChip = function () {        };        Level01.prototype.update = function () {            var cursors = this.game.input.keyboard.createCursorKeys();            cursors.left.onDown.addOnce(this.moveLeft, this);            cursors.right.onDown.addOnce(this.moveRight, this);            cursors.up.onDown.addOnce(this.moveUp, this);            cursors.down.onDown.addOnce(this.moveDown, this);        };        return Level01;    })(Phaser.State);    AddEmUp.Level01 = Level01;})(AddEmUp || (AddEmUp = {}));var AddEmUp;(function (AddEmUp) {    var MainMenu = (function (_super) {        __extends(MainMenu, _super);        function MainMenu() {            _super.apply(this, arguments);        }        MainMenu.prototype.create = function () {            var btn_start = this.add.button(0, 300, 'btn_start', this.startGame, this, 2, 0, 1, 0);            var btn_exit = this.add.button(300, 300, 'btn_exit', this.endGame, this, 2, 0, 1, 0);        };        MainMenu.prototype.endGame = function () {            //this.game.destroy();        };        MainMenu.prototype.startGame = function () {            this.game.state.start('state_Level01', true, false);        };        return MainMenu;    })(Phaser.State);    AddEmUp.MainMenu = MainMenu;})(AddEmUp || (AddEmUp = {}));var AddEmUp;(function (AddEmUp) {    var Preloader = (function (_super) {        __extends(Preloader, _super);        function Preloader() {            _super.apply(this, arguments);        }        //preloadBar: Phaser.Sprite;        Preloader.prototype.preload = function () {            this.load.spritesheet('btn_start', 'assets/img/btn_start.png', 200, 80);            this.load.spritesheet('btn_exit', 'assets/img/btn_exit.png', 200, 80);            this.load.image("tile", "assets/img/tile.png");        };        Preloader.prototype.create = function () {            //this.game.state.start('state_MainMenu', true, false);            this.game.state.start('state_Level01', true, false);        };        return Preloader;    })(Phaser.State);    AddEmUp.Preloader = Preloader;})(AddEmUp || (AddEmUp = {}));//# sourceMappingURL=game.js.map

 

Share this post


Link to post
Share on other sites

you are iterating over the group using foreach and in the callback you remove (some) elements from the group.

You can't do that, because the implementation of foreach is a for loop over all elements in the group.

 

(So if you remove elements while iterating over the elements, you run out of elements before the loop is done see here: http://docs.phaser.io/Group.js.html#sunlight-1-line-1053)

Share this post


Link to post
Share on other sites

Ok, so if I am not mistaken, the weird thing happens in the "spawnBoard" function. While 16 groups are created as planned, only 8 groups exist in the tiles-group after the function finished.

 

The groups seem to get overwritten. I added an iterator in the double loop and attached the iterated value to the generated group for each loop, which should result in the first group having the value 1 (it starts at 1), the second 2 and so on, but that's not the case. When checking the values after both loops are executed, child 0 has the value 2, child 1 the value 4 and so on. Echoing out the values DURING the loop results in the expected behaviour though (child 0 has val 1, child 1 the val 2 and so on), suggesting the following proceedings:

  1. group 1 is added (to "tiles") at the first position (child 0),
  2. group 2 is added, overwriting group 1 and becoming the new child 0,
  3. group 3 is added at the second position (child 1)
  4. group 4 is added, overwriting group 3
  5. and so forth...

The typescript function where to problem occurs:

        spawnBoard() {            this.tiles = this.add.group();            var k = 0;            for (var i = 0; i < this.tilesX; i++) {                for (var j = 0; j < this.tilesY; j++) {                    var posX = i * this.tileSize;                    var posY = j * this.tileSize;                    var group = this.add.group();                    var tile = this.tiles.create(posX, posY, "tile", null, true);                    k++;                    tile.val = k;                    group.add(tile);                    var text = this.add.text(posX + this.tileSize / 2, posY + this.tileSize / 2, tile.val, this.style, group);                    text.anchor.set(0.5, 0.5);                                        this.tiles.add(group);                                        console.log(this.tiles.getAt(this.tiles.length - 1).getAt(0).val);                }            }         }

For the sake of more clarity I removed a bunch of commented-out console.log commands - if anyone is interested in seeing what I already tried to find out what goes wrong, I can put them back in.

 

Help, please? :(

Share this post


Link to post
Share on other sites

you are iterating over the group using foreach and in the callback you remove (some) elements from the group.

You can't do that, because the implementation of foreach is a for loop over all elements in the group.

 

(So if you remove elements while iterating over the elements, you run out of elements before the loop is done see here: http://docs.phaser.io/Group.js.html#sunlight-1-line-1053)

Hey,

 first of all thank you for answering! I must admit I don't understand what you refer to when you say I remove elements. Do you mean

if (value == 0) {                    this.emptyTiles.add(tileGroup);                } else {                    this.occupiedTiles.add(tileGroup);                }

? If so, do I understand that correctly: adding an element of a group to another group removes it from the former group? In this scenario, adding "tileGroup" to "emptyTiles" (or "occupiedTiles") removes it from "tiles"? I actually wanted to have each "tileGroup" exist in "tiles" AND either in "emptyTiles" or "occupiedTiles". Or am I completely on the wrong track and you're talking about something else here?

Share this post


Link to post
Share on other sites
[...] do I understand that correctly: adding an element of a group to another group removes it from the former group? [...]

Ok yeah this really is what happens. I logged the length of the "tiles" group before and after "this.emptyTiles.Add(tileGroup);" and every time "emptyTiles" or "occupiedTiles" receives another child, "tiles" loses one.

 

I tried "cloning" the tileGroup like so:

var newGroup  = tileGroup;

and adding newGroup to the occupiedTiles/emptyTiles groups but that seems to have the same result. :/

 

Any ideas on how to deal with this?

Share this post


Link to post
Share on other sites

assigning tileGroup to the Variable newGroup will not clone anything, it will just put a reference to the same object into newGroup.

 

(newGroup is the same as tileGroup, not a copy or a clone - just another "pointer" to the same object)

 

The phaser groups (at least as I understand them) are there to organize the sprites - but mainly in the sense that the group order defines the order in which the sprites are painted to the screen each time. This is why a sprite an only be in one group at a time.

 

 

I am not exactly sure what you plan to do with your groups. If you just want to do dirrent "stuff" with different sprites, then you can create them all in the "tiles"-Group and leave them there.

Now you can create Arrays in which you can organize those sprites, without removing them form the phaser tile group.)

 

For example:

 

//create a new empty array

this.emptyTiles = new Array();

 

//put something in the array

this.emptyTiles.push(someTileSprite);

 

//iterate over the array content, and do something with each element in it

for (var i = 0; i < this.emptyTiles.length; i++) {

  this.emptyTiles.destroy(); //kill them all <evil laugh/>

}

Share this post


Link to post
Share on other sites
this.tiles.forEach(function(tileGroup) {    if(tileGroup instanceof Phaser.Group) {        var tile = tileGroup.getAt(0);        var value = tile.val;        var valueText = tileGroup.getAt(1);                if (value == 0) {            this.emptyTiles.add(tileGroup);        } else {            this.occupiedTiles.add(tileGroup);        }    }}, this);

That might be helpful.

 

We're typechecking tileGroup to make sure it's actually a group and has those functions.

Share this post


Link to post
Share on other sites

Alright, thanks for the answers!

 

I assumed groups were just another name for arrays in phaser. What I wanted to do is create 16 (4 by 4) groups of a sprite and a text (so the text stays with the sprite when I move it around) and then have three groups for different purposes: one to wrap all the sprite/text groups and two more basically sorting them by some rules (empty tiles & not-empty tiles).

 

Anyways, I'll go with arrays.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.