mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 04:27:13 +08:00
(client) feat:添加基地界面到游玩界面的过程,添加存档管理,技能树变得可用 (#58)
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/58
This commit is contained in:
@@ -122,7 +122,7 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是第一次启动,写回 loadOrder(顺序为 newOrder)
|
||||
// 如果是第一次启动,写回 loadOrder(顺序为 newOrder)
|
||||
if (isFirstLaunch)
|
||||
{
|
||||
// 将 newOrder 转换为 string[],并写回设置
|
||||
@@ -315,6 +315,8 @@ namespace Managers
|
||||
/// <returns>如果找到,返回转换为目标类型的 <see cref="Define"/> 对象;否则返回 null。</returns>
|
||||
public T FindDefine<T>(string defineName) where T : Define
|
||||
{
|
||||
if (string.IsNullOrEmpty(defineName))
|
||||
return null;
|
||||
if (defines.TryGetValue(typeof(T).Name, out var typeDict))
|
||||
{
|
||||
if (typeDict.TryGetValue(defineName, out var define))
|
||||
@@ -460,8 +462,8 @@ namespace Managers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 返回所有加载的定义包的字符串表示。
|
||||
/// </summary>
|
||||
|
||||
@@ -293,6 +293,7 @@ namespace Managers
|
||||
|
||||
var factionKey = def.attributes.defName ?? "default";
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
|
||||
|
||||
return entityComponent;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -363,7 +364,7 @@ namespace Managers
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="entityDef">实体定义对象。</param>
|
||||
/// <param name="pos">生成位置。</param>
|
||||
public EntityPrefab GenerateEntity(string dimensionId, EntityDef entityDef, Vector3 pos)
|
||||
public EntityPrefab GenerateCharacterEntity(string dimensionId, EntityDef entityDef, Vector3 pos)
|
||||
{
|
||||
if (!characterPrefab)
|
||||
{
|
||||
@@ -607,7 +608,7 @@ namespace Managers
|
||||
Relation targetRelationship)
|
||||
{
|
||||
// 参数校验:确保输入参数有效,避免空引用异常。
|
||||
if (sourceEntityPrefab == null || sourceEntityPrefab.entity == null)
|
||||
if (!sourceEntityPrefab || !sourceEntityPrefab.entity)
|
||||
{
|
||||
Debug.LogWarning("实体管理器:FindNearestEntityByRelation 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
|
||||
return null;
|
||||
@@ -620,137 +621,111 @@ namespace Managers
|
||||
return null;
|
||||
}
|
||||
|
||||
// 初始化追踪变量:设置初始值,用于在遍历过程中追踪最近的实体及其距离。
|
||||
// 使用平方距离 (SqrMagnitude) 可以避免在每次距离计算时进行昂贵的开方运算,从而提高性能。
|
||||
EntityPrefab nearestTarget = null;
|
||||
var minDistanceSqr = float.MaxValue;
|
||||
|
||||
|
||||
var sourcePos = sourceEntityPrefab.transform.position;
|
||||
|
||||
// 关系管理器实例检查:确保 AffiliationManager 可用,它是判断实体关系的核心组件。
|
||||
var affiliationManager = AffiliationManager.Instance;
|
||||
if (affiliationManager == null)
|
||||
|
||||
foreach (var (currentFactionKey, factionEntities) in factionDict)
|
||||
{
|
||||
Debug.LogError("实体管理器:FindNearestEntityByRelation 方法中,AffiliationManager 实例为空。无法确定实体关系。");
|
||||
return null;
|
||||
}
|
||||
var factionRelation = affiliationManager.GetRelation(sourceEntityPrefab.entity.affiliation, currentFactionKey);
|
||||
|
||||
// 遍历所有派系和实体:_dimensionFactionEntities 按维度和派系组织,需要遍历所有派系才能找到维度内的所有实体。
|
||||
foreach (var factionEntities in factionDict.Values) // factionDict.Values 是 LinkedList<EntityPrefab> 的集合
|
||||
{
|
||||
if (factionRelation != targetRelationship)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
{
|
||||
// 实体有效性及排除源实体自身:
|
||||
// 1. 排除无效或已死亡的实体,确保只处理活跃的实体。
|
||||
// 2. 在寻找“最近”实体时,通常指的是 *除了自身以外* 的实体。
|
||||
// 如果需要包含自身(例如,当 targetRelationship 是 AffiliationManager.Relation.Self 时),
|
||||
// 可以根据具体需求调整此逻辑,但默认行为是排除自身。
|
||||
if (!currentEntityPrefab || !currentEntityPrefab.entity ||
|
||||
currentEntityPrefab.entity.IsDead || currentEntityPrefab == sourceEntityPrefab)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 关系判断:使用 AffiliationManager 提供的接口判断源实体与当前遍历实体之间的关系。
|
||||
var currentRelation =
|
||||
affiliationManager.GetRelation(sourceEntityPrefab.entity, currentEntityPrefab.entity);
|
||||
if (currentRelation == targetRelationship)
|
||||
if (currentEntityPrefab.entity is not CombatantEntity)
|
||||
{
|
||||
// 距离计算与最近实体更新:
|
||||
// 1. 计算与源实体的距离(使用平方距离优化)。
|
||||
// 2. 如果当前实体更近,则更新 nearestTarget 和 minDistanceSqr。
|
||||
var distanceSqr = Vector3.SqrMagnitude(currentEntityPrefab.transform.position - sourcePos);
|
||||
if (distanceSqr < minDistanceSqr)
|
||||
{
|
||||
minDistanceSqr = distanceSqr;
|
||||
nearestTarget = currentEntityPrefab;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var distanceSqr = Vector3.SqrMagnitude(currentEntityPrefab.transform.position - sourcePos);
|
||||
if (distanceSqr < minDistanceSqr)
|
||||
{
|
||||
minDistanceSqr = distanceSqr;
|
||||
nearestTarget = currentEntityPrefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nearestTarget; // 返回找到的最近实体
|
||||
return nearestTarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,判断是否存在任何与源实体敌对的活跃实体。
|
||||
/// 此版本修正了将掉落物或子弹识别为敌对派系的问题,现在只考虑可转换为 CombatantEntity 的实体。
|
||||
/// 并优化了搜索逻辑,通过先判断派系关系来减少不必要的实体遍历。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要搜索的维度ID。</param>
|
||||
/// <param name="sourceEntityPrefab">作为参照的源实体预制体。</param>
|
||||
/// <returns>如果存在敌对的 CombatantEntity 则返回 true,否则返回 false。</returns>
|
||||
public bool ExistsHostile(string dimensionId, EntityPrefab sourceEntityPrefab)
|
||||
{
|
||||
// 参数校验:确保输入参数有效,避免空引用异常。
|
||||
// 使用 Unity 的 null 检查运算符 '!'
|
||||
if (!sourceEntityPrefab || !sourceEntityPrefab.entity)
|
||||
{
|
||||
Debug.LogWarning("实体管理器:ExistsHostile 方法中,源实体预制体或其内部实体为空。无法执行搜索。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 新增校验:确保源实体自身是一个 CombatantEntity。
|
||||
// 因为我们现在只关注 CombatantEntity 之间的敌对关系。
|
||||
|
||||
var sourceCombatant = sourceEntityPrefab.entity as CombatantEntity;
|
||||
if (!sourceCombatant) // 使用 Unity 的 null 检查运算符 '!'
|
||||
|
||||
if (!sourceCombatant)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"实体管理器:ExistsHostile 方法中,源实体 '{sourceEntityPrefab.name}' 无法转换为 CombatantEntity 类型。无法有效判断是否存在敌对的活跃实体。");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (sourceCombatant.IsDead)
|
||||
{
|
||||
return false; // 源 CombatantEntity 已经失效,它无法判断敌对实体。
|
||||
return false;
|
||||
}
|
||||
|
||||
// 维度数据存在性检查:验证目标维度是否在实体管理器的内部数据结构中被初始化和管理。
|
||||
if (!_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:ExistsHostile 方法中,维度 '{dimensionId}' 未被初始化或未在内部管理实体。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 遍历所有实体:遍历维度内的所有派系,再遍历每个派系下的所有实体。
|
||||
foreach (var factionEntities in factionDict.Values)
|
||||
var affiliationManager = AffiliationManager.Instance; // 获取 AffiliationManager 实例一次
|
||||
|
||||
foreach (var (currentFactionKey, factionEntities) in factionDict)
|
||||
{
|
||||
|
||||
var factionRelation = affiliationManager.GetRelation(sourceCombatant.affiliation, currentFactionKey);
|
||||
if (factionRelation != Relation.Hostile)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var currentEntityPrefab in factionEntities)
|
||||
{
|
||||
// 实体有效性及排除源实体自身:
|
||||
// 1. 排除无效的实体预制体或内部实体。
|
||||
// 2. 排除已失效的 CombatantEntity。
|
||||
// 3. 排除源实体自身,避免自身判断为敌对。
|
||||
if (!currentEntityPrefab || !currentEntityPrefab.entity ||
|
||||
currentEntityPrefab == sourceEntityPrefab) // 源实体自身已被转换为 sourceCombatant 并检查,这里只检查 prefab 引用是否相同
|
||||
if (currentEntityPrefab == null || currentEntityPrefab.entity == null ||
|
||||
currentEntityPrefab == sourceEntityPrefab)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 【核心改动】新增类型检查:只有能转换为 CombatantEntity 的实体才会被进一步判断。
|
||||
// 这样可以排除掉落物、子弹等非 CombatantEntity 的实体。
|
||||
|
||||
var currentCombatant = currentEntityPrefab.entity as CombatantEntity;
|
||||
if (!currentCombatant) // 使用 Unity 的 null 检查运算符 '!'
|
||||
if (currentCombatant == null || currentCombatant.IsDead)
|
||||
{
|
||||
continue; // 如果不是 CombatantEntity,则跳过此实体。
|
||||
}
|
||||
|
||||
// 检查目标 CombatantEntity 是否已失效(死亡或摧毁)。
|
||||
// 同理,这里假设 CombatantEntity 包含 IsDead 属性。
|
||||
// **重要:请确保 CombatantEntity 定义了 IsDead 属性,或者替换为更通用的失效状态属性。**
|
||||
if (currentCombatant.IsDead)
|
||||
{
|
||||
continue; // 目标 CombatantEntity 已经失效,不是活跃的敌对实体。
|
||||
}
|
||||
|
||||
// 关系判断:判断当前 CombatantEntity 与源 CombatantEntity 是否为“敌对”关系。
|
||||
// AffiliationManager.GetRelation 应该接收 CombatantEntity 类型。
|
||||
if (AffiliationManager.Instance.GetRelation(sourceCombatant, currentCombatant) == Relation.Hostile)
|
||||
{
|
||||
return true; // 找到第一个敌对的 CombatantEntity 即返回 true,提高效率。
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // 遍历完所有实体都未找到敌对的 CombatantEntity。
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -913,6 +888,7 @@ namespace Managers
|
||||
$"实体管理器:在场景加载初始化期间,Program 为ID '{dimensionId}' 注册了一个空的 Dimension 对象。这可能表明维度注册存在问题。");
|
||||
}
|
||||
}
|
||||
Clock.AddTick(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Data;
|
||||
using EventWorkClass;
|
||||
using Utils;
|
||||
using UnityEngine;
|
||||
using Base;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
@@ -34,6 +36,8 @@ namespace Managers
|
||||
/// 获取当前加载步骤的描述,用于启动流程的进度显示。
|
||||
/// </summary>
|
||||
public string StepDescription => "正在载入事件和故事";
|
||||
|
||||
public bool HasStoryDisplay=>_activeStoryPlayers.Any();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化事件管理器,从定义管理器中加载所有事件定义和故事定义。
|
||||
@@ -85,8 +89,14 @@ namespace Managers
|
||||
|
||||
// 将自身注册到时钟系统,以便每帧更新故事播放。
|
||||
Clock.AddTick(this);
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
Clock.AddTick(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理事件管理器,释放所有已加载的事件和故事定义。
|
||||
/// </summary>
|
||||
|
||||
@@ -65,18 +65,16 @@ namespace Managers
|
||||
|
||||
public ItemResource GetItem(string defName)
|
||||
{
|
||||
return _items.GetValueOrDefault(defName, defaultItem);
|
||||
return string.IsNullOrEmpty(defName) ? defaultItem : _items.GetValueOrDefault(defName, defaultItem);
|
||||
}
|
||||
public ItemResource FindItemByName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(itemName)) return defaultItem;
|
||||
return _itemsByName.GetValueOrDefault(itemName)?.FirstOrDefault();
|
||||
return string.IsNullOrEmpty(itemName) ? defaultItem : _itemsByName.GetValueOrDefault(itemName)?.FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<ItemResource> FindAllItemsByName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(itemName)) return new List<ItemResource>();
|
||||
return _itemsByName.GetValueOrDefault(itemName, new List<ItemResource>());
|
||||
return string.IsNullOrEmpty(itemName) ? new List<ItemResource>() : _itemsByName.GetValueOrDefault(itemName, new List<ItemResource>());
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
|
||||
128
Client/Assets/Scripts/Managers/KeyValueArchiveManager.cs
Normal file
128
Client/Assets/Scripts/Managers/KeyValueArchiveManager.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine; // 引入 UnityEngine 命名空间以使用 Debug.Log
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个通用的键值对存档管理器
|
||||
/// 允许存储和检索各种类型的数据,并与 SaveManager 集成以进行持久化。
|
||||
/// </summary>
|
||||
public class KeyValueArchiveManager : Utils.Singleton<KeyValueArchiveManager>, ISavableSingleton, ILaunchManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部用于存储键值对的字典,标记为 [Savable] 以便 SaveManager 进行序列化和反序列化。
|
||||
/// </summary>
|
||||
[Savable]
|
||||
private Dictionary<string, object> _data = new Dictionary<string, object>();
|
||||
|
||||
/// <summary>
|
||||
/// 私有构造函数,用于单例模式。
|
||||
/// 在首次创建实例时,将其注册到 SaveManager。
|
||||
/// </summary>
|
||||
public KeyValueArchiveManager()
|
||||
{
|
||||
SaveManager.Instance.RegisterSavable(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定键的值设置为指定值。
|
||||
/// 如果键已存在,则更新其值;如果不存在,则添加键值对。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">值的类型。</typeparam>
|
||||
/// <param name="key">要设置的键,不能为空或空白。</param>
|
||||
/// <param name="value">要存储的值。</param>
|
||||
/// <exception cref="ArgumentException">当键为 null 或空白时抛出。</exception>
|
||||
public void Set<T>(string key, T value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
throw new ArgumentException("键不能为空或空白。", nameof(key));
|
||||
}
|
||||
|
||||
_data[key] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的键获取存储的值。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">期望值的类型。</typeparam>
|
||||
/// <param name="key">要获取值的键,不能为空或空白。</param>
|
||||
/// <param name="defaultValue">如果键不存在或类型不匹配时返回的默认值。默认为类型 T 的默认值。</param>
|
||||
/// <returns>与键关联的值,如果键不存在或类型不匹配则返回 defaultValue。</returns>
|
||||
/// <exception cref="ArgumentException">当键为 null 或空白时抛出。</exception>
|
||||
public T Get<T>(string key, T defaultValue = default(T))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
throw new ArgumentException("键不能为空或空白。", nameof(key));
|
||||
}
|
||||
|
||||
if (_data.TryGetValue(key, out object value))
|
||||
{
|
||||
if (value is T typedValue)
|
||||
{
|
||||
return typedValue;
|
||||
}
|
||||
|
||||
Debug.LogWarning(
|
||||
$"警告: 键 '{key}' 存储的值类型为 '{value.GetType().Name}',但请求的类型为 '{typeof(T).Name}'。返回默认值。");
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查管理器中是否存在指定的键。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键,不能为空或空白。</param>
|
||||
/// <returns>如果键存在则为 true,否则为 false。</returns>
|
||||
/// <exception cref="ArgumentException">当键为 null 或空白时抛出。</exception>
|
||||
public bool HasKey(string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
throw new ArgumentException("键不能为空或空白。", nameof(key));
|
||||
}
|
||||
|
||||
return _data.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从管理器中移除指定的键及其对应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键,不能为空或空白。</param>
|
||||
/// <exception cref="ArgumentException">当键为 null 或空白时抛出。</exception>
|
||||
public void Remove(string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
throw new ArgumentException("键不能为空或空白。", nameof(key));
|
||||
}
|
||||
|
||||
_data.Remove(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ISavableSingleton 接口实现:当游戏状态需要被彻底重置时调用(例如,开始一个新游戏)。
|
||||
/// 将清除所有存储的键值对。
|
||||
/// </summary>
|
||||
public void ResetState()
|
||||
{
|
||||
_data.Clear();
|
||||
}
|
||||
|
||||
// 加载存档数据中的步骤描述。
|
||||
public string StepDescription { get; } = "加载存档数据中";
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// 启动初始化逻辑(当前为空)
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
// 清理逻辑(当前为空)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8bc5d84a1ec4dcea0b47115691d1284
|
||||
timeCreated: 1759402368
|
||||
@@ -25,7 +25,7 @@ namespace Managers
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
}
|
||||
|
||||
public void DisplayMessage(string message, PromptDisplayCategory type,Color? color=null)
|
||||
public void DisplayMessage(string message, PromptDisplayCategory type,Color? color=null,float showTime=3)
|
||||
{
|
||||
if (!_canvas)
|
||||
{
|
||||
@@ -40,7 +40,7 @@ namespace Managers
|
||||
return;
|
||||
// GenerateTemporaryAnimation的第三个参数是显示时间
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation(message,
|
||||
Program.Instance.FocusedEntity.Position, 5); // 5秒显示时间
|
||||
Program.Instance.FocusedEntity.Position, showTime); // 5秒显示时间
|
||||
break;
|
||||
|
||||
case PromptDisplayCategory.Default:
|
||||
@@ -75,6 +75,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
hintTextInstance.Init(message); // Init 方法会处理动画和生命周期
|
||||
hintTextInstance.lifeTime = showTime;
|
||||
// TemporaryAnimatorText 应该在 Init 内部设置好 lifeTime 并自动销毁。
|
||||
break;
|
||||
|
||||
@@ -102,8 +103,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
textInstance.Init(message); // Init 方法会处理动画和生命周期
|
||||
// textInstance.lifeTime 可以在 Init 方法内部设置,如果 Init 没有提供参数,这里就无法直接设置。
|
||||
// 假设 Init 已经处理好生命周期。
|
||||
textInstance.lifeTime = showTime;
|
||||
break;
|
||||
|
||||
case PromptDisplayCategory.FocusedEntityChatBubble:
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Managers
|
||||
/// <summary>
|
||||
/// 用于标记在单例类中需要被SaveManager存储的属性。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
|
||||
public class SavableAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
@@ -40,13 +40,17 @@ namespace Managers
|
||||
private const string SaveFileName = "game_save.json";
|
||||
|
||||
// 存档路径
|
||||
private static string SaveFilePath => Path.Combine(Application.persistentDataPath, SaveFileName);
|
||||
private static string SaveFilePath => Path.Combine("Save", SaveFileName);
|
||||
|
||||
public string StepDescription => "加载存档中";
|
||||
|
||||
// 存储所有需要保存状态的单例实例
|
||||
private readonly Dictionary<Type, ISavableSingleton> _savableSingletons = new();
|
||||
|
||||
~SaveManager()
|
||||
{
|
||||
SaveGame(); // 在销毁时保存游戏
|
||||
}
|
||||
/// <summary>
|
||||
/// 注册一个需要保存状态的单例实例。
|
||||
/// </summary>
|
||||
@@ -86,33 +90,10 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实现ILaunchManager接口的Clear方法,清空存档数据。
|
||||
/// 实现ILaunchManager接口的Clear方法,Clear是重载不做任何事。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(SaveFilePath))
|
||||
{
|
||||
File.Delete(SaveFilePath);
|
||||
Debug.Log($"SaveManager: 存档文件 '{SaveFileName}' 已删除。");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"SaveManager: 清空存档数据失败: {ex.Message}");
|
||||
}
|
||||
|
||||
// 清空所有注册单例的Savable属性(设置为默认值)
|
||||
foreach (var singletonEntry in _savableSingletons)
|
||||
{
|
||||
var singletonType = singletonEntry.Key;
|
||||
var singletonInstance = singletonEntry.Value;
|
||||
foreach (var prop in GetSavableProperties(singletonType))
|
||||
{
|
||||
prop.SetValue(singletonInstance, GetDefaultValue(prop.PropertyType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -144,6 +125,12 @@ namespace Managers
|
||||
|
||||
try
|
||||
{
|
||||
var directoryPath = Path.GetDirectoryName(SaveFilePath);
|
||||
if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(directoryPath);
|
||||
// DEBUG: Debug.Log($"SaveManager: 文件夹 '{directoryPath}' 已创建。");
|
||||
}
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
|
||||
@@ -21,11 +21,7 @@ namespace Managers
|
||||
{
|
||||
SaveManager.Instance.RegisterSavable(this);
|
||||
}
|
||||
|
||||
~SkillTreeManager()
|
||||
{
|
||||
SaveManager.Instance.UnregisterSavable(this);
|
||||
}
|
||||
|
||||
|
||||
public void Init()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user