NLDClient-yudde/ProjectNLD/Assets/Editor/LevelEditor/LevelEditorHelper.cs

403 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Framework;
using Framework.Utils;
using Gameplay;
using Gameplay.Level;
using TGS;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.EventSystems;
using PhxhSDK.Phxh;
using Object = UnityEngine.Object;
public static class LevelEditorHelper
{
private const string LEVEL_TEMPLATE_PATH = "Assets/Scenes/Maps/Template/MapDefault/MapDefault.unity";
private const string TERRAIN_LEVEL_TEMPLATE_PATH = "Assets/Scenes/Maps/Template/MapDefault/TerrainMapDefault.unity";
private const string LEGION_LEVEL_TEMPLATE_PATH = "Assets/Scenes/Maps/Template/MapDefault/LegionMapDefault.unity";
private const string MAP_FOLDER = "Assets/Scenes/Maps";
[MenuItem("Assets/*Create/地图场景/创建标准地图")]
private static void CreateLevel()
{
var currDir = EditorUtil.GetCurrentAssetDirectory().Replace("\\", "/");
var dirName = Path.GetFileName(currDir);
var count = AssetDatabase.FindAssets("t:SceneAsset", new[] { currDir }).Length + 1;
var savePath = EditorUtil.GetAssetUniquePath($"{currDir}/{dirName}_{count}.unity", out var assetName);
AssetDatabase.CopyAsset(LEVEL_TEMPLATE_PATH, savePath);
var newScene = AssetDatabase.LoadAssetAtPath<SceneAsset>(savePath);
EditorGUIUtility.PingObject(newScene);
}
[MenuItem("Assets/*Create/地图场景/创建标准地图(新)")]
private static void CreateTerrainLevel()
{
var currDir = EditorUtil.GetCurrentAssetDirectory().Replace("\\", "/");
var dirName = Path.GetFileName(currDir);
var count = AssetDatabase.FindAssets("t:SceneAsset", new[] { currDir }).Length + 1;
var savePath = EditorUtil.GetAssetUniquePath($"{currDir}/{dirName}_{count}.unity", out var assetName);
AssetDatabase.CopyAsset(TERRAIN_LEVEL_TEMPLATE_PATH, savePath);
var newScene = AssetDatabase.LoadAssetAtPath<SceneAsset>(savePath);
EditorGUIUtility.PingObject(newScene);
}
[MenuItem("Assets/*Create/地图场景/创建军团地图")]
private static void CreateLegionLevel()
{
var currDir = EditorUtil.GetCurrentAssetDirectory().Replace("\\", "/");
var dirName = Path.GetFileName(currDir);
var count = AssetDatabase.FindAssets("t:SceneAsset", new[] { currDir }).Length + 1;
var savePath = EditorUtil.GetAssetUniquePath($"{currDir}/{dirName}_Legion_{count}.unity", out var assetName);
AssetDatabase.CopyAsset(LEGION_LEVEL_TEMPLATE_PATH, savePath);
var newScene = AssetDatabase.LoadAssetAtPath<SceneAsset>(savePath);
EditorGUIUtility.PingObject(newScene);
}
[MenuItem("Tools/*关卡数据工具/关卡任务导出")]
private static void ExportLevelData()
{
// 选择导出路径
var savePath = EditorUtility.SaveFilePanel("导出关卡任务数据", Application.dataPath, "LevelTasks.csv", "csv");
if (string.IsNullOrEmpty(savePath))
return;
var sb = new StringBuilder();
// 表头
sb.AppendLine(string.Join(",",
Csv("关卡json"),
Csv("任务ID"),
Csv("任务条件"),
Csv("任务文本key"),
Csv("任务文本"),
Csv("任务注释"),
Csv("是否为胜利条件"),
Csv("是否为失败条件"),
Csv("是否为1星条件"),
Csv("是否为2星条件"),
Csv("是否为3星条件")));
var levelCfgList = EditorTableManager.instance.tables.Level.DataList;
try
{
var levelCount = levelCfgList.Count;
for (var levelIndex = 0; levelIndex < levelCount; levelIndex++)
{
var cfg = levelCfgList[levelIndex];
// 更新进度条
var progress = (levelIndex + 1) / (float)levelCount;
EditorUtility.DisplayProgressBar("导出关卡任务数据",
$"正在处理关卡: {cfg.LevelData} ({levelIndex + 1}/{levelCount})", progress);
// cfg.LevelData 为关卡数据名(不带扩展名)
var levelJsonName = cfg.LevelData;
var levelPath = string.Format(Constants.LEVEL_CONFIG_FORMAT_PATH, levelJsonName);
if (!File.Exists(levelPath))
{
Debug.LogWarning($"关卡数据不存在: {levelPath}");
continue;
}
LevelData levelData = null;
try
{
levelData = JsonHelper.LoadJson<LevelData>(levelPath);
}
catch (Exception e)
{
Debug.LogError($"解析关卡数据失败: {levelPath}\n{e}");
continue;
}
if (levelData == null || levelData.taskInfos == null || levelData.taskInfos.Count == 0)
continue;
foreach (var task in levelData.taskInfos)
{
if (task == null || task.taskCondition == null)
continue;
var taskId = task.taskID;
var condition = task.taskCondition;
// 任务条件ConditionType(参数1,参数2,...)
var condTypeName = condition.conditionType.ToString();
var args = condition.Args();
string condWithArgs;
if (args != null && args.Count > 0)
{
var argStr = string.Join(",", args.Select(a => a.ToString(CultureInfo.InvariantCulture)));
condWithArgs = $"{condTypeName}({argStr})";
}
else
{
condWithArgs = condTypeName;
}
// 文本 key如果有手写替换 keytaskDesc则使用没有就留空
string textKey = string.IsNullOrEmpty(task.taskDesc) ? string.Empty : task.taskDesc;
// 文本内容:仅当有有效 key 时,从 StringConfig 里拿 value否则留空
string textValue = string.Empty;
if (!string.IsNullOrEmpty(textKey))
{
try
{
var dataString = EditorTableManager.instance.tables.StringConfig.GetOrDefault(textKey);
textValue = dataString?.Value ?? string.Empty;
}
catch
{
// 忽略文本表中不存在的 key
}
}
// 是否为胜利/失败/星级条件
var isSuccess = levelData.successTaskID == taskId;
var isFail = levelData.failTaskID == taskId;
var isStar1 = levelData.starTaskIDs != null && levelData.starTaskIDs.Count > 0 &&
levelData.starTaskIDs[0] == taskId;
var isStar2 = levelData.starTaskIDs != null && levelData.starTaskIDs.Count > 1 &&
levelData.starTaskIDs[1] == taskId;
var isStar3 = levelData.starTaskIDs != null && levelData.starTaskIDs.Count > 2 &&
levelData.starTaskIDs[2] == taskId;
sb.AppendLine(string.Join(",",
Csv(levelJsonName),
Csv(taskId.ToString()),
Csv(condWithArgs),
Csv(textKey),
Csv(textValue),
Csv(task.taskNote ?? string.Empty),
Csv(isSuccess ? "1" : "0"),
Csv(isFail ? "1" : "0"),
Csv(isStar1 ? "1" : "0"),
Csv(isStar2 ? "1" : "0"),
Csv(isStar3 ? "1" : "0")));
}
}
}
finally
{
EditorUtility.ClearProgressBar();
}
File.WriteAllText(savePath, sb.ToString(), Encoding.UTF8);
AssetDatabase.Refresh();
EditorUtility.RevealInFinder(savePath);
}
/// <summary>
/// 简单 CSV 转义:用双引号包裹,并转义内部双引号。
/// </summary>
private static string Csv(string value)
{
if (string.IsNullOrEmpty(value))
return "\"\"";
value = value.Replace("\"", "\"\"");
return $"\"{value}\"";
}
private static string[] GetAllMapScenes(bool includeTemplate = false)
{
var guids = AssetDatabase.FindAssets("t:SceneAsset", new[] { MAP_FOLDER });
return guids.Select(AssetDatabase.GUIDToAssetPath).Where(path => includeTemplate || !path.Contains("Template")).ToArray();
}
private static string[] GetAllPostProcessedMapScenes()
{
var mapScenes = GetAllMapScenes();
var result = mapScenes.Select(PostProcessData.EditorInstance.EditorGetPostProcessedPath).ToArray();
return result;
}
private static List<string> GetModifiedScenes()
{
var scenePaths = GetAllMapScenes();
return scenePaths.Where(path => PostProcessData.EditorInstance.ShouldPostProcess(path)).ToList();
}
//[MenuItem("Tools/地图场景/设置所有场景后处理指导")]
private static void SetAllMapInstructor()
{
var scenePaths = GetAllMapScenes();
foreach (var scenePath in scenePaths)
{
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
EditorSceneManager.MarkSceneDirty(scene);
var instructor = PostProcessor.FindInstructorInScene(scene);
if (instructor == null)
{
continue;
}
EditorUtility.SetDirty(instructor);
var groundObject = instructor.combinedObjectsRoot.Find("GroundObject");
if (groundObject)
{
instructor.rootsToCombine.Add(groundObject);
}
var fog = instructor.combinedObjectsRoot.Find("Fog");
if (fog)
{
instructor.fog = fog.gameObject;
}
EditorSceneManager.SaveScene(scene);
EditorSceneManager.CloseScene(scene, true);
}
}
//[MenuItem("Tools/地图场景/删除所有地图EventSystem")]
private static void DeleteAllEventSystem()
{
var scenePaths = GetAllMapScenes();
foreach (var scenePath in scenePaths)
{
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
EditorSceneManager.MarkSceneDirty(scene);
var eventSystem = FrameWorkUtils.GetSceneRootComponent<EventSystem>(scene);
if (eventSystem)
Object.DestroyImmediate(eventSystem.gameObject);
EditorSceneManager.SaveScene(scene);
EditorSceneManager.CloseScene(scene, true);
}
var postProcessedMapScene = GetAllPostProcessedMapScenes();
foreach (var scenePath in postProcessedMapScene)
{
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
EditorSceneManager.MarkSceneDirty(scene);
var eventSystem = FrameWorkUtils.GetSceneRootComponent<EventSystem>(scene);
if (eventSystem)
Object.DestroyImmediate(eventSystem.gameObject);
EditorSceneManager.SaveScene(scene);
EditorSceneManager.CloseScene(scene, true);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
PostProcessData.EditorInstance.ReCalculateHashCode();
}
[MenuItem("Tools/*地图场景/后处理所有场景", false, (int)NLDMenuID.MapPostProcess)]
private static void PostProcessAllMap()
{
var scenePaths = GetModifiedScenes();
if (EditorUtility.DisplayDialog("后处理所有地图场景", $"当前有修改的地图场景有:{scenePaths.Count}个,是否批量执行?",
"确认", "取消"))
{
int index = 0;
foreach (var scenePath in scenePaths)
{
EditorUtility.DisplayProgressBar("后处理所有地图场景", $"当前场景路径:{scenePath} {index + 1}/{scenePaths.Count}",
(index + 1) / (float)scenePaths.Count);
var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Additive);
try
{
ObjectPostProcessorManager.Instance.PostProcess(scene, true);
}
catch (Exception e)
{
Debug.LogError($"{e.Message} \r\n {e.StackTrace}");
}
finally
{
EditorSceneManager.CloseScene(scene, true);
}
index++;
}
EditorUtility.ClearProgressBar();
Debug.Log("执行所有地图后处理完成!");
}
}
// [MenuItem("Tools/Addressables/转移所有场景到组的根部")]
private static void MoveAllScenesToAddressableGroupRoot()
{
#if USE_ADDRESSABLES
var settings = AddressableAssetSettingsDefaultObject.Settings;
EditorUtility.SetDirty(settings);
List<AddressableAssetEntry> entries = new();
foreach (var group in settings.groups)
{
entries.Clear();
group.GatherAllAssets(entries, true, true, false, e => e.MainAssetType == typeof(SceneAsset) || e.IsFolder);
entries = entries.Where(e => e.MainAssetType == typeof(SceneAsset)).ToList();
if (entries.Count > 0)
{
EditorUtility.SetDirty(group);
foreach (var sceneEntry in entries)
{
settings.MoveEntry(sceneEntry, group);
}
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
#endif
}
// [MenuItem("Tools/Addressables/修改所有group下载信息")]
private static void ModifyAllGroupDownloadInfo()
{
#if USE_ADDRESSABLES
var settings = AddressableAssetSettingsDefaultObject.Settings;
EditorUtility.SetDirty(settings);
foreach (var group in settings.groups)
{
EditorUtility.SetDirty(group);
var schema = group.GetSchema<BundledAssetGroupSchema>();
if (schema)
{
EditorUtility.SetDirty(schema);
schema.RetryCount = 3;
schema.Timeout = 5;
}
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
#endif
}
private static string[] GetAllOriginalAndProcessedMap()
{
var originalMap = GetAllMapScenes(true);
var processedMap = GetAllPostProcessedMapScenes();
return originalMap.Concat(processedMap).ToArray();
}
[MenuItem("Tools/*地图场景/修改所有场景TGS边框厚度", false, (int)NLDMenuID.MapTGSThickness)]
private static void ModifyAllOriginalAndProcessedMapTGSThickness()
{
var maps = GetAllOriginalAndProcessedMap();
foreach (var map in maps)
{
var scene = EditorSceneManager.OpenScene(map, OpenSceneMode.Additive);
var tgs = FrameWorkUtils.GetSceneRootComponent<TerrainGridSystem>(scene);
if (tgs)
{
tgs.cellBorderThickness = 0.04f;
}
EditorSceneManager.SaveScene(scene);
EditorSceneManager.CloseScene(scene, true);
}
PostProcessData.EditorInstance.ReCalculateHashCode();
}
}