地图初步
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b079ad02f2fecb4db40aa5dfe189f0d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9d08dd5a5ec51f4cb5b60bacf0484d6
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5df4eb83565f304aaa8074869fa2239
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 072ed2f87a13a1b4fbf73fa9fc545ca5
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fb9c1d28c78ce94595b66cc283b2b49
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c203ecf84f1bcc24192fd302209a1a4c
|
||||
Reference in New Issue
Block a user