(client) feat:支持定义实体的碰撞体大小和偏移;建筑支持定义实体建筑和瓦片建筑,建筑支持指定按钮回调;添加存档管理器;Dev支持设置是否暂停;实体允许定义事件组;添加基地界面 (#57)

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/57
This commit is contained in:
2025-09-28 15:02:57 +08:00
parent 87a8abe86c
commit aff747be17
232 changed files with 39203 additions and 4161 deletions

View File

@@ -1,6 +1,7 @@
using UnityEngine;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks; // 引入 System.Threading.Tasks
using Managers;
using Utils;
@@ -17,46 +18,55 @@ namespace Map
/// </summary>
[JsonProperty("tileDefName", Required = Required.Always)]
public string TileDefName { get; set; } = "DefaultPlantTile";
/// <summary>
/// 柏林噪声的缩放因子。
/// </summary>
[JsonProperty("scale")]
public double Scale { get; set; } = 0.08;
/// <summary>
/// 柏林噪声阈值,高于此值才可能生成植物 (-1到1之间)。
/// </summary>
[JsonProperty("threshold")]
public double Threshold { get; set; } = 0.3;
/// <summary>
/// 植物生成概率 (0-1之间),控制稀疏度。
/// </summary>
[JsonProperty("density")]
public double Density { get; set; } = 0.6;
/// <summary>
/// 柏林噪声的X轴偏移。
/// </summary>
[JsonProperty("offsetX")]
public double OffsetX { get; set; } = 0.0;
public double OffsetX { get; set; }
/// <summary>
/// 柏林噪声的Y轴偏移。
/// </summary>
[JsonProperty("offsetY")]
public double OffsetY { get; set; } = 0.0;
public double OffsetY { get; set; }
/// <summary>
/// 植物必须生长在其上的基础瓦片名称 (可空)。
/// 植物必须生长在其上的基础瓦片名称列表 (满足其中任意一个即可,可空)。
/// </summary>
[JsonProperty("requiredBaseTileDefName")]
public string RequiredBaseTileDefName { get; set; } = null;
[JsonProperty("requiredBaseTileDefNames")] // 属性名改为复数形式
public List<string> RequiredBaseTileDefNames { get; set; }
/// <summary>
/// 地图生成区域的宽度(单元格数量)。
/// </summary>
[JsonProperty("mapCellSizeX")]
public int MapCellSizeX { get; set; } = 100;
/// <summary>
/// 地图生成区域的高度(单元格数量)。
/// </summary>
[JsonProperty("mapCellSizeY")]
public int MapCellSizeY { get; set; } = 100;
/// <summary>
/// 是否避免植物瓦片互相重叠。
/// </summary>
@@ -67,6 +77,7 @@ namespace Map
public class PlantMapGenerator : MapGeneratorWorkClassBase
{
private PlantGeneratorConfig _config;
private HashSet<string> _requiredBaseTileNames; // 缓存所需基底瓦片的运行时名称集合
/// <summary>
/// 初始化植物生成器解析JSON配置字符串。
@@ -77,22 +88,24 @@ namespace Map
try
{
_config = JsonConvert.DeserializeObject<PlantGeneratorConfig>(value);
if (_config == null)
{
Debug.LogError("植物地图生成器配置反序列化为空。请检查JSON格式。将使用默认配置。");
_config = new PlantGeneratorConfig();
}
if (string.IsNullOrWhiteSpace(_config.TileDefName))
{
Debug.LogWarning("植物地图生成器:瓦片定义名称为空。将使用默认值 'DefaultPlantTile'。");
_config.TileDefName = "DefaultPlantTile";
}
if (_config.Scale <= 0)
{
Debug.LogWarning($"植物地图生成器:缩放值必须为正数。将设置为默认值 0.08。当前值: {_config.Scale}");
_config.Scale = 0.08;
}
// Unity的Mathf.PerlinNoise输出0-1如果PerlinNoise.Instance.Noise也如此这里需要修改范围
// 假设 PerlinNoise.Instance.Noise() 返回值在 -1 到 1 之间。
if (_config.Threshold < -1.0 || _config.Threshold > 1.0)
@@ -100,21 +113,61 @@ namespace Map
Debug.LogWarning($"植物地图生成器:阈值 ({_config.Threshold}) 超出柏林噪声典型范围 (-1.0到1.0)。已钳制到 0.3。");
_config.Threshold = 0.3;
}
if (_config.Density < 0 || _config.Density > 1)
{
Debug.LogWarning($"植物地图生成器:密度 ({_config.Density}) 必须在 0 到 1 之间。已钳制到 0.6。");
_config.Density = Mathf.Clamp((float)_config.Density, 0f, 1f);
}
if (_config.MapCellSizeX <= 0)
{
Debug.LogWarning($"植物地图生成器:地图宽度 ({_config.MapCellSizeX}) 必须为正数。将设置为默认值 100。");
_config.MapCellSizeX = 100;
}
if (_config.MapCellSizeY <= 0)
{
Debug.LogWarning($"植物地图生成器:地图高度 ({_config.MapCellSizeY}) 必须为正数。将设置为默认值 100。");
_config.MapCellSizeY = 100;
}
// 预加载所需的基础瓦片名称集合
if (_config.RequiredBaseTileDefNames != null && _config.RequiredBaseTileDefNames.Count > 0)
{
_requiredBaseTileNames = new HashSet<string>();
foreach (var defName in _config.RequiredBaseTileDefNames)
{
if (string.IsNullOrEmpty(defName))
{
Debug.LogWarning("植物地图生成器:配置中包含空的基底瓦片定义名称。已跳过。");
continue;
}
var baseAsset = TileManager.Instance.GetTile(defName);
if (baseAsset != null)
{
_requiredBaseTileNames.Add(baseAsset.name);
}
else
{
// 注意在Init阶段我们只检查资产是否存在不检查Tilemap。
Debug.LogWarning(
$"植物地图生成器:无法获取所需的基础瓦片 '{defName}' 的资产。该瓦片将不作为有效基底条件。");
}
}
// 如果最终没有有效基底瓦片被加载则将HashSet设为null表示不进行此条件检查
if (_requiredBaseTileNames.Count == 0)
{
_requiredBaseTileNames = null;
}
}
else
{
// 如果配置中未指定基底瓦片列表或者列表为空显式设置为null以表示无需检查此条件
_requiredBaseTileNames = null;
}
}
catch (JsonSerializationException ex)
{
@@ -132,8 +185,8 @@ namespace Map
/// 根据柏林噪声和配置参数在 `MapGenerator` 的 `plantTilemap` 上生成植物。
/// 此方法现在是异步的,以避免阻塞主线程。
/// </summary>
/// <param name="map">MapGenerator实例包含要操作的Tilemap。</param>
public override async Task Process(MapGenerator map) // 标记为 async Task
/// <param name="landform">MapGenerator实例包含要操作的Tilemap。</param>
public override async Task Process(Landform landform) // 标记为 async Task
{
if (_config == null)
{
@@ -141,9 +194,10 @@ namespace Map
return; // async Task 方法直接 return; 意味着返回 Task.CompletedTask
}
if (map == null || map.plantTilemap == null)
// 检查 MapGenerator 和所有需要的 Tilemap 是否都已赋值
if (landform == null || landform.plantTilemap == null || landform.baseTilemap == null)
{
Debug.LogError("植物地图生成器MapGenerator或植物瓦片地图为空。无法生成植物。中止。");
Debug.LogError("植物地图生成器MapGenerator或植物瓦片地图或基础瓦片地图为空。无法生成植物。中止。");
return;
}
@@ -154,44 +208,36 @@ namespace Map
return;
}
// 缓存 requiredBaseTile 的引用,避免在循环中重复查询 TileManager
UnityEngine.Tilemaps.TileBase requiredBaseTile = null;
if (!string.IsNullOrEmpty(_config.RequiredBaseTileDefName))
// 判断是否有基底瓦片条件被配置
bool hasRequiredBaseTilesConfigured = (_requiredBaseTileNames != null && _requiredBaseTileNames.Count > 0);
// 如果配置了基底瓦片要求,但基础瓦片地图不可用,则无法进行检查,视为条件无法满足,直接中止生成
// (这里的 map.baseTilemap == null 已经在函数开头检查过所以这里逻辑上它不会是null)
if (hasRequiredBaseTilesConfigured && landform.baseTilemap == null) // 理论上这个条件不会再发生
{
if (map.baseTilemap == null)
{
Debug.LogWarning($"植物地图生成器:已配置基础瓦片名称 ('{_config.RequiredBaseTileDefName}') 但基础瓦片地图为空。将忽略基础地形要求。");
}
else
{
requiredBaseTile = TileManager.Instance.GetTile(_config.RequiredBaseTileDefName);
if (requiredBaseTile == null)
{
Debug.LogWarning($"植物地图生成器:无法获取所需的基础瓦片 '{_config.RequiredBaseTileDefName}'。将忽略基础地形要求。");
}
}
Debug.LogWarning($"植物地图生成器:已配置基础瓦片名称列表但基础瓦片地图为空。植物生成将无法满足基础地形要求,因此不会生成任何受此条件限制的植物。");
// 此时应该终止,因为不可能满足条件
return;
}
for (var x = 0; x < _config.MapCellSizeX; x++)
{
for (var y = 0; y < _config.MapCellSizeY; y++)
{
var cellPosition = new Vector3Int(x, y, 0);
var sampleX = (x / (double)_config.MapCellSizeX) * _config.Scale + _config.OffsetX;
var sampleY = (y / (double)_config.MapCellSizeY) * _config.Scale + _config.OffsetY;
var noiseValue = PerlinNoise.Instance.Noise(sampleX, sampleY);
if (noiseValue < _config.Threshold)
{
continue;
}
// 检查所需的基础瓦片
if (requiredBaseTile != null)
// 检查所需的基础瓦片 (仅当有条件被配置时)
if (hasRequiredBaseTilesConfigured)
{
var baseTileOnMap = map.baseTilemap.GetTile(cellPosition);
if (baseTileOnMap == null || baseTileOnMap.name != requiredBaseTile.name)
var baseTileOnMap = landform.baseTilemap.GetTile(cellPosition);
// 如果当前位置没有瓦片، 或瓦片名称不在允许的条件列表中، 则条件不满足
if (baseTileOnMap == null || !_requiredBaseTileNames.Contains(baseTileOnMap.name))
{
continue;
}
@@ -200,7 +246,7 @@ namespace Map
// 检查是否重叠
if (_config.PreventOverlap)
{
var existingPlant = map.plantTilemap.GetTile(cellPosition);
var existingPlant = landform.plantTilemap.GetTile(cellPosition);
if (existingPlant != null)
{
continue;
@@ -213,11 +259,18 @@ namespace Map
continue;
}
map.plantTilemap.SetTile(cellPosition, plantTile);
landform.plantTilemap.SetTile(cellPosition, plantTile);
}
// 每处理完一列,就让出控制权给主线程
await Task.Yield();
}
}
public override Vector2Int GetSize()
{
if (_config == null) return Vector2Int.zero;
return new Vector2Int(_config.MapCellSizeX, _config.MapCellSizeY);
}
}
}