JS, Socket.io, Phaser3, Object Not Being Created

I'm a JS, phaser3 and Socket.io newbie and I really want to learn.

Code Context:

  • Phaser3
  • Socket.io
  • Loading state of game room before socket connects
  • Generating the NPC's and players
  • fails when there's an attempt to access NPC's (mass) sprite in the same callback it was created

Issue:

  • Cannot access the sprite property, and in general seems like the object wasn't created in the first place

I have the socket listening in Phaser3's "create()" function for "universeState" after it has connected to the socket server. index.js:

this.socket.on('universeState',(universe)=>
    {
        console.log(`mass? ${JSON.stringify(universe.sys.mass[1].id)}`)
        for(let i = 0;i < universe.sys.mass.length; i++)
        {
            console.log(universe.sys.mass[i].id,universe.sys.mass[i].x,universe.sys.mass[i].y,universe.sys.mass[i].asset);
            this.utils.genMass(universe.sys.mass[i].id,universe.sys.mass[i].x,universe.sys.mass[i].y,universe.sys.mass[i].asset);
            console.log(`check: 3 ${JSON.stringify(this.state.state.mass)}`);
            this.state.state.mass[universe.sys.mass[i].id].sprite.setScale(0.5,0.5); //issue here
            console.log('check: 4');

            this.physics.add.collider(this.state.state.mass[universe.sys.mass[i].id].sprite, this.state.state.playerMass[this.state.id].sprite, function(mass, player) {
                mass.destroy();
                player.setScale(player.scaleX + 0.03);
                let camera = this.cameras.main;
                camera.zoomTo(camera.zoom * 0.975);
            }, null, this);
        }

function used to create the "mass" in utils.js:

genMass(id,x,y,tex)
{
    console.log(`creating sprite with operands: ${id},${x},${y},${tex}`);
    let sprite = this.scene.physics.add.sprite(50,50,tex)//needs to be a physics object
    if(id.substring(0,5)=='mass:')
    {
        console.log(`is equal`)
    }
    id.substring(0,4)=='mass:'?
    this.scene.state.state.mass[id] = {id:id,sprite:sprite,x:x,y:y}:
    this.scene.state.state.playerMass[id] = {id:id,sprite:sprite,x:x,y:y}; //generate an extra parameter argument for quantified mass
    console.log('check: 0');
    id.substring(0,4)=='mass:'?
    this.scene.physics.world.enable(this.scene.state.state.mass[id].sprite):
    this.scene.physics.world.enable(this.scene.state.state.playerMass[id].sprite); 
    console.log('check: 1');
    id.substring(0,4)=='mass:'?
    this.scene.state.state.mass[id].sprite.body.collideWorldBounds = true:
    this.scene.state.state.playerMass[id].sprite.body.collideWorldBounds = true;
    console.log('check: 2');

}

everything first appears wrong in the first code snippet where it's commented "issue here". Since I am using webpack, the bundled files are little help. console:

Player ID returned:null
bundle.min.js:1 connected to game
bundle.min.js:1 mass? "mass:3961482"
bundle.min.js:1 mass:7107097 19134 14999 arc_purple
bundle.min.js:1 creating sprite with operands: mass:7107097,19134,14999,arc_purple
bundle.min.js:1 is equal
bundle.min.js:1 check: 0
bundle.min.js:1 check: 1
bundle.min.js:1 check: 2
bundle.min.js:1 check: 3 {}
bundle.min.js:1 Uncaught TypeError: Cannot read properties of undefined (reading 'sprite')
    at Q.<anonymous> (bundle.min.js:1:978636)
    at Q.i.emit (bundle.min.js:1:40837)
    at Q.emitEvent (bundle.min.js:1:969227)
    at Q.onevent (bundle.min.js:1:969030)
    at Q.onpacket (bundle.min.js:1:968708)
    at et.i.emit (bundle.min.js:1:40837)
    at et.ondecoded (bundle.min.js:1:973286)
    at q.i.emit (bundle.min.js:1:40837)
    at q.add (bundle.min.js:1:964368)
    at et.ondata (bundle.min.js:1:973261)

Questions:

Why is the object I created empty when logging it? How can I successfully create the object and access it?

Notes:

I have suspicions that it's some sort of callback behavior that you can't initialize something and access it in the same callback. I'm relatively new and I want to learn thanks.


Two things we spoke in comment section.

  1. Your substring needed to include the colon to pass the test for creating an object of type mass.

  2. Object for playerMass was not created.

You're on the right track.


The reason why you cant access the property is "easy", it's because the this.state.state.mass object is empty.

since te output:

...
bundle.min.js:1 check: 3 {}
bundle.min.js:1 Uncaught TypeError: Cannot read properties of undefined (reading 'sprite')

and the code lines:

console.log(`check: 3 ${JSON.stringify(this.state.state.mass)}`);

So if this.state.state.mass is an empty object/ {}, so there is no proptery universe.sys.mass[i].id in that object.

So this.state.state.mass[universe.sys.mass[i].id] is undefined, and undefined has no property sprite, and this is the error message.

Why this is undefined is hard to tell without more code, but with the current shared code, I would have to guess: In the socket function you are using this.state.state.mass and in the genMass function you are using this.scene.state.state.mass, this could be the cause, since this are probably different objects.

Update:
After re-reading the code, you should check the part of your code that compares the substring with 'mass:', since here only the second part of the if/else block will be executed, because id.substring(0,4) has a string length of 4 and 'mass:' has a string length of 5 so these will never match.

btw.: to use the ternary operator ?: instead of if/else is not the best option, and makes the code less readable.

    id.substring(0,4)=='mass:'?
        this.scene.state.state.mass[id] = {id:id,sprite:sprite,x:x,y:y}:
        this.scene.state.state.playerMass[id] = {id:id,sprite:sprite,x:x,y:y}; //generate an extra parameter argument for quantified mass
    console.log('check: 0');
    id.substring(0,4)=='mass:'?
        this.scene.physics.world.enable(this.scene.state.state.mass[id].sprite):
        this.scene.physics.world.enable(this.scene.state.state.playerMass[id].sprite); 
    console.log('check: 1');
    id.substring(0,4)=='mass:'?
        this.scene.state.state.mass[id].sprite.body.collideWorldBounds = true:
        this.scene.state.state.playerMass[id].sprite.body.collideWorldBounds = true;
    console.log('check: 2');

this is more readable:

    if(id.substring(0,4)=='mass:'){
        this.scene.state.state.mass[id] = {id:id,sprite:sprite,x:x,y:y};
        this.scene.physics.world.enable(this.scene.state.state.mass[id].sprite);
        this.scene.state.state.mass[id].sprite.body.collideWorldBounds = true;
    } else {
        this.scene.state.state.playerMass[id] = {id:id,sprite:sprite,x:x,y:y};
        this.scene.physics.world.enable(this.scene.state.state.playerMass[id].sprite);
        this.scene.state.state.playerMass[id].sprite.body.collideWorldBounds = true;
    }

... or this, this is probably even clearer:

    let mass = {id:id,sprite:sprite,x:x,y:y};
    this.scene.physics.world.enable(sprite);
    sprite.body.collideWorldBounds = true;

    if(id.substring(0,4)=='mass:'){
        this.scene.state.state.mass[id] = mass;
    } else {
        this.scene.state.state.playerMass[id] = mass;
    }