(client) feat:添加基地界面到游玩界面的过程,添加存档管理,技能树变得可用 (#58)

Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com>
Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/58
This commit is contained in:
2025-10-03 00:31:34 +08:00
parent aff747be17
commit dd9d90439d
134 changed files with 10322 additions and 4872 deletions

View File

@@ -157,6 +157,7 @@ namespace EventWorkClass
Debug.LogWarning($"实体定义类型名为空或null (实体定义名: '{entry.DefName}')。跳过此定义。");
continue;
}
// 遵循“单例总是有效”的指示,不检查 DefineManager.Instance 是否为 null
var entityDef = (EntityDef)DefineManager.Instance.FindDefine(entry.DefTypeName, entry.DefName);
if (entityDef == null)
{
@@ -191,23 +192,31 @@ namespace EventWorkClass
return;
}
// 遵循“单例总是有效”的指示,不检查 EntityManager.Instance 是否为 null
// 如果 EntityManager.Instance 为 null则表明单例系统存在问题
// 此时应由单例系统自身处理其初始化错误,而不是每次使用时都进行防御性检查。
// 例如,如果 EntityManager.Instance 本身就是 null下面的 GenerateCharacterEntity 将抛出 NullReferenceException。
// 根据“单例总是有效”的指示,我们将此视为单例系统层面的设计或实现缺陷,而非 Event_EntityGenerater 的问题。
for (int i = 0; i < _config.Count; i++)
for (var i = 0; i < _config.Count; i++)
{
// 随机选择一个实体定义
var selectedEntityDef = _validatedEntityDefs[Random.Range(0, _validatedEntityDefs.Count)];
var position = GetPosition(dimensionID);
// 检查 GetPosition 是否返回了有效的非零位置,除非它是 AroundSpecificCoordinates 且中心点就是 Vector3.zero
if (position == Vector3.zero && (_config.LocationType != EntitySpawnLocationType.AroundSpecificCoordinates
|| _config.CenterCoordinates != Vector3.zero))
// 逻辑修改点1检查 GetPosition 是否返回了有效的非零位置,或者其零点是明确配置的。
// 如果 position 为 Vector3.zero 且不是由 AroundSpecificCoordinates 且中心点为 Vector3.zero 引起的,则跳过本次生成。
var isZeroDueToError = (position == Vector3.zero && (_config.LocationType != EntitySpawnLocationType.AroundSpecificCoordinates
|| _config.CenterCoordinates != Vector3.zero));
if (isZeroDueToError)
{
Debug.LogWarning($"未能为类型 {_config.LocationType} 获取有效的生成位置。实体可能在原点 (0,0,0) 生成。");
Debug.LogWarning($"未能为类型 {_config.LocationType} 获取有效的生成位置。跳过本次实体生成。");
continue; // 跳过本次循环,不生成实体
}
if (selectedEntityDef is CharacterDef characterDef)
{
EntityManager.Instance.GenerateEntity(dimensionID, characterDef, position);
EntityManager.Instance.GenerateCharacterEntity(dimensionID, characterDef, position);
continue;
}
@@ -233,6 +242,7 @@ namespace EventWorkClass
Debug.LogError("获取位置时配置为空。返回 Vector3.zero。");
return Vector3.zero;
}
// 遵循“单例总是有效”的指示,不检查 Program.Instance 是否为 null
var dimension = Program.Instance.GetDimension(dimensionID);
if (dimension == null)
{
@@ -240,9 +250,10 @@ namespace EventWorkClass
return Vector3.zero;
}
var mapGenerator = dimension.landform;
if (mapGenerator == null)
// 逻辑修改点2更准确的错误信息
if (mapGenerator == null)
{
Debug.LogError($"维度 '{dimensionID}' 的地图生成器为空。无法确定生成位置。");
Debug.LogError($"维度 '{dimensionID}' 的地形 (landform) 为空。无法确定生成位置。"); // 错误信息更清晰
return Vector3.zero;
}
switch (_config.LocationType)
@@ -294,7 +305,9 @@ namespace EventWorkClass
var center = _config.CenterCoordinates;
var radius = _config.Radius;
var randomOffset = Random.insideUnitCircle * radius;
return new Vector3(center.x + randomOffset.x, center.y + randomOffset.y, center.z);
var rawPosition = new Vector3(center.x + randomOffset.x, center.y + randomOffset.y, center.z);
// 逻辑修改点4应用位置钳制确保在地图范围内
return GetClampedWorldPosition(rawPosition, dimensionID);
}
case EntitySpawnLocationType.AroundTargetEntity:
{
@@ -303,6 +316,7 @@ namespace EventWorkClass
Debug.LogWarning($"配置了 'AroundTargetEntity',但 'TargetFactionDefName' 为空或null。无法找到派系实体。将在原点生成。");
return Vector3.zero;
}
// 遵循“单例总是有效”的指示,不检查 EntityManager.Instance 是否为 null
var factionEntities = EntityManager.Instance.FindEntitiesByFaction(dimensionID, _config.TargetFactionDefName);
if (factionEntities == null || factionEntities.Length == 0)
{
@@ -311,7 +325,8 @@ namespace EventWorkClass
}
var randomIndex = Random.Range(0, factionEntities.Length);
var targetEntityPrefab = factionEntities[randomIndex];
if (targetEntityPrefab == null || targetEntityPrefab.transform == null)
// Unity对象隐式空比较
if (targetEntityPrefab == null || targetEntityPrefab.transform == null)
{
Debug.LogWarning($"从派系 '{_config.TargetFactionDefName}' 中随机选择的实体为空或没有Transform组件。将在原点生成。");
return Vector3.zero;
@@ -320,8 +335,10 @@ namespace EventWorkClass
var radius = _config.Radius;
var randomOffset = Random.insideUnitCircle * radius;
var rawPosition = new Vector3(center.x + randomOffset.x, center.y + randomOffset.y, center.z);
Debug.Log($"围绕派系 '{_config.TargetFactionDefName}' 的实体 (世界坐标: {center}) 生成。生成的偏移量: {randomOffset}。");
return new Vector3(center.x + randomOffset.x, center.y + randomOffset.y, center.z);
// 逻辑修改点4应用位置钳制确保在地图范围内
return GetClampedWorldPosition(rawPosition, dimensionID);
}
case EntitySpawnLocationType.RandomlyOnMap:
{
@@ -350,5 +367,60 @@ namespace EventWorkClass
return Vector3.zero;
}
}
// 逻辑修改点3新增 GetClampedWorldPosition 辅助方法
/// <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 是否为 null
var dimension = Program.Instance.GetDimension(dimensionID);
if (dimension == null)
{
Debug.LogError($"无法获取维度 '{dimensionID}' 来限制位置。返回原始位置。");
return worldPosition;
}
var mapGenerator = dimension.landform;
if (mapGenerator == null)
{
Debug.LogError($"维度 '{dimensionID}' 的地形 (landform) 为空。无法限制位置。返回原始位置。");
return worldPosition;
}
var mapGridSize = mapGenerator.GetSize(); // 获取地图的网格尺寸 (宽度, 高度)
// 假设 GetWorldCoordinates(gridPos) 返回瓦片中心,且世界坐标中的单瓦片尺寸为 1x1。
// 所以,瓦片的半个世界尺寸为 0.5f。
var tileWorldHalfSize = 0.5f; // 理想情况下,这应从 mapGenerator 或某个全局配置获取。
// 获取最左下角 (0,0) 瓦片的中心世界坐标
Vector2 worldMinTileCenter = mapGenerator.GetWorldCoordinates(Vector3Int.zero);
// 获取最右上角 (width-1, height-1) 瓦片的中心世界坐标
Vector2 worldMaxTileCenter = mapGenerator.GetWorldCoordinates(new Vector3Int(mapGridSize.x - 1, mapGridSize.y - 1));
// 计算地图在世界坐标系中的绝对边界
// 我们取 x,y 轴的最小值和最大值,确保正确的边界范围
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);
// Z轴通常用于深度此处保持不变
if (clampedPosition != worldPosition)
{
Debug.LogWarning($"生成位置 {worldPosition} (类型: {_config.LocationType}) 超出地图可玩区域边界,已限制为 {clampedPosition}。");
}
return clampedPosition;
}
}
}