(clienr) feat:事件定义

This commit is contained in:
m0_75251201
2025-08-28 15:07:36 +08:00
parent 9713ad9129
commit 56956f351f
17 changed files with 305 additions and 3063 deletions

View File

@ -1,181 +1,75 @@
using System;
using System.Collections.Generic;
using Data;
using Entity;
using Prefab;
using UnityEngine;
using EventType = Data.EventType;
using Utils;
using Random = System.Random;
namespace Managers
{
// 新增私有结构体,用于在事件队列中存储事件定义及其运行时上下文
public struct EventPayload // Make it public if EventManager exposes it directly
class EventManager:Singleton<EventManager>,ILaunchManager
{
public string DimensionId;
public EventDef EventDefinition;
public Vector3 Position; // 适用于 Character, Bullet, Pickup, DefaultEntity
public Vector3 Direction; // 仅适用于 Bullet
public Entity.Entity SourceEntity; // 仅适用于 Bullet (发射源)
public Vector3Int GridPosition; // 仅适用于 Building
}
private static Random _random = new();
public EventDef[] EventDefs { get; private set; }
public string StepDescription => "正在载入事件";
public class EventManager : Utils.MonoSingleton<EventManager>
{
private Queue<EventPayload> _eventQueue = new Queue<EventPayload>();
private EventManager()
public void Init()
{
/* Private constructor for singleton */
}
// ===================================
// 公共入队方法
// ===================================
public void EnqueueCharacterSpawnEvent(string dimensionId, EventDef eventDef, Vector3 pos)
{
if (eventDef == null || eventDef.eventType != EventType.SpawnCharacter ||
eventDef.entityDef_Character == null)
{
Debug.LogError(
$"EnqueueCharacterSpawnEvent: Invalid EventDef, mismatched EventType ({eventDef?.eventType}), or missing characterDef for event '{eventDef?.defName ?? "Unknown"}'.");
if(EventDefs!=null)
return;
}
_eventQueue.Enqueue(new EventPayload
{
DimensionId = dimensionId,
EventDefinition = eventDef,
Position = pos,
});
Debug.Log($"Event '{eventDef.defName}' (SpawnCharacter) enqueued for dimension {dimensionId} at {pos}.");
EventDefs = DefineManager.Instance.QueryDefinesByType<EventDef>();
}
public void EnqueueBuildingSpawnEvent(string dimensionId, EventDef eventDef, Vector3Int gridPos)
public void Clear()
{
if (eventDef == null || eventDef.eventType != EventType.SpawnBuilding ||
eventDef.entityDef_Building == null)
{
Debug.LogError(
$"EnqueueBuildingSpawnEvent: Invalid EventDef, mismatched EventType ({eventDef?.eventType}), or missing buildingDef for event '{eventDef?.defName ?? "Unknown"}'.");
EventDefs = null;
}
public static void ExecuteEvent(EventDef eventDef)
{
if(eventDef == null)
return;
}
_eventQueue.Enqueue(new EventPayload
if (eventDef.hediffEvent != null)
{
DimensionId = dimensionId,
EventDefinition = eventDef,
GridPosition = gridPos,
});
Debug.Log($"Event '{eventDef.defName}' (SpawnBuilding) enqueued for dimension {dimensionId} at grid {gridPos}.");
}
public void EnqueueBulletSpawnEvent(string dimensionId, EventDef eventDef, Vector3 pos, Vector3 dir,
Entity.Entity source = null)
{
if (eventDef == null || eventDef.eventType != EventType.SpawnBullet || eventDef.entityDef_Bullet == null)
{
Debug.LogError(
$"EnqueueBulletSpawnEvent: Invalid EventDef, mismatched EventType ({eventDef?.eventType}), or missing bulletDef for event '{eventDef?.defName ?? "Unknown"}'.");
return;
}
_eventQueue.Enqueue(new EventPayload
{
DimensionId = dimensionId,
EventDefinition = eventDef,
Position = pos,
Direction = dir,
SourceEntity = source,
});
Debug.Log($"Event '{eventDef.defName}' (SpawnBullet) enqueued for dimension {dimensionId} at {pos}, dir {dir}.");
}
public void EnqueuePickupSpawnEvent(string dimensionId, EventDef eventDef, Vector3 pos)
{
if (eventDef == null || eventDef.eventType != EventType.SpawnPickup || eventDef.entityDef_Pickup == null)
{
Debug.LogError(
$"EnqueuePickupSpawnEvent: Invalid EventDef, mismatched EventType ({eventDef?.eventType}), or missing itemDef for event '{eventDef?.defName ?? "Unknown"}'.");
return;
}
_eventQueue.Enqueue(new EventPayload
{
DimensionId = dimensionId,
EventDefinition = eventDef,
Position = pos,
});
Debug.Log($"Event '{eventDef.defName}' (SpawnPickup) enqueued for dimension {dimensionId} at {pos}.");
}
public void EnqueueDefaultEntitySpawnEvent(string dimensionId, EventDef eventDef, Vector3 pos)
{
if (eventDef == null || eventDef.eventType != EventType.SpawnDefaultEntity)
{
Debug.LogError(
$"EnqueueDefaultEntitySpawnEvent: Invalid EventDef or mismatched EventType ({eventDef?.eventType}) for event '{eventDef?.defName ?? "Unknown"}'.");
return;
}
_eventQueue.Enqueue(new EventPayload
{
DimensionId = dimensionId,
EventDefinition = eventDef,
Position = pos,
});
Debug.Log($"Event '{eventDef.defName}' (SpawnDefaultEntity) enqueued for dimension {dimensionId} at {pos}.");
}
// ===================================
// 事件处理方法
// ===================================
/// <summary>
/// 处理所有在队列中的待处理事件。
/// </summary>
public void ProcessEvents()
{
while (_eventQueue.Count > 0)
{
EventPayload eventData = _eventQueue.Dequeue();
if (!Program.Instance.GetDimension(eventData.DimensionId))
var entityList = EntityManage.Instance.FindEntitiesByFaction(Program.Instance.FocusedDimensionId,
eventDef.hediffEvent.affiliation.defName);
List<Entity.Entity> filteredEntitiesTraditional = new();
foreach (var prefab in entityList)
{
Debug.LogWarning(
$"Event '{eventData.EventDefinition.defName}' for dimension {eventData.DimensionId} dropped as dimension is no longer active.");
continue;
var entity = prefab.entity;
if (entity is Character || entity is Monster)
{
filteredEntitiesTraditional.Add(entity);
}
}
// 核心:调用 EntityManage.Instance 的现有公共方法
switch (eventData.EventDefinition.eventType)
var selectedElements = SelectRandomElements_FisherYates(filteredEntitiesTraditional, eventDef.hediffEvent.specificPawnCount);
foreach (var selectedElement in selectedElements)
{
case EventType.SpawnCharacter:
EntityManage.Instance.GenerateEntity(eventData.DimensionId,
eventData.EventDefinition.entityDef_Character, eventData.Position);
break;
case EventType.SpawnBuilding:
EntityManage.Instance.GenerateBuildingEntity(eventData.DimensionId,
eventData.EventDefinition.entityDef_Building, eventData.GridPosition);
break;
case EventType.SpawnBullet:
EntityManage.Instance.GenerateBulletEntity(eventData.DimensionId,
eventData.EventDefinition.entityDef_Bullet, eventData.Position, eventData.Direction,
eventData.SourceEntity);
break;
case EventType.SpawnPickup:
EntityManage.Instance.GeneratePickupEntity(eventData.DimensionId,
eventData.EventDefinition.entityDef_Pickup, eventData.Position);
break;
case EventType.SpawnDefaultEntity:
EntityManage.Instance.GenerateDefaultEntity(eventData.DimensionId, eventData.Position);
break;
case EventType.None:
default:
Debug.LogWarning(
$"EventManager: Unhandled or invalid event type: {eventData.EventDefinition.eventType} for event '{eventData.EventDefinition.defName}'.");
break;
}
}
}
protected override void OnStart()
public static List<T> SelectRandomElements_FisherYates<T>(List<T> sourceList, int count)
{
throw new System.NotImplementedException();
if (sourceList == null) throw new ArgumentNullException(nameof(sourceList));
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "Count cannot be negative.");
if (count == 0) return new List<T>();
if (count >= sourceList.Count) return new List<T>(sourceList); // 如果n大于等于列表大小则返回所有元素副本
List<T> result = new List<T>(count); // 预分配容量
List<T> temp = new List<T>(sourceList); // 创建一个副本以避免修改原始列表
int n = temp.Count;
for (int i = 0; i < count; i++)
{
int k = _random.Next(i, n);
result.Add(temp[k]);
(temp[k], temp[i]) = (temp[i], temp[k]);
}
return result;
}
}
}

