584 lines
17 KiB
C#
584 lines
17 KiB
C#
//////////////////////////////////////////////////////////////////////////
|
||
//
|
||
// 文件:Assets/Editor/Tools/EditorUtil.cs
|
||
// 作者:Xoen Xie
|
||
// 时间:2023/07/18
|
||
// 描述:工具通用函数集
|
||
// 说明:
|
||
//
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using System.IO;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using System.Xml;
|
||
using TGS;
|
||
using UnityEditor.SceneManagement;
|
||
using UnityEditorInternal;
|
||
using UnityEngine.SceneManagement;
|
||
using Debug = UnityEngine.Debug;
|
||
using Object = System.Object;
|
||
using Random = UnityEngine.Random;
|
||
|
||
public static class EditorUtil
|
||
{
|
||
public static bool StartProcess(string sFilename, string sArgs, string sWorkDir)
|
||
{
|
||
Process ps = null;
|
||
bool fail = false;
|
||
|
||
try
|
||
{
|
||
ProcessStartInfo StartInfo = new ProcessStartInfo();
|
||
StartInfo.FileName = sFilename;
|
||
StartInfo.Arguments = sArgs;
|
||
StartInfo.CreateNoWindow = true;
|
||
StartInfo.UseShellExecute = false;
|
||
if (!string.IsNullOrEmpty(sWorkDir))
|
||
StartInfo.WorkingDirectory = sWorkDir;
|
||
StartInfo.RedirectStandardError = true;
|
||
StartInfo.RedirectStandardOutput = true;
|
||
ps = new Process();
|
||
ps.StartInfo = StartInfo;
|
||
ps.Start();
|
||
StreamReader readerErr = ps.StandardError; //截取错误流
|
||
|
||
string line = readerErr.ReadLine();
|
||
while (!readerErr.EndOfStream)
|
||
{
|
||
line = readerErr.ReadLine();
|
||
line = line + "\r\n";
|
||
}
|
||
|
||
ps.WaitForExit();
|
||
|
||
var strOut = ps.StandardOutput.ReadToEnd();
|
||
UnityEngine.Debug.Log(strOut);
|
||
|
||
|
||
string fileName = null;
|
||
string arguments = null;
|
||
|
||
if (ps.ExitCode != 0 && !fail)
|
||
{
|
||
fail = true;
|
||
fileName = ps.StartInfo.FileName;
|
||
arguments = ps.StartInfo.Arguments;
|
||
}
|
||
|
||
if (fail)
|
||
{
|
||
throw new Exception(string.Format("ExitCode:{0}]\nStartProcess Fail.FileName=[{1}]\nArg=[{2}\n{3}",
|
||
ps.ExitCode, fileName, arguments, line));
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
UnityEngine.Debug.LogError(e.Message);
|
||
}
|
||
finally
|
||
{
|
||
ps.Dispose();
|
||
}
|
||
|
||
return !fail;
|
||
}
|
||
|
||
enum FindOp
|
||
{
|
||
None = 0,
|
||
Start,
|
||
End,
|
||
}
|
||
|
||
public static bool ReplaceContentByTag(string sFilename, string sTagStart, string sTagEnd, string sNewContent)
|
||
{
|
||
bool bFindStart = false;
|
||
bool bFindEnd = false;
|
||
|
||
string[] lines = File.ReadAllLines(sFilename);
|
||
|
||
string sSrcContent = "";
|
||
|
||
FindOp nFindFlag = FindOp.Start;
|
||
|
||
for (uint i = 0; i < lines.Length; ++i)
|
||
{
|
||
switch (nFindFlag)
|
||
{
|
||
case FindOp.Start:
|
||
{
|
||
sSrcContent += lines[i] + "\n";
|
||
|
||
if (lines[i].Contains(sTagStart))
|
||
{
|
||
nFindFlag = FindOp.End;
|
||
|
||
sSrcContent += sNewContent;
|
||
|
||
bFindStart = true;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case FindOp.End:
|
||
{
|
||
if (lines[i].Contains(sTagEnd))
|
||
{
|
||
nFindFlag = FindOp.None;
|
||
|
||
sSrcContent += lines[i] + "\n";
|
||
bFindEnd = true;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
sSrcContent += lines[i] + "\n";
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (bFindStart && bFindEnd)
|
||
{
|
||
File.WriteAllText(sFilename, sSrcContent);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public static bool AppendFileByTag(string sFilename, string sTag, string sNewContent)
|
||
{
|
||
if (string.IsNullOrEmpty(sNewContent))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
bool bFindTag = false;
|
||
|
||
string[] lines = File.ReadAllLines(sFilename);
|
||
|
||
string sSrcContent = "";
|
||
|
||
FindOp nFindFlag = FindOp.Start;
|
||
|
||
for (uint i = 0; i < lines.Length; ++i)
|
||
{
|
||
switch (nFindFlag)
|
||
{
|
||
case FindOp.Start:
|
||
{
|
||
if (lines[i].Contains(sTag))
|
||
{
|
||
nFindFlag = FindOp.None;
|
||
|
||
sSrcContent += "\n";
|
||
sSrcContent += sNewContent;
|
||
|
||
bFindTag = true;
|
||
}
|
||
|
||
sSrcContent += lines[i] + "\n";
|
||
}
|
||
break;
|
||
|
||
default:
|
||
sSrcContent += lines[i] + "\n";
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (bFindTag)
|
||
{
|
||
File.WriteAllText(sFilename, sSrcContent);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public static string FirstLetterToUpper(string str)
|
||
{
|
||
if (str.Length > 0)
|
||
return char.ToUpper(str[0]) + str.Substring(1);
|
||
|
||
return str.ToUpper();
|
||
}
|
||
|
||
public static int GetInt(XmlElement e, string attribute = null)
|
||
{
|
||
string tmp = null;
|
||
if (string.IsNullOrEmpty(attribute))
|
||
tmp = e.InnerText;
|
||
else
|
||
tmp = e.GetAttribute(attribute);
|
||
|
||
if (string.IsNullOrEmpty(tmp))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
try
|
||
{
|
||
return Convert.ToInt32(tmp);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
|
||
public static uint GetUInt(XmlElement e, string attribute = null)
|
||
{
|
||
string tmp = null;
|
||
if (string.IsNullOrEmpty(attribute))
|
||
tmp = e.InnerText;
|
||
else
|
||
tmp = e.GetAttribute(attribute);
|
||
|
||
if (string.IsNullOrEmpty(tmp))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
try
|
||
{
|
||
return Convert.ToUInt32(tmp);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
|
||
public static long GetLong(XmlElement e, string attribute = null)
|
||
{
|
||
string tmp = null;
|
||
if (string.IsNullOrEmpty(attribute))
|
||
tmp = e.InnerText;
|
||
else
|
||
tmp = e.GetAttribute(attribute);
|
||
|
||
if (string.IsNullOrEmpty(tmp))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
try
|
||
{
|
||
return Convert.ToInt64(tmp);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
|
||
public static ulong GetULong(XmlElement e, string attribute = null)
|
||
{
|
||
string tmp = null;
|
||
if (string.IsNullOrEmpty(attribute))
|
||
tmp = e.InnerText;
|
||
else
|
||
tmp = e.GetAttribute(attribute);
|
||
|
||
if (string.IsNullOrEmpty(tmp))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
try
|
||
{
|
||
return Convert.ToUInt64(tmp);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
|
||
public static float GetFloat(XmlElement e, string attribute = null)
|
||
{
|
||
string tmp = null;
|
||
if (string.IsNullOrEmpty(attribute))
|
||
tmp = e.InnerText;
|
||
else
|
||
tmp = e.GetAttribute(attribute);
|
||
|
||
if (string.IsNullOrEmpty(tmp))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
try
|
||
{
|
||
return Convert.ToSingle(tmp);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
throw;
|
||
}
|
||
}
|
||
|
||
public static string GetString(XmlElement e, string attribute = null)
|
||
{
|
||
if (string.IsNullOrEmpty(attribute))
|
||
return e.InnerText;
|
||
|
||
return e.GetAttribute(attribute);
|
||
}
|
||
|
||
|
||
public static bool GetBoolean(XmlElement e, string attribute = null)
|
||
{
|
||
string tmp = null;
|
||
if (string.IsNullOrEmpty(attribute))
|
||
tmp = e.InnerText;
|
||
else
|
||
tmp = e.GetAttribute(attribute);
|
||
|
||
if (string.IsNullOrEmpty(tmp))
|
||
return false;
|
||
|
||
tmp = tmp.ToLower();
|
||
if (tmp.Equals("true") || tmp.Equals("1"))
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
public static string GetRealPath(string savePath)
|
||
{
|
||
var finalPath = savePath;
|
||
if (finalPath.StartsWith("/"))
|
||
{
|
||
finalPath = Application.dataPath + finalPath;
|
||
}
|
||
|
||
{
|
||
var dirInfo = new DirectoryInfo(finalPath);
|
||
if (dirInfo.Exists)
|
||
{
|
||
return dirInfo.FullName;
|
||
}
|
||
}
|
||
{
|
||
var fileInfo = new FileInfo(finalPath);
|
||
if (fileInfo.Exists)
|
||
{
|
||
return fileInfo.FullName;
|
||
}
|
||
}
|
||
|
||
return finalPath;
|
||
}
|
||
|
||
//调用此函数可以保证你保存的asset的路径不会覆盖到其他asset,此函数会自动给asset重命名,所以限制是你不能将asset的名字作为你逻辑的数据
|
||
public static void CreateAnUniqueAsset(UnityEngine.Object asset, string path)
|
||
{
|
||
if (asset == null || string.IsNullOrEmpty(path))
|
||
{
|
||
Debug.LogError("输入参数不合法!");
|
||
return;
|
||
}
|
||
|
||
var savePath = GetAssetUniquePath(path, out var assetName);
|
||
asset.name = assetName;
|
||
AssetDatabase.CreateAsset(asset, savePath);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取一个唯一的路径用于保存asset,假设传入的路径已经存在,则自动生成一个新的
|
||
/// </summary>
|
||
/// <param name="assetPath">尝试保存的路径</param>
|
||
/// <returns>第一个string是唯一路径,第二个string是asset的新名字</returns>
|
||
public static string GetAssetUniquePath(string assetPath, out string assetName)
|
||
{
|
||
assetName = Path.GetFileNameWithoutExtension(assetPath);
|
||
var extension = Path.GetExtension(assetPath);
|
||
var dir = Path.GetDirectoryName(assetPath);
|
||
var savedAsset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
|
||
if (savedAsset == null)
|
||
{
|
||
return assetPath;
|
||
}
|
||
|
||
assetName = $"{assetName}_{Random.Range(0, 10000)}";
|
||
var newAssetPath = $"{dir}/{assetName}{extension}";
|
||
return GetAssetUniquePath(newAssetPath, out assetName);
|
||
}
|
||
|
||
public static void LockInspector(bool isLock)
|
||
{
|
||
Type t = typeof(EditorWindow).Assembly.GetType("UnityEditor.InspectorWindow");
|
||
var window = EditorWindow.GetWindow(t);
|
||
|
||
t.GetProperty("isLocked").SetValue(window, isLock);
|
||
}
|
||
|
||
public static bool IsStringContains(this string str1, string str2)
|
||
{
|
||
return str1.ToLower().Contains(str2.ToLower());
|
||
}
|
||
|
||
public static Bounds? GetMaxBounds(this GameObject go)
|
||
{
|
||
var meshRenderers = go.GetComponentsInChildren<MeshRenderer>();
|
||
if (meshRenderers == null || meshRenderers.Length == 0)
|
||
return null;
|
||
Bounds result = new();
|
||
bool hasSetFirst = false;
|
||
foreach (var msr in meshRenderers)
|
||
{
|
||
if (!hasSetFirst)
|
||
{
|
||
result = msr.bounds;
|
||
hasSetFirst = true;
|
||
}
|
||
else
|
||
{
|
||
result.Encapsulate(msr.bounds);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
//[MenuItem("Tools/批量修改场景")]
|
||
public static void CorrectLevels()
|
||
{
|
||
var guids = AssetDatabase.FindAssets("t:SceneAsset", new[] { "Assets/Scenes/Maps" });
|
||
var ArtPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Scenes/Maps/Template/MapDefault/Art.prefab");
|
||
var tgsMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/Scenes/Maps/Template/MapDefault/TGS_Default.mat");
|
||
foreach (var guid in guids)
|
||
{
|
||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||
if (string.IsNullOrEmpty(path) || path.Contains("LevelDefault"))
|
||
continue;
|
||
EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
|
||
// var Art = GameObject.Find("Art");
|
||
// if (Art != null && !PrefabUtility.IsAnyPrefabInstanceRoot(Art))
|
||
// {
|
||
// var convertSetting = new ConvertToPrefabInstanceSettings
|
||
// {
|
||
// objectMatchMode = ObjectMatchMode.ByName,
|
||
// componentsNotMatchedBecomesOverride = true,
|
||
// recordPropertyOverridesOfMatches = true,
|
||
// gameObjectsNotMatchedBecomesOverride = true
|
||
// };
|
||
// PrefabUtility.ConvertToPrefabInstance(Art, ArtPrefab, convertSetting, InteractionMode.AutomatedAction);
|
||
// }
|
||
//
|
||
// if (UnityEngine.Object.FindObjectOfType<MapManager>() == null)
|
||
// {
|
||
// GameObject mapEditor = new("MapEditor");
|
||
// mapEditor.AddComponent<MapManager>();
|
||
// }
|
||
//
|
||
// if (UnityEngine.Object.FindObjectOfType<LevelEditor>() == null)
|
||
// {
|
||
// GameObject levelEditor = new("LevelEditor");
|
||
// levelEditor.AddComponent<LevelEditor>();
|
||
// }
|
||
var tgs = UnityEngine.Object.FindObjectOfType<TerrainGridSystem>();
|
||
var meshRenderer = tgs.GetComponent<MeshRenderer>();
|
||
meshRenderer.sharedMaterial = tgsMat;
|
||
|
||
EditorSceneManager.MarkAllScenesDirty();
|
||
EditorSceneManager.SaveOpenScenes();
|
||
AssetDatabase.SaveAssets();
|
||
AssetDatabase.Refresh();
|
||
}
|
||
}
|
||
|
||
//[MenuItem("Tools/修改level为map")]
|
||
private static void ChangeStringLevelToMap()
|
||
{
|
||
var guids = AssetDatabase.FindAssets("t:Object", new[] { "Assets/Scenes/Maps" });
|
||
foreach (var guid in guids)
|
||
{
|
||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||
if (string.IsNullOrEmpty(path))
|
||
continue;
|
||
var oldName = Path.GetFileName(path);
|
||
var levelName = "Level";
|
||
var mapName = "Map";
|
||
if (oldName.Contains(levelName))
|
||
{
|
||
AssetDatabase.RenameAsset(path, oldName.Replace(levelName, mapName));
|
||
}
|
||
}
|
||
}
|
||
|
||
public static void DrawArrowInScene(Vector3 start,Vector3 end,float arrowLength = 1)
|
||
{
|
||
Handles.DrawLine(start, end);
|
||
Vector3 direction = (end - start).normalized;
|
||
Vector3 arrowEnd = end - (direction * arrowLength);
|
||
|
||
float arrowHeadAngle = 20f;
|
||
|
||
Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * Vector3.forward;
|
||
Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * Vector3.forward;
|
||
|
||
Handles.DrawLine(end, arrowEnd + right * (arrowLength * 0.4f));
|
||
Handles.DrawLine(end, arrowEnd + left * (arrowLength * 0.4f));
|
||
Handles.DrawLine(arrowEnd + right * (arrowLength * 0.4f), arrowEnd);
|
||
Handles.DrawLine(arrowEnd + left * (arrowLength * 0.4f), arrowEnd);
|
||
}
|
||
|
||
public static string GetCurrentAssetDirectory()
|
||
{
|
||
foreach (UnityEngine.Object obj in Selection.GetFiltered<Object>(SelectionMode.Assets))
|
||
{
|
||
var path = AssetDatabase.GetAssetPath(obj);
|
||
if (string.IsNullOrEmpty(path))
|
||
continue;
|
||
|
||
if (System.IO.Directory.Exists(path))
|
||
return path;
|
||
else if (System.IO.File.Exists(path))
|
||
return System.IO.Path.GetDirectoryName(path);
|
||
}
|
||
|
||
return "Assets";
|
||
}
|
||
|
||
public static void AddPathToAddressable(string assetPath,string groupName)
|
||
{
|
||
#if USE_ADDRESSABLES
|
||
// 获取 AddressableAssetSettings
|
||
var settings = AddressableAssetSettingsDefaultObject.Settings;
|
||
|
||
// 获取或创建一个资源组
|
||
var group = settings.FindGroup(groupName);
|
||
if (group == null)
|
||
{
|
||
Debug.LogError($"找不到资源组:{groupName}");
|
||
return;
|
||
}
|
||
// 创建或移动条目
|
||
var guid = AssetDatabase.AssetPathToGUID(assetPath);
|
||
var entry = settings.CreateOrMoveEntry(guid, group);
|
||
|
||
//为了防止出现增量更新bug,需要将场景放在group的根下
|
||
List<AddressableAssetEntry> sceneEntries = new();
|
||
entry.GatherAllAssets(sceneEntries, true, true, false,
|
||
e => e.MainAssetType == typeof(SceneAsset) || e.IsFolder);
|
||
sceneEntries = sceneEntries.Where(e => e.MainAssetType == typeof(SceneAsset)).ToList();
|
||
if (sceneEntries.Count > 0)
|
||
{
|
||
EditorUtility.SetDirty(group);
|
||
foreach (var sceneEntry in sceneEntries)
|
||
{
|
||
settings.MoveEntry(sceneEntry, group);
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
} |