using Data; using System.Collections.Generic; using UnityEngine; using UnityEngine.Tilemaps; using Utils; namespace Managers { /// /// 瓦片管理器,用于加载、初始化和管理瓦片资源。 /// /// /// 实现 接口,以便由 Launcher 统一管理生命周期。 /// public class TileManager : Singleton, ILaunchManager { // ------------- ILaunchManager 接口实现 ------------- /// /// 获取当前瓦片管理器加载步骤的描述。 /// public string StepDescription => "正在切割瓦片"; /// /// 初始化瓦片管理器。 /// /// /// 加载所有瓦片定义、纹理映射表,并生成对应的瓦片对象。 /// 此方法是幂等的,多次调用只会在第一次执行实际初始化逻辑。 /// public void Init() { // 如果已经被初始化,则直接返回 if (tileToTileBaseMapping.Count > 0) { return; } // 确保依赖的 PackagesImageManager 已初始化。 // 虽然 Launcher 会按顺序初始化,但这里做一次检查和调用, // 可以防止其他地方直接调用 TileManager.Instance.Init() 时, // 其依赖未准备好的情况。PackagesImageManager 也应该是幂等的。 PackagesImageManager.Instance.Init(); var imagePack = Managers.PackagesImageManager.Instance; // 获取所有瓦片定义 var tileDefs = DefineManager.Instance.QueryDefinesByType(); for (var i = 0; i < tileDefs.Length; i++) { // 使用 TryAdd 避免重复添加,并处理潜在的定义冲突 if (!tileID.TryAdd(tileDefs[i].name, i)) { Debug.LogWarning($"瓦片定义 '{tileDefs[i].name}' 的名称重复。 将忽略后续定义。"); } } // 处理瓦片纹理映射表定义 var tileTextureMappingDefs = DefineManager.Instance.QueryDefinesByType(); foreach (var mappingTableDef in tileTextureMappingDefs) { var packName = DefineManager.Instance.GetDefinePackageName(mappingTableDef); foreach (var keyVal in mappingTableDef.tileDict) { var compositeKey = keyVal.Key; // 例如 "Dirt_Grass_Dirt_Dirt" var spriteName = keyVal.Value; // 例如 "Dirt_Sprite_001" var parts = compositeKey.Split('_'); if (parts.Length != 4) { Debug.LogError($"来自 '{packName}' 定义的 TileMappingTableDef 键值 '{compositeKey}' 格式不合法!\n应为 '[瓦片名称_瓦片名称_瓦片名称_瓦片名称]' 的格式。"); continue; } // 尝试获取四个部分的瓦片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)) { Debug.LogError($"来自 '{packName}' 定义的 TileMappingTableDef 键值 '{compositeKey}' 中存在未定义的瓦片名称。"); continue; } // 获取对应精灵 var sprite = imagePack.GetSprite(mappingTableDef.packID, spriteName); if (sprite == null) { Debug.LogError($"来自 '{packName}' 定义的 TileMappingTableDef 键值 '{spriteName}' 中存在未定义的图片名称。"); continue; } var tileKey = (k1, k2, k3, k4); // 检查是否存在重复索引 if (tileToTileBaseMapping.ContainsKey(tileKey)) { Debug.LogWarning($"来自 '{packName}' 定义的 TileMappingTableDef 键值 '{tileKey}' (对应原始键 '{compositeKey}') 存在重复索引,将忽略重复项。"); continue; } // 创建瓦片实例并存储到映射表中 var newTile = CreateTileInstance(sprite); tileToTileBaseMapping[tileKey] = newTile; // 同样检查 tileBaseMapping 的重复性 if (tileBaseMapping.ContainsKey(spriteName)) { Debug.LogWarning($"来自 '{packName}' 定义的 TileMappingTableDef 键值 '{spriteName}' 在 tileBaseMapping 中存在重复。 仅保留第一个实例。"); } else { tileBaseMapping[spriteName] = newTile; } } } } /// /// 清理瓦片管理器,释放所有加载的瓦片资源和数据。 /// /// /// 用于重载游戏时彻底重置瓦片系统。 /// 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(); tileID.Clear(); } // ------------- ILaunchManager 接口实现结束 ------------- /// /// 存储瓦片名称与 对象的映射关系。 /// public Dictionary tileBaseMapping = new(); /// /// 存储瓦片组合索引与 对象的映射关系。 /// 索引由四个整数组成,表示瓦片的组合方式。 /// public Dictionary<(int, int, int, int), TileBase> tileToTileBaseMapping = new(); /// /// 存储瓦片名称与唯一 ID 的映射关系。 /// public Dictionary tileID = new(); // 移除了 TileManager 内部的 Reload() 方法,因为它将被 Launcher 的 Clear() + Init() 流程取代。 /// /// 将精灵加载为新的 ScriptableObject 实例。 /// 这是一个内部辅助方法。 /// /// 要加载为瓦片的精灵。 /// 瓦片的碰撞体类型,默认为 。 /// 返回新创建的 对象。 private TileBase CreateTileInstance(Sprite sprite, Tile.ColliderType colliderType = Tile.ColliderType.None) { var newTile = ScriptableObject.CreateInstance(); newTile.sprite = sprite; newTile.color = Color.white; newTile.colliderType = colliderType; return newTile; } /// /// 获取指定组合索引的瓦片对象。 /// /// 由四个整数组成的瓦片组合索引。 /// 对应的 对象,如果不存在则返回 null。 public TileBase GetTile((int, int, int, int) tileKey) { tileToTileBaseMapping.TryGetValue(tileKey, out var tile); return tile; } /// /// 获取指定精灵名称的瓦片对象。 /// /// 精灵的名称。 /// 对应的 对象,如果不存在则返回 null。 public TileBase GetTileBySpriteName(string spriteName) { tileBaseMapping.TryGetValue(spriteName, out var tile); return tile; } } }