如何在Drupal闭包外部的Phaser类中访问DrupalSettings传递的myData变量?
如何在Drupal闭包外部的Phaser类中访问DrupalSettings传递的myData变量?
咱们先拆解下你遇到的核心问题:
- Drupal的
behaviors被包裹在自执行闭包里,内部变量没法直接被外部的Phaser类访问; - 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
相关产品推荐
相关产品推荐