View File

@ -22,20 +22,14 @@ namespace Managers
public Sprite defaultSprite;
/// <summary>
/// 存储所有已加载的纹理,按包ID和图像名称索引。
/// 存储所有已加载的纹理,按图像名称全局唯一的DefName索引。
/// </summary>
/// <remarks>
/// 外层字典键是包ID内层字典键是图像名称。
/// </remarks>
public Dictionary<string, Dictionary<string, Texture2D>> packagesImages = new();
public Dictionary<string, Texture2D> packagesImages = new();
/// <summary>
/// 存储所有已创建的精灵,按包ID和精灵名称索引。
/// 存储所有已创建的精灵,按精灵名称全局唯一的DefName或其带索引后缀索引。
/// </summary>
/// <remarks>
/// 外层字典键是包ID内层字典键是精灵名称如果纹理被分割会包含索引后缀
/// </remarks>
public Dictionary<string, Dictionary<string, Sprite>> sprites = new();
public Dictionary<string, Sprite> sprites = new();
/// <summary>
/// 获取当前启动步骤的描述。
@ -49,21 +43,15 @@ namespace Managers
{
if (packagesImages.Count > 0)
{
// 如果已经加载过,直接返回
StepDescription = "包图像管理器已初始化。";
// 如果已经有数据,则跳过初始化,防止重复加载。
return;
}
StepDescription = "正在加载默认精灵..."; // 更新加载步骤描述
defaultSprite = Resources.Load<Sprite>("Default/DefaultImage");
if (defaultSprite == null)
{
Debug.LogWarning("无法加载默认精灵 'Resources/Default/DefaultImage'。请确保文件存在。");
}
StepDescription = "正在处理图像定义并创建精灵..."; // 更新加载步骤描述
InitImageDef();
StepDescription = "包图像管理器初始化完成。"; // 完成加载
}
/// <summary>
@ -76,7 +64,7 @@ namespace Managers
if (imageDef == null || !imageDef.Any())
{
Debug.Log($"在 DefineManager 中未找到任何 ImageDef 定义。({typeof(ImageDef).Name})");
Debug.Log($"在定义管理器中未找到任何图像定义。({nameof(ImageDef)})");
return;
}
@ -84,7 +72,7 @@ namespace Managers
{
if (string.IsNullOrEmpty(ima.path) || string.IsNullOrEmpty(ima.packID))
{
Debug.LogWarning($"跳过图像定义 '{ima?.name ?? ""}'因为它包含空路径或包ID。(路径: '{ima?.path ?? ""}', 包ID: '{ima?.packID ?? ""}')");
Debug.LogWarning($"跳过图像定义 '{ima?.defName ?? ""}'因为它包含空路径或包ID。(路径: '{ima?.path ?? ""}', 包ID: '{ima?.packID ?? ""}')");
continue;
}
@ -119,7 +107,7 @@ namespace Managers
var packageRoot = Managers.DefineManager.Instance.GetPackagePath(packageID);
if (string.IsNullOrEmpty(packageRoot))
{
Debug.LogWarning($"图像定义 '{ima.name}' (包ID: {ima.packID}): 引用的包ID '{packageID}' 未找到或没有根路径。跳过图像加载。");
Debug.LogWarning($"图像定义 '{ima.defName}' (包ID: {ima.packID}): 引用的包ID '{packageID}' 未找到或没有根路径。跳过图像加载。");
continue;
}
@ -140,7 +128,7 @@ namespace Managers
var pack = Managers.DefineManager.Instance.GetDefinePackage(ima);
if (pack == null)
{
Debug.LogError($"图像定义 '{ima.name}' (包ID: {ima.packID}): 源图像未找到对应的定义包。无法确定 '{ima.path}' 的完整路径。跳过。");
Debug.LogError($"图像定义 '{ima.defName}' (包ID: {ima.packID}): 源图像未找到对应的定义包。无法确定 '{ima.path}' 的完整路径。跳过。");
continue;
}
var fullPath = Path.Combine(pack.packRootPath, ima.path).Replace('\\', '/');
@ -158,31 +146,20 @@ namespace Managers
// 资源加载失败
if (!texture)
{
Debug.LogError($"未能加载图像定义所在的纹理: '{ima.name}' (路径: '{ima.path}', 包ID: '{ima.packID}')。请验证路径和文件是否存在。");
Debug.LogError($"未能加载图像定义关联的纹理: '{ima.defName}' (路径: '{ima.path}', 包ID: '{ima.packID}')。请验证路径和文件是否存在。");
continue;
}
// 存储到包纹理字典(使用定义自身的 packID
var packId = ima.packID;
if (!packagesImages.ContainsKey(packId))
packagesImages[packId] = new Dictionary<string, Texture2D>();
// 警告:如果图片名重复,则覆盖
if (packagesImages[packId].ContainsKey(ima.name))
{
Debug.LogWarning($"包 '{packId}' 中名为 '{ima.name}' 的图像被多次定义。将覆盖之前的纹理引用。这可能表示配置错误。");
}
packagesImages[packId][ima.name] = texture; // 覆盖或添加
packagesImages[ima.defName] = texture;
// 切分精灵
SplitTextureIntoSprites(packId, ima.name, texture, ima.hCount, ima.wCount, ima.pixelsPerUnit);
SplitTextureIntoSprites(ima.defName, texture, ima.hCount, ima.wCount, ima.pixelsPerUnit);
}
catch (Exception ex)
{
// 捕获异常并打印详细错误信息
Debug.LogError(
$"处理图像定义时出错: '{ima.name}' (路径: '{ima.path}', 包ID: '{ima.packID}')。异常: {ex.GetType().Name}: {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
$"处理图像定义时出错: '{ima.defName}' (路径: '{ima.path}', 包ID: '{ima.packID}')。异常: {ex.GetType().Name}: {ex.Message}\n堆栈跟踪: {ex.StackTrace}");
}
}
}
@ -190,14 +167,12 @@ namespace Managers
/// <summary>
/// 将纹理按指定行数和列数分割成多个精灵,并存储起来。
/// </summary>
/// <param name="packId">精灵所属的包ID。</param>
/// <param name="baseName">精灵的基础名称。</param>
/// <param name="baseName">精灵的基础名称全局唯一的DefName。</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,
Texture2D texture,
int rows,
@ -206,7 +181,7 @@ namespace Managers
{
if (!texture)
{
Debug.LogError($"SplitTextureIntoSprites: 包 '{packId}' 中 '{baseName}' 提供的纹理为空。无法分割。");
Debug.LogError($"SplitTextureIntoSprites: '{baseName}' 提供的纹理为空。无法分割。");
return;
}
@ -217,19 +192,11 @@ namespace Managers
var textureWidth = texture.width;
var textureHeight = texture.height;
if (!sprites.ContainsKey(packId))
sprites[packId] = new Dictionary<string, Sprite>();
// 创建未分割的完整精灵,使用原始名称
// 创建未分割的完整精灵,使用原始名称 (baseName即 ImageDef.name)
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; // 确保 Sprite.name 被设置
sprites[packId][baseName] = fullSprite;
sprites[baseName] = fullSprite;
// 如果不分割rows和cols都为1提前返回
if (rows == 1 && cols == 1)
@ -240,7 +207,7 @@ namespace Managers
// 检查纹理尺寸是否可被分割数整除
if (textureWidth % cols != 0 || textureHeight % rows != 0)
{
Debug.LogError($"包 '{packId}' 中 '{baseName}' 的纹理尺寸 ({textureWidth}x{textureHeight}) 不能被指定的行数 ({rows}) 和列数 ({cols}) 完美整除。子精灵将不会生成或可能不正确。仅显示完整精灵。");
Debug.LogError($"'{baseName}' 的纹理尺寸 ({textureWidth}x{textureHeight}) 不能被指定的行数 ({rows}) 和列数 ({cols}) 完美整除。子精灵将不会生成或可能不正确。仅显示完整精灵。");
return; // 终止子精灵分割,只保留完整的精灵
}
@ -253,18 +220,11 @@ namespace Managers
{
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 spriteName = $"{baseName}_{index}";
sprite.name = spriteName;
// 警告:如果子精灵名重复,则覆盖
if (sprites[packId].ContainsKey(spriteName))
{
Debug.LogWarning($"包 '{packId}' 中名为 '{spriteName}' 的精灵已存在。将覆盖之前的子精灵定义。这可能表示配置错误。");
}
sprites[packId][spriteName] = sprite;
sprites[spriteName] = sprite;
}
}
}
@ -299,36 +259,24 @@ namespace Managers
/// <summary>
/// 根据 <see cref="ImageDef"/> 对象获取对应的精灵。
/// </summary>
/// <param name="ima">包含精灵包ID和名称的 <see cref="ImageDef"/> 对象。</param>
/// <param name="ima">包含精灵名称的 <see cref="ImageDef"/> 对象。</param>
/// <returns>如果找到对应的精灵,则返回该精灵;否则返回 <see cref="defaultSprite"/>。</returns>
public Sprite GetSprite(ImageDef ima)
{
if (ima == null) return defaultSprite;
return GetSprite(ima.packID, ima.name);
return GetSprite(ima.packID, ima.defName);
}
/// <summary>
/// 根据包ID和精灵名称获取对应的精灵。
/// </summary>
/// <param name="packID">精灵所属的包ID。如果为空,则会遍历所有包查找。</param>
/// <param name="name">精灵的名称。</param>
/// <param name="packID">精灵所属的包ID。此参数在此版本中已不再用于字典查找,但为保持兼容性而保留。</param>
/// <param name="name">精灵的名称全局唯一的DefName。</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))
return sprite;
}
}
else if (sprites.TryGetValue(packID, out var dict))
{
if (dict.TryGetValue(name, out var sprite))
return sprite;
}
if (sprites.TryGetValue(name, out var sprite))
return sprite;
// 如果未找到,返回默认精灵
return defaultSprite;
@ -337,14 +285,40 @@ namespace Managers
/// <summary>
/// 根据包ID、基础名称和索引获取被分割的子精灵。
/// </summary>
/// <param name="packID">精灵所属的包ID。</param>
/// <param name="name">精灵的基础名称。</param>
/// <param name="packID">精灵所属的包ID。此参数在此版本中已不再用于字典查找,但为保持兼容性而保留。</param>
/// <param name="name">精灵的基础名称全局唯一的DefName。</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);
return GetSprite(packID, fullName);
}
// ---------- 新增的查询接口 ----------
/// <summary>
/// 根据精灵名称全局唯一的DefName获取对应的精灵。
/// </summary>
/// <param name="name">精灵的名称。</param>
/// <returns>如果找到对应的精灵,则返回该精灵;否则返回 <see cref="defaultSprite"/>。</returns>
public Sprite GetSprite(string name)
{
if (sprites.TryGetValue(name, out var sprite))
return sprite;
return defaultSprite;
}
/// <summary>
/// 根据基础名称全局唯一的DefName和索引获取被分割的子精灵。
/// </summary>
/// <param name="name">精灵的基础名称。</param>
/// <param name="index">子精灵的索引。</param>
/// <returns>如果找到对应的子精灵,则返回该精灵;否则返回 <see cref="defaultSprite"/>。</returns>
public Sprite GetSprite(string name, int index)
{
var fullName = $"{name}_{index}";
return GetSprite(fullName);
}
}
}