Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/Map/ConditionalTileGenerator.cs

253 lines
12 KiB
C#
Raw 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.Collections.Generic;
using System.Threading.Tasks;
using Managers;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Tilemaps;
namespace Map
{
public class ConditionalTileGeneratorConfig
{
// ====== 生成瓦片配置 ======
[JsonProperty("outputTileDefName", Required = Required.Always)]
public string OutputTileDefName { get; set; }
[JsonProperty("outputLayer", Required = Required.Always)]
public string OutputLayer { get; set; } // "base", "building", "plant"
// ====== 条件检查配置 ======
[JsonProperty("conditionTileDefNames")] // 属性名改为复数形式
public List<string> ConditionTileDefNames { get; set; } // 如果非空,则只有在目标层有此瓦片列表中的任意一个时才生成
[JsonProperty("conditionLayer")] public string ConditionLayer { get; set; } // 检查条件瓦片的层
// ====== 区域配置 ======
[JsonProperty("positionX", Required = Required.Always)]
public int PositionX { get; set; } // 起始位置X
[JsonProperty("positionY", Required = Required.Always)]
public int PositionY { get; set; } // 起始位置Y
[JsonProperty("width", Required = Required.Always)]
public int Width { get; set; } = 1; // 区域宽度
[JsonProperty("height", Required = Required.Always)]
public int Height { get; set; } = 1; // 区域高度
// ====== 柏林噪声配置 ======
[JsonProperty("usePerlinNoise")] public bool UsePerlinNoise { get; set; }
[JsonProperty("perlinScale")] public float PerlinScale { get; set; } = 10f; // 柏林噪声频率,数值越大越粗糙
[JsonProperty("perlinThreshold")] public float PerlinThreshold { get; set; } = 0.5f; // 柏林噪声阈值,只有噪声值高于此值才生成瓦片
[JsonProperty("perlinOffsetX")] public float PerlinOffsetX { get; set; } // 柏林噪声X偏移
[JsonProperty("perlinOffsetY")] public float PerlinOffsetY { get; set; } // 柏林噪声Y偏移
}
public class ConditionalTileGenerator : MapGeneratorWorkClassBase
{
private HashSet<string> _conditionTileBaseNames; // 缓存条件瓦片的运行时名称集合
private ConditionalTileGeneratorConfig _config;
private TileBase _outputTileBase; // 缓存要生成的实际 TileBase 资源
/// <summary>
/// 初始化条件瓦片生成器解析JSON配置并预加载资源。
/// </summary>
/// <param name="value">包含ConditionalTileGeneratorConfig的JSON字符串。</param>
public override void Init(string value)
{
if (string.IsNullOrEmpty(value))
{
Debug.LogError("ConditionalTileGenerator: Init received empty or null JSON value.");
return;
}
try
{
_config = JsonConvert.DeserializeObject<ConditionalTileGeneratorConfig>(value);
if (_config == null)
{
Debug.LogError(
"ConditionalTileGenerator: Failed to deserialize ConditionalTileGeneratorConfig from JSON.");
return;
}
// 预加载输出瓦片
if (!string.IsNullOrEmpty(_config.OutputTileDefName))
{
_outputTileBase = TileManager.Instance.GetTile(_config.OutputTileDefName);
if (_outputTileBase == null)
Debug.LogWarning(
$"ConditionalTileGenerator: Output tile asset for '{_config.OutputTileDefName}' not found via TileManager. Generation may fail.");
}
else
{
Debug.LogError(
"ConditionalTileGenerator: OutputTileDefName is required but not provided in config.");
return;
}
// 预加载条件瓦片的名称集合 (如果指定)
if (_config.ConditionTileDefNames != null && _config.ConditionTileDefNames.Count > 0)
{
_conditionTileBaseNames = new HashSet<string>();
foreach (var defName in _config.ConditionTileDefNames)
{
if (string.IsNullOrEmpty(defName))
{
Debug.LogWarning(
"ConditionalTileGenerator: Found empty or null condition tile definition name in config. Skipping.");
continue;
}
var conditionAsset = TileManager.Instance.GetTile(defName);
if (conditionAsset != null)
_conditionTileBaseNames.Add(conditionAsset.name);
else
Debug.LogWarning(
$"ConditionalTileGenerator: Condition tile asset for '{defName}' not found via TileManager. This specific condition tile will be ignored in checks.");
}
// 如果最终没有有效条件瓦片被加载则将HashSet设为null表示不进行此条件检查
if (_conditionTileBaseNames.Count == 0) _conditionTileBaseNames = null;
}
else
{
// 如果配置中未指定条件瓦片列表或者列表为空显式设置为null以表示无需检查此条件
_conditionTileBaseNames = null;
}
}
catch (JsonException ex)
{
Debug.LogError($"ConditionalTileGenerator: JSON deserialization error: {ex.Message}\nJSON: {value}");
}
}
/// <summary>
/// 获取条件瓦片生成器将影响的区域大小。
/// </summary>
/// <returns>表示区域宽和高的Vector2Int。</returns>
public override Vector2Int GetSize()
{
if (_config == null) return Vector2Int.zero;
return new Vector2Int(_config.Width, _config.Height);
}
/// <summary>
/// 执行条件瓦片生成逻辑。
/// </summary>
/// <param name="landform">MapGenerator实例提供Tilemap和坐标转换功能。</param>
public override async Task Process(Landform landform)
{
if (_config == null || _outputTileBase == null)
{
Debug.LogError("ConditionalTileGenerator: Configuration or output tile not properly initialized.");
return;
}
if (landform == null || landform.baseTilemap == null || landform.buildingTilemap == null ||
landform.plantTilemap == null)
{
Debug.LogError("ConditionalTileGenerator: MapGenerator or its Tilemaps are not assigned.");
return;
}
// 获取输出层 Tilemap
var outputTilemap = GetTilemapByName(landform, _config.OutputLayer);
if (outputTilemap == null)
{
Debug.LogError(
$"ConditionalTileGenerator: Output Tilemap '{_config.OutputLayer}' not found or invalid.");
return;
}
// 获取条件层 Tilemap (如果需要检查条件瓦片)
Tilemap conditionTilemap = null;
// 只有当有实际的条件瓦片名称列表配置时才尝试获取条件层Tilemap
var hasConditionTileNamesConfigured = _conditionTileBaseNames != null && _conditionTileBaseNames.Count > 0;
if (hasConditionTileNamesConfigured)
{
if (string.IsNullOrEmpty(_config.ConditionLayer))
{
Debug.LogWarning(
"ConditionalTileGenerator: 'ConditionTileDefNames' specified, but 'ConditionLayer' is not. Condition check related to specific tiles will implicitly fail for the tile conditions.");
// 此时不设置 conditionTilemap会在循环内部触发 conditionsMet = false
}
else
{
conditionTilemap = GetTilemapByName(landform, _config.ConditionLayer);
if (conditionTilemap == null)
Debug.LogError(
$"ConditionalTileGenerator: Condition Tilemap '{_config.ConditionLayer}' not found or invalid. Condition check related to specific tiles will implicitly fail for the tile conditions.");
// 此时不设置 conditionTilemap会在循环内部触发 conditionsMet = false
}
}
for (var y = 0; y < _config.Height; y++)
{
for (var x = 0; x < _config.Width; x++)
{
var currentGridPos = new Vector3Int(_config.PositionX + x, _config.PositionY + y, 0);
var conditionsMet = true;
// 1. 检查条件瓦片 (仅当有有效的条件瓦片和条件层Tilemap时执行)
if (hasConditionTileNamesConfigured) // 只有配置了条件瓦片才进行进一步检查
{
if (conditionTilemap != null) // 确保条件层Tilemap有效
{
var tileOnConditionLayer = conditionTilemap.GetTile(currentGridPos);
// 如果当前位置没有瓦片,或者瓦片名称不在允许的条件列表中,则条件不满足
if (tileOnConditionLayer == null ||
!_conditionTileBaseNames.Contains(tileOnConditionLayer.name))
conditionsMet = false; // 条件不满足
// Debug.Log($"Skipping {currentGridPos}: Condition tile not found or not in allowed list.");
}
else
{
// 如果 hasConditionTileNamesConfigured 为 true, 但 conditionTilemap 为 null (因为_config.ConditionLayer为空或GetTilemapByName失败)
// 那么条件瓦片检查无法进行,应该视为条件不满足
conditionsMet = false;
}
}
// 2. 检查柏林噪声
if (conditionsMet && _config.UsePerlinNoise)
{
var sampleX = (currentGridPos.x + _config.PerlinOffsetX) / _config.PerlinScale;
var sampleY = (currentGridPos.y + _config.PerlinOffsetY) / _config.PerlinScale;
var perlinValue = Mathf.PerlinNoise(sampleX, sampleY);
if (perlinValue < _config.PerlinThreshold) conditionsMet = false; // 噪声条件不满足
// Debug.Log($"Skipping {currentGridPos}: Perlin noise {perlinValue:F2} below threshold {_config.PerlinThreshold:F2}.");
}
// 如果所有条件都满足,则放置瓦片
if (conditionsMet) outputTilemap.SetTile(currentGridPos, _outputTileBase);
// Debug.Log($"ConditionalTileGenerator: Placed '{_config.OutputTileDefName}' at {currentGridPos}.");
}
await Task.Yield(); // 允许在每行结束时暂停,避免阻塞
}
}
/// <summary>
/// 根据名称字符串获取 MapGenerator 中的对应 Tilemap。
/// </summary>
private Tilemap GetTilemapByName(Landform landform, string tilemapName)
{
switch (tilemapName?.ToLower()) // 使用ToLower以便不区分大小写
{
case "base":
return landform.baseTilemap;
case "building":
return landform.buildingTilemap;
case "plant":
return landform.plantTilemap;
default:
Debug.LogWarning($"ConditionalTileGenerator: Unknown Tilemap name '{tilemapName}'.");
return null;
}
}
}
}