162 lines
6.1 KiB
C#
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;
|
|
}
|
|
}
|
|
} |