(client)chore:将维度区分独立,将加载页面独立,降低代码耦合,写了更好看的注释
This commit is contained in:
@ -10,13 +10,15 @@ namespace Managers
|
||||
/// 阵营管理器,负责管理游戏中的所有阵营定义及其相互关系。
|
||||
/// 继承自 <see cref="Utils.Singleton{T}"/> ,确保全局只有一个实例。
|
||||
/// </summary>
|
||||
public class AffiliationManager:Utils.Singleton<AffiliationManager>
|
||||
public class AffiliationManager:Utils.Singleton<AffiliationManager>,ILaunchManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 存储所有已加载的阵营定义,键为阵营的唯一名称,值为对应的 <see cref="AffiliationDef"/> 对象。
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, AffiliationDef> _affiliations = new();
|
||||
|
||||
|
||||
public string StepDescription => "正在区分派系";
|
||||
|
||||
/// <summary>
|
||||
/// 初始化阵营管理器,从 <see cref="DefineManager"/> 加载所有 <see cref="AffiliationDef"/>。
|
||||
/// 在首次需要阵营数据时调用。
|
||||
|
@ -1,40 +1,73 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Data;
|
||||
using UnityEngine;
|
||||
using Utils;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
public class DefineManager : Singleton<DefineManager>
|
||||
/// <summary>
|
||||
/// 定义管理器,负责加载、管理和查询所有数据定义(Define)对象。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该管理器是一个单例,用于在应用程序中集中管理各种游戏或系统定义,
|
||||
/// 包括从不同数据包(Mods)加载定义,处理定义之间的引用,以及提供多种查询方法。
|
||||
/// </remarks>
|
||||
public class DefineManager : Singleton<DefineManager>,ILaunchManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据集文件路径数组,用于指定定义包的根目录。
|
||||
/// </summary>
|
||||
private static readonly string[] dataSetFilePath = { "Data", "Mods" };
|
||||
//类别,定义名,定义
|
||||
|
||||
/// <summary>
|
||||
/// 存储所有按类别和定义名索引的定义。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 外层字典的键是定义类别(例如,类的类型名),内层字典的键是定义的名称。
|
||||
/// </remarks>
|
||||
public Dictionary<string, Dictionary<string, Define>> defines = new();
|
||||
//包id,包
|
||||
|
||||
/// <summary>
|
||||
/// 存储所有按包ID索引的定义包。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 键是定义包的唯一ID,值是对应的 <see cref="DefinePack"/> 对象。
|
||||
/// </remarks>
|
||||
public Dictionary<string, DefinePack> packs = new();
|
||||
//类别,定义
|
||||
|
||||
/// <summary>
|
||||
/// 存储所有按类别索引的匿名定义列表。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 匿名定义是没有明确 <see cref="Define.defName"/> 的定义,它们通常作为其他定义的子元素出现。
|
||||
/// 外层字典的键是定义类别,值是该类别下的匿名定义列表。
|
||||
/// </remarks>
|
||||
public Dictionary<string, List<Define>> anonymousDefines = new();
|
||||
|
||||
public string StepDescription => "加载数据定义中";
|
||||
|
||||
/// <summary>
|
||||
/// 初始化定义管理器,加载所有定义包并构建定义字典。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该方法执行以下操作:
|
||||
/// 1. 获取指定路径下的所有子文件夹,每个子文件夹代表一个定义包。
|
||||
/// 2. 遍历每个定义包,尝试加载其中的定义数据。
|
||||
/// 3. 将加载的定义数据按类型分类,并存储到定义字典中。
|
||||
/// <list type="number">
|
||||
/// <item>获取指定路径下的所有子文件夹,每个子文件夹代表一个定义包。</item>
|
||||
/// <item>遍历每个定义包,尝试加载其中的定义数据。</item>
|
||||
/// <item>将加载的定义数据按类型分类,并存储到定义字典中。</item>
|
||||
/// <item>处理定义内部的引用关系,将引用占位符替换为实际定义对象。</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public void Init()
|
||||
{
|
||||
if (packs.Count > 0)
|
||||
return;
|
||||
// 单线程
|
||||
|
||||
// 获取所有定义包的文件夹路径
|
||||
var packFolder = Configs.ConfigProcessor.GetSubFolders(new(dataSetFilePath));
|
||||
foreach (var folder in packFolder)
|
||||
{
|
||||
@ -45,14 +78,16 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 字段信息缓存,用于优化反射性能。
|
||||
Dictionary<Type, FieldInfo[]> fieldCache = new();
|
||||
// 需要链接的定义、需要链接的字段、链接信息
|
||||
|
||||
// 存储需要进行链接的定义引用信息。
|
||||
// Tuple的元素依次代表:被引用的定义(Define),引用该定义的字段(FieldInfo),以及引用占位符(Define)。
|
||||
List<Tuple<Define, FieldInfo, Define>> defineCache = new();
|
||||
|
||||
string currentPackID = string.Empty;
|
||||
|
||||
string currentPackID;
|
||||
|
||||
// 递归处理定义对象及其内部的嵌套定义和引用。
|
||||
void ProcessDefine(Define def)
|
||||
{
|
||||
if (def == null || def.isReferene)
|
||||
@ -60,15 +95,14 @@ namespace Managers
|
||||
|
||||
def.packID = currentPackID;
|
||||
|
||||
// 如果字段信息已经缓存,则直接使用缓存
|
||||
// 检查是否已缓存字段信息,如果已缓存则直接使用。
|
||||
if (!fieldCache.TryGetValue(def.GetType(), out var defineFields))
|
||||
{
|
||||
// 获取所有字段类型为 Define 或其派生类型的字段
|
||||
// 获取所有公共实例字段。
|
||||
defineFields = def.GetType()
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||
.ToArray(); // 不再过滤,先获取所有字段
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
// 缓存字段信息
|
||||
// 缓存当前类型的字段信息。
|
||||
fieldCache[def.GetType()] = defineFields;
|
||||
}
|
||||
|
||||
@ -99,7 +133,7 @@ namespace Managers
|
||||
ProcessDefine(defRef);
|
||||
}
|
||||
}
|
||||
// 处理集合类型字段
|
||||
// 处理 List<Define> 类型的字段
|
||||
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
var elementType = fieldType.GenericTypeArguments[0];
|
||||
@ -110,7 +144,7 @@ namespace Managers
|
||||
{
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (item is Define defItem && !defItem.isReferene)
|
||||
if (item is Define { isReferene: false } defItem)
|
||||
{
|
||||
ProcessDefine(defItem);
|
||||
}
|
||||
@ -118,7 +152,7 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
}
|
||||
// 处理数组类型字段
|
||||
// 处理 Define[] 类型的数组字段
|
||||
else if (fieldType.IsArray)
|
||||
{
|
||||
var elementType = fieldType.GetElementType();
|
||||
@ -129,7 +163,7 @@ namespace Managers
|
||||
{
|
||||
foreach (var item in array)
|
||||
{
|
||||
if (item is Define defItem && !defItem.isReferene)
|
||||
if (item is Define { isReferene: false } defItem)
|
||||
{
|
||||
ProcessDefine(defItem);
|
||||
}
|
||||
@ -139,6 +173,7 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var pack in packs)
|
||||
{
|
||||
currentPackID = pack.Value.packID;
|
||||
@ -154,6 +189,8 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理所有定义间的引用关系
|
||||
foreach (var defRef in defineCache)
|
||||
{
|
||||
if (defRef.Item1 == null)
|
||||
@ -186,19 +223,19 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
public void Clear()
|
||||
{
|
||||
defines.Clear();
|
||||
packs.Clear();
|
||||
anonymousDefines.Clear();
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找指定定义类型的定义名对应的 Define 对象。
|
||||
/// 查找指定定义类型的定义名对应的 <see cref="Define"/> 对象。
|
||||
/// </summary>
|
||||
/// <param name="defineType">定义类型</param>
|
||||
/// <param name="defineName">定义名</param>
|
||||
/// <returns>如果找到,返回 Define 对象;否则返回 null。</returns>
|
||||
/// <param name="defineType">定义类型(通常是类的名称)。</param>
|
||||
/// <param name="defineName">定义名。</param>
|
||||
/// <returns>如果找到,返回 <see cref="Define"/> 对象;否则返回 null。</returns>
|
||||
public Define FindDefine(string defineType, string defineName)
|
||||
{
|
||||
if (defines.TryGetValue(defineType, out var typeDict))
|
||||
@ -210,12 +247,13 @@ namespace Managers
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用模板查找并返回指定类型的 Define 对象。
|
||||
/// 使用泛型模板查找并返回指定类型的 <see cref="Define"/> 对象。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">目标类型</typeparam>
|
||||
/// <param name="defineName">定义名</param>
|
||||
/// <returns>如果找到,返回转换为目标类型的 Define 对象;否则返回 null。</returns>
|
||||
/// <typeparam name="T">目标类型,必须继承自 <see cref="Define"/>。</typeparam>
|
||||
/// <param name="defineName">定义名。</param>
|
||||
/// <returns>如果找到,返回转换为目标类型的 <see cref="Define"/> 对象;否则返回 null。</returns>
|
||||
public T FindDefine<T>(string defineName) where T : Define
|
||||
{
|
||||
if (defines.TryGetValue(typeof(T).Name, out var typeDict))
|
||||
@ -227,6 +265,12 @@ namespace Managers
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定 <see cref="Define"/> 对象所属的定义包。
|
||||
/// </summary>
|
||||
/// <param name="define">要查询的 <see cref="Define"/> 对象。</param>
|
||||
/// <returns>如果找到对应的定义包,则返回 <see cref="DefinePack"/> 对象;否则返回 null。</returns>
|
||||
public DefinePack GetDefinePackage(Define define)
|
||||
{
|
||||
if (define == null || define.packID == null)
|
||||
@ -235,10 +279,21 @@ namespace Managers
|
||||
return pack;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定 <see cref="Define"/> 对象所属定义包的名称。
|
||||
/// </summary>
|
||||
/// <param name="define">要查询的 <see cref="Define"/> 对象。</param>
|
||||
/// <returns>如果找到对应的定义包,则返回其名称;否则返回 null。</returns>
|
||||
public string GetDefinePackageName(Define define)
|
||||
{
|
||||
return GetDefinePackage(define)?.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定包ID对应定义包的根路径。
|
||||
/// </summary>
|
||||
/// <param name="packID">定义包的唯一标识ID。</param>
|
||||
/// <returns>如果找到对应的定义包,则返回其根路径;否则返回 null。</returns>
|
||||
public string GetPackagePath(string packID)
|
||||
{
|
||||
if (packs.TryGetValue(packID, out var pack))
|
||||
@ -248,6 +303,10 @@ namespace Managers
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有已加载的 <see cref="Define"/> 对象。
|
||||
/// </summary>
|
||||
/// <returns>包含所有命名定义和匿名定义的数组。</returns>
|
||||
public Define[] GetAllDefine()
|
||||
{
|
||||
List<Define> defineList = new();
|
||||
@ -264,10 +323,10 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定类型下的所有 Define 对象。(包括匿名定义)
|
||||
/// 查询指定类型下的所有 <see cref="Define"/> 对象(包括命名定义和匿名定义)。
|
||||
/// </summary>
|
||||
/// <param name="defineType">定义类型(外层字典的键)。</param>
|
||||
/// <returns>该类型下的 Define 数组,如果未找到则返回 null。</returns>
|
||||
/// <param name="defineType">定义类型(外层字典的键,通常是类的名称)。</param>
|
||||
/// <returns>该类型下的 <see cref="Define"/> 数组。如果未找到任何定义,则返回 null。</returns>
|
||||
public Define[] QueryDefinesByType(string defineType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(defineType))
|
||||
@ -278,19 +337,19 @@ namespace Managers
|
||||
|
||||
List<Define> result = new List<Define>();
|
||||
|
||||
// 从命名定义中查询
|
||||
// 从命名定义中查询。
|
||||
if (defines.TryGetValue(defineType, out var namedDefinitions))
|
||||
{
|
||||
result.AddRange(namedDefinitions.Values);
|
||||
}
|
||||
|
||||
// 从匿名定义中查询
|
||||
// 从匿名定义中查询。
|
||||
if (anonymousDefines.TryGetValue(defineType, out var anonymousDefinitionList))
|
||||
{
|
||||
result.AddRange(anonymousDefinitionList);
|
||||
}
|
||||
|
||||
// 如果结果为空,则返回 null
|
||||
// 如果结果为空,则返回 null。
|
||||
if (result.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"查询失败:未找到定义类型 '{defineType}'");
|
||||
@ -299,12 +358,13 @@ namespace Managers
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定类型下的所有 Define 对象,并尝试转换为目标类型。(包括匿名定义)
|
||||
/// 查询指定类型下的所有 <see cref="Define"/> 对象,并尝试转换为目标类型(包括命名定义和匿名定义)。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">目标类型。</typeparam>
|
||||
/// <returns>转换后的目标类型数组,如果未找到或转换失败则返回 null。</returns>
|
||||
public T[] QueryDefinesByType<T>()
|
||||
/// <typeparam name="T">目标类型,必须继承自 <see cref="Define"/>。</typeparam>
|
||||
/// <returns>转换后的目标类型数组。如果未找到或转换失败,则返回 null。</returns>
|
||||
public T[] QueryDefinesByType<T>() where T : Define
|
||||
{
|
||||
var defineType = typeof(T).Name;
|
||||
|
||||
@ -316,7 +376,7 @@ namespace Managers
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试将所有 Define 对象转换为目标类型 T
|
||||
// 尝试将所有 <see cref="Define"/> 对象转换为目标类型 T。
|
||||
var result = new List<T>();
|
||||
foreach (var item in allDefines)
|
||||
{
|
||||
@ -341,10 +401,10 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定类型下的所有 Define 对象(仅包含命名定义,不包括匿名定义)。
|
||||
/// 查询指定类型下的所有命名 <see cref="Define"/> 对象(不包括匿名定义)。
|
||||
/// </summary>
|
||||
/// <param name="defineType">定义类型(外层字典的键)。</param>
|
||||
/// <returns>该类型下的 Define 数组,如果未找到则返回 null。</returns>
|
||||
/// <param name="defineType">定义类型(外层字典的键,通常是类的名称)。</param>
|
||||
/// <returns>该类型下的命名 <see cref="Define"/> 数组。如果未找到任何命名定义,则返回 null。</returns>
|
||||
public Define[] QueryNamedDefinesByType(string defineType)
|
||||
{
|
||||
if (string.IsNullOrEmpty(defineType))
|
||||
@ -355,13 +415,13 @@ namespace Managers
|
||||
|
||||
List<Define> result = new List<Define>();
|
||||
|
||||
// 仅从命名定义中查询
|
||||
// 仅从命名定义中查询。
|
||||
if (defines.TryGetValue(defineType, out var namedDefinitions))
|
||||
{
|
||||
result.AddRange(namedDefinitions.Values);
|
||||
}
|
||||
|
||||
// 如果结果为空,则返回 null
|
||||
// 如果结果为空,则返回 null。
|
||||
if (result.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"查询失败:未找到定义类型 '{defineType}' 的命名定义");
|
||||
@ -372,11 +432,11 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定类型下的所有 Define 对象,并尝试转换为目标类型(仅包含命名定义,不包括匿名定义)。
|
||||
/// 查询指定类型下的所有命名 <see cref="Define"/> 对象,并尝试转换为目标类型(不包括匿名定义)。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">目标类型。</typeparam>
|
||||
/// <returns>转换后的目标类型数组,如果未找到或转换失败则返回 null。</returns>
|
||||
public T[] QueryNamedDefinesByType<T>()
|
||||
/// <typeparam name="T">目标类型,必须继承自 <see cref="Define"/>。</typeparam>
|
||||
/// <returns>转换后的目标类型数组。如果未找到或转换失败,则返回 null。</returns>
|
||||
public T[] QueryNamedDefinesByType<T>() where T : Define
|
||||
{
|
||||
var defineType = typeof(T).Name;
|
||||
|
||||
@ -388,7 +448,7 @@ namespace Managers
|
||||
|
||||
try
|
||||
{
|
||||
// 尝试将所有 Define 对象转换为目标类型 T
|
||||
// 尝试将所有 <see cref="Define"/> 对象转换为目标类型 T。
|
||||
var result = new List<T>();
|
||||
foreach (var item in allDefines)
|
||||
{
|
||||
@ -411,22 +471,28 @@ namespace Managers
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回所有加载的定义包的字符串表示。
|
||||
/// </summary>
|
||||
/// <returns>一个包含所有定义包信息的字符串,每个包信息占一行。</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (packs == null || packs.Count == 0)
|
||||
{
|
||||
return "No packs available"; // 如果集合为空或为 null,返回默认信息
|
||||
// 如果集合为空或为 null,返回默认信息。
|
||||
return "No packs available";
|
||||
}
|
||||
|
||||
var result = new System.Text.StringBuilder();
|
||||
|
||||
foreach (var definePack in packs)
|
||||
{
|
||||
result.AppendLine(definePack.ToString()); // 每个元素占一行
|
||||
// 每个定义包对象占一行。
|
||||
result.AppendLine(definePack.ToString());
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,126 +10,44 @@ using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 管理游戏中所有实体(角色、建筑、子弹等)的生命周期、存储和分发。
|
||||
/// 实现了维度感知,这意味着不同维度的实体数据是独立管理的。
|
||||
/// </summary>
|
||||
public class EntityManage : Utils.MonoSingleton<EntityManage>, ITick
|
||||
{
|
||||
// --- 新增:维度感知的实体存储结构 ---
|
||||
// 外层字典:DimensionId -> 内层字典
|
||||
// 内层字典:FactionKey -> LinkedList<EntityPrefab>
|
||||
/// <summary>
|
||||
/// 维度感知的实体存储结构。
|
||||
/// 外层字典按维度ID(DimensionId)索引,内层字典按派系键(FactionKey)索引,
|
||||
/// 存储该派系下的实体预制体链表。
|
||||
/// </summary>
|
||||
private Dictionary<string, Dictionary<string, LinkedList<EntityPrefab>>> _dimensionFactionEntities = new();
|
||||
|
||||
// --- 新增:当前场景中活跃的维度实例 ---
|
||||
private Dictionary<string, Dimension> _activeDimensions = new();
|
||||
|
||||
// --- 新增:维度感知的层级缓存 ---
|
||||
// DimensionId -> LayerName -> Transform
|
||||
/// <summary>
|
||||
/// 维度感知的层级Transform缓存。
|
||||
/// 外层字典按维度ID(DimensionId)索引,内层字典按层级名称(LayerName)索引,
|
||||
/// 存储其对应的Transform。
|
||||
/// </summary>
|
||||
private Dictionary<string, Dictionary<string, Transform>> _dimensionLayerCache = new();
|
||||
|
||||
// --- 待添加实体列表,现在包含 DimensionId ---
|
||||
/// <summary>
|
||||
/// 待添加的实体列表,用于在Tick循环中安全地将实体加入管理。
|
||||
/// Tuple包含:Item1=维度ID,Item2=派系键,Item3=实体预制体。
|
||||
/// </summary>
|
||||
private List<Tuple<string, string, EntityPrefab>>
|
||||
_pendingAdditions = new(); // Item1: DimensionId, Item2: FactionKey, Item3: EntityPrefab
|
||||
|
||||
// --- 现有预制体 (保持不变) ---
|
||||
/// <summary> 角色实体的预制体。 </summary>
|
||||
public EntityPrefab characterPrefab;
|
||||
/// <summary> 建筑实体的预制体。 </summary>
|
||||
public EntityPrefab buildingPrefab;
|
||||
/// <summary> 子弹实体的预制体。 </summary>
|
||||
public EntityPrefab bulletPrefab;
|
||||
/// <summary> 默认实体的预制体,用于生成失败时的回退。 </summary>
|
||||
public EntityPrefab defaultEntityPrefab;
|
||||
|
||||
// 已移除:RegisterDimension 和 UnregisterDimension 方法的相关注释,因为这些方法已不存在且与逻辑无关。
|
||||
|
||||
public void RegisterDimension(Dimension dimension)
|
||||
{
|
||||
if (dimension == null || string.IsNullOrEmpty(dimension.DimensionId))
|
||||
{
|
||||
Debug.LogError("Attempted to register a null or invalid Dimension.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_activeDimensions.TryGetValue(dimension.DimensionId, out var existingDimension))
|
||||
{
|
||||
if (existingDimension == dimension)
|
||||
{
|
||||
// 【逻辑修改】如果是同一个实例重复注册,只做信息提示,不警告
|
||||
Debug.Log($"Dimension with ID '{dimension.DimensionId}' already registered with the same instance. Skipping re-registration.");
|
||||
return; // 已经注册且是同一个实例,直接返回
|
||||
}
|
||||
else
|
||||
{
|
||||
// 【逻辑修改】如果是不同实例但ID相同,这是严重错误
|
||||
Debug.LogError(
|
||||
$"CRITICAL ERROR: Dimension with ID '{dimension.DimensionId}' is already registered with a DIFFERENT instance ({existingDimension.name} vs {dimension.name}). This indicates a duplicate DimensionId in the scene. The new instance will overwrite the old one, which may lead to unexpected behavior. Please ensure all Dimension objects have unique IDs.");
|
||||
// 允许覆盖,但以错误日志形式提示,强制开发者修复场景配置。
|
||||
}
|
||||
}
|
||||
|
||||
_activeDimensions[dimension.DimensionId] = dimension;
|
||||
Debug.Log($"Dimension '{dimension.DimensionId}' registered with EntityManage.");
|
||||
|
||||
// 为新注册的维度初始化其数据结构
|
||||
// 这些检查是必要的,以防止在重复注册时清空现有数据。
|
||||
if (!_dimensionFactionEntities.ContainsKey(dimension.DimensionId))
|
||||
{
|
||||
_dimensionFactionEntities[dimension.DimensionId] = new Dictionary<string, LinkedList<EntityPrefab>>();
|
||||
}
|
||||
|
||||
if (!_dimensionLayerCache.ContainsKey(dimension.DimensionId))
|
||||
{
|
||||
_dimensionLayerCache[dimension.DimensionId] = new Dictionary<string, Transform>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从实体管理器注销一个维度。
|
||||
/// </summary>
|
||||
/// <param name="dimension">要注销的维度实例。</param>
|
||||
public void UnregisterDimension(Dimension dimension)
|
||||
{
|
||||
if (dimension == null || string.IsNullOrEmpty(dimension.DimensionId))
|
||||
{
|
||||
Debug.LogError("Attempted to unregister a null or invalid Dimension.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_activeDimensions.Remove(dimension.DimensionId))
|
||||
{
|
||||
// 当维度被注销时,清理其所有相关的实体和层级缓存
|
||||
_dimensionFactionEntities.Remove(dimension.DimensionId);
|
||||
_dimensionLayerCache.Remove(dimension.DimensionId);
|
||||
|
||||
// 【逻辑修改】立即清理_pendingAdditions中属于该维度的实体
|
||||
// 创建一个新列表来存储不属于该维度的实体
|
||||
var remainingPendingAdditions = new List<Tuple<string, string, EntityPrefab>>();
|
||||
foreach (var pending in _pendingAdditions)
|
||||
{
|
||||
if (pending.Item1 == dimension.DimensionId)
|
||||
{
|
||||
// 销毁实体GameObject
|
||||
if (pending.Item3 != null && pending.Item3.gameObject != null) // 增加gameObject的null检查
|
||||
{
|
||||
Destroy(pending.Item3.gameObject);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
remainingPendingAdditions.Add(pending);
|
||||
}
|
||||
}
|
||||
_pendingAdditions = remainingPendingAdditions; // 更新_pendingAdditions列表
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取一个活跃的维度实例。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">维度的唯一标识符。</param>
|
||||
/// <returns>对应的 Dimension 实例,如果不存在则为 null。</returns>
|
||||
public Dimension GetDimension(string dimensionId)
|
||||
{
|
||||
_activeDimensions.TryGetValue(dimensionId, out var dimension);
|
||||
return dimension;
|
||||
}
|
||||
|
||||
// --- 查找实体 (现在维度感知) ---
|
||||
/// <summary>
|
||||
/// 在指定维度中,根据派系键查找所有实体。
|
||||
/// </summary>
|
||||
@ -138,6 +56,13 @@ namespace Managers
|
||||
/// <returns>指定派系下的实体列表,如果未找到则返回空列表。</returns>
|
||||
public LinkedList<EntityPrefab> FindEntitiesByFaction(string dimensionId, string factionKey)
|
||||
{
|
||||
// 如果在场景加载前或维度未在Program中注册,_dimensionFactionEntities可能还没有该维度ID的条目。
|
||||
if (!_dimensionFactionEntities.ContainsKey(dimensionId))
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:尝试在维度 '{dimensionId}' 中查找实体,但其内部存储未初始化。");
|
||||
return new LinkedList<EntityPrefab>();
|
||||
}
|
||||
|
||||
if (_dimensionFactionEntities.TryGetValue(dimensionId, out var factionDict))
|
||||
{
|
||||
if (factionDict.TryGetValue(factionKey, out var entities))
|
||||
@ -149,34 +74,46 @@ namespace Managers
|
||||
return new LinkedList<EntityPrefab>(); // 如果未找到,返回一个空列表
|
||||
}
|
||||
|
||||
// --- Tick 方法 (现在维度感知) ---
|
||||
/// <summary>
|
||||
/// 每帧更新(Tick)方法:处理实体逻辑,清理死亡实体,并将待添加的实体加入管理。
|
||||
/// </summary>
|
||||
public void Tick()
|
||||
{
|
||||
// 遍历每个活跃的维度
|
||||
foreach (var dimensionEntry in _dimensionFactionEntities.ToList()) // ToList() 避免在迭代时修改字典
|
||||
// 获取当前管理器中所有活跃维度ID的副本,以安全地迭代和移除。
|
||||
List<string> activeDimensionIdsInManager = _dimensionFactionEntities.Keys.ToList();
|
||||
|
||||
foreach (var dimensionId in activeDimensionIdsInManager)
|
||||
{
|
||||
var dimensionId = dimensionEntry.Key;
|
||||
var factionDict = dimensionEntry.Value;
|
||||
|
||||
// 检查维度对象本身是否仍然活跃在场景中
|
||||
// 注意:这里检查的是 _activeDimensions,确保 Dimension 对象实例仍然存在且被管理器追踪。
|
||||
if (!_activeDimensions.ContainsKey(dimensionId))
|
||||
// 从 Program 中获取维度对象,验证其是否仍然活跃。
|
||||
var dimension = Program.Instance.GetDimension(dimensionId);
|
||||
if (dimension == null)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"Skipping Tick for dimension '{dimensionId}' as its Dimension object is no longer active. Clearing its entities.");
|
||||
_dimensionFactionEntities.Remove(dimensionId); // 移除已失效维度的实体数据
|
||||
_dimensionLayerCache.Remove(dimensionId); // 移除已失效维度的层级缓存
|
||||
continue;
|
||||
Debug.LogWarning($"实体管理器:跳过维度 '{dimensionId}' 的Tick处理,因为其维度对象在程序(Program)中已不再活跃(或从未注册)。正在清理相关内部数据。");
|
||||
// 如果 Program 不再有这个维度,说明它已经失效或被注销,EntityManage 需要清理自己的相关数据。
|
||||
_dimensionFactionEntities.Remove(dimensionId);
|
||||
_dimensionLayerCache.Remove(dimensionId);
|
||||
// 清理属于该维度的待添加实体。
|
||||
RemovePendingAdditionsForDimension(dimensionId);
|
||||
continue; // 跳过处理这个不存在的维度
|
||||
}
|
||||
|
||||
// 现在真正处理的是 _dimensionFactionEntities 中该维度ID对应的字典。
|
||||
var factionDict = _dimensionFactionEntities[dimensionId];
|
||||
|
||||
foreach (var faction in factionDict)
|
||||
// 迭代 factionDict 的副本,以允许在迭代中修改 factionDict.Keys (即移除派系)。
|
||||
// ToList() 创建 KeyValuePair 的副本。
|
||||
foreach (var factionEntry in factionDict.ToList())
|
||||
{
|
||||
var factionKey = factionEntry.Key;
|
||||
var entities = factionEntry.Value;
|
||||
|
||||
var entitiesToRemove = new List<EntityPrefab>();
|
||||
var currentEntities = faction.Value.ToList(); // 创建副本以避免在迭代时修改列表
|
||||
// 创建副本以避免在迭代时修改列表。
|
||||
var currentEntities = entities.ToList();
|
||||
|
||||
foreach (var entityPrefab in currentEntities)
|
||||
{
|
||||
// 检查实体预制体或其内部实体是否已销毁或死亡
|
||||
// 检查实体预制体或其内部实体是否已销毁或死亡。
|
||||
if (entityPrefab == null || entityPrefab.entity == null || entityPrefab.entity.IsDead)
|
||||
{
|
||||
entitiesToRemove.Add(entityPrefab);
|
||||
@ -188,34 +125,59 @@ namespace Managers
|
||||
}
|
||||
}
|
||||
|
||||
// 删除所有标记为死亡的实体
|
||||
// 删除所有标记为死亡的实体。
|
||||
foreach (var entityToRemove in entitiesToRemove)
|
||||
{
|
||||
faction.Value.Remove(entityToRemove);
|
||||
if (entityToRemove != null) // 确保它没有被外部销毁
|
||||
entities.Remove(entityToRemove);
|
||||
if (entityToRemove != null)
|
||||
{
|
||||
Destroy(entityToRemove.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果一个派系下没有任何实体了,清理掉这个派系条目。
|
||||
if (entities.Count == 0)
|
||||
{
|
||||
factionDict.Remove(factionKey);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果某个维度在清理后已经没有了任何派系,也一并清理掉这个维度的层级缓存和实体数据。
|
||||
if (factionDict.Count == 0)
|
||||
{
|
||||
_dimensionFactionEntities.Remove(dimensionId);
|
||||
_dimensionLayerCache.Remove(dimensionId); // 对应的层级缓存也可以清理。
|
||||
}
|
||||
}
|
||||
|
||||
// 处理待添加实体 (现在维度感知)
|
||||
|
||||
// 处理待添加实体 (现在维度感知)。
|
||||
if (_pendingAdditions.Any())
|
||||
{
|
||||
foreach (var pending in _pendingAdditions)
|
||||
// 使用 ToList() 创建副本进行迭代,允许在循环内部修改或清空 _pendingAdditions。
|
||||
foreach (var pending in _pendingAdditions.ToList())
|
||||
{
|
||||
var dimensionId = pending.Item1;
|
||||
var factionKey = pending.Item2;
|
||||
var entityPrefab = pending.Item3;
|
||||
|
||||
// 再次检查维度是否活跃,防止在添加前维度被注销
|
||||
// 再次检查维度是否活跃(在 Program 中注册),防止在添加前维度被 Program 注销。
|
||||
if (Program.Instance.GetDimension(dimensionId) == null)
|
||||
{
|
||||
Debug.LogError($"实体管理器:尝试将实体 '{entityPrefab?.name ?? "null"}' 添加到在程序(Program)中未注册或不活跃的维度 '{dimensionId}'。该实体将被销毁。");
|
||||
if (entityPrefab != null && entityPrefab.gameObject != null) Destroy(entityPrefab.gameObject);
|
||||
_pendingAdditions.Remove(pending); // 立即从待添加列表中移除已处理的条目。
|
||||
continue;
|
||||
}
|
||||
|
||||
// 确保 _dimensionFactionEntities 包含该 dimensionId,否则初始化(以防_OnSceneLoaded时Program中没有,但之后又注册了)。
|
||||
if (!_dimensionFactionEntities.ContainsKey(dimensionId))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Attempted to add entity '{entityPrefab.name}' to unregistered or inactive dimension '{dimensionId}'. Entity will be destroyed.");
|
||||
if (entityPrefab != null) Destroy(entityPrefab.gameObject);
|
||||
continue;
|
||||
// 这情况意味着 Program 中有该维度,但在 EntityManage 内部数据结构中没有。
|
||||
// 这可能发生在 Program 动态注册了一个维度,但 EntityManage 尚未在 Tick 中检测到并初始化。
|
||||
// 或者 OnSceneLoaded 时 Program 中没有这个维度,但在游戏运行中被注册了。
|
||||
_dimensionFactionEntities[dimensionId] = new Dictionary<string, LinkedList<EntityPrefab>>();
|
||||
_dimensionLayerCache[dimensionId] = new Dictionary<string, Transform>();
|
||||
Debug.LogWarning($"实体管理器:在处理待添加实体时,按需为维度 '{dimensionId}' 初始化内部数据结构。");
|
||||
}
|
||||
|
||||
var factionDict = _dimensionFactionEntities[dimensionId];
|
||||
@ -225,67 +187,93 @@ namespace Managers
|
||||
}
|
||||
|
||||
factionDict[factionKey].AddLast(entityPrefab);
|
||||
_pendingAdditions.Remove(pending); // 成功添加后从待添加列表中移除。
|
||||
}
|
||||
|
||||
_pendingAdditions.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 辅助方法:清理属于特定维度的所有待添加实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">要清理的维度ID。</param>
|
||||
private void RemovePendingAdditionsForDimension(string dimensionId)
|
||||
{
|
||||
// 创建一个新的列表来存储不属于指定维度的待添加实体。
|
||||
var remainingPendingAdditions = new List<Tuple<string, string, EntityPrefab>>();
|
||||
foreach (var pending in _pendingAdditions)
|
||||
{
|
||||
if (pending.Item1 == dimensionId)
|
||||
{
|
||||
// 如果实体属于要清理的维度,则销毁其GameObject。
|
||||
if (pending.Item3 != null && pending.Item3.gameObject != null)
|
||||
{
|
||||
Destroy(pending.Item3.gameObject);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
remainingPendingAdditions.Add(pending);
|
||||
}
|
||||
}
|
||||
_pendingAdditions = remainingPendingAdditions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据给定的Def生成实体对象(内部通用方法)。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="prefab">要实例化的预制体</param>
|
||||
/// <param name="pos">生成位置</param>
|
||||
/// <param name="def">实体定义对象</param>
|
||||
/// <param name="extraInit">额外的初始化操作(如子弹方向设置)</param>
|
||||
/// <returns>成功时返回EntityPrefab组件,失败时返回null</returns>
|
||||
/// <param name="prefab">要实例化的预制体。</param>
|
||||
/// <param name="pos">生成位置。</param>
|
||||
/// <param name="def">实体定义对象。</param>
|
||||
/// <param name="extraInit">额外的初始化操作(如子弹方向设置)。</param>
|
||||
/// <returns>成功时返回EntityPrefab组件,失败时返回null。</returns>
|
||||
private EntityPrefab GenerateEntityInternal(
|
||||
string dimensionId, // 新增参数:维度ID
|
||||
string dimensionId,
|
||||
GameObject prefab,
|
||||
Vector3 pos,
|
||||
Data.EntityDef def,
|
||||
Action<EntityPrefab> extraInit = null)
|
||||
{
|
||||
// 验证维度是否活跃
|
||||
if (!_activeDimensions.TryGetValue(dimensionId, out var dimension))
|
||||
// 验证维度是否活跃 (通过 Program)。
|
||||
var dimension = Program.Instance.GetDimension(dimensionId);
|
||||
if (dimension == null)
|
||||
{
|
||||
Debug.LogError($"Cannot generate entity: Dimension '{dimensionId}' is not active or registered.");
|
||||
Debug.LogError($"实体管理器:无法生成实体:维度 '{dimensionId}' 在程序(Program)中不活跃或未注册。");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取或创建实体所属的层级Transform,并确保其在维度根下
|
||||
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel"); // 使用一个默认层级名称
|
||||
// 获取或创建实体所属的层级Transform,并确保其在维度根下。
|
||||
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
|
||||
if (parentLayer == null)
|
||||
{
|
||||
Debug.LogError($"Failed to get or create parent layer for entity in dimension '{dimensionId}'.");
|
||||
Debug.LogError($"实体管理器:无法在维度 '{dimensionId}' 中获取或创建实体的父层。");
|
||||
return null;
|
||||
}
|
||||
|
||||
GameObject instantiatedEntity = null;
|
||||
try
|
||||
{
|
||||
// 实例化实体,并将其父级设置为维度下的层级Transform
|
||||
// 实例化实体,并将其父级设置为维度下的层级Transform。
|
||||
instantiatedEntity = Instantiate(prefab, pos, Quaternion.identity, parentLayer);
|
||||
|
||||
var entityComponent = instantiatedEntity.GetComponent<EntityPrefab>();
|
||||
if (!entityComponent)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"EntityPrefab component missing on: {instantiatedEntity.name}");
|
||||
$"在 '{instantiatedEntity.name}' 上缺少 EntityPrefab 组件,无法完成实体初始化。");
|
||||
}
|
||||
|
||||
entityComponent.Init(def);
|
||||
extraInit?.Invoke(entityComponent);
|
||||
|
||||
var factionKey = def.attributes.defName ?? "default"; // 假设 attributes.defName 是派系键
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent)); // 添加维度ID
|
||||
var factionKey = def.attributes.defName ?? "default";
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
|
||||
return entityComponent;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
if (instantiatedEntity) Destroy(instantiatedEntity);
|
||||
Debug.LogError($"Entity generation failed in dimension '{dimensionId}': {ex.Message}\n{ex.StackTrace}");
|
||||
Debug.LogError($"实体管理器:在维度 '{dimensionId}' 中生成实体失败:{ex.Message}\n{ex.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -299,12 +287,22 @@ namespace Managers
|
||||
/// <returns>层级Transform,如果维度不存在或其根Transform为空则返回null。</returns>
|
||||
private Transform EnsureLayerExists(string dimensionId, string layerName)
|
||||
{
|
||||
// 尝试从维度层级缓存中获取
|
||||
// 尝试从维度层级缓存中获取。如果没有该维度的缓存,先尝试初始化。
|
||||
if (!_dimensionLayerCache.TryGetValue(dimensionId, out var layerCacheForDimension))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Dimension '{dimensionId}' not found in layer cache. This should not happen if dimension is registered.");
|
||||
return null;
|
||||
// 如果 Program 中有这个维度,但在 EntityManage 内部没有初始化,则尝试进行初始化。
|
||||
// 这种情况可能发生在 OnSceneLoaded 之后,Program 动态注册了新的维度。
|
||||
if (Program.Instance.GetDimension(dimensionId) != null)
|
||||
{
|
||||
_dimensionLayerCache[dimensionId] = new Dictionary<string, Transform>();
|
||||
layerCacheForDimension = _dimensionLayerCache[dimensionId]; // 更新引用。
|
||||
Debug.LogWarning($"实体管理器:按需为维度 '{dimensionId}' 初始化层级缓存。");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"实体管理器:在层级缓存中未找到维度 '{dimensionId}',且在程序(Program)中也未注册。无法创建层级 '{layerName}'。");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (layerCacheForDimension.TryGetValue(layerName, out var layerTransform))
|
||||
@ -312,12 +310,11 @@ namespace Managers
|
||||
return layerTransform;
|
||||
}
|
||||
|
||||
// 如果缓存中没有,尝试在维度根下查找
|
||||
var dimension = GetDimension(dimensionId);
|
||||
// 如果缓存中没有,尝试在维度根下查找。
|
||||
var dimension = Program.Instance.GetDimension(dimensionId);
|
||||
if (dimension == null || dimension.DimensionRoot == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Dimension '{dimensionId}' or its root transform is null. Cannot create layer '{layerName}'.");
|
||||
Debug.LogError($"实体管理器:维度 '{dimensionId}' (来自 Program) 或其根 Transform 为空。无法创建层级 '{layerName}'。");
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -325,33 +322,36 @@ namespace Managers
|
||||
|
||||
if (!layerTransform)
|
||||
{
|
||||
// 如果层不存在,动态创建并将其父级设置为维度的根Transform
|
||||
// 如果层不存在,动态创建并将其父级设置为维度的根Transform。
|
||||
var layerObject = new GameObject(layerName);
|
||||
layerTransform = layerObject.transform;
|
||||
layerTransform.SetParent(dimension.DimensionRoot);
|
||||
// 移除了此处的 Debug.Log,因为它属于非必要的普通日志。
|
||||
}
|
||||
|
||||
// 将新创建的层加入缓存
|
||||
// 将新创建的层加入缓存。
|
||||
layerCacheForDimension[layerName] = layerTransform;
|
||||
return layerTransform;
|
||||
}
|
||||
|
||||
// --- 公共生成方法 (现在维度感知) ---
|
||||
/// <summary>
|
||||
/// 在指定维度中,根据PawnDef生成普通实体。
|
||||
/// 在指定维度和位置生成一个通用实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="entityDef">实体定义对象。</param>
|
||||
/// <param name="pos">生成位置。</param>
|
||||
public void GenerateEntity(string dimensionId, Data.EntityDef entityDef, Vector3 pos)
|
||||
{
|
||||
if (!characterPrefab)
|
||||
{
|
||||
Debug.LogError("characterPrefab is null! Assign a valid prefab.");
|
||||
Debug.LogError("实体管理器:characterPrefab 为空!请分配一个有效的预制体。");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (entityDef == null)
|
||||
{
|
||||
Debug.LogError("EntityDef is null! Cannot generate entity.");
|
||||
Debug.LogError("实体管理器:EntityDef 为空!无法生成实体。");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
@ -362,24 +362,27 @@ namespace Managers
|
||||
pos,
|
||||
entityDef
|
||||
);
|
||||
if (!result) GenerateDefaultEntity(dimensionId, pos);
|
||||
if (result == null) GenerateDefaultEntity(dimensionId, pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,生成建筑实体(位置使用Vector3Int)。
|
||||
/// 在指定维度和网格位置生成一个建筑实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="buildingDef">建筑定义对象。</param>
|
||||
/// <param name="pos">网格位置。</param>
|
||||
public void GenerateBuildingEntity(string dimensionId, Data.BuildingDef buildingDef, Vector3Int pos)
|
||||
{
|
||||
if (!buildingPrefab)
|
||||
{
|
||||
Debug.LogError("buildingPrefab is null! Assign a valid prefab.");
|
||||
Debug.LogError("实体管理器:buildingPrefab 为空!请分配一个有效的预制体。");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (buildingDef == null)
|
||||
{
|
||||
Debug.LogError("BuildingDef is null! Cannot generate building.");
|
||||
Debug.LogError("实体管理器:BuildingDef 为空!无法生成建筑。");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
@ -391,25 +394,30 @@ namespace Managers
|
||||
worldPos,
|
||||
buildingDef
|
||||
);
|
||||
if (!result) GenerateDefaultEntity(dimensionId, worldPos);
|
||||
if (result == null) GenerateDefaultEntity(dimensionId, worldPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,生成子弹实体(含方向设置)。
|
||||
/// 在指定维度、位置和方向上生成一个子弹实体。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="bulletDef">子弹定义对象。</param>
|
||||
/// <param name="pos">生成位置。</param>
|
||||
/// <param name="dir">子弹的初始方向。</param>
|
||||
/// <param name="source">发射子弹的源实体。</param>
|
||||
public void GenerateBulletEntity(string dimensionId, Data.BulletDef bulletDef, Vector3 pos, Vector3 dir,
|
||||
Entity.Entity source = null)
|
||||
{
|
||||
if (!bulletPrefab)
|
||||
{
|
||||
Debug.LogError("bulletPrefab is null! Assign a valid prefab.");
|
||||
Debug.LogError("实体管理器:bulletPrefab 为空!请分配一个有效的预制体。");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bulletDef == null)
|
||||
{
|
||||
Debug.LogError("BulletDef is null! Cannot generate bullet.");
|
||||
Debug.LogError("实体管理器:BulletDef 为空!无法生成子弹。");
|
||||
GenerateDefaultEntity(dimensionId, pos);
|
||||
return;
|
||||
}
|
||||
@ -419,53 +427,80 @@ namespace Managers
|
||||
bulletPrefab.gameObject,
|
||||
pos,
|
||||
bulletDef,
|
||||
// 子弹特有的方向设置
|
||||
entityComponent => entityComponent.entity.SetTarget(pos + dir)
|
||||
entityComponent =>
|
||||
{
|
||||
if (entityComponent != null && entityComponent.entity != null)
|
||||
{
|
||||
entityComponent.entity.SetTarget(pos + dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:在维度 '{dimensionId}' 的额外初始化期间,子弹实体组件或其内部实体为空。");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 确保 result 不为 null 且 entity 是 Bullet 类型
|
||||
if (result != null && result.entity is Bullet bullet)
|
||||
{
|
||||
bullet.bulletSource = source;
|
||||
if (source != null) bullet.affiliation = source.affiliation; // 确保 source 不为 null
|
||||
if (source != null) bullet.affiliation = source.affiliation;
|
||||
}
|
||||
|
||||
if (!result) GenerateDefaultEntity(dimensionId, pos);
|
||||
if (result == null) GenerateDefaultEntity(dimensionId, pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定维度中,生成默认实体(错误回退)。
|
||||
/// 在指定维度和位置生成一个默认实体(通常作为回退选项)。
|
||||
/// </summary>
|
||||
/// <param name="dimensionId">实体所属的维度ID。</param>
|
||||
/// <param name="pos">生成位置。</param>
|
||||
public void GenerateDefaultEntity(string dimensionId, Vector3 pos)
|
||||
{
|
||||
if (!_activeDimensions.ContainsKey(dimensionId))
|
||||
if (Program.Instance.GetDimension(dimensionId) == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Cannot generate default entity: Dimension '{dimensionId}' is not active or registered.");
|
||||
Debug.LogError($"实体管理器:无法生成默认实体:维度 '{dimensionId}' 在程序(Program)中不活跃或未注册。");
|
||||
return;
|
||||
}
|
||||
|
||||
var parentLayer = EnsureLayerExists(dimensionId, "DefaultEntityLevel");
|
||||
if (parentLayer == null)
|
||||
{
|
||||
Debug.LogError($"Failed to get parent transform for default entity in dimension '{dimensionId}'.");
|
||||
Debug.LogError($"实体管理器:无法在维度 '{dimensionId}' 中获取默认实体的父 Transform。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (defaultEntityPrefab == null || defaultEntityPrefab.gameObject == null)
|
||||
{
|
||||
Debug.LogError($"实体管理器:defaultEntityPrefab 为空或没有GameObject。无法为维度 '{dimensionId}' 生成默认实体。");
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = Instantiate(defaultEntityPrefab, pos, Quaternion.identity, parentLayer);
|
||||
var entityComponent = entity.GetComponent<EntityPrefab>();
|
||||
|
||||
if (entityComponent == null)
|
||||
{
|
||||
Debug.LogError($"实体管理器:默认实体预制体 '{defaultEntityPrefab.name}' 缺少 EntityPrefab 组件。正在销毁已实例化的对象。");
|
||||
Destroy(entity.gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
const string factionKey = "default";
|
||||
_pendingAdditions.Add(Tuple.Create(dimensionId, factionKey, entityComponent));
|
||||
entityComponent.DefaultInit();
|
||||
}
|
||||
|
||||
// --- 单例生命周期与场景切换处理 ---
|
||||
/// <summary>
|
||||
/// 单例管理器启动时调用,订阅场景加载事件。
|
||||
/// </summary>
|
||||
protected override void OnStart()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单例管理器销毁时调用,取消订阅场景加载事件。
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
@ -473,17 +508,47 @@ namespace Managers
|
||||
|
||||
/// <summary>
|
||||
/// 场景加载完成时的回调。
|
||||
/// 清理旧场景的实体数据,并重新扫描新场景中的维度。
|
||||
/// 清理旧场景的实体数据,并基于 Program 中已注册的维度重新初始化内部结构。
|
||||
/// </summary>
|
||||
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
_dimensionFactionEntities.Clear(); // 清理所有维度下的实体数据
|
||||
_dimensionLayerCache.Clear(); // 清理所有维度下的层级缓存
|
||||
_pendingAdditions.Clear(); // 清理待添加实体列表
|
||||
_activeDimensions.Clear(); // 清理活跃维度列表,因为旧场景的维度对象已被销毁
|
||||
}
|
||||
|
||||
_dimensionFactionEntities.Clear(); // 清理所有旧场景的实体数据。
|
||||
_dimensionLayerCache.Clear(); // 清理所有旧场景的层级缓存。
|
||||
|
||||
// 场景加载时清理待添加列表中的所有实体,因为它们可能属于旧场景或未在任何维度中被处理。
|
||||
foreach (var pending in _pendingAdditions)
|
||||
{
|
||||
if (pending.Item3 != null && pending.Item3.gameObject != null)
|
||||
{
|
||||
Destroy(pending.Item3.gameObject);
|
||||
}
|
||||
}
|
||||
_pendingAdditions.Clear(); // 清理待添加实体列表。
|
||||
|
||||
// 在新场景加载完成后,遍历 Program 中已注册的所有维度,并为每个维度初始化 EntityManage 的内部数据结构。
|
||||
// 此时 Program.Awake() 应该已经完成,所有 Dimension 组件的 Awake() 也已执行,Program.RegisteredDimensions 应该包含了当前场景的所有维度。
|
||||
foreach (var registeredDimensionEntry in Program.Instance.RegisteredDimensions)
|
||||
{
|
||||
string dimensionId = registeredDimensionEntry.Key;
|
||||
Dimension dimension = registeredDimensionEntry.Value;
|
||||
|
||||
if (dimension != null) // 确保维度实例本身不是null。
|
||||
{
|
||||
// 为该维度ID初始化实体存储结构。
|
||||
_dimensionFactionEntities[dimensionId] = new Dictionary<string, LinkedList<EntityPrefab>>();
|
||||
// 为该维度ID初始化层级缓存。
|
||||
_dimensionLayerCache[dimensionId] = new Dictionary<string, Transform>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"实体管理器:在场景加载初始化期间,Program 为ID '{dimensionId}' 注册了一个空的 Dimension 对象。这可能表明维度注册存在问题。");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MonoBehaviour 的 Start 方法,用于初始化默认实体预制体。
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
if (defaultEntityPrefab == null)
|
||||
@ -492,14 +557,17 @@ namespace Managers
|
||||
if (pre != null)
|
||||
{
|
||||
defaultEntityPrefab = pre.GetComponent<EntityPrefab>();
|
||||
if (defaultEntityPrefab == null)
|
||||
{
|
||||
Debug.LogError($"实体管理器:已加载 DefaultEntity 预制体,但它缺少来自 Resources/Default/DefaultEntity 的 EntityPrefab 组件。请确保它包含该组件。");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError(
|
||||
"Failed to load DefaultEntity prefab from Resources/Default/DefaultEntity. Please ensure it exists at 'Assets/Resources/Default/DefaultEntity.prefab'.");
|
||||
"实体管理器:无法从 Resources/Default/DefaultEntity 加载 DefaultEntity 预制体。请确保它存在于 'Assets/Resources/Default/DefaultEntity.prefab'。");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
9
Client/Assets/Scripts/Managers/ILaunchManager.cs
Normal file
9
Client/Assets/Scripts/Managers/ILaunchManager.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Managers
|
||||
{
|
||||
public interface ILaunchManager
|
||||
{
|
||||
string StepDescription { get; } // 获取当前加载步骤的描述
|
||||
void Init(); // 初始化管理器
|
||||
void Clear(); // 清理管理器,用于重载
|
||||
}
|
||||
}
|
3
Client/Assets/Scripts/Managers/ILaunchManager.cs.meta
Normal file
3
Client/Assets/Scripts/Managers/ILaunchManager.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8ff16ea8a2b4418b313286456905b99
|
||||
timeCreated: 1756125228
|
@ -6,11 +6,13 @@ using UnityEngine;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
public class ItemResourceManager : Utils.Singleton<ItemResourceManager>
|
||||
public class ItemResourceManager : Utils.Singleton<ItemResourceManager>,ILaunchManager
|
||||
{
|
||||
private readonly Dictionary<string, Item.ItemResource> _items = new();
|
||||
private readonly Dictionary<string, List<Item.ItemResource>> _itemsByName = new(); // 保持按显示名称查找的字典
|
||||
|
||||
public string StepDescription => "加载物品定义中";
|
||||
|
||||
public void Init()
|
||||
{
|
||||
var baseItemDefs = Managers.DefineManager.Instance.QueryDefinesByType<ItemDef>();
|
||||
@ -89,8 +91,6 @@ namespace Managers
|
||||
|
||||
_itemsByName[itemResource.Name].Add(itemResource);
|
||||
}
|
||||
|
||||
Debug.Log($"ItemResourceManager: Initialized {_items.Count} items.");
|
||||
}
|
||||
|
||||
public Item.ItemResource GetItem(string defName)
|
||||
@ -115,7 +115,6 @@ namespace Managers
|
||||
{
|
||||
_items.Clear();
|
||||
_itemsByName.Clear();
|
||||
Debug.Log("ItemResourceManager: All item resources cleared.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,54 +3,106 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Data;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
public class PackagesImageManager : Utils.Singleton<PackagesImageManager>
|
||||
/// <summary>
|
||||
/// 包图像管理器,负责加载、管理和提供从定义数据中解析出的纹理和精灵。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 该管理器是一个单例,并在启动过程中实现 ILaunchManager 接口,
|
||||
/// 用于处理游戏或应用启动时图像资源的加载和初始化。
|
||||
/// </remarks>
|
||||
public class PackagesImageManager : Utils.Singleton<PackagesImageManager>, ILaunchManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认精灵,在找不到对应图像时返回。
|
||||
/// </summary>
|
||||
public Sprite defaultSprite;
|
||||
//包名,图片名
|
||||
|
||||
/// <summary>
|
||||
/// 存储所有已加载的纹理,按包ID和图像名称索引。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 外层字典键是包ID,内层字典键是图像名称。
|
||||
/// </remarks>
|
||||
public Dictionary<string, Dictionary<string, Texture2D>> packagesImages = new();
|
||||
//包名,图片名
|
||||
|
||||
/// <summary>
|
||||
/// 存储所有已创建的精灵,按包ID和精灵名称索引。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 外层字典键是包ID,内层字典键是精灵名称(如果纹理被分割,会包含索引后缀)。
|
||||
/// </remarks>
|
||||
public Dictionary<string, Dictionary<string, Sprite>> sprites = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前启动步骤的描述。
|
||||
/// </summary>
|
||||
public string StepDescription { get; private set; } = "包图像管理器正在准备中...";
|
||||
|
||||
/// <summary>
|
||||
/// 初始化图像管理器,加载默认精灵并处理所有 ImageDef 定义。
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
if (packagesImages.Count > 0)
|
||||
return;
|
||||
{
|
||||
// 如果已经加载过,直接返回。
|
||||
StepDescription = "包图像管理器已初始化。";
|
||||
return;
|
||||
}
|
||||
|
||||
StepDescription = "正在加载默认精灵..."; // 更新加载步骤描述
|
||||
defaultSprite = Resources.Load<Sprite>("Default/DefaultImage");
|
||||
if (defaultSprite == null)
|
||||
{
|
||||
Debug.LogWarning("无法加载默认精灵 'Resources/Default/DefaultImage'。请确保文件存在。");
|
||||
}
|
||||
|
||||
StepDescription = "正在处理图像定义并创建精灵..."; // 更新加载步骤描述
|
||||
InitImageDef();
|
||||
StepDescription = "包图像管理器初始化完成。"; // 完成加载
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ImageDef 定义初始化并加载所有纹理和精灵。
|
||||
/// </summary>
|
||||
public void InitImageDef()
|
||||
{
|
||||
var textureCache = new Dictionary<string, Texture2D>();
|
||||
var imageDef = Managers.DefineManager.Instance.QueryDefinesByType<ImageDef>();
|
||||
|
||||
if (imageDef == null || !imageDef.Any())
|
||||
{
|
||||
Debug.Log($"在 DefineManager 中未找到任何 ImageDef 定义。({typeof(ImageDef).Name})");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var ima in imageDef)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ima.path) || string.IsNullOrEmpty(ima.packID))
|
||||
{
|
||||
Debug.LogWarning($"跳过图像定义 '{ima?.name ?? "未知"}',因为它包含空路径或包ID。(路径: '{ima?.path ?? ""}', 包ID: '{ima?.packID ?? ""}')");
|
||||
continue;
|
||||
|
||||
// 解析路径前缀
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string cacheKey;
|
||||
Texture2D texture;
|
||||
if (ima.path.StartsWith("res:"))
|
||||
Texture2D texture = null;
|
||||
|
||||
if (ima.path.StartsWith("res:"))
|
||||
{
|
||||
// 处理Resources路径
|
||||
// 处理 Resources 路径
|
||||
var resPath = ima.path.Substring(4).Replace('\\', '/').TrimStart('/');
|
||||
|
||||
cacheKey = "res://" + resPath.ToLower();
|
||||
cacheKey = "res://" + resPath.ToLower(); // 缓存键使用小写路径
|
||||
|
||||
// 检查缓存
|
||||
// 检查纹理缓存
|
||||
if (!textureCache.TryGetValue(cacheKey, out texture))
|
||||
{
|
||||
// 去掉扩展名
|
||||
var cleanPath = Path.ChangeExtension(resPath, null);
|
||||
var cleanPath = Path.ChangeExtension(resPath, null); // 去掉扩展名
|
||||
texture = Resources.Load<Texture2D>(cleanPath);
|
||||
if (texture)
|
||||
textureCache[cacheKey] = texture;
|
||||
@ -66,12 +118,15 @@ namespace Managers
|
||||
// 获取包根路径
|
||||
var packageRoot = Managers.DefineManager.Instance.GetPackagePath(packageID);
|
||||
if (string.IsNullOrEmpty(packageRoot))
|
||||
{
|
||||
Debug.LogWarning($"图像定义 '{ima.name}' (包ID: {ima.packID}): 引用的包ID '{packageID}' 未找到或没有根路径。跳过图像加载。");
|
||||
continue;
|
||||
}
|
||||
|
||||
var fullPath = Path.Combine(packageRoot, relativePath).Replace('\\', '/');
|
||||
cacheKey = "file://" + fullPath.ToLower();
|
||||
cacheKey = "file://" + fullPath.ToLower(); // 缓存键使用小写路径
|
||||
|
||||
// 检查缓存
|
||||
// 检查纹理缓存
|
||||
if (!textureCache.TryGetValue(cacheKey, out texture))
|
||||
{
|
||||
texture = Configs.ConfigProcessor.LoadTextureByIO(fullPath);
|
||||
@ -83,10 +138,15 @@ namespace Managers
|
||||
{
|
||||
// 无前缀:使用当前定义所在包的路径
|
||||
var pack = Managers.DefineManager.Instance.GetDefinePackage(ima);
|
||||
if (pack == null)
|
||||
{
|
||||
Debug.LogError($"图像定义 '{ima.name}' (包ID: {ima.packID}): 源图像未找到对应的定义包。无法确定 '{ima.path}' 的完整路径。跳过。");
|
||||
continue;
|
||||
}
|
||||
var fullPath = Path.Combine(pack.packRootPath, ima.path).Replace('\\', '/');
|
||||
cacheKey = "file://" + fullPath.ToLower();
|
||||
cacheKey = "file://" + fullPath.ToLower(); // 缓存键使用小写路径
|
||||
|
||||
// 检查缓存
|
||||
// 检查纹理缓存
|
||||
if (!textureCache.TryGetValue(cacheKey, out texture))
|
||||
{
|
||||
texture = Configs.ConfigProcessor.LoadTextureByIO(fullPath);
|
||||
@ -98,20 +158,22 @@ namespace Managers
|
||||
// 资源加载失败
|
||||
if (!texture)
|
||||
{
|
||||
Debug.LogError($"Failed to load texture: {ima.path} (PackID: {ima.packID}, Name: {ima.name})");
|
||||
Debug.LogError($"未能加载图像定义所在的纹理: '{ima.name}' (路径: '{ima.path}', 包ID: '{ima.packID}')。请验证路径和文件是否存在。");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 存储到包资源字典(使用原始packID,非路径中的包ID)
|
||||
// 存储到包纹理字典(使用定义自身的 packID)
|
||||
var packId = ima.packID;
|
||||
if (!packagesImages.ContainsKey(packId))
|
||||
packagesImages[packId] = new Dictionary<string, Texture2D>();
|
||||
|
||||
// 避免同一包内重复添加(查重)
|
||||
if (!packagesImages[packId].ContainsKey(ima.name))
|
||||
packagesImages[packId].Add(ima.name, texture);
|
||||
else
|
||||
packagesImages[packId][ima.name] = texture; // 覆盖已存在的引用
|
||||
// 警告:如果图片名重复,则覆盖
|
||||
if (packagesImages[packId].ContainsKey(ima.name))
|
||||
{
|
||||
Debug.LogWarning($"包 '{packId}' 中名为 '{ima.name}' 的图像被多次定义。将覆盖之前的纹理引用。这可能表示配置错误。");
|
||||
}
|
||||
packagesImages[packId][ima.name] = texture; // 覆盖或添加
|
||||
|
||||
|
||||
// 切分精灵
|
||||
SplitTextureIntoSprites(packId, ima.name, texture, ima.hCount, ima.wCount, ima.pixelsPerUnit);
|
||||
@ -120,161 +182,20 @@ namespace Managers
|
||||
{
|
||||
// 捕获异常并打印详细错误信息
|
||||
Debug.LogError(
|
||||
$"Error processing image definition: {ima.name} (Path: {ima.path}, PackID: {ima.packID}). Exception: {ex.Message}");
|
||||
$"处理图像定义时出错: '{ima.name}' (路径: '{ima.path}', 包ID: '{ima.packID}')。异常: {ex.GetType().Name}: {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
// public void InitDrawOrder()
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // 查询绘制顺序定义
|
||||
// var drawOrderDef = Managers.DefineManager.Instance.QueryDefinesByType<DrawingOrderDef>();
|
||||
// if (drawOrderDef == null || drawOrderDef.Length == 0)
|
||||
// {
|
||||
// Debug.LogWarning("No DrawingOrderDef found.");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 初始化包路径字典
|
||||
// Dictionary<string, string> packRootSite = new();
|
||||
//
|
||||
// foreach (var drawOrder in drawOrderDef)
|
||||
// {
|
||||
// // 检查必要字段是否为空
|
||||
// if (string.IsNullOrEmpty(drawOrder.texturePath) || string.IsNullOrEmpty(drawOrder.packID))
|
||||
// {
|
||||
// Debug.LogWarning(
|
||||
// $"Skipping invalid drawOrder: texturePath or packID is null or empty. PackID: {drawOrder.packID}");
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // 获取包路径
|
||||
// if (!packRootSite.ContainsKey(drawOrder.packID))
|
||||
// {
|
||||
// var packagePath = Managers.DefineManager.Instance.GetPackagePath(drawOrder.packID);
|
||||
// if (string.IsNullOrEmpty(packagePath))
|
||||
// {
|
||||
// Debug.LogError($"Package path not found for packID: {drawOrder.packID}");
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// packRootSite[drawOrder.packID] = packagePath;
|
||||
// }
|
||||
//
|
||||
// // 判断是否为 Unity 资源路径
|
||||
// var isUnityResource = drawOrder.texturePath.StartsWith("res:", StringComparison.OrdinalIgnoreCase);
|
||||
// var rootPath = packRootSite[drawOrder.packID];
|
||||
//
|
||||
// if (isUnityResource)
|
||||
// {
|
||||
// // 移除 "res:" 前缀并适配 Unity 资源路径规则
|
||||
// var resourceFolder = drawOrder.texturePath.Substring(4).TrimStart('/').Replace('\\', '/');
|
||||
//
|
||||
// // 加载文件夹下的所有纹理资源
|
||||
// var textures = Resources.LoadAll<Texture2D>(resourceFolder);
|
||||
// if (textures == null || textures.Length == 0)
|
||||
// {
|
||||
// Debug.LogWarning($"No textures found in Unity resource folder: {resourceFolder}");
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// foreach (var image in textures)
|
||||
// {
|
||||
// if (image == null)
|
||||
// {
|
||||
// Debug.LogWarning(
|
||||
// $"Texture loaded from Unity resource folder: {resourceFolder} is null.");
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // 创建精灵
|
||||
// try
|
||||
// {
|
||||
// var spr = Sprite.Create(
|
||||
// image,
|
||||
// new Rect(0, 0, image.width, image.height),
|
||||
// new Vector2(0.5f, 0.5f), // 中心点
|
||||
// drawOrder.pixelsPerUnit
|
||||
// );
|
||||
// var name = image.name;
|
||||
//
|
||||
// // 插入纹理
|
||||
// InsertBodyTexture(drawOrder.packID, drawOrder.texturePath, name, spr);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Debug.LogError(
|
||||
// $"Failed to create sprite from Unity resource: {image.name}. Error: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 文件系统路径处理
|
||||
// var folderPath = Path.Combine(rootPath, drawOrder.texturePath);
|
||||
//
|
||||
// // 获取图像文件列表
|
||||
// try
|
||||
// {
|
||||
// var imagePath = Configs.ConfigProcessor.GetFilesByExtensions(folderPath,
|
||||
// new[] { "jpg", "jpeg", "png", "tga", "tif", "tiff", "psd", "bmp" });
|
||||
//
|
||||
// foreach (var path in imagePath)
|
||||
// {
|
||||
// // 加载纹理
|
||||
// Texture2D image = null;
|
||||
// try
|
||||
// {
|
||||
// image = Configs.ConfigProcessor.LoadTextureByIO(path);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Debug.LogError($"Failed to load texture from path: {path}. Error: {ex.Message}");
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if (image == null)
|
||||
// {
|
||||
// Debug.LogWarning($"Texture loaded from path: {path} is null.");
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// // 创建精灵
|
||||
// try
|
||||
// {
|
||||
// var spr = Sprite.Create(
|
||||
// image,
|
||||
// new Rect(0, 0, image.width, image.height),
|
||||
// new Vector2(0.5f, 0.5f), // 中心点
|
||||
// drawOrder.pixelsPerUnit
|
||||
// );
|
||||
//
|
||||
// var name = Path.GetFileNameWithoutExtension(path);
|
||||
//
|
||||
// // 插入纹理
|
||||
// InsertBodyTexture(drawOrder.packID, drawOrder.texturePath, name, spr);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Debug.LogError(
|
||||
// $"Failed to create sprite from texture: {path}. Error: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Debug.LogError($"Failed to retrieve files from folder: {folderPath}. Error: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Debug.LogError($"An unexpected error occurred in InitDrawOrder: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 将纹理按指定行数和列数分割成多个精灵,并存储起来。
|
||||
/// </summary>
|
||||
/// <param name="packId">精灵所属的包ID。</param>
|
||||
/// <param name="baseName">精灵的基础名称。</param>
|
||||
/// <param name="texture">要分割的 <see cref="Texture2D"/> 对象。</param>
|
||||
/// <param name="rows">水平分割的行数。</param>
|
||||
/// <param name="cols">垂直分割的列数。</param>
|
||||
/// <param name="pixelsPerUnit">每个单元的像素数,用于Sprite.Create。</param>
|
||||
private void SplitTextureIntoSprites(
|
||||
string packId,
|
||||
string baseName,
|
||||
@ -285,7 +206,7 @@ namespace Managers
|
||||
{
|
||||
if (!texture)
|
||||
{
|
||||
Debug.LogError("Texture is null.");
|
||||
Debug.LogError($"SplitTextureIntoSprites: 包 '{packId}' 中 '{baseName}' 提供的纹理为空。无法分割。");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -296,64 +217,107 @@ namespace Managers
|
||||
var textureWidth = texture.width;
|
||||
var textureHeight = texture.height;
|
||||
|
||||
// 首先创建一个未分割的精灵,使用原始名称
|
||||
if (!sprites.ContainsKey(packId))
|
||||
sprites[packId] = new Dictionary<string, Sprite>();
|
||||
|
||||
// 创建未分割的精灵
|
||||
// 创建未分割的完整精灵,使用原始名称
|
||||
var fullSpriteRect = new Rect(0, 0, textureWidth, textureHeight);
|
||||
// 警告:如果精灵名重复,则覆盖
|
||||
if (sprites[packId].ContainsKey(baseName))
|
||||
{
|
||||
Debug.LogWarning($"包 '{packId}' 中名为 '{baseName}' 的精灵已存在。将覆盖之前的完整精灵定义。");
|
||||
}
|
||||
var fullSprite = Sprite.Create(texture, fullSpriteRect, new Vector2(0.5f, 0.5f), pixelsPerUnit);
|
||||
fullSprite.name = baseName; // 使用原始名称
|
||||
fullSprite.name = baseName; // 确保 Sprite.name 被设置
|
||||
sprites[packId][baseName] = fullSprite;
|
||||
|
||||
// 如果不分割(rows和cols都为1),直接返回
|
||||
// 如果不分割(rows和cols都为1),提前返回
|
||||
if (rows == 1 && cols == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查纹理尺寸是否可被分割数整除
|
||||
if (textureWidth % cols != 0 || textureHeight % rows != 0)
|
||||
{
|
||||
Debug.LogError($"包 '{packId}' 中 '{baseName}' 的纹理尺寸 ({textureWidth}x{textureHeight}) 不能被指定的行数 ({rows}) 和列数 ({cols}) 完美整除。子精灵将不会生成或可能不正确。仅显示完整精灵。");
|
||||
return; // 终止子精灵分割,只保留完整的精灵
|
||||
}
|
||||
|
||||
var tileWidth = textureWidth / cols;
|
||||
var tileHeight = textureHeight / rows;
|
||||
|
||||
if (tileWidth * cols != textureWidth || tileHeight * rows != textureHeight)
|
||||
{
|
||||
Debug.LogError("Texture dimensions are not divisible by the specified rows and columns.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 分割纹理并创建多个精灵
|
||||
for (var row = 0; row < rows; row++)
|
||||
{
|
||||
for (var col = 0; col < cols; col++)
|
||||
{
|
||||
Rect spriteRect = new(col * tileWidth, row * tileHeight, tileWidth, tileHeight);
|
||||
Rect spriteRect = new(col * tileWidth, row * tileHeight, tileWidth, tileHeight);
|
||||
var sprite = Sprite.Create(texture, spriteRect, new Vector2(0.5f, 0.5f), pixelsPerUnit);
|
||||
|
||||
var index = (rows - row - 1) * cols + col; // 计算索引
|
||||
// 精灵索引计算方式
|
||||
var index = (rows - row - 1) * cols + col;
|
||||
var spriteName = $"{baseName}_{index}";
|
||||
sprite.name = spriteName;
|
||||
|
||||
// 添加到字典中
|
||||
// 警告:如果子精灵名重复,则覆盖
|
||||
if (sprites[packId].ContainsKey(spriteName))
|
||||
{
|
||||
Debug.LogWarning($"包 '{packId}' 中名为 '{spriteName}' 的精灵已存在。将覆盖之前的子精灵定义。这可能表示配置错误。");
|
||||
}
|
||||
sprites[packId][spriteName] = sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
/// <summary>
|
||||
/// 清理所有已加载的纹理和精灵数据。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 此方法会清空 <see cref="packagesImages"/> 和 <see cref="sprites"/> 字典,
|
||||
/// 但不会卸载 <see cref="defaultSprite"/>,因为它通常通过 Resources.Load 加载,由 Unity 管理其生命周期。
|
||||
/// </remarks>
|
||||
public void Clear()
|
||||
{
|
||||
packagesImages.Clear();
|
||||
sprites.Clear();
|
||||
Init();
|
||||
StepDescription = "包图像管理器数据已清理。"; // 更新状态
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新加载所有图像数据。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 此方法会首先调用 <see cref="Clear()"/> 清理所有数据,然后调用 <see cref="Init()"/> 重新初始化。
|
||||
/// </remarks>
|
||||
public void Reload()
|
||||
{
|
||||
Clear();
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据 <see cref="ImageDef"/> 对象获取对应的精灵。
|
||||
/// </summary>
|
||||
/// <param name="ima">包含精灵包ID和名称的 <see cref="ImageDef"/> 对象。</param>
|
||||
/// <returns>如果找到对应的精灵,则返回该精灵;否则返回 <see cref="defaultSprite"/>。</returns>
|
||||
public Sprite GetSprite(ImageDef ima)
|
||||
{
|
||||
return GetSprite(ima.packID,ima.name);
|
||||
if (ima == null) return defaultSprite;
|
||||
return GetSprite(ima.packID, ima.name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据包ID和精灵名称获取对应的精灵。
|
||||
/// </summary>
|
||||
/// <param name="packID">精灵所属的包ID。如果为空,则会遍历所有包查找。</param>
|
||||
/// <param name="name">精灵的名称。</param>
|
||||
/// <returns>如果找到对应的精灵,则返回该精灵;否则返回 <see cref="defaultSprite"/>。</returns>
|
||||
public Sprite GetSprite(string packID, string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(packID))
|
||||
{
|
||||
// 如果packID为空,遍历所有包以name查找
|
||||
foreach (var kvp in sprites)
|
||||
{
|
||||
if (kvp.Value.TryGetValue(name, out var sprite))
|
||||
@ -366,104 +330,21 @@ namespace Managers
|
||||
return sprite;
|
||||
}
|
||||
|
||||
// 如果未找到,返回默认精灵
|
||||
return defaultSprite;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据包ID、基础名称和索引获取被分割的子精灵。
|
||||
/// </summary>
|
||||
/// <param name="packID">精灵所属的包ID。</param>
|
||||
/// <param name="name">精灵的基础名称。</param>
|
||||
/// <param name="index">子精灵的索引。</param>
|
||||
/// <returns>如果找到对应的子精灵,则返回该精灵;否则返回 <see cref="defaultSprite"/>。</returns>
|
||||
public Sprite GetSprite(string packID, string name, int index)
|
||||
{
|
||||
var fullName = $"{name}_{index}";
|
||||
return GetSprite(packID, fullName);
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 向 bodyTexture 插入一张 Sprite。
|
||||
// /// 如果包名、路径或部件名原本不存在,会自动建立对应的 Dictionary。
|
||||
// /// </summary>
|
||||
// /// <param name="packageName">包名</param>
|
||||
// /// <param name="filePath">文件路径</param>
|
||||
// /// <param name="bodyPartName">身体部件名</param>
|
||||
// /// <param name="sprite">要插入的 Sprite</param>
|
||||
// public void InsertBodyTexture(string packageName,
|
||||
// string filePath,
|
||||
// string bodyPartName,
|
||||
// Sprite sprite)
|
||||
// {
|
||||
// if (sprite == null)
|
||||
// {
|
||||
// Debug.LogWarning("InsertBodyTexture: sprite 为 null,已忽略。");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 1) 处理包名层级
|
||||
// if (!bodyTexture.TryGetValue(packageName, out var pathDict))
|
||||
// {
|
||||
// pathDict = new Dictionary<string, Dictionary<string, Sprite>>();
|
||||
// bodyTexture[packageName] = pathDict;
|
||||
// }
|
||||
//
|
||||
// // 2) 处理文件路径层级
|
||||
// if (!pathDict.TryGetValue(filePath, out var partDict))
|
||||
// {
|
||||
// partDict = new Dictionary<string, Sprite>();
|
||||
// pathDict[filePath] = partDict;
|
||||
// }
|
||||
//
|
||||
// // 3) 插入或覆盖部件名
|
||||
// partDict[bodyPartName] = sprite;
|
||||
// }
|
||||
// /// <summary>
|
||||
// /// 查找身体部件的所有Sprite变体(支持带编号的图片)
|
||||
// /// </summary>
|
||||
// /// <param name="packageName">包名</param>
|
||||
// /// <param name="filePath">文件路径</param>
|
||||
// /// <param name="bodyPartName">身体部件名</param>
|
||||
// /// <returns>按编号排序的Sprite数组,未找到时返回空数组</returns>
|
||||
// public Sprite[] FindBodyTextures(string packageName, string filePath, string bodyPartName)
|
||||
// {
|
||||
// // 检查包名是否存在
|
||||
// if (!bodyTexture.TryGetValue(packageName, out var packageDict))
|
||||
// {
|
||||
// Debug.LogWarning($"Package '{packageName}' not found.");
|
||||
// return new[] { defaultSprite };
|
||||
// }
|
||||
//
|
||||
// // 检查文件路径是否存在
|
||||
// if (!packageDict.TryGetValue(filePath, out var pathDict))
|
||||
// {
|
||||
// Debug.LogWarning($"File path '{filePath}' not found in package '{packageName}'.");
|
||||
// return new[] { defaultSprite };
|
||||
// }
|
||||
//
|
||||
// // 收集所有匹配的Sprite
|
||||
// var sprites = new List<(int order, Sprite sprite)>();
|
||||
//
|
||||
// // 查找完全匹配的项(无编号)
|
||||
// if (pathDict.TryGetValue(bodyPartName, out var baseSprite))
|
||||
// {
|
||||
// sprites.Add((0, baseSprite));
|
||||
// }
|
||||
//
|
||||
// // 查找带编号的变体
|
||||
// var prefix = bodyPartName + "_";
|
||||
// foreach (var (key, value) in pathDict)
|
||||
// {
|
||||
// // 检查是否以部件名+下划线开头
|
||||
// if (key.StartsWith(prefix) && key.Length > prefix.Length)
|
||||
// {
|
||||
// var suffix = key.Substring(prefix.Length);
|
||||
//
|
||||
// // 尝试解析编号
|
||||
// if (int.TryParse(suffix, out var number))
|
||||
// {
|
||||
// sprites.Add((number, value));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 按编号排序(无编号视为0)
|
||||
// return sprites
|
||||
// .OrderBy(x => x.order)
|
||||
// .Select(x => x.sprite)
|
||||
// .ToArray();
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,121 +2,177 @@ using System.Collections.Generic;
|
||||
using Data;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Tilemaps;
|
||||
using Utils;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
/// <summary>
|
||||
/// 瓦片管理器,用于加载、初始化和管理瓦片资源。
|
||||
/// </summary>
|
||||
public class TileManager : Utils.Singleton<TileManager>
|
||||
/// <remarks>
|
||||
/// 实现 <see cref="ILaunchManager"/> 接口,以便由 Launcher 统一管理生命周期。
|
||||
/// </remarks>
|
||||
public class TileManager : Singleton<TileManager>, ILaunchManager
|
||||
{
|
||||
/// <summary>
|
||||
/// 存储瓦片名称与瓦片对象的映射关系。
|
||||
/// </summary>
|
||||
public Dictionary<string, TileBase> tileBaseMapping = new();
|
||||
// ------------- ILaunchManager 接口实现 -------------
|
||||
|
||||
/// <summary>
|
||||
/// 存储瓦片索引与瓦片对象的映射关系。
|
||||
/// 索引由四个整数组成,表示瓦片的组合方式。
|
||||
/// 获取当前瓦片管理器加载步骤的描述。
|
||||
/// </summary>
|
||||
public Dictionary<(int, int, int, int), TileBase> tileToTileBaseMapping = new();
|
||||
|
||||
/// <summary>
|
||||
/// 存储瓦片名称与唯一 ID 的映射关系。
|
||||
/// </summary>
|
||||
public Dictionary<string, int> tileID = new();
|
||||
public string StepDescription => "正在切割瓦片";
|
||||
|
||||
/// <summary>
|
||||
/// 初始化瓦片管理器。
|
||||
/// 加载所有瓦片定义、纹理映射表,并生成对应的瓦片对象。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 加载所有瓦片定义、纹理映射表,并生成对应的瓦片对象。
|
||||
/// 此方法是幂等的,多次调用只会在第一次执行实际初始化逻辑。
|
||||
/// </remarks>
|
||||
public void Init()
|
||||
{
|
||||
// 如果已经被初始化,则直接返回
|
||||
if (tileToTileBaseMapping.Count > 0)
|
||||
{
|
||||
Debug.Log($"<color=green>{StepDescription}</color> 已初始化。跳过。");
|
||||
return;
|
||||
|
||||
// 初始化图像包管理器
|
||||
Managers.PackagesImageManager.Instance.Init();
|
||||
var imagePack = Managers.PackagesImageManager.Instance;
|
||||
|
||||
// 获取所有瓦片定义
|
||||
var tileType = Managers.DefineManager.Instance.QueryDefinesByType<TileDef>();
|
||||
for (var i = 0; i < tileType.Length; i++)
|
||||
{
|
||||
tileID.Add(tileType[i].name, i);
|
||||
}
|
||||
|
||||
// 处理瓦片纹理映射表定义
|
||||
var tileTextureMappingDef = Managers.DefineManager.Instance.QueryDefinesByType<TileMappingTableDef>();
|
||||
foreach (var mappingTableDef in tileTextureMappingDef)
|
||||
|
||||
// 确保依赖的 PackagesImageManager 已初始化。
|
||||
// 虽然 Launcher 会按顺序初始化,但这里做一次检查和调用,
|
||||
// 可以防止其他地方直接调用 TileManager.Instance.Init() 时,
|
||||
// 其依赖未准备好的情况。PackagesImageManager 也应该是幂等的。
|
||||
PackagesImageManager.Instance.Init();
|
||||
|
||||
var imagePack = Managers.PackagesImageManager.Instance;
|
||||
|
||||
// 获取所有瓦片定义
|
||||
var tileDefs = DefineManager.Instance.QueryDefinesByType<TileDef>();
|
||||
for (var i = 0; i < tileDefs.Length; i++)
|
||||
{
|
||||
// 使用 TryAdd 避免重复添加,并处理潜在的定义冲突
|
||||
if (!tileID.TryAdd(tileDefs[i].name, i))
|
||||
{
|
||||
Debug.LogWarning($"<color=orange>瓦片定义 '{tileDefs[i].name}' 的名称重复。</color> 将忽略后续定义。");
|
||||
}
|
||||
}
|
||||
|
||||
// 处理瓦片纹理映射表定义
|
||||
var tileTextureMappingDefs = DefineManager.Instance.QueryDefinesByType<TileMappingTableDef>();
|
||||
foreach (var mappingTableDef in tileTextureMappingDefs)
|
||||
{
|
||||
var packName = DefineManager.Instance.GetDefinePackageName(mappingTableDef);
|
||||
foreach (var keyVal in mappingTableDef.tileDict)
|
||||
{
|
||||
var key = keyVal.Key;
|
||||
var val = keyVal.Value;
|
||||
|
||||
// 检查键值格式是否合法
|
||||
var parts = key.Split('_');
|
||||
var compositeKey = keyVal.Key; // 例如 "Dirt_Grass_Dirt_Dirt"
|
||||
var spriteName = keyVal.Value; // 例如 "Dirt_Sprite_001"
|
||||
var parts = compositeKey.Split('_');
|
||||
if (parts.Length != 4)
|
||||
{
|
||||
var packName = Managers.DefineManager.Instance.GetDefinePackageName(mappingTableDef);
|
||||
Debug.LogError($"来自{packName}定义的TileMappingTableDef键值{key}内容不合法!\n应该为[瓦片名称_瓦片名称_瓦片名称_瓦片名称]的格式");
|
||||
Debug.LogError($"<color=red>来自 '{packName}' 定义的 TileMappingTableDef 键值 '{compositeKey}' 格式不合法!</color>\n应为 '[瓦片名称_瓦片名称_瓦片名称_瓦片名称]' 的格式。");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查键值中是否存在未定义的瓦片名称
|
||||
if (!(tileID.TryGetValue(parts[0], out var k1) &&
|
||||
tileID.TryGetValue(parts[1], out var k2) &&
|
||||
tileID.TryGetValue(parts[2], out var k3) &&
|
||||
tileID.TryGetValue(parts[3], out var k4)))
|
||||
// 尝试获取四个部分的瓦片ID
|
||||
if (!tileID.TryGetValue(parts[0], out var k1) ||
|
||||
!tileID.TryGetValue(parts[1], out var k2) ||
|
||||
!tileID.TryGetValue(parts[2], out var k3) ||
|
||||
!tileID.TryGetValue(parts[3], out var k4))
|
||||
{
|
||||
var packName = Managers.DefineManager.Instance.GetDefinePackageName(mappingTableDef);
|
||||
Debug.LogError($"来自{packName}定义的TileMappingTableDef键值{key}中存在未定义的瓦片名称");
|
||||
Debug.LogError($"<color=red>来自 '{packName}' 定义的 TileMappingTableDef 键值 '{compositeKey}' 中存在未定义的瓦片名称。</color>");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取对应精灵
|
||||
var sprite = imagePack.GetSprite(mappingTableDef.packID, val);
|
||||
var sprite = imagePack.GetSprite(mappingTableDef.packID, spriteName);
|
||||
if (sprite == null)
|
||||
{
|
||||
var packName = Managers.DefineManager.Instance.GetDefinePackageName(mappingTableDef);
|
||||
Debug.LogError($"来自{packName}定义的TileMappingTableDef键值{val}中存在未定义的图片名称");
|
||||
Debug.LogError($"<color=red>来自 '{packName}' 定义的 TileMappingTableDef 键值 '{spriteName}' 中存在未定义的图片名称。</color>");
|
||||
continue;
|
||||
}
|
||||
|
||||
var tileKey = (k1, k2, k3, k4);
|
||||
// 检查是否存在重复索引
|
||||
if (tileToTileBaseMapping.ContainsKey((k1, k2, k3, k4)))
|
||||
if (tileToTileBaseMapping.ContainsKey(tileKey))
|
||||
{
|
||||
var packName = Managers.DefineManager.Instance.GetDefinePackageName(mappingTableDef);
|
||||
Debug.LogWarning($"来自{packName}定义的TileMappingTableDef键值{(k1, k2, k3, k4)}存在重复索引,将忽略重复项");
|
||||
Debug.LogWarning($"<color=orange>来自 '{packName}' 定义的 TileMappingTableDef 键值 '{tileKey}' (对应原始键 '{compositeKey}') 存在重复索引,将忽略重复项。</color>");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 加载瓦片并存储到映射表中
|
||||
var tile = LoadTile(sprite);
|
||||
tileToTileBaseMapping[(k1, k2, k3, k4)] = tile;
|
||||
tileBaseMapping[val] = tile;
|
||||
// 创建瓦片实例并存储到映射表中
|
||||
var newTile = CreateTileInstance(sprite);
|
||||
tileToTileBaseMapping[tileKey] = newTile;
|
||||
|
||||
// 同样检查 tileBaseMapping 的重复性
|
||||
if (tileBaseMapping.ContainsKey(spriteName))
|
||||
{
|
||||
Debug.LogWarning($"<color=orange>来自 '{packName}' 定义的 TileMappingTableDef 键值 '{spriteName}' 在 tileBaseMapping 中存在重复。</color> 仅保留第一个实例。");
|
||||
}
|
||||
else
|
||||
{
|
||||
tileBaseMapping[spriteName] = newTile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新加载瓦片管理器。
|
||||
/// 清空当前的瓦片映射表并重新初始化。
|
||||
/// 清理瓦片管理器,释放所有加载的瓦片资源和数据。
|
||||
/// </summary>
|
||||
public void Reload()
|
||||
/// <remarks>
|
||||
/// 用于重载游戏时彻底重置瓦片系统。
|
||||
/// </remarks>
|
||||
public void Clear()
|
||||
{
|
||||
// 销毁所有动态创建的 Tile ScriptableObject
|
||||
foreach (var tileBase in tileToTileBaseMapping.Values)
|
||||
{
|
||||
if (tileBase is Tile tile) // 确认是 Unity.Tilemap.Tile 类型
|
||||
{
|
||||
// 在运行时,直接 Destroy 会导致一些警告,但对于动态创建的 ScriptableObject,
|
||||
// 这是清理内存的正确方法。如果此方法在编辑器模式且不在Play模式下调用,
|
||||
// 应该使用 DestroyImmediate。考虑到此方法通常由 Launcher 在Play模式下调用,
|
||||
// Destroy 即可。
|
||||
Object.Destroy(tile);
|
||||
}
|
||||
}
|
||||
foreach (var tileBase in tileBaseMapping.Values)
|
||||
{
|
||||
if (tileBase is Tile tile && !tileToTileBaseMapping.ContainsValue(tile))
|
||||
{
|
||||
// 销毁可能在 tileBaseMapping 中但不在 tileToTileBaseMapping 中的额外 Tile 实例
|
||||
// (尽管根据 Init 逻辑,它们应该是指向相同的实例)
|
||||
Object.Destroy(tile);
|
||||
}
|
||||
}
|
||||
|
||||
tileBaseMapping.Clear();
|
||||
tileToTileBaseMapping.Clear();
|
||||
Init();
|
||||
tileID.Clear();
|
||||
}
|
||||
// ------------- ILaunchManager 接口实现结束 -------------
|
||||
|
||||
/// <summary>
|
||||
/// 存储瓦片名称与 <see cref="TileBase"/> 对象的映射关系。
|
||||
/// </summary>
|
||||
public Dictionary<string, TileBase> tileBaseMapping = new();
|
||||
|
||||
/// <summary>
|
||||
/// 存储瓦片组合索引与 <see cref="TileBase"/> 对象的映射关系。
|
||||
/// 索引由四个整数组成,表示瓦片的组合方式。
|
||||
/// </summary>
|
||||
public Dictionary<(int, int, int, int), TileBase> tileToTileBaseMapping = new();
|
||||
|
||||
/// <summary>
|
||||
/// 存储瓦片名称与唯一 ID 的映射关系。
|
||||
/// </summary>
|
||||
public Dictionary<string, int> tileID = new();
|
||||
|
||||
// 移除了 TileManager 内部的 Reload() 方法,因为它将被 Launcher 的 Clear() + Init() 流程取代。
|
||||
|
||||
/// <summary>
|
||||
/// 将精灵加载为瓦片对象。
|
||||
/// 将精灵加载为新的 <see cref="Tile"/> ScriptableObject 实例。
|
||||
/// 这是一个内部辅助方法。
|
||||
/// </summary>
|
||||
/// <param name="sprite">要加载的精灵。</param>
|
||||
/// <param name="colliderType">瓦片的碰撞体类型,默认为 None。</param>
|
||||
/// <returns>返回加载成功的瓦片对象。</returns>
|
||||
public TileBase LoadTile(Sprite sprite, Tile.ColliderType colliderType = Tile.ColliderType.None)
|
||||
/// <param name="sprite">要加载为瓦片的精灵。</param>
|
||||
/// <param name="colliderType">瓦片的碰撞体类型,默认为 <see cref="Tile.ColliderType.None"/>。</param>
|
||||
/// <returns>返回新创建的 <see cref="TileBase"/> 对象。</returns>
|
||||
private TileBase CreateTileInstance(Sprite sprite, Tile.ColliderType colliderType = Tile.ColliderType.None)
|
||||
{
|
||||
var newTile = ScriptableObject.CreateInstance<Tile>();
|
||||
newTile.sprite = sprite;
|
||||
@ -124,5 +180,27 @@ namespace Managers
|
||||
newTile.colliderType = colliderType;
|
||||
return newTile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定组合索引的瓦片对象。
|
||||
/// </summary>
|
||||
/// <param name="tileKey">由四个整数组成的瓦片组合索引。</param>
|
||||
/// <returns>对应的 <see cref="TileBase"/> 对象,如果不存在则返回 null。</returns>
|
||||
public TileBase GetTile((int, int, int, int) tileKey)
|
||||
{
|
||||
tileToTileBaseMapping.TryGetValue(tileKey, out var tile);
|
||||
return tile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定精灵名称的瓦片对象。
|
||||
/// </summary>
|
||||
/// <param name="spriteName">精灵的名称。</param>
|
||||
/// <returns>对应的 <see cref="TileBase"/> 对象,如果不存在则返回 null。</returns>
|
||||
public TileBase GetTileBySpriteName(string spriteName)
|
||||
{
|
||||
tileBaseMapping.TryGetValue(spriteName, out var tile);
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user