Files
Temp/temp.md
2026-03-25 14:46:41 +08:00

11 KiB

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<GameObject> mapPiecePrefabs = new List<GameObject>();
public GameObject wallPiecePrefab; // 封口模块

[Header("后处理")]
public bool enableMeshCombining = true;
public bool enableNavMesh = true;

[Header("调试")]
public bool showDebugGizmos = true;

// 内部状态
private Dictionary<Vector3Int, GameObject> placedPieces = new Dictionary<Vector3Int, GameObject>();
private List<ConnectionPoint> openConnections = new List<ConnectionPoint>();
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<MapPiece>();
        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<MapPiece>();
    
    // 记录位置
    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<MapPiece>();
    
    // 检查碰撞
    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<GameObject> FindCandidatePieces(ConnectionPoint connection)
{
    var candidates = new List<GameObject>();
    
    foreach (var prefab in mapPiecePrefabs)
    {
        var mapPiece = prefab.GetComponent<MapPiece>();
        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<Collider>();
    if (collider == null) return false;
    
    // 简单的重叠检测
    var bounds = collider.bounds;
    foreach (var placedPiece in placedPieces.Values)
    {
        var placedCollider = placedPiece.GetComponent<Collider>();
        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<MeshFilter>();
    if (meshFilters.Length == 0) return;
    
    // 按材质分组合并
    var combineInstances = new List<CombineInstance>();
    var materials = new List<Material>();
    
    foreach (var meshFilter in meshFilters)
    {
        if (meshFilter.sharedMesh == null) continue;
        
        var renderer = meshFilter.GetComponent<MeshRenderer>();
        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<MeshFilter>();
        var combinedRenderer = combinedObject.AddComponent<MeshRenderer>();
        
        // 合并网格
        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<NavMeshSurface>();
    if (navMeshSurface == null)
        navMeshSurface = mapRoot.gameObject.AddComponent<NavMeshSurface>();
    
    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);
}

}