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

Phaser嵌套游戏数据在注册表、本地存储与场景间的处理方案咨询

游戏嵌套数据模型的多场景处理方案

核心思路:分层可观测类+路径化事件

把每个嵌套数据模块封装成独立的可观测类,既保留类的嵌套结构便于管理,又能通过带路径标识的事件让registry精准监听特定属性的变化,同时实现统一的序列化/反序列化逻辑适配localStorage。

具体实现步骤

1. 封装基础可观测类

实现通用的ObservableBase类,负责属性监听、事件触发和序列化逻辑,核心是给每个属性变化事件带上完整路径前缀(比如characters.name),让registry能识别具体是哪个层级的属性更新。

2. 拆分嵌套模块为独立可观测类

根据你的数据模型,把每个嵌套节点拆成单独的类,继承基础可观测类并指定路径前缀:

  • Character:对应Characters节点,路径前缀characters,包含name、powerBase属性
  • League:对应League节点,路径前缀league,包含title、bossName属性
  • Money:对应Match.Money节点,路径前缀match.money,包含loan1、loan2、funding1、funding2属性
  • Match:对应Match节点,路径前缀match,包含player、progress、powerTotal属性,同时关联Character、League、Money实例

3. 主数据类整合所有模块

创建GameData主类,组合所有子模块实例,提供统一的事件订阅入口、localStorage存储/加载方法,同时处理模块间的关联(比如Match关联全局的Character和League)。

4. 适配registry事件与场景传递

  • registry事件:通过订阅带路径的事件(比如gameData.on('match.money.loan1', callback)),在回调中触发registry对应的更新事件,实现精准触发。
  • localStorage:利用每个可观测类的toJSON方法把实例转成普通JSON对象存储,加载时再把JSON数据恢复为类实例,保留嵌套结构和可观测性。
  • 游戏场景传递:把GameData做成全局单例(比如引擎中的持久化对象/自动加载节点),场景间直接访问实例即可,避免频繁序列化/反序列化。

代码示例(JavaScript/TypeScript风格)

// 基础可观测类
class ObservableBase {
  #pathPrefix;
  #listeners = new Map();

  constructor(pathPrefix) {
    this.#pathPrefix = pathPrefix;
  }

  #triggerEvent(propName, value) {
    const fullPath = `${this.#pathPrefix}.${propName}`;
    this.#listeners.get(fullPath)?.forEach(cb => cb(value));
    // 可选:触发父级路径事件,方便监听整个模块的变化
    const parentPath = this.#pathPrefix;
    this.#listeners.get(parentPath)?.forEach(cb => cb({ [propName]: value }));
  }

  // 订阅指定路径的事件
  on(path, callback) {
    if (!this.#listeners.has(path)) {
      this.#listeners.set(path, []);
    }
    this.#listeners.get(path).push(callback);
  }

  // 安全设置属性并触发事件
  set(propName, value) {
    if (this[propName] !== value) {
      this[propName] = value;
      this.#triggerEvent(propName, value);
    }
  }

  // 序列化为普通对象
  toJSON() {
    const plainObj = {};
    Object.keys(this).forEach(key => {
      if (!key.startsWith('#')) {
        plainObj[key] = this[key] instanceof ObservableBase ? this[key].toJSON() : this[key];
      }
    });
    return plainObj;
  }
}

// 子模块类
class Character extends ObservableBase {
  name = '';
  powerBase = 0;
  constructor() { super('characters'); }
}

class League extends ObservableBase {
  title = '';
  bossName = '';
  constructor() { super('league'); }
}

class Money extends ObservableBase {
  loan1 = 0;
  loan2 = 0;
  funding1 = 0;
  funding2 = 0;
  constructor() { super('match.money'); }
}

class Match extends ObservableBase {
  player = '';
  character = null;
  league = null;
  progress = 0;
  powerTotal = 0;
  money = null;

  constructor() {
    super('match');
    this.money = new Money();
  }
}

// 主数据类
class GameData {
  characters = new Character();
  league = new League();
  match = new Match();

  constructor() {
    // 关联嵌套实例
    this.match.character = this.characters;
    this.match.league = this.league;
  }

  // 统一订阅入口
  on(path, callback) {
    let target = this;
    const parts = path.split('.');
    for (const part of parts) {
      if (target[part] instanceof ObservableBase) {
        target = target[part];
      } else break;
    }
    target.on(path, callback);
  }

  // 保存到localStorage
  saveToLocalStorage() {
    localStorage.setItem('gameData', JSON.stringify(this));
  }

  // 从localStorage加载
  static loadFromLocalStorage() {
    const json = localStorage.getItem('gameData');
    if (!json) return new GameData();

    const plainObj = JSON.parse(json);
    const gameData = new GameData();

    // 恢复各模块数据
    Object.assign(gameData.characters, plainObj.characters);
    Object.assign(gameData.league, plainObj.league);
    Object.assign(gameData.match, plainObj.match);
    Object.assign(gameData.match.money, plainObj.match.money);

    return gameData;
  }
}

使用场景示例

// 初始化数据
const gameData = GameData.loadFromLocalStorage();

// 监听角色名称变化,触发registry事件
gameData.on('characters.name', (newName) => {
  registry.trigger('update-character-name', newName);
});

// 监听贷款1变化,触发registry事件
gameData.on('match.money.loan1', (newLoan) => {
  registry.trigger('update-loan1', newLoan);
});

// 修改属性自动触发事件
gameData.characters.set('name', 'Iron Guardian');
gameData.match.money.set('loan1', 800);

// 保存数据
gameData.saveToLocalStorage();

额外优化建议

  • 批量更新:添加batchUpdate方法,暂时关闭事件触发,批量修改属性后再一次性触发批量事件,减少性能开销。
  • 引擎适配:如果用Unity/Godot等引擎,可替换为对应引擎的事件系统(如Unity的EventSystem、Godot的Signal),类结构保持一致。
  • 数据校验:在set方法中添加属性合法性校验,避免非法数据写入。

内容的提问来源于stack exchange,提问作者anderlaini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 15:57:21