Files

604 lines
29 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.

using System;
using System.Collections.Generic;
using Data;
using Managers;
using Newtonsoft.Json;
using UnityEngine;
using Random = UnityEngine.Random;
namespace EventWorkClass
{
/// <summary>
/// Event_EntityGenerater 事件的配置数据结构。
/// </summary>
[Serializable]
public class EntityGenerateConfig
{
// 要生成的实体定义列表,将从中随机选择。
public List<EntityDefinitionEntry> DefinitionsToChooseFrom;
// 要生成的实体总数。
public int Count = 1;
// 生成位置类型。
public EntitySpawnLocationType LocationType;
// 用于 AroundSpecificCoordinates 类型:生成中心坐标。
public Vector3 CenterCoordinates;
// 用于 AroundSpecificCoordinates 或 AroundTargetEntity 类型:生成半径。
public float Radius = 10f;
// 用于 InsideSpecificBuildingType 类型目标建筑的类型ID。
public string BuildingTypeDefName;
// 用于 AroundTargetEntity 类型:目标派系的定义名 (factionDefName)。
public string TargetFactionDefName;
// 用于 AtPredefinedSpawnPoint 类型预定义生成点的ID。
public string SpawnPointTileMapDefName;
// 用于 OffMap 类型:距离地图边界的额外偏移量。
// (新的解释:从地图边界向内偏移的距离,确保实体生成在地图内部可放置区域)
public float OffMapOffset = 5f;
}
/// <summary>
/// 单个实体定义的配置条目。
/// </summary>
[Serializable]
public class EntityDefinitionEntry
{
// 实体定义名 (defName)。
public string DefName;
// 实体定义的具体类型标识符。
// 已从 string 改为枚举,以增加类型安全。
public EntityDefTypeIdentifier DefTypeName;
}
/// <summary>
/// 定义生成器生成地图实体的可能位置类型。
/// </summary>
public enum EntitySpawnLocationType
{
// 未指定或无效的生成位置类型。
None = 0,
// 在地图的可玩区域边界生成实体(实体将生成在地图内部,靠近边界)。
OffMap,
// 在指定类型建筑的内部或附近生成实体。
InsideSpecificBuildingType,
// 在指定坐标点的周围区域内生成实体。
AroundSpecificCoordinates,
// 在特定派系或其他指定目标实体例如NPC或特定对象的周围区域内生成实体。
AroundTargetEntity,
// 在地图上的随机可生成点生成实体。
RandomlyOnMap,
// 在预定义的生成点Spawn Point生成实体。
AtPredefinedSpawnPoint,
//调用者位置生成
AtCallerPosition
}
/// <summary>
/// 支持的实体定义类型标识符。
/// </summary>
public enum EntityDefTypeIdentifier
{
/// <summary>
/// 未指定或无效的实体定义类型。
/// </summary>
None = 0,
/// <summary>
/// 角色定义类型。
/// </summary>
CharacterDef,
/// <summary>
/// 怪物定义类型。
/// </summary>
MonsterDef,
/// <summary>
/// 建筑定义类型。
/// </summary>
BuildingDef,
/// <summary>
/// 子弹定义类型。
/// </summary>
BulletDef,
/// <summary>
/// 物品定义类型(用于可拾取物)。
/// </summary>
ItemDef,
/// <summary>
/// 武器定义类型。
/// </summary>
WeaponDef // 新增 WeaponDef 类型
// 根据您的Def命名约定和需要添加其他Def类型。
}
public class Event_EntityGenerater : EventWorkClassBase
{
private EntityGenerateConfig _config;
// _validatedEntityDefs 类型从 List<EntityDef> 变为 List<Define>
private List<Define> _validatedEntityDefs; // 用于存储所有已验证的实体定义
/// <summary>
/// 初始化实体生成器事件。
/// </summary>
/// <param name="value">包含事件配置的JSON字符串。</param>
public override void Init(string value)
{
if (string.IsNullOrEmpty(value))
{
Debug.LogError("Event_EntityGenerater.Init初始化值为空或null。请提供一个JSON配置字符串。");
return;
}
try
{
_config = JsonConvert.DeserializeObject<EntityGenerateConfig>(value);
}
catch (Exception ex)
{
Debug.LogError($"Event_EntityGenerater.Init解析配置JSON时出错: {value}。异常信息: {ex.Message}");
return;
}
if (_config == null)
{
Debug.LogError($"Event_EntityGenerater.Init无法解析配置JSON: {value}");
return;
}
if (_config.DefinitionsToChooseFrom == null || _config.DefinitionsToChooseFrom.Count == 0)
{
// 更具体地说明为何有问题
Debug.LogError(
"Event_EntityGenerater.Init实体生成配置中没有定义任何要生成的实体。请检查 'DefinitionsToChooseFrom' 列表。事件初始化失败。");
return; // 如果没有配置直接返回避免后续NPE
}
// _validatedEntityDefs 初始化类型从 List<EntityDef> 变为 List<Define>
_validatedEntityDefs = new List<Define>();
foreach (var entry in _config.DefinitionsToChooseFrom)
{
// 确保 DefName 不为空
if (string.IsNullOrEmpty(entry.DefName))
{
// 细化日志,指明来源方法
Debug.LogWarning($"Event_EntityGenerater.Init实体定义名为空或null (类型标识符: '{entry.DefTypeName}')。跳过此定义。");
continue;
}
// 确保枚举类型不是 None否则无法确定查找哪种Def
if (entry.DefTypeName == EntityDefTypeIdentifier.None)
{
// 细化日志,指明来源方法
Debug.LogWarning($"Event_EntityGenerater.Init实体定义类型标识符为 None (实体定义名: '{entry.DefName}')。跳过此定义。");
continue;
}
Define entityDef = null;
// 使用泛型 FindDefine<T>(defName) 获取定义
switch (entry.DefTypeName)
{
case EntityDefTypeIdentifier.CharacterDef:
entityDef = DefineManager.Instance.FindDefine<CharacterDef>(entry.DefName);
break;
case EntityDefTypeIdentifier.MonsterDef:
entityDef = DefineManager.Instance.FindDefine<MonsterDef>(entry.DefName);
break;
case EntityDefTypeIdentifier.BuildingDef:
entityDef = DefineManager.Instance.FindDefine<BuildingDef>(entry.DefName);
break;
case EntityDefTypeIdentifier.BulletDef:
entityDef = DefineManager.Instance.FindDefine<BulletDef>(entry.DefName);
break;
case EntityDefTypeIdentifier.ItemDef:
entityDef = DefineManager.Instance.FindDefine<ItemDef>(entry.DefName);
break;
// 新增:处理 WeaponDef 类型
case EntityDefTypeIdentifier.WeaponDef:
entityDef = DefineManager.Instance.FindDefine<WeaponDef>(entry.DefName);
break;
default:
// 细化日志,指明来源方法并提供更多上下文
Debug.LogWarning(
$"Event_EntityGenerater.Init不支持的实体定义类型标识符 '{entry.DefTypeName}' (实体定义名: '{entry.DefName}')。跳过此定义。");
continue; // 跳过此定义,因为它无法被匹配到已知类型
}
if (entityDef == null)
// 细化日志,指明来源方法
Debug.LogWarning(
$"Event_EntityGenerater.Init未找到实体定义 (名称: '{entry.DefName}', 类型标识符: '{entry.DefTypeName}')。请检查配置或类型。跳过此定义。");
else
// 修改:直接添加 Define 对象到 _validatedEntityDefs无需强制转换
_validatedEntityDefs.Add(entityDef);
}
if (_validatedEntityDefs.Count == 0)
// 提高错误级别并提供清晰的后果说明
Debug.LogError("Event_EntityGenerater.Init所有配置的实体定义都无效或未找到。事件初始化失败。事件将无法在Run时生成任何实体。");
}
/// <summary>
/// 运行实体生成器事件,在指定维度生成实体。
/// </summary>
/// <param name="dimensionID">要生成实体的维度ID。</param>
/// <param name="initiator">发起者</param>
public override void Run(string dimensionID, Entity.Entity initiator = null)
{
if (_config == null)
{
Debug.LogError("Event_EntityGenerater.Run事件配置(_config)为空。Init()可能失败了。");
return;
}
if (_validatedEntityDefs == null || _validatedEntityDefs.Count == 0)
{
Debug.LogError("Event_EntityGenerater.Run没有有效实体定义可供生成。请检查Init()方法和配置。");
return;
}
const int MAX_RETRY_ATTEMPTS = 10;
for (var i = 0; i < _config.Count; i++)
{
var retryCount = 0;
var generated = false;
// 随机选择一个实体定义
var selectedEntityDef = _validatedEntityDefs[Random.Range(0, _validatedEntityDefs.Count)];
// 尝试生成,最多重试 MAX_RETRY_ATTEMPTS 次
while (retryCount < MAX_RETRY_ATTEMPTS)
{
// 获取生成位置
var position = GetPosition(dimensionID,initiator);
if (position == Vector3.zero) // 如果 GetPosition 失败,直接跳过本次生成
{
Debug.LogWarning($"Event_EntityGenerater.Run: 无法获取有效的生成位置。尝试下一次生成。");
retryCount++;
continue;
}
// 检查移动成本是否允许生成(成本 < 1 才允许)
// 对于需要放置在地图内的实体,此检查是必要的。
var cost = Program.Instance.GetDimension(dimensionID).landform.GetTravelCost(position);
if (cost >= 1) // 假设 cost >= 1 表示不可通行或不可放置
{
retryCount++;
continue; // 重试
}
position.z = 0;
switch (selectedEntityDef)
{
case CharacterDef characterDef:
EntityManager.Instance.GenerateCharacterEntity(dimensionID, characterDef, position);
generated = true;
break;
case MonsterDef monsterDef:
EntityManager.Instance.GenerateMonsterEntity(dimensionID, monsterDef, position);
generated = true;
break;
case BuildingDef buildingDef:
EntityManager.Instance.GenerateBuildingEntity(dimensionID, buildingDef, position);
generated = true;
break;
case BulletDef bulletDef:
EntityManager.Instance.GenerateBulletEntity(dimensionID, bulletDef, position,
Vector3.right); // 子弹生成可能不需要严格的 walkable 检查,但为了保持一致性。
generated = true;
break;
case ItemDef itemDef:
EntityManager.Instance.GeneratePickupEntity(dimensionID, itemDef, position);
generated = true;
break;
default:
Debug.LogWarning(
$"Event_EntityGenerater.Run目标实体 '{selectedEntityDef.defName}' (实际类型: {selectedEntityDef.GetType().Name}) 未匹配到任何已知的实体生成逻辑。未生成此实体。");
generated = false; // 未成功生成(类型不支持)
break;
}
if (generated) break;
retryCount++;
}
if (!generated)
{
Debug.LogWarning($"Event_EntityGenerater.Run: 尝试 {MAX_RETRY_ATTEMPTS} 次后仍无法成功生成实体 (预期生成 {selectedEntityDef?.defName ?? ""})。本次生成失败。");
}
}
}
/// <summary>
/// 根据配置获取实体生成位置。
/// </summary>
/// <param name="dimensionID">要获取位置的维度ID。</param>
/// <returns>计算出的生成位置,如果失败则返回 Vector3.zero。</returns>
private Vector3 GetPosition(string dimensionID,Entity.Entity initiator = null)
{
if (_config == null)
{
// 细化日志,指明来源方法
Debug.LogError("Event_EntityGenerater.GetPosition获取位置时配置(_config)为空。返回 Vector3.zero。");
return Vector3.zero;
}
var dimension = Program.Instance.GetDimension(dimensionID);
if (!dimension)
{
// 细化日志,指明来源方法
Debug.LogError($"Event_EntityGenerater.GetPosition未找到维度 '{dimensionID}'。无法确定生成位置。返回 Vector3.zero。");
return Vector3.zero;
}
var mapGenerator = dimension.landform;
if (!mapGenerator)
{
// 细化日志,指明来源方法
Debug.LogError(
$"Event_EntityGenerater.GetPosition维度 '{dimensionID}' 的地形 (landform) 为空。无法确定生成位置。返回 Vector3.zero。");
return Vector3.zero;
}
switch (_config.LocationType)
{
// 逻辑修改点 1: 优化 OffMap 位置生成逻辑
case EntitySpawnLocationType.OffMap:
{
var mapGridSize = mapGenerator.GetSize();
const float tileWorldHalfSize = 0.5f; // 假设每个瓦片在世界空间中的大小是1x1GetWorldCoordinates返回瓦片中心
// 计算地图的实际可玩世界边界(包含边界瓦片)
Vector2 worldMinTileCenter = mapGenerator.GetWorldCoordinates(Vector3Int.zero);
Vector2 worldMaxTileCenter = mapGenerator.GetWorldCoordinates(new Vector3Int(mapGridSize.x - 1, mapGridSize.y - 1));
var mapWorldMinX = worldMinTileCenter.x - tileWorldHalfSize; // 地图的实际左边界
var mapWorldMaxX = worldMaxTileCenter.x + tileWorldHalfSize; // 地图的实际右边界
var mapWorldMinY = worldMinTileCenter.y - tileWorldHalfSize; // 地图的实际下边界
var mapWorldMaxY = worldMaxTileCenter.y + tileWorldHalfSize; // 地图的实际上边界
var dir = (OffMapDirection)Random.Range(0, 4); // 随机选择一个方向
Vector3 worldPos3D;
// 确保_config.OffMapOffset至少有一个小值以便实体能进入地图内部
var safeOffset = Mathf.Max(0.1f, _config.OffMapOffset);
// 定义在另一个维度上的随机范围,考虑到偏移量
var minSpanX = mapWorldMinX + safeOffset;
var maxSpanX = mapWorldMaxX - safeOffset;
var minSpanY = mapWorldMinY + safeOffset;
var maxSpanY = mapWorldMaxY - safeOffset;
// 检查偏移量是否过大导致有效生成范围无效
if (minSpanX > maxSpanX || minSpanY > maxSpanY)
{
Debug.LogWarning(
$"Event_EntityGenerater.GetPosition: OffMapOffset ({_config.OffMapOffset}) 对于地图尺寸来说太大了。尝试使用最小偏移量。");
safeOffset = 0.1f; // 回退到最小偏移量
minSpanX = mapWorldMinX + safeOffset;
maxSpanX = mapWorldMaxX - safeOffset;
minSpanY = mapWorldMinY + safeOffset;
maxSpanY = mapWorldMaxY - safeOffset;
// 如果即使使用最小偏移量,地图也无法提供有效范围,则返回地图中心
if (minSpanX > maxSpanX || minSpanY > maxSpanY) {
Debug.LogError("Event_EntityGenerater.GetPosition: 地图太小,即使使用最小 OffMapOffset 也无法正确生成。返回地图中心。");
return new Vector3(mapWorldMinX + (mapWorldMaxX - mapWorldMinX) / 2, mapWorldMinY + (mapWorldMaxY - mapWorldMinY) / 2, 0f);
}
}
// 根据选择的方向,在地图边界内部偏移量处生成随机点
switch (dir)
{
case OffMapDirection.Up: // 上边界 (Y 靠近 mapWorldMaxY)
worldPos3D = new Vector3(Random.Range(minSpanX, maxSpanX), mapWorldMaxY - safeOffset, 0f);
break;
case OffMapDirection.Down: // 下边界 (Y 靠近 mapWorldMinY)
worldPos3D = new Vector3(Random.Range(minSpanX, maxSpanX), mapWorldMinY + safeOffset, 0f);
break;
case OffMapDirection.Left: // 左边界 (X 靠近 mapWorldMinX)
worldPos3D = new Vector3(mapWorldMinX + safeOffset, Random.Range(minSpanY, maxSpanY), 0f);
break;
case OffMapDirection.Right: // 右边界 (X 靠近 mapWorldMaxX)
worldPos3D = new Vector3(mapWorldMaxX - safeOffset, Random.Range(minSpanY, maxSpanY), 0f);
break;
default:
Debug.LogError("Event_EntityGenerater.GetPositionOffMap 生成方向无效。不应该发生此情况。返回 Vector3.zero。");
return Vector3.zero;
}
// 在指定偏移量附近再增加一些随机性,使生成更自然,但要确保仍在 clamped 范围内,
// 这里不做,因为上面的随机范围已经考虑了,再加可能推出去。
// 如果需要,可以在这里增加一个小的随机扰动,然后调用 GetClampedWorldPosition 确保不越界。
return worldPos3D; // 返回在地图边界内部的有效世界坐标
}
case EntitySpawnLocationType.AroundSpecificCoordinates:
{
var worldPos = Program.Instance.GetDimension(dimensionID)?.landform
.GetWorldCoordinates(new Vector3Int((int)_config.CenterCoordinates.x,(int) _config.CenterCoordinates.y));
if (worldPos != null)
{
var rawPosition = GetRandomPositionInCircle(worldPos.Value, _config.Radius);
return GetClampedWorldPosition(rawPosition, dimensionID);
}
return GetClampedWorldPosition(Vector3.one, dimensionID);
}
case EntitySpawnLocationType.AroundTargetEntity:
{
if (string.IsNullOrEmpty(_config.TargetFactionDefName))
{
// 细化日志,指明来源方法
Debug.LogWarning(
"Event_EntityGenerater.GetPosition配置了 'AroundTargetEntity',但 'TargetFactionDefName' 为空或null。无法找到派系实体。返回 Vector3.zero。");
return Vector3.zero;
}
var factionEntities =
EntityManager.Instance.FindEntitiesByFaction(dimensionID, _config.TargetFactionDefName);
if (factionEntities == null || factionEntities.Length == 0)
{
// 细化日志,指明来源方法
Debug.LogWarning(
$"Event_EntityGenerater.GetPosition在维度 '{dimensionID}' 中未找到派系 '{_config.TargetFactionDefName}' 的任何实体。返回 Vector3.zero。");
return Vector3.zero;
}
var targetEntity = factionEntities[Random.Range(0, factionEntities.Length)];
// 检查随机选择的实体是否有效且有Transform组件
if (!targetEntity || !targetEntity.transform)
{
// 细化日志,指明来源方法
Debug.LogWarning(
$"Event_EntityGenerater.GetPosition从派系 '{_config.TargetFactionDefName}' 中随机选择的实体为空或没有Transform组件。返回 Vector3.zero。");
return Vector3.zero;
}
// 调用辅助方法 GetRandomPositionInCircle
var rawPosition = GetRandomPositionInCircle(targetEntity.transform.position, _config.Radius);
return GetClampedWorldPosition(rawPosition, dimensionID);
}
case EntitySpawnLocationType.RandomlyOnMap:
{
var size = mapGenerator.GetSize();
var randomGridPos = new Vector3Int(Random.Range(0, size.x), Random.Range(0, size.y));
var worldCoord2D = mapGenerator.GetWorldCoordinates(randomGridPos);
var worldPos3D = new Vector3(worldCoord2D.x, worldCoord2D.y, 0f);
return worldPos3D;
}
case EntitySpawnLocationType.InsideSpecificBuildingType:
{
// 细化日志,指明来源方法
Debug.LogWarning(
$"Event_EntityGenerater.GetPosition类型为 'InsideSpecificBuildingType' ({_config.BuildingTypeDefName ?? ""}) 的生成逻辑尚未实现。返回 Vector3.zero。");
return Vector3.zero;
}
case EntitySpawnLocationType.AtPredefinedSpawnPoint:
{
// 细化日志,指明来源方法
Debug.LogWarning(
$"Event_EntityGenerater.GetPosition类型为 'AtPredefinedSpawnPoint' ({_config.SpawnPointTileMapDefName ?? ""}) 的生成逻辑尚未实现。返回 Vector3.zero。");
return Vector3.zero;
}
case EntitySpawnLocationType.AtCallerPosition:
{
if (initiator == null || initiator.transform == null)
{
Debug.LogWarning(
"Event_EntityGenerater.GetPosition配置了 'AtCallerPosition'但发起者实体或其Transform为空。返回 Vector3.zero。");
return Vector3.zero;
}
return initiator.Position;
}
case EntitySpawnLocationType.None:
default:
// 细化日志,指明来源方法
Debug.LogWarning(
$"Event_EntityGenerater.GetPosition未知或不支持的生成位置类型: {_config.LocationType}。返回 Vector3.zero。");
return Vector3.zero;
}
}
/// <summary>
/// 在指定中心点周围半径内获取一个随机世界坐标。
/// </summary>
/// <param name="center">中心坐标。</param>
/// <param name="radius">半径。</param>
/// <returns>随机生成的坐标。</returns>
private Vector3 GetRandomPositionInCircle(Vector3 center, float radius)
{
if (radius <= 0f)
// 如果半径无效,直接返回中心点。可以根据需求选择是否打印警告。
// Debug.LogWarning($"Event_EntityGenerater.GetRandomPositionInCircle半径为 {radius},将直接返回中心点 {center}。");
return center;
var randomOffset = Random.insideUnitCircle * radius; // 在圆形区域内随机偏移
return new Vector3(center.x + randomOffset.x, center.y + randomOffset.y, center.z);
}
/// <summary>
/// 将给定的世界坐标限制在指定维度的地图可玩区域边界内。
/// 假设 mapGenerator.GetWorldCoordinates(gridPos) 返回瓦片中心,且瓦片世界大小为 1x1 单位。
/// </summary>
/// <param name="worldPosition">待限制的世界坐标。</param>
/// <param name="dimensionID">目标维度ID。</param>
/// <returns>限制在地图边界内的世界坐标。</returns>
private Vector3 GetClampedWorldPosition(Vector3 worldPosition, string dimensionID)
{
// Program.Instance 是一个单例,根据约定不进行空检查
var dimension = Program.Instance.GetDimension(dimensionID);
if (!dimension)
{
// 细化日志,指明来源方法
Debug.LogError($"Event_EntityGenerater.GetClampedWorldPosition无法获取维度 '{dimensionID}' 来限制位置。返回原始位置。");
return worldPosition;
}
var mapGenerator = dimension.landform;
if (!mapGenerator)
{
// 细化日志,指明来源方法
Debug.LogError(
$"Event_EntityGenerater.GetClampedWorldPosition维度 '{dimensionID}' 的地形 (landform) 为空。无法限制位置。返回原始位置。");
return worldPosition;
}
var mapGridSize = mapGenerator.GetSize();
// 假设每个瓦片在世界空间中的大小是1x1GetWorldCoordinates返回瓦片中心
var tileWorldHalfSize = 0.5f;
// 计算地图的最小和最大世界坐标(包含边界瓦片)
Vector2 worldMinTileCenter = mapGenerator.GetWorldCoordinates(Vector3Int.zero);
Vector2 worldMaxTileCenter =
mapGenerator.GetWorldCoordinates(new Vector3Int(mapGridSize.x - 1, mapGridSize.y - 1));
var worldMinX = worldMinTileCenter.x - tileWorldHalfSize; // 地图的实际左边界
var worldMaxX = worldMaxTileCenter.x + tileWorldHalfSize; // 地图的实际右边界
var worldMinY = worldMinTileCenter.y - tileWorldHalfSize; // 地图的实际下边界
var worldMaxY = worldMaxTileCenter.y + tileWorldHalfSize; // 地图的实际上边界
var clampedPosition = worldPosition;
clampedPosition.x = Mathf.Clamp(worldPosition.x, worldMinX, worldMaxX);
clampedPosition.y = Mathf.Clamp(worldPosition.y, worldMinY, worldMaxY);
if (clampedPosition != worldPosition)
// 细化日志指明来源方法并增加_config为null的健壮性检查
Debug.LogWarning(
$"Event_EntityGenerater.GetClampedWorldPosition原始生成位置 {worldPosition} (LocationType: {_config?.LocationType.ToString() ?? ""}) 超出地图可玩区域边界,已限制为 {clampedPosition}。");
return clampedPosition;
}
// 辅助枚举OffMap 生成方向
private enum OffMapDirection
{
Up,
Down,
Left,
Right
}
}
}