1. 理解LocalStorage在Laya引擎中的定位
在LayaAir引擎的游戏开发中,数据持久化是基础但关键的需求。LocalStorage作为HTML5标准提供的Web存储方案,其核心价值在于:
- 轻量级存储:适合保存玩家设置、进度存档等小型数据(通常5MB以内)
- 键值对结构:采用简单的key-value存储模式,API直观易用
- 同源隔离:数据按域名隔离,不同游戏间不会互相干扰
- 同步操作:所有读写操作均为同步执行,无需回调处理
与Unity的PlayerPrefs不同,Laya的LocalStorage直接基于浏览器原生API实现。在Android平台上,数据实际存储在/data/user/0/[包名]/files/layacache/localstorage/localstorage.db文件中,这是WebView的标准实现方式。
提示:模拟器环境(如MuMu)可能出现存储失败的情况,通常需要检查存储权限设置或改用真机调试
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. LocalStorage基础操作全解析
2.1 核心API使用方法
Laya对原生LocalStorage进行了封装,通过Laya.LocalStorage类提供统一接口:
typescript复制// 写入数据(自动转为字符串存储)
Laya.LocalStorage.setItem('player_name', '刀客塔');
// 读取数据(返回字符串或null)
const name = Laya.LocalStorage.getItem('player_name');
// 删除指定键
Laya.LocalStorage.removeItem('player_name');
// 清空所有数据(慎用!)
Laya.LocalStorage.clear();
2.2 数据类型处理技巧
由于LocalStorage只能存储字符串,复杂数据需要序列化处理:
typescript复制// 存储对象
const playerData = {
level: 99,
skills: ['斩击', '格挡'],
equipment: { weapon: '光剑', armor: '重甲' }
};
Laya.LocalStorage.setItem('save1', JSON.stringify(playerData));
// 读取并解析
const saveStr = Laya.LocalStorage.getItem('save1');
if(saveStr) {
const data = JSON.parse(saveStr);
console.log(data.equipment.weapon); // 输出:光剑
}
2.3 存储空间管理
通过以下方法避免超出存储限制:
typescript复制// 估算当前使用量(字节)
function getUsedSpace() {
let total = 0;
for(let i=0; i<Laya.LocalStorage.length; i++) {
const key = Laya.LocalStorage.key(i);
const value = Laya.LocalStorage.getItem(key);
total += key.length + (value ? value.length : 0);
}
return total;
}
// 定期清理旧数据示例
function cleanOldSaves(maxKeep=3) {
const saves = [];
for(let i=0; i<Laya.LocalStorage.length; i++) {
const key = Laya.LocalStorage.key(i);
if(key.startsWith('save_')) {
saves.push({
key,
time: parseInt(Laya.LocalStorage.getItem(key+'_time'))
});
}
}
saves.sort((a,b) => b.time - a.time);
for(let i=maxKeep; i<saves.length; i++) {
Laya.LocalStorage.removeItem(saves[i].key);
Laya.LocalStorage.removeItem(saves[i].key+'_time');
}
}
3. 实战中的进阶应用方案
3.1 多标签页数据同步问题
当游戏需要在多个浏览器标签页间保持数据同步时,需监听storage事件:
typescript复制Laya.Browser.window.addEventListener('storage', (e) => {
console.log(`存储变更:${e.key}`, {
oldValue: e.oldValue,
newValue: e.newValue,
url: e.url
});
// 更新游戏内对应数据
if(e.key === 'inventory') {
game.player.inventory = JSON.parse(e.newValue || '[]');
}
});
3.2 数据加密保护方案
防止玩家轻易修改存档数据:
typescript复制const CRYPT_KEY = 'your-secret-key';
// 简单异或加密
function encrypt(data: string): string {
let result = '';
for(let i=0; i<data.length; i++) {
result += String.fromCharCode(
data.charCodeAt(i) ^ CRYPT_KEY.charCodeAt(i % CRYPT_KEY.length)
);
}
return btoa(result); // 转为Base64
}
// 解密过程
function decrypt(encrypted: string): string {
const decoded = atob(encrypted);
let result = '';
for(let i=0; i<decoded.length; i++) {
result += String.fromCharCode(
decoded.charCodeAt(i) ^ CRYPT_KEY.charCodeAt(i % CRYPT_KEY.length)
);
}
return result;
}
// 使用示例
Laya.LocalStorage.setItem('secure_save', encrypt(JSON.stringify(data)));
const saved = JSON.parse(decrypt(Laya.LocalStorage.getItem('secure_save') || ''));
3.3 自动存档与版本控制
实现可靠的存档系统:
typescript复制interface SaveData {
version: string;
timestamp: number;
data: any;
}
const CURRENT_VERSION = '1.2.0';
function autoSave(gameData: any) {
const save: SaveData = {
version: CURRENT_VERSION,
timestamp: Date.now(),
data: gameData
};
try {
Laya.LocalStorage.setItem('auto_save', JSON.stringify(save));
return true;
} catch(e) {
console.error('存档失败:', e);
return false;
}
}
function loadSave(): any {
const saveStr = Laya.LocalStorage.getItem('auto_save');
if(!saveStr) return null;
const save = JSON.parse(saveStr) as SaveData;
// 版本迁移示例
if(save.version === '1.0.0') {
// 将旧版数据转换为新版格式
save.data.player.equipmentSlots = 4;
save.version = CURRENT_VERSION;
}
return save.data;
}
4. 性能优化与疑难排解
4.1 高频写入的性能陷阱
LocalStorage的同步特性可能导致性能问题:
typescript复制// 错误示例:每帧都写入
Laya.stage.on(Laya.Event.ENTER_FRAME, () => {
Laya.LocalStorage.setItem('position', JSON.stringify(player.pos));
});
// 正确做法:节流写入
let saveTimer = 0;
const SAVE_INTERVAL = 5000; // 5秒
Laya.stage.on(Laya.Event.ENTER_FRAME, () => {
saveTimer += Laya.timer.delta;
if(saveTimer >= SAVE_INTERVAL) {
saveTimer = 0;
Laya.LocalStorage.setItem('position', JSON.stringify(player.pos));
}
});
4.2 常见问题解决方案
问题1:模拟器存储失败
- 检查模拟器是否开启了存储权限
- 尝试改用
Laya.LocalStorage.support = true强制启用 - 真机测试确认是否为环境问题
问题2:数据意外丢失
- 实现双备份机制:
typescript复制function safeSave(key: string, value: any) {
try {
Laya.LocalStorage.setItem(key, value);
Laya.LocalStorage.setItem(key+'_backup', value);
} catch(e) {
console.warn('主存储失败,使用备份');
Laya.LocalStorage.setItem(key,
Laya.LocalStorage.getItem(key+'_backup') || '');
}
}
问题3:跨域访问限制
- 确保所有游戏页面使用相同域名和端口
- 在微信小游戏中需要使用wx.setStorage系列API
4.3 存储监控与错误处理
typescript复制// 封装安全的存储方法
function safeStorage(action: 'get'|'set', key: string, value?: string) {
try {
if(action === 'get') {
return Laya.LocalStorage.getItem(key);
} else {
Laya.LocalStorage.setItem(key, value!);
return true;
}
} catch(e) {
console.error('LocalStorage错误:', e);
// 尝试清理1/3最旧的数据
if(e.name === 'QuotaExceededError') {
const keys = [];
for(let i=0; i<Laya.LocalStorage.length; i++) {
keys.push(Laya.LocalStorage.key(i));
}
for(let i=0; i<Math.ceil(keys.length/3); i++) {
Laya.LocalStorage.removeItem(keys[i]);
}
// 重试
if(action === 'set') {
Laya.LocalStorage.setItem(key, value!);
}
}
return null;
}
}
5. 替代方案与扩展思路
当LocalStorage不能满足需求时,可以考虑:
5.1 IndexedDB方案
适合大量结构化数据存储:
typescript复制const dbPromise = new Promise((resolve) => {
const request = indexedDB.open('GameDB', 1);
request.onupgradeneeded = (e) => {
const db = e.target.result;
if(!db.objectStoreNames.contains('saves')) {
db.createObjectStore('saves', { keyPath: 'id' });
}
};
request.onsuccess = (e) => {
resolve(e.target.result);
};
});
async function saveToIDB(data: any) {
const db = await dbPromise;
return new Promise((resolve) => {
const tx = db.transaction('saves', 'readwrite');
tx.objectStore('saves').put({
id: 'current',
data,
timestamp: Date.now()
});
tx.oncomplete = () => resolve(true);
});
}
5.2 服务器同步方案
重要数据应实现云端备份:
typescript复制async function syncWithServer() {
const localData = Laya.LocalStorage.getItem('save_data');
if(!localData) return;
try {
const res = await Laya.HttpRequest.send(
'/api/save',
JSON.parse(localData),
'POST',
'json',
['Content-Type', 'application/json']
);
if(res.status === 200) {
console.log('云端同步成功');
}
} catch(e) {
console.warn('同步失败,使用本地数据');
}
}
5.3 文件系统API(针对CocosRuntime等环境)
typescript复制function saveToFile(data: any) {
if(Laya.Browser.window.requestFileSystem) {
Laya.Browser.window.requestFileSystem(
Laya.Browser.window.PERSISTENT,
1024*1024,
(fs) => {
fs.root.getFile('save.json', { create: true }, (fileEntry) => {
fileEntry.createWriter((writer) => {
writer.onwriteend = () => console.log('保存成功');
writer.write(new Blob([JSON.stringify(data)]));
});
});
}
);
}
}
在实际项目中,我通常会采用分层存储策略:高频变更的临时数据用LocalStorage,重要进度数据用IndexedDB,关键账户数据同步到服务器。这种组合方案既保证了性能,又确保了数据安全性。特别是在处理移动端浏览器时,一定要考虑存储可能被系统自动清理的情况,重要数据应该提供导出备份功能。
