using System.Collections.Generic; using Unity.AI.Navigation; using UnityEngine; using UnityEngine.AI; public class ProceduralMapGenerator : MonoBehaviour { [Header("生成配置")] public int seed = 12345; public int maxPieces = 20; public float gridSize = 2.0f; [Header("预制体库")] public List mapPiecePrefabs = new List(); public GameObject wallPiecePrefab; // 封口模块 [Header("后处理")] public bool enableMeshCombining = true; public bool enableNavMesh = true; [Header("调试")] public bool showDebugGizmos = true; // 内部状态 private Dictionary placedPieces = new Dictionary(); private List openConnections = new List(); private System.Random random; private Transform mapRoot; void Start() { GenerateMap(); } [ContextMenu("生成地图")] public void GenerateMap() { ClearMap(); InitializeRandom(); // 创建地图根对象 mapRoot = new GameObject("GeneratedMap").transform; mapRoot.SetParent(transform); // 选择起始模块 PlaceStartPiece(); // 主生成循环 GenerateLoop(); // 后处理 if (enableMeshCombining) CombineMeshes(); if (enableNavMesh) BuildNavMesh(); } [ContextMenu("清除地图")] public void ClearMap() { if (mapRoot != null) DestroyImmediate(mapRoot.gameObject); placedPieces.Clear(); openConnections.Clear(); } private void InitializeRandom() { random = new System.Random(seed); } private void PlaceStartPiece() { // 查找起始模块e GameObject startPrefab = null; foreach (var prefab in mapPiecePrefabs) { var piece = prefab.GetComponent(); if (piece != null && piece.isStartPiece) { startPrefab = prefab; break; } } if (startPrefab == null && mapPiecePrefabs.Count > 0) { startPrefab = mapPiecePrefabs[0]; } if (startPrefab == null) { Debug.LogError("没有可用的起始模块预制体!"); return; } // 实例化起始模块 var startPiece = Instantiate(startPrefab, Vector3.zero, Quaternion.identity, mapRoot); var mapPiece = startPiece.GetComponent(); // 记录位置 Vector3Int gridPos = WorldToGrid(Vector3.zero); placedPieces[gridPos] = startPiece; // 添加所有连接点到开放列表 openConnections.AddRange(mapPiece.GetAvailableConnectionPoints()); Debug.Log($"放置起始模块: {mapPiece.pieceName} 在位置 {gridPos}"); } private void GenerateLoop() { int attempts = 0; int maxAttempts = maxPieces * 3; // 最大尝试次数 while (openConnections.Count > 0 && placedPieces.Count < maxPieces && attempts < maxAttempts) { attempts++; // 从开放列表中取出一个连接点 var connection = openConnections[0]; openConnections.RemoveAt(0); // 尝试放置新模块 if (TryPlacePieceAtConnection(connection)) { Debug.Log($"成功放置第 {placedPieces.Count} 个模块"); } else { // 放置失败,放置封口模块 PlaceWallPiece(connection); } } Debug.Log($"生成完成!共放置 {placedPieces.Count} 个模块,尝试次数: {attempts}"); } private bool TryPlacePieceAtConnection(ConnectionPoint connection) { // 获取连接点的世界位置和方向 Vector3 worldPos = connection.localPosition; // 这里需要修正,应该从父对象获取 Vector3 worldDir = connection.localDirection; // 计算新模块的位置 Vector3 newPiecePos = worldPos + worldDir * gridSize; Vector3Int gridPos = WorldToGrid(newPiecePos); // 检查位置是否已被占用 if (placedPieces.ContainsKey(gridPos)) return false; // 查找合适的预制体 var candidates = FindCandidatePieces(connection); if (candidates.Count == 0) return false; // 随机选择一个候选 GameObject selectedPrefab = candidates[random.Next(candidates.Count)]; // 计算旋转(使新模块的连接点与当前连接点对齐) Quaternion rotation = Quaternion.LookRotation(-worldDir, Vector3.up); // 实例化新模块 var newPiece = Instantiate(selectedPrefab, newPiecePos, rotation, mapRoot); var newMapPiece = newPiece.GetComponent(); // 检查碰撞 if (CheckCollision(newPiece)) { DestroyImmediate(newPiece); return false; } // 记录位置 placedPieces[gridPos] = newPiece; // 添加新模块的连接点到开放列表(除了与当前连接点匹配的那个) var newConnections = newMapPiece.GetAvailableConnectionPoints(); foreach (var newConn in newConnections) { // 简单的方向检查,避免添加反向连接 Vector3 newWorldDir = newPiece.transform.TransformDirection(newConn.localDirection); if (Vector3.Dot(newWorldDir, worldDir) < -0.5f) // 大致相反方向 continue; openConnections.Add(newConn); } return true; } private List FindCandidatePieces(ConnectionPoint connection) { var candidates = new List(); foreach (var prefab in mapPiecePrefabs) { var mapPiece = prefab.GetComponent(); if (mapPiece == null) continue; // 简单的风格匹配(可扩展) if (mapPiece.style != connection.style && connection.style != "default") continue; candidates.Add(prefab); } return candidates; } private void PlaceWallPiece(ConnectionPoint connection) { if (wallPiecePrefab == null) return; Vector3 worldPos = connection.localPosition; Vector3 worldDir = connection.localDirection; Vector3 wallPos = worldPos + worldDir * gridSize * 0.5f; Vector3Int gridPos = WorldToGrid(wallPos); if (!placedPieces.ContainsKey(gridPos)) { var wallPiece = Instantiate(wallPiecePrefab, wallPos, Quaternion.LookRotation(-worldDir), mapRoot); placedPieces[gridPos] = wallPiece; } } private bool CheckCollision(GameObject piece) { var collider = piece.GetComponent(); if (collider == null) return false; // 简单的重叠检测 var bounds = collider.bounds; foreach (var placedPiece in placedPieces.Values) { var placedCollider = placedPiece.GetComponent(); if (placedCollider != null && placedCollider.bounds.Intersects(bounds)) return true; } return false; } private Vector3Int WorldToGrid(Vector3 worldPos) { return new Vector3Int( Mathf.RoundToInt(worldPos.x / gridSize), Mathf.RoundToInt(worldPos.y / gridSize), Mathf.RoundToInt(worldPos.z / gridSize) ); } private void CombineMeshes() { // 简单的网格合并实现 var meshFilters = mapRoot.GetComponentsInChildren(); if (meshFilters.Length == 0) return; // 按材质分组合并 var combineInstances = new List(); var materials = new List(); foreach (var meshFilter in meshFilters) { if (meshFilter.sharedMesh == null) continue; var renderer = meshFilter.GetComponent(); if (renderer == null || renderer.sharedMaterial == null) continue; var combine = new CombineInstance { mesh = meshFilter.sharedMesh, transform = meshFilter.transform.localToWorldMatrix }; combineInstances.Add(combine); // 禁用原始渲染器 renderer.enabled = false; } if (combineInstances.Count > 0) { // 创建合并的网格对象 var combinedObject = new GameObject("CombinedMesh"); combinedObject.transform.SetParent(mapRoot); var combinedFilter = combinedObject.AddComponent(); var combinedRenderer = combinedObject.AddComponent(); // 合并网格 var combinedMesh = new Mesh(); combinedMesh.CombineMeshes(combineInstances.ToArray()); combinedFilter.sharedMesh = combinedMesh; // 设置材质(使用第一个材质) if (materials.Count > 0) combinedRenderer.sharedMaterial = materials[0]; Debug.Log($"网格合并完成,合并了 {combineInstances.Count} 个网格"); } } private void BuildNavMesh() { var navMeshSurface = mapRoot.gameObject.GetComponent(); if (navMeshSurface == null) navMeshSurface = mapRoot.gameObject.AddComponent(); navMeshSurface.BuildNavMesh(); Debug.Log("NavMesh 生成完成"); } private void OnDrawGizmos() { if (!showDebugGizmos) return; // 绘制已放置模块的网格位置 Gizmos.color = Color.blue; foreach (var kvp in placedPieces) { Vector3 worldPos = GridToWorld(kvp.Key); Gizmos.DrawWireCube(worldPos, Vector3.one * gridSize * 0.8f); } // 绘制开放连接点 Gizmos.color = Color.red; foreach (var connection in openConnections) { Vector3 worldPos = connection.localPosition; Gizmos.DrawSphere(worldPos, 0.2f); } } private Vector3 GridToWorld(Vector3Int gridPos) { return new Vector3(gridPos.x * gridSize, gridPos.y * gridSize, gridPos.z * gridSize); } }