常驻节点上的全局单例组件在配置为远程包的Asset bundle 里使用会重新初始化

  • Creator 版本: 3.8.4

  • 目标平台: iOS

  • 重现方式:
    希望在游戏中用全局单例组件的方式实现场景间的数据传递。

我在本地 main bundle 里创建一个启动场景页,按官方文档和论坛指引添加一个常驻节点GameMgr,在这个节点上添加一个全局单例组件。然后在另一个本地 asset bundle,比如 demo bundle里调用单例组件。单例能正常发挥作用。
但是把demo bundle配置为远程时,打印日志能看到常驻节点还是能找到,挂载的单例组件也能获取到,但是单例组件会重新初始化。

有人能解释一下这是为什么吗?不是我的配置有问题吧?不是本身就不支持?

目前暂时是加入本地缓存的机制。每次获取数据尝试通过本地缓存读取。

  • 重现概率: 必现

show your demo~~~

可以添加常驻节点来防止 场景切换中的代码重新初始化

/**

     * @en

     * Add a persistent root node to the game, the persistent node won't be destroyed during scene transition.<br>

     * The target node must be placed in the root level of hierarchy, otherwise this API won't have any effect.

     * @zh

     * 声明常驻根节点,该节点不会在场景切换中被销毁。<br>

     * 目标节点必须位于为层级的根节点,否则无效。

     * @param node - The node to be made persistent

     */

    addPersistRootNode(node: Node): void;

cocos-ios-demo 2.zip (939.0 KB)

增加一个demo代码, 体积太大,删除了一部分目录,按文档说的,应该是能跑进来。其实核心代码都是挺简单的,但是目前测试的情况,配置为 local bundle 是正确的,改为 remote bundle 就无效。想请求确定一下是不是官方本来就不支持。@ 老年UI仔 @CocosDevs 不知道这样通知对不对,如有打扰,请多包涵。

  • 步骤1: 在canvas 同级添加了一个节点 GameMgr。

  • 步骤2: 核心代码以下:
    @ccclass(‘GameMgr’)
    export class GameMgr extends Component {
    private static _instance: GameMgr = null;

    // 游戏状态管理
    private score: string = '';
    

    public static getInstance(): GameMgr {
    if (!GameMgr._instance) {
    throw new Error(‘GameMgr instance not found. Please ensure the GameMgr component is attached to a node in the scene.’);
    }
    return GameMgr._instance;
    }

    onLoad() {
    console.log(‘GameMgr onLoad’);

      // 防止重复实例
      if (GameMgr._instance && GameMgr._instance !== this) {
          console.warn('Duplicate GameMgr instance detected. Destroying this node.');
          this.node.destroy();
          return;
      }
      GameMgr._instance = this;
      // 设置为常驻节点
      director.addPersistRootNode(this.node);
    

    }

    start() {
    // 初始化游戏配置
    this.initializeGame();
    }

    private initializeGame() {
    // 初始化游戏设置
    console.log(‘Game initialized’);
    // this.addScore(10); // 示例:添加分数
    }

    public getScore(): string {
    return this.score;
    }

    public addScore(score: string) {
    this.score = score;
    // 这里可以添加分数更新的回调
    }

}

嗯,谢谢提醒。按照官方文档的说明做的,没用

顶一下,希望有大佬可以指正讨论一下

跟了一下demo,确实报错了.但是如果只是传递数据用的,可以直接用个ts的单例模式,不用挂到结点上面.TS的单例问一下ai.

谢谢你的建议,纯单例实现的方式也验证过了,在设置为remote的bundle 里使用也是失效的,local bundle验证过才有单例的效果。

demo 按官方说明,把一些非必需的文件目录删除了,不然突破不了上传大小限制。但重新导入应该是可以的才对。

目前只能采取借助globalThis 这种方案来达到一些相同的效果了。

assets.zip (24.0 KB)

传递参数的一些技巧,没远程bundle试过.但你可以尝试一下.
local是可以的.

谢谢!看起来好像是迭代查找子组件的类似思路,如果是,之前也试过,remote里也不行。不过晚点再实际跑一下看看

Remote asset bundle 难道就这么少人用?

微信上远程包里是不能有代码的,这是微信的平台规定的,远程包更新代码被发现大概率下架,其次bundle包是有优先级的你加载远程包之前保证远程包依赖的其他包已经加载完成了,最后单例也不一定要挂在什么常驻节点上多此一举

常驻节点在场景切换时,是会重新触发 start、onEnable 等生命周期的,你需要新增一个标记位去区分。

个人推荐调用自定义组件方法的形式去触发初始化回调,而不是把初始化回调放在 start 里

谢谢提醒。目前还没有发布到小程序平台的计划。只是移动平台。所以之前只是大概浏览了这一块的知识,但好像没怎么看到官方asset bundle 有类似的限制提醒什么的。 这个只能后面再重新评估一下。

另外不知道我有没有理解错的,你的意思应该是只要用语言本身纯单例实现,不需要利用常驻节点特性吧?如果是的话,试过也是没用的。

之前我也很好奇。不过这种好像也是AI 建议的防御性代码而已。

bundle没限制,但是微信对远程包有限制不发小游戏平台就不说了,正题别用什么常驻节点了不知道是不是你重复创建销毁了,另外远程bundle优先级设置成除了内置的bundle以外最高试试,大概率是你自己使用的问题但是懒得看你的demo了