Состояние игры — это совокупность всех данных, определяющих текущий игровой процесс: позиции объектов, значения очков, параметры персонажа, активные квесты, таймеры, настройки уровня, состояние интерфейса и другие динамические данные.
В контексте разработки на Phaser (версии 3) сериализация и десериализация позволяют:
Сериализация — это преобразование состояния в формат, пригодный для хранения или передачи (чаще всего JSON). Десериализация — обратный процесс восстановления игровых объектов из сохранённых данных.
Phaser не предоставляет встроенной универсальной системы сохранений. Разработчик самостоятельно определяет структуру сохраняемых данных.
Чаще всего сериализации подлежат:
x, y)body.velocity)Существует два основных подхода:
Создаётся единый объект состояния:
const GameState = {
level: 1,
score: 0,
player: {
x: 100,
y: 200,
health: 100
},
enemies: []
};
Этот объект используется всеми сценами.
Каждая сцена или объект отвечает за собственное сохранение:
class Player extends Phaser.Physics.Arcade.Sprite {
serialize() {
return {
x: this.x,
y: this.y,
health: this.health
};
}
deserialize(data) {
this.setPosition(data.x, data.y);
this.health = data.health;
}
}
Такой подход повышает масштабируемость и упрощает поддержку.
Основной способ сериализации — JSON.stringify():
const saveData = JSON.stringify(GameState);
Полученная строка может быть:
localStorageБраузерное хранилище — простой способ реализации системы сохранений.
localStorage.setItem('phaserSave', JSON.stringify(GameState));
Загрузка:
const savedData = localStorage.getItem('phaserSave');
if (savedData) {
const parsed = JSON.parse(savedData);
Object.assign(GameState, parsed);
}
Важно учитывать ограничения localStorage:
Нельзя напрямую сериализовать объекты Phaser:
JSON.stringify(player); // некорректно
Причина — циклические ссылки и служебные поля движка.
Извлекать только необходимые данные:
function serializePlayer(player) {
return {
x: player.x,
y: player.y,
velocityX: player.body.velocity.x,
velocityY: player.body.velocity.y,
health: player.health,
currentAnim: player.anims.currentAnim?.key
};
}
Группы требуют обхода:
function serializeEnemies(group) {
const data = [];
group.children.iterate(enemy => {
data.push({
x: enemy.x,
y: enemy.y,
health: enemy.health
});
});
return data;
}
Сначала необходимо:
Пример восстановления:
function loadPlayer(scene, data) {
const player = scene.physics.add.sprite(data.x, data.y, 'player');
player.health = data.health;
player.body.setVelocity(data.velocityX, data.velocityY);
if (data.currentAnim) {
player.anims.play(data.currentAnim);
}
return player;
}
function loadEnemies(scene, enemiesData) {
const group = scene.physics.add.group();
enemiesData.forEach(data => {
const enemy = group.create(data.x, data.y, 'enemy');
enemy.health = data.health;
});
return group;
}
В Phaser каждая сцена имеет this.registry, позволяющий
хранить данные между сценами.
this.registry.set('score', 100);
Получение:
const score = this.registry.get('score');
Для сериализации:
const data = this.registry.getAll();
localStorage.setItem('registrySave', JSON.stringify(data));
Объекты могут иметь собственный менеджер данных:
player.setData('health', 100);
Получение:
player.getData('health');
Сериализация:
const playerData = player.data.getAll();
Таймеры (this.time.addEvent) нельзя напрямую сохранить.
Необходимо сохранять:
Пример:
const timerData = {
delay: timer.delay,
remaining: timer.getRemaining(),
repeat: timer.repeat
};
При восстановлении:
this.time.addEvent({
delay: timerData.remaining,
callback: someFunction,
repeat: timerData.repeat
});
Чекпоинт — частичная сериализация состояния.
Стратегия:
Пример структуры:
{
level: 2,
player: {...},
inventory: [...],
checkpointId: 'room_3'
}
При изменении структуры данных старые сохранения могут стать несовместимыми.
Решение — хранить версию:
{
version: 2,
player: {...}
}
При загрузке:
if (save.version === 1) {
migrateFromV1(save);
}
Для онлайн-игр состояние отправляется через HTTP или WebSocket.
Пример отправки:
fetch('/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(GameState)
});
На сервере данные могут храниться в базе данных.
Методы уменьшения объёма:
Пример минимизированной структуры:
{
l: 3,
s: 2500,
p: { x: 120, y: 80, h: 90 }
}
При загрузке возможны ошибки:
Безопасная загрузка:
function safeLoad(key) {
try {
const raw = localStorage.getItem(key);
if (!raw) return null;
return JSON.parse(raw);
} catch (e) {
console.error('Ошибка загрузки сохранения', e);
return null;
}
}
class SaveManager {
static save(scene) {
const state = {
level: scene.level,
score: scene.score,
player: serializePlayer(scene.player),
enemies: serializeEnemies(scene.enemies)
};
localStorage.setItem('save', JSON.stringify(state));
}
static load(scene) {
const raw = localStorage.getItem('save');
if (!raw) return;
const state = JSON.parse(raw);
scene.level = state.level;
scene.score = state.score;
scene.player = loadPlayer(scene, state.player);
scene.enemies = loadEnemies(scene, state.enemies);
}
}
Не сохраняются:
Сохраняются:
Система сериализации должна быть:
Грамотно реализованная сериализация превращает игру из временной сессии в полноценный продукт с устойчивым прогрессом, поддержкой паузы, перезапуска и масштабирования логики на сетевые сценарии.