坦克大战游戏开发实战(十九)-游戏存档系统实现

前言

游戏存档系统是游戏开发的重要组成部分,它允许玩家保存游戏进度,随时继续游戏。本文将详细讲解游戏存档系统的设计与实现,包括存档管理器、多存档槽、自动存档、数据持久化等核心内容。

存档系统的重要性

  1. 进度保存:保存玩家游戏进度,避免重复游玩
  2. 时间灵活:玩家可以随时中断和继续游戏
  3. 数据安全:确保存档数据不丢失
  4. 多存档支持:支持多个存档,方便管理

存档系统

存档管理器

存档槽位

数据持久化

保存功能

加载功能

槽位1

槽位2

槽位3

Preferences

文件存储

一、存档数据结构

1.1 存档数据接口

export interface SaveData {
  version: string;              // 存档版本
  timestamp: number;            // 保存时间戳
  level: number;                // 当前关卡
  score: number;                // 当前得分
  lives: number;                // 当前生命
  enemies: number;              // 剩余敌人
  playTime: number;             // 游戏时长
  playerX: number;              // 玩家X坐标
  playerY: number;              // 玩家Y坐标
  playerDirection: number;      // 玩家方向
}

1.2 存档槽位接口

export interface SaveSlot {
  id: number;                   // 槽位ID
  name: string;                 // 槽位名称
  data: SaveData | null;        // 存档数据
  isEmpty: boolean;             // 是否为空
}

二、存档管理器实现

2.1 单例模式

export class SaveManager {
  private static instance: SaveManager;
  
  // 存档配置
  private readonly SAVE_VERSION = '1.0.0';
  private readonly MAX_SLOTS = 3;
  private readonly PREFERENCES_NAME = 'tank_battle_save';
  
  // 存档槽位
  private slots: Map<number, SaveSlot> = new Map();
  private preferencesInstance: preferences.Preferences | null = null;
  
  private constructor() {
    this.initSlots();
  }
  
  static getInstance(): SaveManager {
    if (!SaveManager.instance) {
      SaveManager.instance = new SaveManager();
    }
    return SaveManager.instance;
  }
}

2.2 初始化存档槽位

/**
 * 初始化存档槽位
 */
private initSlots(): void {
  for (let i = 1; i <= this.MAX_SLOTS; i++) {
    this.slots.set(i, {
      id: i,
      name: `存档 ${i}`,
      data: null,
      isEmpty: true
    });
  }
}

三、存档保存实现

3.1 保存游戏

/**
 * 保存游戏
 */
async saveGame(slotId: number, data: SaveData): Promise<boolean> {
  if (slotId < 1 || slotId > this.MAX_SLOTS) {
    console.error('无效的存档槽位:', slotId);
    return false;
  }
  
  if (!this.preferencesInstance) {
    console.error('Preferences未初始化');
    return false;
  }
  
  try {
    // 添加版本和时间戳
    const saveData: SaveData = {
      ...data,
      version: this.SAVE_VERSION,
      timestamp: Date.now()
    };
    
    // 保存到Preferences
    const saveJson = JSON.stringify(saveData);
    await this.preferencesInstance.put(`slot_${slotId}`, saveJson);
    await this.preferencesInstance.flush();
    
    // 更新内存中的槽位数据
    const slot = this.slots.get(slotId);
    if (slot) {
      slot.data = saveData;
      slot.isEmpty = false;
    }
    
    console.info(`游戏已保存到存档 ${slotId}`);
    return true;
  } catch (error) {
    console.error('保存游戏失败:', error);
    return false;
  }
}

3.2 自动存档

/**
 * 自动存档
 */
async autoSave(data: SaveData): Promise<boolean> {
  // 自动存档使用特殊槽位(槽位1)
  return await this.saveGame(1, data);
}

/**
 * 快速保存
 */
async quickSave(data: SaveData): Promise<boolean> {
  return await this.saveGame(1, data);
}

四、存档加载实现

4.1 加载游戏

/**
 * 加载游戏
 */
async loadGame(slotId: number): Promise<SaveData | null> {
  if (slotId < 1 || slotId > this.MAX_SLOTS) {
    console.error('无效的存档槽位:', slotId);
    return null;
  }
  
  const slot = this.slots.get(slotId);
  if (!slot || slot.isEmpty || !slot.data) {
    console.error('存档槽位为空:', slotId);
    return null;
  }
  
  console.info(`从存档 ${slotId} 加载游戏`);
  return slot.data;
}

4.2 快速加载

/**
 * 快速加载
 */
async quickLoad(): Promise<SaveData | null> {
  return await this.loadGame(1);
}

五、存档管理功能

5.1 删除存档

/**
 * 删除存档
 */
