using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace MindPowerSdk.Editor
{
///
/// 地图生成工具窗口,用于配置地图数据参数、材质映射,以及生成、清除、保存地图到预制体。
/// 注意:每个地图配置项对应一个 MapName 与资源数据名称组合生成一个地图。
///
public class MapGeneratorEditorWindow : EditorWindow
{
// 文件夹路径设置
private string mapDataFolder = "";
private string resourceDataFolder = "";
// 地图数据配置:每个地图对应一个名称与数据资源名称
private List mapEntries = new List();
// 其他地图参数
private int chunkSize = 64;
private Material terrainMaterial;
// 材质映射设置(草、树等),关键字支持多个,用逗号分隔
private List materialMappings = new List();
// 生成地图时的父物体名称
private const string GENERATED_MAPS_PARENT = "GeneratedMaps";
[MenuItem("Tools/地图生成工具")]
public static void ShowWindow()
{
GetWindow("地图生成工具");
}
private void OnEnable()
{
// 初始化默认数据
if (mapEntries == null) mapEntries = new List();
if (materialMappings == null) materialMappings = new List();
}
private void OnGUI()
{
GUILayout.Label("文件夹路径设置", EditorStyles.boldLabel);
// 数据源文件夹选择
EditorGUILayout.BeginHorizontal();
// 从持久化文件中读取
mapDataFolder = EditorPrefs.GetString("MapDataFolder", mapDataFolder);
resourceDataFolder = EditorPrefs.GetString("ResourceDataFolder", resourceDataFolder);
mapDataFolder = EditorGUILayout.TextField("数据源文件夹", mapDataFolder);
if (GUILayout.Button("选择", GUILayout.Width(60)))
{
string folder = EditorUtility.OpenFolderPanel("选择数据源文件夹", "", "");
if (!string.IsNullOrEmpty(folder))
{
mapDataFolder = folder;
}
}
EditorGUILayout.EndHorizontal();
// 资源数据文件夹选择
EditorGUILayout.BeginHorizontal();
resourceDataFolder = EditorGUILayout.TextField("资源数据文件夹", resourceDataFolder);
if (GUILayout.Button("选择", GUILayout.Width(60)))
{
string folder = EditorUtility.OpenFolderPanel("选择资源数据文件夹", "", "");
if (!string.IsNullOrEmpty(folder))
{
resourceDataFolder = folder;
}
}
EditorPrefs.SetString("ResourceDataFolder", resourceDataFolder);
EditorPrefs.SetString("MapDataFolder", mapDataFolder);
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.Label("地图数据配置 (每个配置项对应一个地图)", EditorStyles.boldLabel);
// 列表显示地图配置项
for (int i = 0; i < mapEntries.Count; i++)
{
EditorGUILayout.BeginHorizontal();
mapEntries[i].mapName = EditorGUILayout.TextField("Map 名称", mapEntries[i].mapName).Replace(".map", "");
mapEntries[i].resourceDataName = EditorGUILayout.TextField("资源名称", mapEntries[i].resourceDataName)
.Replace(".obj", "");
mapEntries[i].isSmoothing = EditorGUILayout.Toggle("对地形平滑处理", mapEntries[i].isSmoothing);
if (GUILayout.Button("删除", GUILayout.Width(60)))
{
mapEntries.RemoveAt(i);
i--;
}
EditorGUILayout.EndHorizontal();
}
if (GUILayout.Button("添加地图配置"))
{
mapEntries.Add(new MapInfoEntry());
}
GUILayout.Space(10);
GUILayout.Label("地图其他参数", EditorStyles.boldLabel);
chunkSize = EditorGUILayout.IntField("Chunk Size", chunkSize);
terrainMaterial =
(Material)EditorGUILayout.ObjectField("Terrain Material", terrainMaterial, typeof(Material), false);
GUILayout.Space(10);
GUILayout.Label("材质映射配置 (草、树等)", EditorStyles.boldLabel);
// 材质映射配置项
for (int i = 0; i < materialMappings.Count; i++)
{
EditorGUILayout.BeginHorizontal();
// 关键字输入,支持多个关键字,用逗号分隔
materialMappings[i].keywords = EditorGUILayout.TextField("匹配关键字(,分隔)", materialMappings[i].keywords);
materialMappings[i].targetMaterial =
(Material)EditorGUILayout.ObjectField("目标材质", materialMappings[i].targetMaterial, typeof(Material),
false);
if (GUILayout.Button("删除", GUILayout.Width(60)))
{
materialMappings.RemoveAt(i);
i--;
}
EditorGUILayout.EndHorizontal();
}
if (GUILayout.Button("添加材质映射配置"))
{
materialMappings.Add(new MaterialMappingEntry());
}
GUILayout.Space(10);
// 操作按钮
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("生成到场景"))
{
GenerateMaps();
}
if (GUILayout.Button("清除生成地图"))
{
ClearGeneratedMaps();
}
if (GUILayout.Button("保存为预制体"))
{
SaveMapsAsPrefabs();
}
EditorGUILayout.EndHorizontal();
}
///
/// 根据当前配置生成所有地图,并生成到场景下的 GENERATED_MAPS_PARENT 父物体内。
///
private void GenerateMaps()
{
// 检查必要的路径和配置
if (string.IsNullOrEmpty(mapDataFolder) || string.IsNullOrEmpty(resourceDataFolder))
{
Debug.LogError("请先设置数据源和资源数据的文件夹路径!");
return;
}
if (mapEntries.Count == 0)
{
Debug.LogError("请添加至少一个地图配置项!");
return;
}
// 查找或创建生成地图的父物体
GameObject parent = GameObject.Find(GENERATED_MAPS_PARENT);
if (parent == null)
{
parent = new GameObject(GENERATED_MAPS_PARENT);
}
// 遍历每个地图配置项生成地图
foreach (var entry in mapEntries)
{
// 拼接完整数据路径
string mapDataPath = Path.Combine(mapDataFolder, entry.mapName + ".map");
string resourceDataPath = Path.Combine(resourceDataFolder, entry.resourceDataName + ".obj");
// 读取地图数据
using FileStream fs = File.OpenRead(mapDataPath);
BinaryReader reader = new BinaryReader(fs);
var map = new MPMap();
map.Load(reader);
Debug.Log($"map size:({map.Width},{map.Height}) | section size:({map.SectionWidth},{map.SectionHeight})");
// 调用生成 Terrain 的方法,传入 mapName 作为标识
TerrainGenerator.GenTerrain(map, chunkSize, terrainMaterial, parent.transform, entry.mapName,
entry.isSmoothing);
Debug.Log($"生成地图:{entry.mapName}\n数据路径:{mapDataPath}\n资源路径:{resourceDataPath}");
CreateMapObject(resourceDataPath, map, parent.transform);
}
}
void CreateMapObject(string objPath, MPMap map, Transform parent)
{
CSceneObjSet sceneObjSet = new CSceneObjSet();
sceneObjSet.LoadBin("Assets/Resources/sceneobjinfo.bin");
SceneObjFile sceneObjFile = new SceneObjFile();
sceneObjFile.Load(objPath);
string objName = Path.GetFileNameWithoutExtension(objPath);
GameObject root = new GameObject($"{objName}_SceneAssetsRoot");
root.transform.SetParent(parent);
for (int y = 0; y < sceneObjFile.FileHeader.SectionCntY; y++)
{
for (int x = 0; x < sceneObjFile.FileHeader.SectionCntX; x++)
{
var list = sceneObjFile.objInfos[x, y];
if (list is null) continue;
foreach (var sceneObjInfo in list)
{
if (sceneObjInfo.GetTypeId() == 1)
continue;
int id = sceneObjInfo.GetID();
CSceneObjInfo modeInfo = sceneObjSet.Get(id);
Vector3 pos = new Vector3(sceneObjInfo.X / 100f, sceneObjInfo.HeightOff / 100f,
sceneObjInfo.Y / 100f * -1f);
float mapHeight = map.GetHeight(pos.x, (sceneObjInfo.Y / 100f));
pos.y += mapHeight;
//Quaternion rot = Quaternion.AngleAxis(sceneObjInfo.YawAngle, Vector3.up);
string modePath = "Assets/Resources/Model/Scene/" +
Path.GetFileNameWithoutExtension(modeInfo.szDataName) + ".lmo.obj";
Object mode = AssetDatabase.LoadAssetAtPath