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
相关产品推荐
相关产品推荐

