The second time I load a scene I lose reference to certain components

I have a situation where CC loses reference to certain objects - but only the second time I load a scene.

In this case I have flow like this
Menu -> Select level -> Load level
Level -> Back button -> Load Menu
Menu -> Select level -> Load level and here the errors appears.

The script attached to the component doesn’t even know what this.node is.

This my component script:

import { TweenMax, Power2, TimelineLite } from 'gsap';
import EventManager from '../Common/EventManager';
import Ball from './Ball';
import GameState from '../GameState';

const { ccclass, property } = cc._decorator;

@ccclass
export default class RetryContainer extends cc.Component {
  @property({
    default: null
  })
  ballComp: Ball;

  @property(cc.Label)
  retryLabel: cc.Label;

  @property(cc.Button)
  retryButton: cc.Button;

  static SHOW: string = 'RETRY_SHOW';
  static HIDE: string = 'RETRY_HIDE';
  static HIDDEN: string = 'RETRY_HIDDEN';
  private widget: cc.Widget;

  onLoad() {
    this.widget = this.node.getComponent(cc.Widget);
    EventManager.EE.on(RetryContainer.SHOW, () => this.showRetry());
    EventManager.EE.on(RetryContainer.HIDE, () => this.hideRetry());
    this.node.y = -this.node.height;
  }

  public set ballComponent(b: Ball) {
    this.ballComp = b;
  }

  showRetry() {
    if (!this.retryLabel) {
      cc.log('RETRY LABEL FIXED');
      this.retryLabel = cc.find('Canvas/UI/Retry Container/Retry Button/Label').getComponent(cc.Label);
    }
    if (!this.retryButton) {
      cc.log('RETRY BUTTON FIXED');
      this.retryButton = cc.find('Canvas/UI/Retry Container/Retry Button').getComponent(cc.Button);
    }
    this.retryLabel.string = 'RETRIES LEFT: ' + GameState.retriesLeft.toString();
    if (GameState.retriesLeft == 0) {
      this.retryButton.interactable = false;
      this.retryButton.enabled = false;
    } else {
      this.retryButton.interactable = true;
      this.retryButton.enabled = true;
    }
    if (!this.node) {
      // ???
    }
    TweenMax.to(this.node, 0.3, { y: this.node.height / 2 });
  }

  hideRetry() {
    TweenMax.to(this.node, 0.3, {
      y: -this.node.height, onComplete: () => {
        EventManager.EE.emit(RetryContainer.HIDDEN);
    } });
  }
}

As you can see the console logs this.node as null

Do I have to clean up my scene I a certain way before loading a different scene?

TIA!

1 Like

I’m facing this bug too. Still cannot figure out how to solve it. :sob:

I’ve ignored it until now but as release date creeps closer it’s something I’ll need to fix.
You can sort of fix it with using cc.find instead of using properties but it’s very cumbersome to work with.
But one serious issue is that I’m loading some JSON for building a list of buttons and even the JSON that I load is lost the second time I load the scene.
It looks like the class loses it’s scope the 2nd time it’s instantiated.

I did use cc.find, but it couldn’t find the button I need even though that button was visually appearing on the scene, and I still could interact with it just fine! This is the biggest thing I don’t understand about Cocos Creator at all.

did you type out the complete path to the button?

Of course I did. I even double checked, triple checked it many times. I placed that code in onLoad(), the first time the scene was loaded, it can find just well. The second time it couldn’t find anything. It’s really weird.

Yep, same that happens to me. But you do manage to load it even 3 times? Because for me it’s the 2nd time every time.

The 1st time I didn’t play in the game scene at all. I went back to menu scene immediately after the game scene was loaded. If I played in the game scene at the 1st time, the 2nd time it will lost that reference! :weary:

try to cc.find on onEnable instead of onLoad? I can’t try it myself because I have that code in a previous commit.
onEnable get’s triggered later than onLoad.

onEnable() {
  cc.find('Canvas/stuff');
}

I’ve make a quick check again: the cc.find() works well in onLoad(). But as soon as I call the method which involves in processing that node the reference was lost.

ok, then never mind my previous post

would you mind sharing your onload and the method processing the node?

Np. I’ve made a check with onEnable() too. Not work. :tired_face:
The game is 99% done now but I cannot deliver it due to this bug. So mad. :rage:

Of course, but I have to remove some (but not related) parts.

Note: ChosenCatagory is a right name, I copied it directly from the scene. Not a typo.

what if you turn selectCategory into a lambda?

public selectCategory = (catId, catName) => {
  //code
}
1 Like

Oh thank you so much! It works like a charm now. Your suggestion first didn’t work but I’ve figured out what has gone wrong. I have made an static _instance field for that class, but when the scene reloaded, the already set _instance somehow is different from the current this. I’ve set Manager._instance = this again in onLoad() and it works. Because this instance will be called from other objects that are instantiated at runtime.

FYI, I’m using static _instance extensively in this game.

Glad it worked out for you! Now I just hope that I can use this info for my own errors :slight_smile:

Let’s try :laughing:

I’ll be sure to ping you in a couple of days when I get back to it :slight_smile:

In the meantime maybe @jare and @nantas from the CC team can take a guess at what is going on here.

When you load scene, everything under canvas would be released. Including components tied with nodes which are children of canvas. There several more ways to do it:

  1. You would need to tie your static component to a node which is children of the persist node using addPersistRootNode().

  2. Remove the node with the static component is tied to and cache is somewhere else before you do a scene switch, add the same node back to canvas once the new scene is loaded.