You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Drupal闭包外部的Phaser类中访问DrupalSettings传递的myData变量?

如何在Drupal闭包外部的Phaser类中访问DrupalSettings传递的myData变量?

咱们先拆解下你遇到的核心问题:

  1. Drupal的behaviors被包裹在自执行闭包里,内部变量没法直接被外部的Phaser类访问;
  2. Phaser场景的preload方法执行时机可能早于Drupalattach方法的触发,就算能拿到变量,数据也可能还没准备好。

下面给你几个实用的解决方案,你可以根据自己的代码结构来选:


方案一:用全局命名空间存储数据+事件通知(适合Phaser类在外部文件的场景)

这种方法既避免了闭包的隔离,又能确保Phaser在数据准备好后再加载资源。

首先修改你的Drupal behaviors代码,把数据挂载到自定义的全局对象上,同时触发一个自定义事件通知数据就绪:

(function ($, Drupal) {
    Drupal.behaviors.customJs = {
        attach: function (context, settings) {
            if (settings.my_module && settings.my_module.myData) {
                // 创建自定义全局对象,避免直接污染window
                window.MyGameAssets = window.MyGameAssets || {};
                window.MyGameAssets.cardData = settings.my_module.myData;
                
                // 触发自定义事件,告诉Phaser数据准备好了
                document.dispatchEvent(new CustomEvent('gameDataReady'));
            }
        }
    };
})(jQuery, Drupal);

然后修改你的Phaser Preloader类,监听事件或者直接检查全局数据:

class Preloader extends Phaser.Scene {
  constructor() {
    super({ key: 'Preloader' });
    this.cardData = null;
  }

  preload() {
    // 先检查数据是否已经加载完成
    if (window.MyGameAssets?.cardData) {
      this.cardData = window.MyGameAssets.cardData;
      this.loadCardAssets();
    } else {
      // 如果数据还没准备好,监听事件等待
      document.addEventListener('gameDataReady', () => {
        this.cardData = window.MyGameAssets.cardData;
        this.loadCardAssets();
      }, { once: true }); // 只监听一次,防止重复触发
    }
  }

  loadCardAssets() {
    if (this.cardData.length > 0 && this.cardData[0].back_file?.url) {
      this.load.image("card-back", this.cardData[0].back_file.url);
    }
  }

  create() {
    // 确保资源加载完成后再进入游戏场景
    this.load.on('complete', () => {
      this.scene.start("Play");
    });
  }
}

方案二:把Phaser初始化放到Drupal闭包内(最干净的方案)

如果你的Phaser代码不需要在多个文件中拆分,可以把整个游戏的初始化逻辑放到Drupal的behaviors里,这样就能直接访问settings里的数据,还能确保数据就绪后再启动游戏:

(function ($, Drupal) {
    Drupal.behaviors.customJs = {
        attach: function (context, settings) {
            // 用once确保游戏只初始化一次,避免Drupal behaviors重复触发
            if (settings.my_module && settings.my_module.myData && !$(context).data('gameInitialized')) {
                const cardData = settings.my_module.myData;

                // 定义Preloader场景,直接使用cardData
                class Preloader extends Phaser.Scene {
                  constructor() {
                    super({ key: 'Preloader' });
                    this.cardData = cardData;
                  }

                  preload() {
                    if (this.cardData.length > 0 && this.cardData[0].back_file?.url) {
                      this.load.image("card-back", this.cardData[0].back_file.url);
                    }
                  }

                  create() {
                    // 把数据存入Phaser的全局注册表,方便其他场景使用
                    this.registry.set('cardData', this.cardData);
                    this.scene.start("Play");
                  }
                }

                // 定义你的Play场景(如果需要)
                class Play extends Phaser.Scene {
                  constructor() {
                    super({ key: 'Play' });
                  }

                  create() {
                    // 从注册表获取数据
                    const cardData = this.registry.get('cardData');
                    // 这里就可以使用cardData了
                  }
                }

                // 初始化Phaser游戏
                const gameConfig = {
                  type: Phaser.AUTO,
                  width: 800,
                  height: 600,
                  scene: [Preloader, Play]
                };

                new Phaser.Game(gameConfig);
                // 标记游戏已初始化
                $(context).data('gameInitialized', true);
            }
        }
    };
})(jQuery, Drupal);

这种方法的好处是完全不需要全局变量,而且能保证数据在游戏启动前就已经就绪,避免了异步加载的问题。


方案三:直接访问全局的Drupal.settings(最简单的快速方案)

其实Drupal会把settings挂载到全局的Drupal对象上,你可以直接在Phaser里访问,不过要注意代码的加载顺序(确保Phaser代码在Drupal settings之后执行):

class Preloader extends Phaser.Scene {
  constructor() {
    super({ key: 'Preloader' });
  }

  preload() {
    // 等待DOM加载完成,确保Drupal.settings已就绪
    document.addEventListener('DOMContentLoaded', () => {
      const myData = Drupal.settings?.my_module?.myData;
      if (myData && myData.length > 0 && myData[0].back_file?.url) {
        this.load.image("card-back", myData[0].back_file.url);
      }
    }, { once: true });
  }

  create() {
    this.load.on('complete', () => {
      this.scene.start("Play");
    });
  }
}

这个方案最省事,但依赖全局的Drupal对象,如果你的网站有修改全局对象的配置,可能会出问题,适合快速测试或者简单场景。


备注:内容来源于stack exchange,提问作者JoKa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 15:34:33