2025-09-28 15:02:57 +08:00
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 ; }
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
[JsonProperty("outputLayer", Required = Required.Always)]
public string OutputLayer { get ; set ; } // "base", "building", "plant"
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// ====== 条件检查配置 ======
[JsonProperty("conditionTileDefNames")] // 属性名改为复数形式
2025-10-10 14:08:23 +08:00
public List < string > ConditionTileDefNames { get ; set ; } // 如果非空,则只有在目标层有此瓦片列表中的任意一个时才生成
[JsonProperty("conditionLayer")] public string ConditionLayer { get ; set ; } // 检查条件瓦片的层
2025-09-28 15:02:57 +08:00
// ====== 区域配置 ======
[JsonProperty("positionX", Required = Required.Always)]
2025-10-10 14:08:23 +08:00
public int PositionX { get ; set ; } // 起始位置X
2025-09-28 15:02:57 +08:00
[JsonProperty("positionY", Required = Required.Always)]
2025-10-10 14:08:23 +08:00
public int PositionY { get ; set ; } // 起始位置Y
2025-09-28 15:02:57 +08:00
[JsonProperty("width", Required = Required.Always)]
public int Width { get ; set ; } = 1 ; // 区域宽度
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
[JsonProperty("height", Required = Required.Always)]
public int Height { get ; set ; } = 1 ; // 区域高度
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// ====== 柏林噪声配置 ======
2025-10-10 14:08:23 +08:00
[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偏移
2025-09-28 15:02:57 +08:00
}
2025-10-10 14:08:23 +08:00
public class ConditionalTileGenerator : MapGeneratorWorkClassBase
2025-09-28 15:02:57 +08:00
{
2025-10-10 14:08:23 +08:00
private HashSet < string > _conditionTileBaseNames ; // 缓存条件瓦片的运行时名称集合
2025-09-28 15:02:57 +08:00
private ConditionalTileGeneratorConfig _config ;
private TileBase _outputTileBase ; // 缓存要生成的实际 TileBase 资源
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
/// <summary>
2025-10-10 14:08:23 +08:00
/// 初始化条件瓦片生成器, 解析JSON配置并预加载资源。
2025-09-28 15:02:57 +08:00
/// </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 ;
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
try
{
_config = JsonConvert . DeserializeObject < ConditionalTileGeneratorConfig > ( value ) ;
if ( _config = = null )
{
Debug . LogError (
"ConditionalTileGenerator: Failed to deserialize ConditionalTileGeneratorConfig from JSON." ) ;
return ;
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// 预加载输出瓦片
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 ;
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// 预加载条件瓦片的名称集合 (如果指定)
if ( _config . ConditionTileDefNames ! = null & & _config . ConditionTileDefNames . Count > 0 )
{
_conditionTileBaseNames = new HashSet < string > ( ) ;
foreach ( var defName in _config . ConditionTileDefNames )
{
if ( string . IsNullOrEmpty ( defName ) )
{
2025-10-10 14:08:23 +08:00
Debug . LogWarning (
"ConditionalTileGenerator: Found empty or null condition tile definition name in config. Skipping." ) ;
2025-09-28 15:02:57 +08:00
continue ;
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
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." ) ;
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// 如果最终没有有效条件瓦片被加载, 则将HashSet设为null, 表示不进行此条件检查
2025-10-10 14:08:23 +08:00
if ( _conditionTileBaseNames . Count = = 0 ) _conditionTileBaseNames = null ;
2025-09-28 15:02:57 +08:00
}
else
{
// 如果配置中未指定条件瓦片列表, 或者列表为空, 显式设置为null以表示无需检查此条件
_conditionTileBaseNames = null ;
}
}
catch ( JsonException ex )
{
Debug . LogError ( $"ConditionalTileGenerator: JSON deserialization error: {ex.Message}\nJSON: {value}" ) ;
}
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
/// <summary>
2025-10-10 14:08:23 +08:00
/// 获取条件瓦片生成器将影响的区域大小。
2025-09-28 15:02:57 +08:00
/// </summary>
/// <returns>表示区域宽和高的Vector2Int。</returns>
public override Vector2Int GetSize ( )
{
2025-10-10 14:08:23 +08:00
if ( _config = = null ) return Vector2Int . zero ;
2025-09-28 15:02:57 +08:00
return new Vector2Int ( _config . Width , _config . Height ) ;
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
/// <summary>
2025-10-10 14:08:23 +08:00
/// 执行条件瓦片生成逻辑。
2025-09-28 15:02:57 +08:00
/// </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 ;
}
2025-10-10 14:08:23 +08:00
if ( landform = = null | | landform . baseTilemap = = null | | landform . buildingTilemap = = null | |
landform . plantTilemap = = null )
2025-09-28 15:02:57 +08:00
{
Debug . LogError ( "ConditionalTileGenerator: MapGenerator or its Tilemaps are not assigned." ) ;
return ;
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// 获取输出层 Tilemap
var outputTilemap = GetTilemapByName ( landform , _config . OutputLayer ) ;
if ( outputTilemap = = null )
{
Debug . LogError (
$"ConditionalTileGenerator: Output Tilemap '{_config.OutputLayer}' not found or invalid." ) ;
return ;
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// 获取条件层 Tilemap (如果需要检查条件瓦片)
Tilemap conditionTilemap = null ;
// 只有当有实际的条件瓦片名称列表配置时才尝试获取条件层Tilemap
2025-10-10 14:08:23 +08:00
var hasConditionTileNamesConfigured = _conditionTileBaseNames ! = null & & _conditionTileBaseNames . Count > 0 ;
2025-09-28 15:02:57 +08:00
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." ) ;
2025-10-10 14:08:23 +08:00
// 此时不设置 conditionTilemap, 会在循环内部触发 conditionsMet = false
2025-09-28 15:02:57 +08:00
}
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
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时执行)
2025-10-10 14:08:23 +08:00
if ( hasConditionTileNamesConfigured ) // 只有配置了条件瓦片才进行进一步检查
2025-09-28 15:02:57 +08:00
{
if ( conditionTilemap ! = null ) // 确保条件层Tilemap有效
{
var tileOnConditionLayer = conditionTilemap . GetTile ( currentGridPos ) ;
// 如果当前位置没有瓦片,或者瓦片名称不在允许的条件列表中,则条件不满足
2025-10-10 14:08:23 +08:00
if ( tileOnConditionLayer = = null | |
! _conditionTileBaseNames . Contains ( tileOnConditionLayer . name ) )
2025-09-28 15:02:57 +08:00
conditionsMet = false ; // 条件不满足
2025-10-10 14:08:23 +08:00
// Debug.Log($"Skipping {currentGridPos}: Condition tile not found or not in allowed list.");
2025-09-28 15:02:57 +08:00
}
else
{
// 如果 hasConditionTileNamesConfigured 为 true, 但 conditionTilemap 为 null (因为_config.ConditionLayer为空或GetTilemapByName失败)
// 那么条件瓦片检查无法进行,应该视为条件不满足
conditionsMet = false ;
}
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// 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 ) ;
2025-10-10 14:08:23 +08:00
if ( perlinValue < _config . PerlinThreshold ) conditionsMet = false ; // 噪声条件不满足
// Debug.Log($"Skipping {currentGridPos}: Perlin noise {perlinValue:F2} below threshold {_config.PerlinThreshold:F2}.");
2025-09-28 15:02:57 +08:00
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
// 如果所有条件都满足,则放置瓦片
2025-10-10 14:08:23 +08:00
if ( conditionsMet ) outputTilemap . SetTile ( currentGridPos , _outputTileBase ) ;
// Debug.Log($"ConditionalTileGenerator: Placed '{_config.OutputTileDefName}' at {currentGridPos}.");
2025-09-28 15:02:57 +08:00
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
await Task . Yield ( ) ; // 允许在每行结束时暂停,避免阻塞
}
}
2025-10-10 14:08:23 +08:00
2025-09-28 15:02:57 +08:00
/// <summary>
2025-10-10 14:08:23 +08:00
/// 根据名称字符串获取 MapGenerator 中的对应 Tilemap。
2025-09-28 15:02:57 +08:00
/// </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 ;
}
}
}
}