How to pass parameters between scenes, transition scenes in Cocos Creator

Hi All

  1. I have 2 scenes A and B. How to pass a string to B when loadScene B from A
    cc.director.loadScene(‘B’,function(){
    // how to pass string to B here
    });

  2. How to transition Scenes with effects like TransitionSlideInL.
    I can’t find any example to transition scenes in cocos creator

Thank you and sorry for my bad english :smiley:

For both of your questions, we recommend that you create a scene structure like this:

Launcher Scene (managing all scene loading, loading animations and transition effects)
|- SceneA
|- SceneB

In Launcher scene, you can use addPersistRootNode to make one or more nodes ‘persist’, thus not destroyed when loading another scene. So you can keep your scene managing script in that node, you can also add fade in/ fade out or any other transition animation to that node. And in your scene managing script, you can use loadScene('sceneName', callback) to pass arguments, or just usecc.find` after new scene loaded to pass your argument to nodes in the new scene.

Transition effects are taken out, so you have to rely on animation system for all those effects.

1 Like

@nantas2 thank you so much.
Here is my code for solve this problem. Maybe someone need it.
In LoadingSence just call enterLoginScene(). :smile:

var LoginSence =cc.Class({
extends: cc.Component
statics: {
instance: null;
},
// use this for initialization
onLoad: function () {
LoginSence.instance = this;
},
initDataFromLoading: function(data){
cc.log(’==== '+data);
},
});

Class LoadingSence

var LoginSence = require('LoginScene');
 var LoadingScene = cc.Class({
     extends: Component,
     statics: {
          instance: null;
     },
     onLoad: function () {
         this.transitNode = this.node;
         //persist node for transit scene - remove later
         cc.game.addPersistRootNode(this.transitNode);
    },
    enterLoginScene:function () {
         var self = this;
          cc.director.loadScene('LoginScene',function(err, data){
                 var loginNode = cc.director.getScene();
                 var containerLogin = loginNode.getChildByName('Canvas').getChildByName('container');
                 containerLogin.setPositionX(1280);
                 var sequence = cc.spawn(cc.moveBy(.5, cc.p(-1280, 0)), 
                     cc.callFunc(function () {
                         cc.game.removePersistRootNode(self.transitNode);
                     }
                 ));
                  self.transitNode.runAction(cc.spawn(sequence, 
                        cc.callFunc(function () {
                             var action2 =  cc.moveBy(.5, cc.p(-1280, 0));
                             containerLogin.runAction(action2);
                         })
                 ));
                 var dataStr = 'string data';
                  LoginSence.instance.initDataFromLoading(dataStr);
         });
     },

});

7 Likes

Hi @nantas2! Would you be so kind as to put together a super quick example of the things you mentioned? I’d love to see some code in order to implement it the way you said! Thanks so much.

Yes, I agree with @Lifz

@nantas2 It would be super helpful if you could give some context on how to use it. I am adding music to my game, and I would like to have the music constantly playing no matter which scene the user is in. I don’t want it to be replayed once I enter a new scene, so I think keeping the node would be good.

Thanks in advanced!

-Ty

Hi,
here got Chinese Version example. If you understand, then can follow.
–> http://www.cocoachina.com/bbs/3g/read.php?tid=461222

If just want pass parameter only, then use cc.sys.localStorage (just like save as cache).

// save the parameter
let key = "username";
let parameter = "cutie";
cc.sys.localStorage.setItem(key, parameter );

// get the parameter 
let username = cc.sys.localStorage.getItem(key);

// remove the item of the key (username)
cc.sys.localStorage.removeItem(key);
cc.sys.localStorage.clear();

Reference:
Chinese Version --> http://www.cocos.com/docs/creator/advanced-topics/data-storage.html

2 Likes

Hate to resurrect this, but it is a major software design issue. I’m also, looking to do this, as I can imagine it is a fairly common use case to want to pass data between scenes nicely.

Local storage seems ill-suited for it, since I don’t need to necessarily persist it on the device, nor do I need to serialize or deserialize the data (it’s things like “current score” and metadata pertaining to the game world at large).

Meanwhile, creating a persisted node for a class containing a bunch of globals seems overkill to me.

Is there any more elegant way to share global data among/across multiple scenes in Cocos Creator?

Cocos2D-x had a really nice director paradigm for this.

Basically, I’m trying to reference a cc.Class from a separate JS file that stores global data that is accessible across scenes. Would you recommend window for this?

1 Like

it is a major software design issue

+1

When I used to use Cocos Studio, I used to instantiate scenes when cc.director.pushing them, and in the instantiation line, you could supply args, which would then be accessible in that scene’s ctor. Is there anything like this in Cocos Creator?

1 Like

I ended up using window.

1 Like

hi Kaka167
i recently tried your solution with creator 1.6 but i got an error
Can't init canvas 'Canvas' because it conflicts with the existing 'Canvas', the scene should only have one active canvas at the same time how did you overcome this error?

@matanp
You would get that error if the node you added as a persistentRootNode was the canvas (or, perhaps had the Canvas as a child if that is even possible).

@farproc
does that mean that if i use a child of a canvas it should work?
either way i seem to have an issue where once i load the new scene - the persistent node is set to null, without me having a chance to use it like in the example @kaka167 wrote

Ok, I managed to create my own transition animations Utils ( slide, fade).
The key thing here, is that right before loading a new scene, the container node ( first child) of the old scene needs to be removed from its parent and only then added as a persistent root node. This will cause the old node to offset by half the screen size in each direction so remember to fix this offset before doing any transition animation . Hope this helps anyone

2 Likes

I’m running into pretty much the same problem here, needing to pass information from one scene to another. I can use the ‘window’ solution as @TheChuckster is doing, but I’d like to have more structured data, animations, etc. passed through as well and so would like to see a real, working example (or english documentation) of how it is done with the use of a persistent node. I agree with @TheChuckster and @chestermjr that this is a pretty big architectural issue. I think I understand conceptually what needs to be done, but am missing something in putting the pieces together.

So, @kaka167, @nantas, @slackmoehrle, would you guys be kind enough to put a little something together that demonstrates how to make this happen? Thank you!

Happy to report that I figured it out. I was overcomplicating things. I’ll post a working example once I get a chance.

In short:

  1. Create a cc.Node class that holds all the stuff you want, let’s call this the persistent node.
  2. Add it as a member (not a component, just a plain old member) to each scene you want it in.
  3. In the first scene’s onLoad() method, instantiate a new instance of this persistent node, and call cc.game.addPersistentRootNode() with the persistent node as the parameter.
  4. When calling cc.loadScene(‘newScene’, callback()), make the callback set the member persistent node. Note, that this takes place after the new scene’s onLoad is called, I think, but I haven’t verified that part, so I don’t think you can access the persistent node in onLoad().
  5. In the new scene, do with the persistent node as needed.
1 Like

Here’s an example, note I’m using typescript.

Define the persistent node as follows:

GlobalScript.ts:

const { ccclass } = cc._decorator;

@ccclass
export default class GlobalScript extends cc.Node {

    user_name: string = "TODO here";
    other_settings: number = 123; 
    //Put whatever else you want in this class

}

Next, in each scene that needs to access this persistent node, you do the following: Suppose you have a scene that is a ‘lobby’, and it needs to get the user_name value: Then first make the LobbyScript a component of the LobbyScene, and have the following:

LobbyScript.ts

const { ccclass, property } = cc._decorator;

import GlobalScript from './GlobalScript';

@ccclass
export default class LobbyScript extends cc.Component {

  @property(cc.Node)
  canvas: cc.Node = null;

  @property(cc.Label)
  user_name: cc.Label = null;

  global_data: GlobalScript = null;

  //We'll call this method in the loadScene callback to initialize this object with the persistent data we need
  do_setup() {
    console.log("Initializing user name here as" + this.global_data);
    this.user_level.string = this.global_data.user_name;
  }
}

Finally, in the script of the first scene, let’s call it TitleScene, we need to do a few things: instantiate and populate the GlobalScript object, set it as a persistentNode, and then set global_data member in new scene’s component object during the loadScene callback. See the following, e.g.:

TitleScript.ts

const { ccclass, property } = cc._decorator;

import GlobalScript from './GlobalScript';
import LobbyScript from './LobbyScript';

@ccclass
export default class TitleScript extends cc.Component {

  global_data: GlobalScript = null;

  onLoad() {
    this.global_data = new GlobalScript();
    cc.game.addPersistRootNode(this.global_data);
    console.log("addPersistRootNode here for global_data");
  }

  onPlayButtonPushed() {
    var gd: GlobalScript = this.global_data;
    cc.director.loadScene('LobbyScene', function(){
        var lobby_scr: LobbyScript = cc.director.getScene().getChildByName('Canvas').getComponent(LobbyScript);
        lobby_scr.global_data = gd;
        lobbt_scr.do_setup();
    });
  }
}

OK, that’s about it, you can do things a little differently, but I think that outlines the steps needed to take care of this problem.

1 Like

Hi All

I got scaled scene while switching between two scene, Can any one give example of switching scenes with transitions animations.Here is my code where second scene got scaled down

cc.Class({
extends: cc.Component,

properties: {
    label: {
        default: null,
        type: cc.Label
    },
    // defaults, set visually when attaching this script to the Canvas
    text: 'Hello, World!'
},

// use this for initialization
onLoad: function () {
    
    this.label.string = this.text;
    var self = this;

    this.transitNode = this.node;
    cc.game.addPersistRootNode(this.transitNode);
    this.node.on(cc.Node.EventType.TOUCH_END, function(event){
        
        
        cc.director.loadScene("scene2",function(err, data){
           
            cc.game.removePersistRootNode(self.transitNode);
           
        });
    });
},

start :function(){
    cc.log("start")
    
},
// called every frame
update: function (dt) {

},

});

Although an old topic, I guess it is still useful for some developers using Typescript. Thanks for Typescript resources, for “global” parameters I just use a static class like so:

export default class GameSettings {
    public static readonly SOME_CONSTANT: number = 12345;
    public static someVariable: number = 0;
    (...)
}

Now you can just use the class like the following:

GameSettings.someVariable = someOtherValue;

The same for constants (much better than inline hardcoded literals:

if (myLocalVariabel == GameSettings.SOME_CONSTANT) ...

I find this way a much cleaner solution. Hope this helps somehow.

Hi all
after doing lots of brainstorming i got solution which contains transition between scenes,

cc.Class({
extends: cc.Component,

properties: {
    label: {
        default: null,
        type: cc.Label
    },
    // defaults, set visually when attaching this script to the Canvas
    text: 'Hello, World!'
},

// use this for initialization
onLoad: function () {
    var self = this;
    window.persistNode = this.node.getChildByName("container")//create container at root of scene and add all nodes as child of this container
    this.label.string = this.text;
    
    this.node.on(cc.Node.EventType.TOUCH_END, function(event){
        window.persistNode.removeFromParent();
        cc.game.addPersistRootNode(window.persistNode);
       //reset position to center of screen, its necessary  
        window.persistNode.setPosition(cc.v2(cc.view.getVisibleSize().width/2,cc.view.getVisibleSize().height/2));
        self.switchScene2();
    });

},

switchScene2 :function(){
    cc.director.loadScene("scene2",function(err, data){
        var loginNode = cc.director.getScene();
        var containerLogin = loginNode.getChildByName('Canvas').getChildByName('container');
        containerLogin.setPosition(cc.v2(1280,0));

        var sequence = cc.spawn(cc.moveBy(0.5, cc.v2(-1280, 0)), 
            cc.callFunc(function () {
               
                cc.game.removePersistRootNode(window.persistNode);
                delete(window.persistNode)
            }
        ));

        window.persistNode.runAction(cc.spawn(sequence, 
                cc.callFunc(function () {
                    var action2 =  cc.moveBy(.5, cc.v2(-1280, 0));
                    containerLogin.runAction(action2);
                }
            )       
        ));

    });
}

});