async deleteSave(slotId: number): Promise<boolean> {
  if (slotId < 1 || slotId > this.MAX_SLOTS) {
    console.error('无效的存档槽位:', slotId);
    return false;
  }
  
  if (!this.preferencesInstance) {
    console.error('Preferences未初始化');
    return false;
  }
  
  try {
    // 从Preferences删除
    await this.preferencesInstance.delete(`slot_${slotId}`);
    await this.preferencesInstance.flush();
    
    // 更新内存中的槽位数据
    const slot = this.slots.get(slotId);
    if (slot) {
      slot.data = null;
      slot.isEmpty = true;
    }
    
    console.info(`存档 ${slotId} 已删除`);
    return true;
  } catch (error) {
    console.error('删除存档失败:', error);
    return false;
  }
}

5.2 获取存档信息

/**
 * 获取存档槽位信息
 */
getSlot(slotId: number): SaveSlot | undefined {
  return this.slots.get(slotId);
}

/**
 * 获取所有存档槽位
 */
getAllSlots(): SaveSlot[] {
  const slotArray: SaveSlot[] = [];
  for (let i = 1; i <= this.MAX_SLOTS; i++) {
    const slot = this.slots.get(i);
    if (slot) {
      slotArray.push(slot);
    }
  }
  return slotArray;
}

六、数据持久化

6.1 Preferences存储

HarmonyOS使用Preferences进行数据持久化:

/**
 * 初始化Preferences
 */
async initPreferences(context: Context): Promise<void> {
  try {
    this.preferencesInstance = await preferences.getPreferences(
      context, 
      this.PREFERENCES_NAME
    );
    await this.loadAllSlots();
  } catch (error) {
    console.error('初始化Preferences失败:', error);
  }
}

6.2 加载所有存档

/**
 * 加载所有存档槽位
 */
private async loadAllSlots(): Promise<void> {
  if (!this.preferencesInstance) {
    return;
  }
  
  for (let i = 1; i <= this.MAX_SLOTS; i++) {
    try {
      const saveJson = await this.preferencesInstance.get(`slot_${i}`, '');
      if (saveJson) {
        const saveData = JSON.parse(saveJson as string) as SaveData;
        const slot = this.slots.get(i);
        if (slot) {
          slot.data = saveData;
          slot.isEmpty = false;
        }
      }
    } catch (error) {
      console.error(`加载存档槽位 ${i} 失败:`, error);
    }
  }
}

七、版本兼容性

7.1 版本检查

/**
 * 检查存档版本兼容性
 */
isCompatible(data: SaveData): boolean {
  // 简单的版本检查
  return data.version === this.SAVE_VERSION;
}

7.2 数据迁移

如果版本不兼容,需要进行数据迁移:

/**
 * 迁移存档数据
 */
migrateData(oldData: any): SaveData {
  // 根据版本号进行数据迁移
  // 这里可以实现版本升级逻辑
  return oldData as SaveData;
}

八、存档UI集成

8.1 存档列表显示

/**
 * 获取存档摘要信息
 */
getSaveSummary(slotId: number): string {
  const slot = this.slots.get(slotId);
  if (!slot || slot.isEmpty || !slot.data) {
    return '空存档';
  }
  
  const data = slot.data;
  const time = this.formatSaveTime(data.timestamp);
  
  return [
    `关卡 ${data.level}`,
    `得分 ${data.score}`,
    `生命 ${data.lives}`,
    `时间 ${time}`
  ].join(' | ');
}

8.2 时间格式化

/**
 * 格式化存档时间
 */
formatSaveTime(timestamp: number): string {
  const date = new Date(timestamp);
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, '0');
  const day = date.getDate().toString().padStart(2, '0');
  const hour = date.getHours().toString().padStart(2, '0');
  const minute = date.getMinutes().toString().padStart(2, '0');
  
  return `${year}-${month}-${day} ${hour}:${minute}`;
}

九、错误处理

9.1 错误类型

存档系统可能遇到的错误:

  1. 存储失败:Preferences写入失败
  2. 读取失败:Preferences读取失败
  3. 数据损坏:存档数据损坏
  4. 版本不兼容:存档版本不匹配

9.2 错误处理策略

try {
  // 保存操作
  await this.saveGame(slotId, data);
} catch (error) {
  console.error('存档操作失败:', error);
  
  // 可以选择:
  // 1. 重试操作
  // 2. 使用备用存储
  // 3. 提示用户
}

十、总结

游戏存档系统是游戏开发的重要组成部分。本文详细讲解了存档管理器、多存档槽、自动存档、数据持久化等核心内容,为游戏提供了完整的存档功能。

关键要点

  1. 多存档槽:支持多个存档,方便管理
  2. 自动存档:自动保存游戏进度
  3. 数据持久化:使用Preferences进行数据存储
  4. 版本兼容:处理存档版本兼容性问题
  5. 错误处理:完善的错误处理机制

系列文章导航:

下期预告: 坦克大战游戏开发实战(二十)-性能优化策略总结

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