Files
Cielonos/Assets/Scripts/SLSUtilities/MapCreator/LayeredMapGenerator.cs
SoulliesOfficial 50ee502684 完善
2026-02-13 09:22:11 -05:00

162 lines
6.1 KiB
C#

using System.Collections.Generic;
using System.Linq; // 需要引用 Linq 进行排序
using UnityEngine;
namespace SLSUtilities.Map
{
public class LayeredMapGenerator : MapGeneratorBase
{
[Header("分层生成设置")]
[Tooltip("地图有多少层(环)")]
public int layerCount = 3;
[Tooltip("每一层的半径间距")]
public float radiusStep = 5f;
[Tooltip("每一层最少/最多生成多少个房间")]
public Vector2Int nodesPerLayerRange = new Vector2Int(5, 8); // 建议最小值稍微调高一点
[Tooltip("位置随机偏移量")]
public float positionJitter = 1.0f;
[Header("连通性设置")]
[Range(0f, 1f)]
public float ringConnectChance = 1.0f; // 设为1方便调试环状结构
// 辅助数据:按层级索引
private Dictionary<int, List<MapNode>> nodesByLayer = new Dictionary<int, List<MapNode>>();
// 覆盖基类的 GenerateMap
[ContextMenu("Generate Layered Map")]
public override void GenerateMap()
{
base.ClearMap();
nodesByLayer.Clear();
GenerateNodesInLayers();
ConnectLayers();
}
private void GenerateNodesInLayers()
{
int globalID = 0;
// --- Layer 0 (Center) ---
MapNode centerNode = new MapNode(globalID++, Vector2.zero, defaultNodeData);
allNodes.Add(centerNode);
nodesByLayer[0] = new List<MapNode> { centerNode };
List<int> layerNodeCounts = new List<int>();
for (int layerIndex = 1; layerIndex <= layerCount; layerIndex++)
{
layerNodeCounts.Add(Random.Range(nodesPerLayerRange.x, nodesPerLayerRange.y + 1));
}
layerNodeCounts.Sort(); // 从小到大排序,确保外层节点数不少于内层
// --- Layer 1 to N ---
for (int layerIndex = 1; layerIndex <= layerCount; layerIndex++)
{
List<MapNode> currentLayerNodes = new List<MapNode>();
int count = layerNodeCounts[layerIndex - 1];
float currentRadius = layerIndex * radiusStep; // 半径随层级增长略微加快
Debug.Log($"Layer {layerIndex}: Generating {count} nodes at radius {currentRadius}");
for (int i = 0; i < count; i++)
{
float angleStep = 360f / count;
float currentAngle = (i * angleStep); // 先不加随机角度,靠 jitter 产生偏移
float radian = currentAngle * Mathf.Deg2Rad;
// 基础极坐标位置
float x = Mathf.Cos(radian) * currentRadius;
float y = Mathf.Sin(radian) * currentRadius;
Vector2 basePos = new Vector2(x, y);
// 加上 Jitter
Vector2 finalPos = basePos + (Random.insideUnitCircle * positionJitter);
MapNode newNode = new MapNode(globalID++, finalPos, defaultNodeData);
allNodes.Add(newNode);
currentLayerNodes.Add(newNode);
}
// 【关键优化点】:在生成完这一层所有点后,根据实际生成的“角度”重新排序
// 这样即使 Jitter 把点的位置打乱了,连接时依然是顺时针有序的,不会出现交叉
currentLayerNodes = currentLayerNodes.OrderBy(n =>
Mathf.Atan2(n.position.y, n.position.x)
).ToList();
nodesByLayer[layerIndex] = currentLayerNodes;
}
}
private void ConnectLayers()
{
for (int layerIndex = 1; layerIndex <= layerCount; layerIndex++)
{
if (!nodesByLayer.ContainsKey(layerIndex)) continue;
List<MapNode> currentLayer = nodesByLayer[layerIndex];
List<MapNode> previousLayer = nodesByLayer[layerIndex - 1];
for (int i = 0; i < currentLayer.Count; i++)
{
MapNode currentNode = currentLayer[i];
// 1. 向心连接 (Connect to inner layer)
MapNode targetInner = FindClosestNode(currentNode, previousLayer);
CreateEdge(currentNode, targetInner);
// 2. 环状连接 (Ring Connection)
if (Random.value <= ringConnectChance)
{
// 因为我们在生成时已经按 Atan2 排序了,所以 i+1 一定是几何上的相邻点
int nextIndex = (i + 1) % currentLayer.Count;
MapNode neighborNode = currentLayer[nextIndex];
if (!ConnectionExists(currentNode, neighborNode))
{
CreateEdge(currentNode, neighborNode);
}
}
}
}
}
private void CreateEdge(MapNode a, MapNode b)
{
// 简单的防重检查
if (!ConnectionExists(a, b))
{
allEdges.Add(new MapEdge(a, b, true));
}
}
private bool ConnectionExists(MapNode a, MapNode b)
{
foreach (var edge in allEdges)
{
if ((edge.fromNode == a && edge.toNode == b) ||
(edge.fromNode == b && edge.toNode == a))
return true;
}
return false;
}
private MapNode FindClosestNode(MapNode source, List<MapNode> candidates)
{
MapNode bestTarget = null;
float minDst = float.MaxValue;
foreach (var target in candidates)
{
float dst = Vector2.Distance(source.position, target.position);
if (dst < minDst)
{
minDst = dst;
bestTarget = target;
}
}
return bestTarget;
}
}
}