Skip to main content

data-manager

Use this skill when using the Phaser 4 DataManager to store custom key-value data on game objects, listen for data change events, or manage game state. Triggers on: setData, getData, data events, custom data storage.

Source facts

Repository
phaserjs/phaser
Last source activity
April 10, 2026 at 12:38
Detected SKILL.md language
English
Stars
40,490
Forks
7,173

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
data-manager
description
Use this skill when using the Phaser 4 DataManager to store custom key-value data on game objects, listen for data change events, or manage game state. Triggers on: setData, getData, data events, custom data storage.
# DataManager > Phaser's DataManager provides key-value storage with event-driven change tracking. It operates at three levels: per-GameObject (`sprite.setData`/`getData`), per-Scene (`this.data`), and global (`this.registry`). Every set/change/remove operation emits events, enabling reactive data binding between game systems without tight coupling. **Key source paths:** `src/data/DataManager.js`, `src/data/DataManagerPlugin.js`, `src/data/events/`, `src/gameobjects/GameObject.js` (setData/getData/incData/toggleData) **Related skills:** ../scenes/SKILL.md, ../events-system/SKILL.md ## Quick Start ```js // Per-GameObject data (auto-creates DataManager on first use) const gem = this.add.sprite(100, 100, 'gem'); gem.setData('value', 50); gem.setData({ color: 'red', level: 2 }); gem.getData('value'); // 50 gem.getData(['value', 'color']); // [50, 'red'] // Increment / toggle helpers gem.incData('value', 10); // value is now 60 gem.incData('value', -5); // value is now 55 (negative to decrement) gem.toggleData('active'); // false -> true (starts from false if unset) // Scene-level data (this.data is a DataManagerPlugin) this.data.set('score', 0); this.data.get('score'); // 0 this.data.values.score += 100; // triggers changedata event // Global registry (shared across ALL scenes) this.registry.set('highScore', 9999); // Any scene can read it: this.registry.get('highScore'); // 9999 ``` ## Core Concepts ### DataManager (`Phaser.Data.DataManager`) The base class that stores key-value pairs in an internal `list` object. It provides: - **`set(key, value)`** -- stores a value; emits `setdata` (new key) or `changedata` + `changedata-{key}` (existing key). Accepts an object to set multiple keys at once. - **`get(key)`** -- retrieves a value, or pass an array of keys to get an array of values. - **`inc(key, amount)`** -- increments a numeric value (defaults to +1). Creates from 0 if key does not exist. - **`toggle(key)`** -- flips a boolean value. Creates from `false` if key does not exist. - **`remove(key)`** -- deletes a key; emits `removedata`. Accepts an array of keys. - **`has(key)`** -- returns `true` if the key exists. - **`getAll()`** -- returns a shallow copy of all key-value pairs as a plain object. - **`query(regex)`** -- returns all entries whose keys match the given RegExp. - **`each(callback, context, ...args)`** -- iterates all entries. Callback signature: `(parent, key, value, ...args)`. - **`merge(data, overwrite)`** -- bulk-imports from an object. `overwrite` defaults to `true`; set `false` to skip existing keys. - **`pop(key)`** -- retrieves and deletes a key in one call; emits `removedata`. - **`reset()`** -- clears all data and unfreezes. - **`freeze` / `setFreeze(bool)`** -- when frozen, all set/remove/inc/toggle operations silently no-op. - **`count`** -- read-only property returning the number of stored entries. The `values` proxy object allows direct property access with event emission: ```js // After set('gold', 100), you can do: data.values.gold += 50; // emits changedata and changedata-gold // But you MUST use set() to create a key first -- direct assignment // to values for a new key will NOT set up the event proxy. ``` ### Scene Data Plugin (`Phaser.Data.DataManagerPlugin`) Extends DataManager. Registered as the `data` scene plugin, accessible as `this.data` in any Scene. It uses the Scene's event emitter (`scene.sys.events`), so data events fire on the Scene's event bus. ```js // In a Scene's create(): this.data.set('lives', 3); // Listen on the scene's event emitter this.events.on('changedata-lives', (scene, value, previousValue) => { console.log('Lives changed from', previousValue, 'to', value); }); ``` The plugin auto-cleans on scene shutdown (removes its shutdown listener) and fully destroys on scene destroy. ### Registry (Global Data Store) The registry is a plain `DataManager` instance on the `Game` object (`game.registry`). It has its own dedicated `EventEmitter` (not shared with any scene). Every scene gets a reference as `this.registry` via the injection map. ```js // Scene A sets global data this.registry.set('currentLevel', 1); // Scene B reads it const level = this.registry.get('currentLevel'); // Listen for registry changes (note: events fire on registry.events, NOT this.events) this.registry.events.on('changedata-currentLevel', (game, value, previousValue) => { console.log('Level changed to', value); }); ``` The registry persists for the lifetime of the Game. It is never automatically cleared on scene restart or shutdown. ### Per-GameObject Data GameObjects do NOT have a DataManager by default. It is created lazily on first call to `setData()`, `getData()`, `incData()`, or `toggleData()`. You can also explicitly call `setDataEnabled()`. The DataManager's event emitter is the GameObject itself (which extends EventEmitter), so data events fire directly on the GameObject: ```js const player = this.add.sprite(0, 0, 'player'); player.setData('hp', 100); // Listen directly on the game object player.on('changedata-hp', (gameObject, value, previousValue) => { if (value <= 0) { gameObject.destroy(); } }); ``` ## Common Patterns ### Setting and Getting Data ```js // Single key sprite.setData('speed', 200); sprite.getData('speed'); // 200 // Multiple keys at once (object form) sprite.setData({ speed: 200, direction: 'left', hp: 100 }); // Batch get with destructuring const [speed, hp] = sprite.getData(['speed', 'hp']); // Direct values access (read and write after initial set) sprite.data.values.speed = 300; // emits changedata event const s = sprite.data.values.speed; // 300 ``` ### Listening for Changes ```js // Listen for ANY data change on a game object sprite.on('changedata', (gameObject, key, value, previousValue) => { console.log(key, 'changed to', value); }); // Listen for a SPECIFIC key change (preferred -- more efficient) sprite.on('changedata-hp', (gameObject, value, previousValue) => { this.hpBar.setValue(value); }); // Listen for new data being set (first time only) sprite.on('setdata', (gameObject, key, value) => { console.log('New data key:', key); }); // Listen for data removal sprite.on('removedata', (gameObject, key, value) => { console.log('Removed:', key, 'was', value); }); ``` ### Global Registry for Cross-Scene State ```js // HUD scene watches for score changes set by the Game scene // In HUDScene.create(): this.registry.events.on('changedata-score', (game, value) => { this.scoreText.setText('Score: ' + value); }); this.events.once('shutdown', () => { this.registry.events.off('changedata-score'); }); // In GameScene.update(): this.registry.set('score', this.score); ``` ### Complex Data and Objects ```js // You can store any value type: numbers, strings, booleans, objects, arrays sprite.setData('inventory', ['sword', 'shield']); sprite.setData('stats', { str: 10, dex: 8, int: 12 }); // CAUTION: mutating a stored object/array does NOT trigger changedata const inv = sprite.getData('inventory'); inv.push('potion'); // No event fired! The reference didn't change. // To trigger the event, re-set the key: sprite.setData('inventory', [...inv]); // new array reference triggers changedata ``` ### Merging Data ```js // Merge defaults -- only sets keys that don't already exist sprite.data.merge({ hp: 100, speed: 200, armor: 0 }, false); // Merge and overwrite existing values sprite.data.merge(savedState, true); ``` ### Querying Data by Pattern ```js // Find all keys matching a regex this.data.set('enemy_1_hp', 50); this.data.set('enemy_2_hp', 80); this.data.set('player_hp', 100); const enemyData = this.data.query(/^enemy_/); // { enemy_1_hp: 50, enemy_2_hp: 80 } ``` ### Freezing Data ```js // Prevent all modifications (set, remove, inc, toggle all become no-ops) sprite.data.freeze = true; sprite.setData('hp', 0); // silently ignored sprite.data.freeze = false; sprite.setData('hp', 0); // works again // Also available as a chainable method sprite.data.setFreeze(true); ``` ### Data Persistence Pattern ```js // Save to localStorage function saveGame(scene) { const state = scene.data.getAll(); localStorage.setItem('saveData', JSON.stringify(state)); } // Restore from localStorage function loadGame(scene) { const raw = localStorage.getItem('saveData'); if (raw) { scene.data.merge(JSON.parse(raw), true); } } ``` ## Events All data events are defined in `Phaser.Data.Events`. The emitter depends on context: for GameObjects it is the GameObject itself; for scene data it is `scene.sys.events`; for the registry it is `registry.events`. | Constant | String | Fired When | Callback Args | |---|---|---|---| | `SET_DATA` | `'setdata'` | A new key is created | `(parent, key, value)` | | `CHANGE_DATA` | `'changedata'` | An existing key's value changes | `(parent, key, value, previousValue)` | | `CHANGE_DATA_KEY` | `'changedata-'` | Specific key changes (append key name) | `(parent, value, previousValue)` | | `REMOVE_DATA` | `'removedata'` | A key is removed | `(parent, key, value)` | | `DESTROY` | `'destroy'` | DataManager's parent is destroyed | (none) | The `parent` argument is the owner of the DataManager (the GameObject, Scene, or Game instance). Note: `CHANGE_DATA_KEY` is a prefix. The actual event string is `'changedata-'` + the key name. For example, setting a key called `score` emits `'changedata-score'`. ## API Quick Reference ### GameObject Convenience Methods | Method | Returns | Description | |---|---|---| | `setDataEnabled()` | `this` | Explicitly creates the DataManager (normally auto-created) | | `setData(key, value)` | `this` | Set one key or pass an object for multiple | | `getData(key)` | `*` | Get one value or pass an array for multiple | | `incData(key, amount?)` | `this` | Increment numeric value (default +1, negative to decrement) | | `toggleData(key)` | `this` | Toggle boolean value | ### DataManager Methods | Method | Returns | Description | |---|---|---| | `set(key, value)` | `this` | Set single key or object of key-value pairs | | `get(key)` | `*` | Get value(s) -- string or array of strings | | `getAll()` | `object` | Shallow copy of all entries | | `query(regex)` | `object` | All entries with keys matching the RegExp | | `each(cb, ctx, ...args)` | `this` | Iterate all entries | | `merge(data, overwrite?)` | `this` | Bulk import; overwrite defaults to `true` | | `remove(key)` | `this` | Delete key(s) -- string or array |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub