Files

278 lines
13 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Managers/TileManager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Data;
using Map;
using UnityEngine;
using UnityEngine.Tilemaps;
using Utils;
using Object = UnityEngine.Object; // 明确指定UnityEngine.Object
namespace Managers
{
public class TileManager : Singleton<TileManager>, ILaunchManager
{
// 缓存所有根据定义生成的RuleTile实例
private readonly Dictionary<string, RuleTile> _cachedTiles = new();
// 地图生成器工作类映射Key: MapGeneratorDef.defName, Value: MapGeneratorWorkClassBase
private readonly Dictionary<string, MapGeneratorWorkClassBase> _mapGeneratorWorkClassMap = new();
/// <summary>
/// 指示管理器是否已完成初始化。
/// </summary>
public bool Completed { get; set; } // 新增:实现 ILaunchManager 接口的 Completed 属性
public string StepDescription => "正在加载瓦片";
/// <summary>
/// 初始化瓦片管理器加载所有瓦片定义并创建RuleTile实例加载地图生成器定义。
/// </summary>
/// <returns>一个表示异步操作完成的 Task。</returns>
public Task Init() // 接口变更:方法签名变为 async Task
{
// 使用 Completed 属性作为统一的状态检查,防止重复初始化
if (Completed) return Task.CompletedTask;
// 遵循不检查单例的原则,假定 DefineManager.Instance 始终可用。
var tileDefs = DefineManager.Instance.QueryDefinesByType<TileDef>();
if (tileDefs == null)
{
Completed = true; // 即使没有定义,也认为初始化流程已完成
return Task.CompletedTask;
}
foreach (var def in tileDefs)
{
if (string.IsNullOrEmpty(def.defName))
{
Debug.LogWarning("发现未定义名称或defName的TileDef已跳过。");
continue;
}
var tileName = def.defName;
var ruleTile = ScriptableObject.CreateInstance<RuleTile>();
ruleTile.name = tileName; // 设置ScriptableObject的名称
ruleTile.m_DefaultColliderType = def.collider;
// 遵循不检查单例的原则,假定 PackagesImageManager.Instance 始终可用。
ruleTile.m_DefaultSprite = PackagesImageManager.Instance.GetSprite(def.texture);
ruleTile.m_TilingRules = GetTileRules(def.rules);
_cachedTiles[tileName] = ruleTile;
}
// 遵循不检查单例的原则,假定 DefineManager.Instance 始终可用。
var generatorDefs = DefineManager.Instance.QueryDefinesByType<MapGeneratorDef>();
if (generatorDefs != null) // 检查 generatorDefs 是否为 null
{
foreach (var mapGeneratorDef in generatorDefs)
{
var workClass = StringUtils.CreateMapGeneratorInstance(mapGeneratorDef.workClass);
if (workClass == null)
{
Debug.LogWarning($"无法为地图生成器 '{mapGeneratorDef.defName}' 创建工作类 '{mapGeneratorDef.workClass}',已跳过。");
continue;
}
workClass.Init(mapGeneratorDef.value);
_mapGeneratorWorkClassMap[mapGeneratorDef.defName] = workClass;
}
}
Completed = true; // 在所有初始化逻辑完成后设置 Completed 为 true
return Task.CompletedTask;
// 由于 Init 方法内部当前没有真正的异步操作,不需要显式 await Task.CompletedTask;
// 编译器会为同步的 async Task 方法自动生成一个已完成的 Task。
}
/// <summary>
/// 清空所有缓存的瓦片定义和地图生成器工作类。
/// </summary>
public void Clear()
{
foreach (var tile in _cachedTiles.Values)
if (tile != null)
Object.Destroy(tile); // 销毁动态创建的 ScriptableObject 实例
_cachedTiles.Clear();
_mapGeneratorWorkClassMap.Clear(); // 清空地图生成器工作类映射
Completed = false; // 清理后将 Completed 置为 false
}
private static List<RuleTile.TilingRule> GetTileRules(RuleTileRuleDef[] rules)
{
// Null-conditional operator 和 ToList() 确保即使 rules 为 null 也不会抛出异常
return rules?.Select(GetTileRule).ToList() ?? new List<RuleTile.TilingRule>();
}
/// <summary>
/// 将自定义的 RuleTileRuleDef 转换为 Unity 的 RuleTile.TilingRule。
/// </summary>
/// <param name="ruleDef">自定义的 RuleTileRuleDef 规则定义。</param>
/// <returns>转换后的 RuleTile.TilingRule 实例。</returns>
private static RuleTile.TilingRule GetTileRule(RuleTileRuleDef ruleDef)
{
if (ruleDef == null)
return null;
var tilingRule = new RuleTile.TilingRule
{
// 遵循不检查单例的原则,假定 PackagesImageManager.Instance 始终可用。
m_Sprites = PackagesImageManager.Instance.GetSprites(ruleDef.animationTextures.ToArray()),
m_Output = ruleDef.outputType,
m_ColliderType = ruleDef.outputCollider,
m_PerlinScale = ruleDef.chance,
m_MaxAnimationSpeed = ruleDef.animationSpeed,
m_MinAnimationSpeed = ruleDef.animationSpeed,
m_RuleTransform = ruleDef.transform
};
// 使用 Dictionary 存储邻居条件,键为位置,值为邻居类型
var neighborConditionsMap = new Dictionary<Vector3Int, int>();
// 预设的8个邻居位置
var defaultPositions = new[]
{
new Vector3Int(-1, 1, 0),
new Vector3Int(0, 1, 0),
new Vector3Int(1, 1, 0),
new Vector3Int(-1, 0, 0),
new Vector3Int(1, 0, 0),
new Vector3Int(-1, -1, 0),
new Vector3Int(0, -1, 0),
new Vector3Int(1, -1, 0)
};
// 处理标准8个邻居条件
for (var i = 0; i < ruleDef.neighborConditions.Length && i < 8; i++)
{
int neighborType;
switch (ruleDef.neighborConditions[i])
{
case RuleTileRuleDef.NeighborConditionType.NotThis:
neighborType = RuleTile.TilingRuleOutput.Neighbor.NotThis;
neighborConditionsMap[defaultPositions[i]] = neighborType; // 添加或更新
break;
case RuleTileRuleDef.NeighborConditionType.This:
neighborType = RuleTile.TilingRuleOutput.Neighbor.This;
neighborConditionsMap[defaultPositions[i]] = neighborType; // 添加或更新
break;
case RuleTileRuleDef.NeighborConditionType.Any:
default:
// 如果是Any或默认则不添加到map中表示不关心此位置
break;
}
}
// 处理扩展邻居条件如果位置重复则覆盖标准8个邻居的定义
if (ruleDef.neighborConditionExtend != null)
foreach (var neighborConditionDef in ruleDef.neighborConditionExtend)
{
var pos = StringUtils.StringToVector3Int(neighborConditionDef.position);
int neighborType;
switch (neighborConditionDef.Type)
{
case RuleTileRuleDef.NeighborConditionType.NotThis:
neighborType = RuleTile.TilingRuleOutput.Neighbor.NotThis;
neighborConditionsMap[pos] = neighborType; // 添加或覆盖
break;
case RuleTileRuleDef.NeighborConditionType.This:
neighborType = RuleTile.TilingRuleOutput.Neighbor.This;
neighborConditionsMap[pos] = neighborType; // 添加或覆盖
break;
case RuleTileRuleDef.NeighborConditionType.Any:
default:
// 如果是Any或默认则不添加到map中
// 如果我们希望Any清除之前的显式定义可以在这里neighborConditionsMap.Remove(pos);
// 但通常Any意味着不关心所以保持不添加即可
break;
}
}
tilingRule.m_NeighborPositions = new List<Vector3Int>(neighborConditionsMap.Keys);
tilingRule.m_Neighbors = new List<int>(neighborConditionsMap.Values);
return tilingRule;
}
/// <summary>
/// 根据名称获取一个 RuleTile 实例。
/// </summary>
/// <param name="tileName">瓦片的名称 (TileDef.defName)</param>
/// <returns>对应的 RuleTile 实例,如果找不到则返回 null。</returns>
public TileBase GetTile(string tileName)
{
if (_cachedTiles.TryGetValue(tileName, out var tile)) return tile;
Debug.LogWarning($"瓦片 '{tileName}' 未在TileManager缓存中找到。");
return null;
}
/// <summary>
/// 应用指定的地图生成器,现在是非阻塞的异步操作。
/// </summary>
/// <param name="generatorName">要应用的生成器名称。</param>
/// <param name="landform">可选:指定要使用的 MapGenerator 实例。如果为 null将尝试从 Program.Instance.FocusedDimension 获取。</param>
/// <returns>一个表示异步操作完成的 Task。</returns>
public async Task ApplyMapGenerator(string generatorName, Landform landform = null)
{
if (landform == null)
{
// 尝试从 Program.Instance.FocusedDimension 获取 mapGenerator
// 假设 Program 和 FocusedDimension 结构存在
// 遵循不检查单例、不检查给定的API的原则。
landform = Program.Instance.FocusedDimension.landform;
if (landform == null)
{
Debug.LogError(
$"ApplyMapGenerator: 无法找到地图生成器 '{generatorName}' 对应的 mapGenerator。Program.Instance、FocusedDimension 或其 landform 可能为空。");
return; // async Task 方法直接 return; 意味着返回 Task.CompletedTask
}
}
if (_mapGeneratorWorkClassMap == null) // 这个检查在 Init 保证了 map 不会为 null 后,理论上在这里是不需要的。
{
Debug.LogError($"ApplyMapGenerator: _mapGeneratorWorkClassMap 未初始化,无法找到生成器 '{generatorName}'。");
return;
}
if (_mapGeneratorWorkClassMap.TryGetValue(generatorName, out var mapGeneratorWorkClass))
{
if (mapGeneratorWorkClass == null)
{
Debug.LogError(
$"ApplyMapGenerator: 为生成器 '{generatorName}' 找到了一个空的 IMapGeneratorWorkClass 实例。这表明 _mapGeneratorWorkClassMap 配置存在问题。");
return;
}
try
{
await mapGeneratorWorkClass.Process(landform); // <-- 使用 await 调用异步 Process 方法
}
catch (Exception ex)
{
Debug.LogError($"ApplyMapGenerator: 地图生成器 '{generatorName}' 异步处理过程中发生错误: {ex.Message}");
throw; // 抛出异常以通知调用者,或者根据实际情况选择捕获并完全处理
}
}
else
{
Debug.LogError(
$"ApplyMapGenerator: 未找到名为 '{generatorName}' 的 IMapGeneratorWorkClass。请确保该生成器已在 _mapGeneratorWorkClassMap 中注册。");
}
}
public MapGeneratorWorkClassBase GetMapGeneratorWorkClass(string defName)
{
return _mapGeneratorWorkClassMap?.GetValueOrDefault(defName, null);
}
}
}