// Assets/Scripts/Test/AutoMap/AutoMapGenerator.cs using UnityEngine; using UnityEngine.Networking; using System.Collections; using System.Collections.Generic; using System.Resources; public class AutoMapGenerator : MonoBehaviour { [Header("地图区域设置")] public string areaName = "目标区域"; public double southLat = 39.9100; public double northLat = 39.9300; public double westLon = 116.3800; public double eastLon = 116.4100; [Header("资源引用 - 如为空将自动创建")] public Material buildingMaterial; public Material roadMaterial; public Material parkMaterial; public Material waterMaterial; public Material groundMaterial; [Header("生成设置")] public float buildingHeight = 8f; public float roadWidth = 6f; [Header("调试选项")] public bool showDebugInfo = true; public bool logMissingBuildings = true; public bool includeAllWays = true; // 新增 private OSMParser osmParser; private ResourceManager resourceManager; void Start() { InitializeComponents(); StartCoroutine(DownloadAndGenerateMap()); } void InitializeComponents() { // 确保有必要的组件 osmParser = GetComponent(); if (osmParser == null) osmParser = gameObject.AddComponent(); // 设置解析器选项 osmParser.includeAllWays = includeAllWays; // 初始化资源管理器 resourceManager = new ResourceManager(); resourceManager.InitializeResources(this); // 调试:显示现有材质 if (showDebugInfo) { resourceManager.LogExistingMaterials(); } } // 添加一个编辑器菜单项来清理和重新创建材质 [ContextMenu("重新初始化材质")] public void ReinitializeMaterials() { #if UNITY_EDITOR Debug.Log("重新初始化材质..."); // 强制重新创建所有材质 resourceManager = new ResourceManager(); // 清空现有材质引用 buildingMaterial = null; roadMaterial = null; parkMaterial = null; waterMaterial = null; groundMaterial = null; // 重新初始化 resourceManager.InitializeResources(this); Debug.Log("材质重新初始化完成"); #endif } // 添加一个方法来检查材质状态 [ContextMenu("检查材质状态")] public void CheckMaterialStatus() { Debug.Log("=== 材质状态检查 ==="); Debug.Log($"建筑材质: {buildingMaterial?.name ?? "未设置"}"); Debug.Log($"道路材质: {roadMaterial?.name ?? "未设置"}"); Debug.Log($"公园材质: {parkMaterial?.name ?? "未设置"}"); Debug.Log($"水域材质: {waterMaterial?.name ?? "未设置"}"); Debug.Log($"地面材质: {groundMaterial?.name ?? "未设置"}"); resourceManager.LogExistingMaterials(); } IEnumerator DownloadAndGenerateMap() { Debug.Log($"开始下载 {areaName} 地图数据..."); string overpassQuery = BuildOverpassQuery(); string encodedQuery = UnityWebRequest.EscapeURL(overpassQuery); string url = $"https://overpass-api.de/api/interpreter?data={encodedQuery}"; if (showDebugInfo) { Debug.Log($"目标区域: {areaName}"); Debug.Log($"经纬度范围: {southLat},{westLon} 到 {northLat},{eastLon}"); } using (UnityWebRequest request = UnityWebRequest.Get(url)) { yield return request.SendWebRequest(); if (request.result == UnityWebRequest.Result.Success) { string osmData = request.downloadHandler.text; Debug.Log($"成功下载地图数据! 长度: {osmData.Length} 字符"); GenerateMap(osmData); } else { Debug.LogError($"下载失败: {request.error}"); CreateTestMap(); } } } string BuildOverpassQuery() { return $@" [out:xml][timeout:45]; ( way[""building""]({southLat},{westLon},{northLat},{eastLon}); way[""highway""]({southLat},{westLon},{northLat},{eastLon}); way[""leisure""]({southLat},{westLon},{northLat},{eastLon}); way[""waterway""]({southLat},{westLon},{northLat},{eastLon}); way[""natural""][""natural""~""water""]({southLat},{westLon},{northLat},{eastLon}); ); (._;>;); out body;"; } void GenerateMap(string osmData) { ClearExistingMap(); if (string.IsNullOrEmpty(osmData) || osmData.Length < 100) { Debug.LogError("OSM数据无效,创建测试地图"); CreateTestMap(); return; } OSMData parsedData = osmParser.ParseOSMXML(osmData); CreateMapElements(parsedData); Debug.Log("地图生成完成!"); } void CreateMapElements(OSMData data) { // 创建地面 CreateGround(data); int totalBuildings = 0; int generatedBuildings = 0; int skippedBuildings = 0; // 创建各种地图元素 foreach (var way in data.ways) { var points = GetWayPoints(way, data); if (way.IsBuilding) { totalBuildings++; // 放宽建筑生成条件 if (points.Count >= 3) { CreateBuilding(way, points); generatedBuildings++; } else if (points.Count == 2) { // 对于只有2个点的建筑,创建简化版本 CreateSimpleBuilding(way, points); generatedBuildings++; } else { Debug.LogWarning($"跳过建筑 {way.id}: 只有 {points.Count} 个点"); skippedBuildings++; } } else if (way.IsRoad && points.Count >= 2) { CreateRoad(way, points); } else if (way.IsPark && points.Count >= 3) { CreatePark(way, points); } else if (way.IsWater && points.Count >= 3) { CreateWater(way, points); } } Debug.Log($"建筑生成统计: 总计{totalBuildings}, 生成{generatedBuildings}, 跳过{skippedBuildings}"); } // 创建简化建筑(用于只有2个点的情况) void CreateSimpleBuilding(OSMWay way, List points) { if (points.Count != 2) return; GameObject building = new GameObject($"SimpleBuilding_{way.id}"); building.transform.SetParent(transform); // 计算矩形的四个点 Vector3 direction = (points[1] - points[0]).normalized; Vector3 perpendicular = new Vector3(-direction.z, 0, direction.x); // 垂直方向 float width = 10f; // 默认宽度 List footprint = new List { points[0] - perpendicular * width * 0.5f, points[1] - perpendicular * width * 0.5f, points[1] + perpendicular * width * 0.5f, points[0] + perpendicular * width * 0.5f }; var meshFilter = building.AddComponent(); var renderer = building.AddComponent(); meshFilter.mesh = CreateBuildingMesh(footprint, buildingHeight); renderer.material = buildingMaterial; building.AddComponent(); Debug.Log($"创建简化建筑: {way.id} (从{points.Count}个点生成)"); } // 改进的节点获取方法 List GetWayPoints(OSMWay way, OSMData data) { var points = new List(); var nodeDict = CreateNodeDictionary(data); int foundNodes = 0; foreach (long nodeId in way.nodeRefs) { if (nodeDict.ContainsKey(nodeId)) { points.Add(nodeDict[nodeId].unityPosition); foundNodes++; } } if (foundNodes < way.nodeRefs.Count && way.IsBuilding) { Debug.LogWarning($"建筑 {way.id}: 引用 {way.nodeRefs.Count} 个节点,只找到 {foundNodes} 个"); } return points; } Dictionary CreateNodeDictionary(OSMData data) { var dict = new Dictionary(); foreach (var node in data.nodes) dict[node.id] = node; return dict; } void CreateGround(OSMData data) { if (data.nodes.Count == 0) return; GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane); ground.name = "MapGround"; ground.transform.SetParent(transform); Bounds bounds = CalculateBounds(data); ground.transform.position = new Vector3(bounds.center.x, -0.1f, bounds.center.z); ground.transform.localScale = new Vector3(bounds.size.x / 10f, 1, bounds.size.z / 10f); ground.GetComponent().material = groundMaterial; } void CreateBuilding(OSMWay way, List footprint) { GameObject building = new GameObject($"Building_{way.id}"); building.transform.SetParent(transform); var meshFilter = building.AddComponent(); var renderer = building.AddComponent(); meshFilter.mesh = CreateBuildingMesh(footprint, buildingHeight); renderer.material = buildingMaterial; building.AddComponent(); } void CreateRoad(OSMWay way, List path) { GameObject road = new GameObject($"Road_{way.id}"); road.transform.SetParent(transform); var lineRenderer = road.AddComponent(); lineRenderer.positionCount = path.Count; lineRenderer.SetPositions(path.ToArray()); lineRenderer.startWidth = roadWidth; lineRenderer.endWidth = roadWidth; lineRenderer.material = roadMaterial; lineRenderer.textureMode = LineTextureMode.Tile; } void CreatePark(OSMWay way, List area) { GameObject park = new GameObject($"Park_{way.id}"); park.transform.SetParent(transform); var ground = GameObject.CreatePrimitive(PrimitiveType.Plane); ground.transform.SetParent(park.transform); ground.name = "ParkGround"; Bounds bounds = CalculateBounds(area); ground.transform.position = bounds.center; ground.transform.localScale = new Vector3(bounds.size.x / 10f, 1, bounds.size.z / 10f); ground.GetComponent().material = parkMaterial; } void CreateWater(OSMWay way, List area) { GameObject water = new GameObject($"Water_{way.id}"); water.transform.SetParent(transform); var surface = GameObject.CreatePrimitive(PrimitiveType.Plane); surface.transform.SetParent(water.transform); surface.name = "WaterSurface"; Bounds bounds = CalculateBounds(area); surface.transform.position = bounds.center + Vector3.up * 0.1f; surface.transform.localScale = new Vector3(bounds.size.x / 10f, 1, bounds.size.z / 10f); surface.GetComponent().material = waterMaterial; } Mesh CreateBuildingMesh(List footprint, float height) { Mesh mesh = new Mesh(); var vertices = new List(); var triangles = new List(); int count = footprint.Count; // 底面顶点 vertices.AddRange(footprint); // 顶面顶点 foreach (var point in footprint) vertices.Add(point + Vector3.up * height); // 侧面 for (int i = 0; i < count; i++) { int next = (i + 1) % count; triangles.Add(i); triangles.Add(next); triangles.Add(i + count); triangles.Add(next); triangles.Add(next + count); triangles.Add(i + count); } // 底面和顶面 var bottomTris = TriangulatePolygon(footprint, false); var topTris = TriangulatePolygon(footprint, true); triangles.AddRange(bottomTris); foreach (int tri in topTris) triangles.Add(tri + count); mesh.vertices = vertices.ToArray(); mesh.triangles = triangles.ToArray(); mesh.RecalculateNormals(); return mesh; } List TriangulatePolygon(List vertices, bool clockwise) { var triangles = new List(); if (vertices.Count < 3) return triangles; for (int i = 1; i < vertices.Count - 1; i++) { if (clockwise) { triangles.Add(0); triangles.Add(i); triangles.Add(i + 1); } else { triangles.Add(0); triangles.Add(i + 1); triangles.Add(i); } } return triangles; } Bounds CalculateBounds(OSMData data) { if (data.nodes.Count == 0) return new Bounds(); Bounds bounds = new Bounds(data.nodes[0].unityPosition, Vector3.zero); foreach (var node in data.nodes) bounds.Encapsulate(node.unityPosition); return bounds; } Bounds CalculateBounds(List points) { if (points.Count == 0) return new Bounds(); Bounds bounds = new Bounds(points[0], Vector3.zero); foreach (var point in points) bounds.Encapsulate(point); return bounds; } void CreateTestMap() { Debug.Log("创建测试地图..."); ClearExistingMap(); // 创建测试地面 GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane); ground.name = "TestGround"; ground.transform.SetParent(transform); ground.transform.localScale = new Vector3(10, 1, 10); ground.GetComponent().material = groundMaterial; // 创建几个测试建筑 for (int i = 0; i < 5; i++) { var building = GameObject.CreatePrimitive(PrimitiveType.Cube); building.name = $"TestBuilding_{i}"; building.transform.SetParent(transform); building.transform.position = new Vector3(i * 15, 4, 0); building.transform.localScale = new Vector3(10, 8, 10); building.GetComponent().material = buildingMaterial; } Debug.Log("测试地图创建完成!"); } void ClearExistingMap() { foreach (Transform child in transform) DestroyImmediate(child.gameObject); } [ContextMenu("重新生成地图")] public void RegenerateMap() { StartCoroutine(DownloadAndGenerateMap()); } }