地图初步

This commit is contained in:
SoulliesOfficial
2026-04-30 07:06:38 -04:00
parent 8ad26129b2
commit 47125f95f4
98 changed files with 2237 additions and 20524 deletions

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 3b079ad02f2fecb4db40aa5dfe189f0d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,17 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c203ecf84f1bcc24192fd302209a1a4c, type: 3}
m_Name: DefaultNode 1
m_EditorClassIdentifier: SLSUtilities::SLSUtilities.Map.MapNodeData
nodeName: Default2
debugColor: {r: 1, g: 0, b: 0, a: 1}
weight: 1

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: f9d08dd5a5ec51f4cb5b60bacf0484d6
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,17 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c203ecf84f1bcc24192fd302209a1a4c, type: 3}
m_Name: DefaultNode
m_EditorClassIdentifier: SLSUtilities::SLSUtilities.Map.MapNodeData
nodeName: Default
debugColor: {r: 1, g: 1, b: 1, a: 1}
weight: 1

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: c5df4eb83565f304aaa8074869fa2239
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,162 +0,0 @@
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;
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 072ed2f87a13a1b4fbf73fa9fc545ca5

View File

@@ -1,88 +0,0 @@
using UnityEngine;
using System.Collections.Generic;
namespace SLSUtilities.Map
{
// 将类声明为 abstract表示它不能直接挂载使用必须被继承
public abstract class MapGeneratorBase : MonoBehaviour
{
[Header("基础配置")] public MapNodeData defaultNodeData;
// 存储生成的地图数据 (protected 使得子类可以访问)
[Header("生成结果 (Debug)")] [SerializeField] // 加上这个是为了在Inspector里能看到方便调试
protected List<MapNode> allNodes = new List<MapNode>();
[SerializeField] protected List<MapEdge> allEdges = new List<MapEdge>();
// 抽象方法:强制子类必须实现这个方法
public abstract void GenerateMap();
[ContextMenu("Clear Map")]
public virtual void ClearMap()
{
allNodes.Clear();
allEdges.Clear();
}
// 通用的清理与重置逻辑
protected void ResetIDs()
{
// 如果需要重新整理ID可以在这里写
}
// 通用的 Gizmos 绘制逻辑
protected virtual void OnDrawGizmos()
{
if (allNodes == null) return;
// 画边
Gizmos.color = Color.gray;
if (allEdges != null)
{
foreach (var edge in allEdges)
{
if (edge.fromNode != null && edge.toNode != null)
Gizmos.DrawLine(edge.fromNode.position, edge.toNode.position);
}
}
// 画节点
foreach (var node in allNodes)
{
Gizmos.color = node.data != null ? node.data.debugColor : Color.white;
Gizmos.DrawSphere(node.position, 0.5f);
}
}
}
[System.Serializable]
public class MapNode
{
public int id; // 唯一ID
public Vector2 position; // 在地图上的坐标
public MapNodeData data; // 引用上面的ScriptableObject数据
// 构造函数
public MapNode(int id, Vector2 pos, MapNodeData data)
{
this.id = id;
this.position = pos;
this.data = data;
}
}
[System.Serializable]
public class MapEdge
{
public MapNode fromNode;
public MapNode toNode;
public bool isBidirectional; // 是否双向
public MapEdge(MapNode from, MapNode to, bool bidirectional)
{
this.fromNode = from;
this.toNode = to;
this.isBidirectional = bidirectional;
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 5fb9c1d28c78ce94595b66cc283b2b49

View File

@@ -1,12 +0,0 @@
using UnityEngine;
namespace SLSUtilities.Map
{
[CreateAssetMenu(fileName = "MapNodeData", menuName = "SLSUtilities/Map/Map Node Data", order = 1)]
public class MapNodeData : ScriptableObject
{
public string nodeName;
public Color debugColor = Color.white;
public float weight = 1.0f;
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: c203ecf84f1bcc24192fd302209a1a4c