[SOLVED] Loose entities after loading?

In my project when I load a scene during runtime and fire an event, it seems that there are some leftover entities which are empty. In this example I try to set the text of the entity to which this script is connected to:

var CrossButtonTextChange = pc.createScript('crossButtonTextChange');

    // initialize code called once per entity
    CrossButtonTextChange.prototype.initialize = function() {
        this.app.on("loadVehicles", function() {
            console.log(this.entity)
            this.entity.element.text = "Crosses: off";
        }, this);
    };

The console.log(this.entity) is giving me an entity which has the correct name, but almost everything else is undefined. No elements, parent entities, children entities, etc. Does anyone know why this might be happening?

Probably that code is executed too early. Might be due to the way you are creating a script and adding an event listener when it gets initialized. Somewhere there is a race condition. Try adding a small timeout as a test.

I tried it. Unfortunately I’m still getting an error that ‘text’ is undefined.

The problem also only seems to occur when I use the this.app.fire method. When I add global attributes to the script, add entities manually and activate the function directly, it works.
For my case this is not very handy because there are a lot of entities with functions which need to be activated.

I am not sure about what or how you are trying to do things. It is hard to derive from your description. Perhaps, you could create a blank project with a couple of entities/functions that demonstrate the concept? It would be easier for someone to give an advice then.

I’ll see if I can do that but I think the project is a bit to complicated for that.

Basicaly this is my sending code. I tagged the receiving entity for the direct function access method:

      // Direct function access method (works)
var crossButton = rootEntity.findByTag('crossText');
crossButton[0].script.crossButtonTextChange._changeText();

// Event fire method (not working)
self.app.fire('setCrossVis');

This is my recieving code:

var CrossButtonTextChange = pc.createScript('crossButtonTextChange');
  
CrossButtonTextChange.prototype.initialize = function() {

// Event fire method (not working)
  this.app.on("setCrossVis", function() {
      console.log(this.entity)
      this.entity.element.text = "Crosses: off";
  }, this);
};

// Direct function access method (works)
CrossButtonTextChange.prototype._changeText = function() {
    console.log(this.entity)
    this.entity.element.text = "Crosses: off";
};

The snippet you showed should work fine (assuming self has correct scope). There must be something else affecting it, something that is not shown here. Like the loading order of scripts, or font assets not loaded at the time. Place a breakpoint inside the initialize, and inside the event callback. See what is triggered first, debug from there.

1 Like

Are you changing scenes? If so, you need to make sure that you cleanup event listeners when the script/entities are destroyed otherwise you have still have callbacks registered in the app event handler.

It’s worth remembering that in JS, functions are objects and is a GC language. So if the callback function is not removed from the app event handler, the function is still in memory and all the associated data including the entity object. This is why you can still get the name but when the entity is destroyed, components, etc are removed and nulled hence there are no elements, children, parents etc

eg

CrossButtonTextChange.prototype.initialize = function() {
  const cb = function() {
    console.log(this.entity)
    this.entity.element.text = "Crosses: off";
  }.bind(this)

  // Event fire method (not working)
  this.app.on("setCrossVis", cb);
  this.on('destroy', function() {
    this.app.off("setCrossVis", cb);
  }, this);
};
2 Likes

Yes I am changing scenes. This looks like the answer I’m looking for!

I thought this bit of code would also get rid of the event handlers.

var rootChildren = self.app.root.children;
while(rootChildren.length > 0) {
    rootChildren[0].destroy();
}

So to remove event handlers I have to do this.app.off for each of them?

I tried this and I’m getting no errors :ok_hand:

Thank you!

It seems like you and @yaustar are both right.

If I just fire the event without a setTimeout, the cb function is not executed. I had to set a pretty big number on the timeout aswell:

setTimeout(function(){
    self.app.fire("setCrossVis");
},2000);

So it also must be a script loading order problem then.

Alright I found the solution.
In my case the receiving entity is not enabled at the time of firing because it’s part of a menu that’s not enabled when a scene is loaded, so it misses the call completely.
That’s why the timeout worked with such a large number. During testing I enabled the entity manually by that time.

Thank you @LeXXik and @yaustar

1 Like