更新
This commit is contained in:
40
Assets/Scripts/MainGame/Saving/GameSave.cs
Normal file
40
Assets/Scripts/MainGame/Saving/GameSave.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
|
||||
namespace Continentis.MainGame.Saving
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏存档的根节点,作为单一顶层对象写入 ES3 文件。
|
||||
/// ES3 用法:ES3.Save("GameSave", gameSave, "save.es3")
|
||||
/// ES3.Load<GameSave>("GameSave", "save.es3")
|
||||
/// </summary>
|
||||
public class GameSave
|
||||
{
|
||||
/// <summary>
|
||||
/// 存档格式版本号。当数据结构发生破坏性变更时递增,
|
||||
/// SaveManager 读取时据此执行迁移逻辑。
|
||||
/// </summary>
|
||||
public int saveVersion = 1;
|
||||
|
||||
/// <summary>最后一次保存的时间戳(UTC)。</summary>
|
||||
public DateTime saveTime;
|
||||
|
||||
/// <summary>
|
||||
/// 玩家账号级别数据(解锁内容、统计数据),跨跑局持久。
|
||||
/// </summary>
|
||||
public PlayerSave playerData;
|
||||
|
||||
/// <summary>
|
||||
/// 当前进行中的跑局快照。null 表示无进行中的跑局(主菜单状态)。
|
||||
/// </summary>
|
||||
public RunSave currentRun;
|
||||
|
||||
public GameSave()
|
||||
{
|
||||
playerData = new PlayerSave();
|
||||
currentRun = null;
|
||||
}
|
||||
|
||||
/// <summary>判断当前是否存在未完成的跑局。</summary>
|
||||
public bool HasActiveRun => currentRun != null;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/MainGame/Saving/GameSave.cs.meta
Normal file
2
Assets/Scripts/MainGame/Saving/GameSave.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6d5fe0b6ae9a064b839dd40c4ad91d8
|
||||
8
Assets/Scripts/MainGame/Saving/PlayerSave.meta
Normal file
8
Assets/Scripts/MainGame/Saving/PlayerSave.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30a96afbfcef5ed46b1813d61f7825ef
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
31
Assets/Scripts/MainGame/Saving/PlayerSave/PlayerSave.cs
Normal file
31
Assets/Scripts/MainGame/Saving/PlayerSave/PlayerSave.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Continentis.MainGame.Saving
|
||||
{
|
||||
/// <summary>
|
||||
/// 玩家账号级别的存档数据。
|
||||
/// 跨跑局持久,记录解锁内容和统计数据。
|
||||
/// </summary>
|
||||
public class PlayerSave
|
||||
{
|
||||
/// <summary>总跑局次数(含失败)。</summary>
|
||||
public int totalRuns;
|
||||
|
||||
/// <summary>总胜利次数(成功通关次数)。</summary>
|
||||
public int totalVictories;
|
||||
|
||||
/// <summary>
|
||||
/// 已解锁内容的 DataID 列表。
|
||||
/// 供菜单界面、职业选择、卡牌图鉴等系统查询。
|
||||
/// 格式与 ModManager 资产 DataID 保持一致:Type_ModName_AssetName
|
||||
/// </summary>
|
||||
public List<string> unlockedContentIDs;
|
||||
|
||||
// TODO: 后续扩展 — 成就记录、统计数据(最高伤害、最长存活回合等)
|
||||
|
||||
public PlayerSave()
|
||||
{
|
||||
unlockedContentIDs = new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b12d4d21c439d5c41ae0c797248037d8
|
||||
8
Assets/Scripts/MainGame/Saving/RunSave.meta
Normal file
8
Assets/Scripts/MainGame/Saving/RunSave.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9283203f9ca79f4e96919a472f55d65
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
28
Assets/Scripts/MainGame/Saving/RunSave/CardSave.cs
Normal file
28
Assets/Scripts/MainGame/Saving/RunSave/CardSave.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Continentis.MainGame.Saving
|
||||
{
|
||||
/// <summary>
|
||||
/// 单张卡牌的存档快照。
|
||||
/// 只记录能够重建运行时 CardInstance 所需的最小信息。
|
||||
/// </summary>
|
||||
public class CardSave
|
||||
{
|
||||
/// <summary>
|
||||
/// 卡牌数据 DataID,格式:CardData_ModName_CardName
|
||||
/// 通过 ModManager.GetData<CardData>(cardDataID) 还原。
|
||||
/// </summary>
|
||||
public string cardDataID;
|
||||
|
||||
/// <summary>
|
||||
/// 升级层数(0 = 未升级)。
|
||||
/// </summary>
|
||||
public int upgradeLevel;
|
||||
|
||||
public CardSave() { }
|
||||
|
||||
public CardSave(string cardDataID, int upgradeLevel = 0)
|
||||
{
|
||||
this.cardDataID = cardDataID;
|
||||
this.upgradeLevel = upgradeLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/MainGame/Saving/RunSave/CardSave.cs.meta
Normal file
2
Assets/Scripts/MainGame/Saving/RunSave/CardSave.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e280529f33e40e4408aeaff395007ad6
|
||||
53
Assets/Scripts/MainGame/Saving/RunSave/HeroSave.cs
Normal file
53
Assets/Scripts/MainGame/Saving/RunSave/HeroSave.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Continentis.MainGame.Saving
|
||||
{
|
||||
/// <summary>
|
||||
/// 单个玩家英雄的存档快照。
|
||||
/// 记录英雄在跑局中跨战斗保持的所有可变状态。
|
||||
/// </summary>
|
||||
public class HeroSave
|
||||
{
|
||||
/// <summary>
|
||||
/// 英雄角色数据 DataID,格式:CharacterData_ModName_HeroName
|
||||
/// 通过 ModManager.GetData<CharacterData>(characterDataID) 还原。
|
||||
/// </summary>
|
||||
public string characterDataID;
|
||||
|
||||
/// <summary>
|
||||
/// 当前 HP(跨战斗保持,受到伤害后降低,不自动回满)。
|
||||
/// </summary>
|
||||
public int currentHP;
|
||||
|
||||
/// <summary>
|
||||
/// 当前最大 HP(初始值来自 CharacterData,装备或跑局事件可能修改)。
|
||||
/// </summary>
|
||||
public int maxHP;
|
||||
|
||||
/// <summary>
|
||||
/// 当前完整牌组快照,包括初始牌、奖励牌和装备附带牌。
|
||||
/// </summary>
|
||||
public List<CardSave> deck;
|
||||
|
||||
/// <summary>
|
||||
/// 当前装备 DataID 列表,格式:EquipmentData_ModName_EquipmentName
|
||||
/// 通过 ModManager.GetData<EquipmentData>(id) 还原。
|
||||
/// </summary>
|
||||
public List<string> equipmentIDs;
|
||||
|
||||
public HeroSave()
|
||||
{
|
||||
deck = new List<CardSave>();
|
||||
equipmentIDs = new List<string>();
|
||||
}
|
||||
|
||||
public HeroSave(string characterDataID, int currentHP, int maxHP)
|
||||
{
|
||||
this.characterDataID = characterDataID;
|
||||
this.currentHP = currentHP;
|
||||
this.maxHP = maxHP;
|
||||
deck = new List<CardSave>();
|
||||
equipmentIDs = new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/MainGame/Saving/RunSave/HeroSave.cs.meta
Normal file
2
Assets/Scripts/MainGame/Saving/RunSave/HeroSave.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f70f65f2802e0e942bc433d15442e5af
|
||||
59
Assets/Scripts/MainGame/Saving/RunSave/RunSave.cs
Normal file
59
Assets/Scripts/MainGame/Saving/RunSave/RunSave.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Continentis.MainGame.Saving
|
||||
{
|
||||
/// <summary>
|
||||
/// 当次跑局的完整存档快照。
|
||||
/// 记录跑局开始配置、当前进度、资源,以及所有英雄的状态。
|
||||
/// </summary>
|
||||
public class RunSave
|
||||
{
|
||||
/// <summary>
|
||||
/// 当次跑局使用的配置 DataID,格式:RunConfig_ModName_ConfigName
|
||||
/// 通过 ModManager.GetData<RunConfig>(runConfigID) 还原关卡序列等初始设置。
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 当前关卡序列进度,即 combatNodeIDs 的当前索引。
|
||||
/// </summary>
|
||||
public int currentNodeIndex;
|
||||
|
||||
/// <summary>
|
||||
/// 关卡序列快照:开局时从 RunConfig.encounterSequenceIDs 拷贝而来。
|
||||
/// 格式:CombatNodeData_ModName_NodeName
|
||||
/// EnterCombat 直接读取此列表,跑局中无需再查询 RunConfig。
|
||||
/// </summary>
|
||||
public List<string> combatNodeIDs;
|
||||
|
||||
/// <summary>
|
||||
/// 当前金币数量。
|
||||
/// </summary>
|
||||
public int gold;
|
||||
|
||||
/// <summary>
|
||||
/// 跑局随机种子,用于保证重新进入游戏后奖励池与随机事件一致。
|
||||
/// </summary>
|
||||
public int randomSeed;
|
||||
|
||||
/// <summary>
|
||||
/// 所有玩家英雄的状态快照列表,顺序与 RunConfig.initialHeroIDs 一致。
|
||||
/// </summary>
|
||||
public List<HeroSave> heroes;
|
||||
|
||||
// TODO: Phase 4+ — 添加 relics(遗物列表)和 mapNodeRecord(地图选择记录)
|
||||
|
||||
public RunSave()
|
||||
{
|
||||
combatNodeIDs = new List<string>();
|
||||
heroes = new List<HeroSave>();
|
||||
}
|
||||
|
||||
public RunSave(int randomSeed, int startingGold, List<string> combatNodeIDs)
|
||||
{
|
||||
this.randomSeed = randomSeed;
|
||||
this.gold = startingGold;
|
||||
this.combatNodeIDs = combatNodeIDs ?? new List<string>();
|
||||
this.currentNodeIndex = 0;
|
||||
heroes = new List<HeroSave>();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/MainGame/Saving/RunSave/RunSave.cs.meta
Normal file
2
Assets/Scripts/MainGame/Saving/RunSave/RunSave.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57ef00a1f49761341923dcb281587be4
|
||||
123
Assets/Scripts/MainGame/Saving/SaveManager.cs
Normal file
123
Assets/Scripts/MainGame/Saving/SaveManager.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using Continentis.MainGame.Saving;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Continentis.MainGame
|
||||
{
|
||||
/// <summary>
|
||||
/// 负责游戏存档的读写、校验和删除。
|
||||
/// 跨场景持久,挂载在初始场景的 SaveManager GameObject 上。
|
||||
/// 内存中缓存一份 GameSave,所有操作均通过缓存进行,
|
||||
/// 保证 PlayerSave(成就、统计)不会被跑局操作意外覆盖。
|
||||
/// </summary>
|
||||
public class SaveManager : Singleton<SaveManager>
|
||||
{
|
||||
private const string SaveKey = "GameSave";
|
||||
private const string SaveFilePath = "save.es3";
|
||||
|
||||
/// <summary>内存中的存档缓存,Awake 时从文件加载,不存在则新建。</summary>
|
||||
private GameSave _cache;
|
||||
|
||||
// ── 生命周期 ──────────────────────────────────────────────────────
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
Initialize(dontDestroy: true);
|
||||
LoadOrCreate();
|
||||
}
|
||||
|
||||
// ── 查询 API ──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>返回缓存中的 PlayerSave(账号级数据)。</summary>
|
||||
public PlayerSave PlayerData => _cache.playerData;
|
||||
|
||||
/// <summary>判断当前是否存在未完成的跑局。</summary>
|
||||
public bool HasActiveRun() => _cache.HasActiveRun;
|
||||
|
||||
// ── 跑局存档 API ──────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 将 RunSave 写入缓存并持久化。
|
||||
/// 由 MainGameManager 在开局和节点边界(战斗胜利后)调用。
|
||||
/// </summary>
|
||||
public void SaveRunSave(RunSave runSave)
|
||||
{
|
||||
_cache.currentRun = runSave;
|
||||
Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除当前跑局(战斗失败 / 放弃跑局),保留 PlayerSave。
|
||||
/// </summary>
|
||||
public void DeleteRunSave()
|
||||
{
|
||||
_cache.currentRun = null;
|
||||
Flush();
|
||||
Debug.Log("[Save] 跑局存档已清除(PlayerSave 保留)。");
|
||||
}
|
||||
|
||||
/// <summary>返回缓存中的 RunSave,若无进行中的跑局则返回 null。</summary>
|
||||
public RunSave GetRunSave() => _cache.currentRun;
|
||||
|
||||
// ── 账号存档 API ──────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 更新 PlayerSave 并持久化(解锁内容、统计数据变更时调用)。
|
||||
/// </summary>
|
||||
public void SavePlayerData(PlayerSave playerSave)
|
||||
{
|
||||
_cache.playerData = playerSave;
|
||||
Flush();
|
||||
}
|
||||
|
||||
// ── 全局存档 API ──────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有存档数据,包括 PlayerSave(账号重置时调用)。
|
||||
/// </summary>
|
||||
public void DeleteAllData()
|
||||
{
|
||||
if (ES3.FileExists(SaveFilePath))
|
||||
{
|
||||
ES3.DeleteFile(SaveFilePath);
|
||||
Debug.Log("[Save] 所有存档已删除。");
|
||||
}
|
||||
|
||||
_cache = new GameSave();
|
||||
}
|
||||
|
||||
// ── 内部方法 ──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>从文件加载 GameSave;文件不存在则新建并立即持久化。</summary>
|
||||
private void LoadOrCreate()
|
||||
{
|
||||
if (!ES3.FileExists(SaveFilePath))
|
||||
{
|
||||
_cache = new GameSave();
|
||||
Flush();
|
||||
Debug.Log("[Save] 未找到存档文件,已新建。");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_cache = ES3.Load<GameSave>(SaveKey, SaveFilePath, defaultValue: null) ?? new GameSave();
|
||||
Debug.Log($"[Save] 存档加载成功(版本 {_cache.saveVersion})。");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[Save] 存档加载异常,将新建:{e.Message}");
|
||||
_cache = new GameSave();
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>将当前缓存写入文件。</summary>
|
||||
private void Flush()
|
||||
{
|
||||
_cache.saveTime = DateTime.UtcNow;
|
||||
ES3.Save<GameSave>(SaveKey, _cache, SaveFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/MainGame/Saving/SaveManager.cs.meta
Normal file
2
Assets/Scripts/MainGame/Saving/SaveManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac19ca7000e542e4390e00760eda2fad
|
||||
Reference in New Issue
Block a user