策划工具移动到策划SubModule中
This commit is contained in:
parent
59298e4d8f
commit
dc5e458475
8
Scripts/Editor.meta
Normal file
8
Scripts/Editor.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5706044f5f8a1d9469a0d192ad6a6da8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Scripts/Editor/Design_Tools.meta
Normal file
8
Scripts/Editor/Design_Tools.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5700364c75144e9418afa8c01c54ec6d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Scripts/Editor/Design_Tools/Collections.meta
Normal file
8
Scripts/Editor/Design_Tools/Collections.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 878d1e2621294df439f0cf7a06d20f32
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
937
Scripts/Editor/Design_Tools/Collections/EmojiConfigEditor.cs
Normal file
937
Scripts/Editor/Design_Tools/Collections/EmojiConfigEditor.cs
Normal file
@ -0,0 +1,937 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using OfficeOpenXml;
|
||||
using ArtResource;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace DesignTools.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 表情配置Editor工具
|
||||
/// 读取Docs/config/Emoji.xlsx,关联Art_SO/Collections/EmojiResource.asset
|
||||
/// </summary>
|
||||
public class EmojiConfigEditor : EditorWindow
|
||||
{
|
||||
private const string EMOJI_SO_PATH = "Assets/Art_SubModule/Art_SO/Collections";
|
||||
private const string EMOJI_SO_NAME = "EmojiResource";
|
||||
private const string EMOJI_EXCEL_NAME = "Emoji.xlsx";
|
||||
private const string LANGUAGE_EXCEL_NAME = "AllLanguage.xlsx";
|
||||
private const string EMOJI_SHEET_NAME = "Emoji";
|
||||
private const string LANGUAGE_SHEET_NAME = "client";
|
||||
private const string DOCS_PATH_PREF_KEY = "EmojiConfigEditor_DocsPath";
|
||||
private const string DESIGN_SUBMODULE_PATH = "Assets/Design_SubModule";
|
||||
|
||||
private string docsRootPath = "";
|
||||
private List<EmojiData> emojiDataList = new List<EmojiData>();
|
||||
private ArtTableSO emojiTableSO;
|
||||
private Dictionary<string, Dictionary<string, string>> languageDict = new Dictionary<string, Dictionary<string, string>>();
|
||||
private Vector2 scrollPosition;
|
||||
private bool isDataLoaded = false;
|
||||
private string pendingTooltipText = "";
|
||||
|
||||
[MenuItem("策划工具/收藏品/表情")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<EmojiConfigEditor>("表情配置");
|
||||
window.minSize = new Vector2(1000, 600);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// 读取上次保存的路径
|
||||
if (EditorPrefs.HasKey(DOCS_PATH_PREF_KEY))
|
||||
{
|
||||
docsRootPath = EditorPrefs.GetString(DOCS_PATH_PREF_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
EditorGUILayout.LabelField("表情配置工具", EditorStyles.boldLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Docs路径选择
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
EditorGUILayout.LabelField("Docs项目根目录", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
docsRootPath = EditorGUILayout.TextField("路径", docsRootPath);
|
||||
if (GUILayout.Button("选择文件夹", GUILayout.Width(100)))
|
||||
{
|
||||
string selectedPath = EditorUtility.OpenFolderPanel("选择Docs项目根目录", "", "");
|
||||
if (!string.IsNullOrEmpty(selectedPath))
|
||||
{
|
||||
docsRootPath = selectedPath;
|
||||
EditorPrefs.SetString(DOCS_PATH_PREF_KEY, docsRootPath);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (GUILayout.Button("加载配置数据", GUILayout.Height(30)))
|
||||
{
|
||||
LoadData();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// 数据编辑区域
|
||||
if (isDataLoaded)
|
||||
{
|
||||
DrawDataEditor();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("请先选择Docs根目录并加载配置数据", MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置数据
|
||||
/// </summary>
|
||||
private void LoadData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 校验路径
|
||||
if (string.IsNullOrEmpty(docsRootPath) || !Directory.Exists(docsRootPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "请选择有效的Docs根目录", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 检查Docs是否为Git仓库并更新
|
||||
if (!CheckAndUpdateDocsRepository())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 检查Design_SubModule分支
|
||||
if (!CheckAndSwitchDesignSubModuleBranch())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验Emoji SO是否存在
|
||||
string emojiSOPath = Path.Combine(EMOJI_SO_PATH, $"{EMOJI_SO_NAME}.asset");
|
||||
emojiTableSO = AssetDatabase.LoadAssetAtPath<ArtTableSO>(emojiSOPath);
|
||||
|
||||
if (emojiTableSO == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误",
|
||||
$"未找到表情资源配置\n路径: {emojiSOPath}\n\n请先在美术资源配置工具中创建",
|
||||
"确定");
|
||||
return;
|
||||
}
|
||||
|
||||
if (emojiTableSO.Items == null || emojiTableSO.Items.Count == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "表情资源配置数据为空", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载语言表
|
||||
LoadLanguageData();
|
||||
|
||||
// 加载Emoji.xlsx
|
||||
LoadEmojiExcel();
|
||||
|
||||
isDataLoaded = true;
|
||||
EditorUtility.DisplayDialog("成功", "配置数据加载成功!", "确定");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"加载数据失败: {e.Message}\n{e.StackTrace}", "确定");
|
||||
isDataLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行Git命令
|
||||
/// </summary>
|
||||
private string ExecuteGitCommand(string workingDirectory, string arguments, out bool success)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "git",
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = workingDirectory,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process process = Process.Start(startInfo))
|
||||
{
|
||||
string output = process.StandardOutput.ReadToEnd();
|
||||
string error = process.StandardError.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
success = process.ExitCode == 0;
|
||||
return success ? output : error;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
success = false;
|
||||
return $"执行Git命令失败: {e.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查并更新Docs仓库
|
||||
/// </summary>
|
||||
private bool CheckAndUpdateDocsRepository()
|
||||
{
|
||||
string gitPath = Path.Combine(docsRootPath, ".git");
|
||||
if (!Directory.Exists(gitPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误",
|
||||
$"Docs目录不是Git仓库\n路径: {docsRootPath}\n\n请确保Docs项目已正确克隆",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查更新", "正在检查Docs仓库远程更新...", 0.3f);
|
||||
|
||||
string fetchResult = ExecuteGitCommand(docsRootPath, "fetch", out bool fetchSuccess);
|
||||
|
||||
if (!fetchSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Fetch失败",
|
||||
$"无法检查远程更新:\n{fetchResult}\n\n请在SourceTree或GitHubDesktop中检查网络连接和仓库状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查更新", "正在检查是否有新提交...", 0.6f);
|
||||
|
||||
string statusResult = ExecuteGitCommand(docsRootPath, "status -uno", out bool statusSuccess);
|
||||
|
||||
if (!statusSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Status失败",
|
||||
$"无法获取仓库状态:\n{statusResult}\n\n请在SourceTree或GitHubDesktop中检查仓库状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (statusResult.Contains("Changes not staged") || statusResult.Contains("Changes to be committed"))
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
bool proceed = EditorUtility.DisplayDialog("警告:有未提交的更改",
|
||||
"Docs仓库中有未提交的更改,这可能导致拉取时产生冲突。\n\n建议先提交或暂存这些更改。\n\n是否继续加载配置?(不推荐)",
|
||||
"继续(不推荐)", "取消,前往处理");
|
||||
|
||||
if (!proceed)
|
||||
{
|
||||
Debug.Log("请在SourceTree或GitHubDesktop中处理未提交的更改");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (statusResult.Contains("Your branch is behind"))
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("更新中", "正在从远程拉取最新代码...", 0.8f);
|
||||
|
||||
string pullResult = ExecuteGitCommand(docsRootPath, "pull", out bool pullSuccess);
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (!pullSuccess)
|
||||
{
|
||||
if (pullResult.Contains("CONFLICT") || pullResult.Contains("conflict"))
|
||||
{
|
||||
EditorUtility.DisplayDialog("拉取失败:存在冲突",
|
||||
$"拉取远程更新时发生冲突:\n{pullResult}\n\n请在SourceTree或GitHubDesktop中解决冲突后再操作",
|
||||
"确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("拉取失败",
|
||||
$"无法拉取远程更新:\n{pullResult}\n\n请在SourceTree或GitHubDesktop中检查并解决问题",
|
||||
"确定");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Log($"Docs仓库已更新到最新版本:\n{pullResult}");
|
||||
EditorUtility.DisplayDialog("更新成功", "Docs仓库已更新到最新版本", "确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.Log("Docs仓库已是最新版本");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查并切换Design_SubModule到main分支
|
||||
/// </summary>
|
||||
private bool CheckAndSwitchDesignSubModuleBranch()
|
||||
{
|
||||
string designSubModulePath = Path.Combine(Application.dataPath, "..", DESIGN_SUBMODULE_PATH);
|
||||
designSubModulePath = Path.GetFullPath(designSubModulePath);
|
||||
|
||||
if (!Directory.Exists(designSubModulePath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误",
|
||||
$"Design_SubModule目录不存在:\n{designSubModulePath}\n\n请确保子模块已正确初始化",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查分支", "正在检查Design_SubModule分支...", 0.5f);
|
||||
|
||||
string branchResult = ExecuteGitCommand(designSubModulePath, "branch --show-current", out bool branchSuccess);
|
||||
|
||||
if (!branchSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Branch失败",
|
||||
$"无法获取当前分支:\n{branchResult}\n\n请在SourceTree或GitHubDesktop中检查Design_SubModule状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
string currentBranch = branchResult.Trim();
|
||||
|
||||
if (currentBranch != "main")
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("切换分支", "正在切换到main分支...", 0.8f);
|
||||
|
||||
string checkoutResult = ExecuteGitCommand(designSubModulePath, "checkout main", out bool checkoutSuccess);
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (!checkoutSuccess)
|
||||
{
|
||||
EditorUtility.DisplayDialog("切换分支失败",
|
||||
$"无法切换到main分支:\n{checkoutResult}\n\n当前分支: {currentBranch}\n\n请在SourceTree或GitHubDesktop中手动切换到main分支",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Log($"Design_SubModule已从 {currentBranch} 切换到 main 分支");
|
||||
EditorUtility.DisplayDialog("分支切换成功",
|
||||
$"Design_SubModule已从 {currentBranch} 切换到 main 分支",
|
||||
"确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.Log("Design_SubModule已在main分支");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载语言数据
|
||||
/// </summary>
|
||||
private void LoadLanguageData()
|
||||
{
|
||||
languageDict.Clear();
|
||||
|
||||
string languageExcelPath = Path.Combine(docsRootPath, "config", LANGUAGE_EXCEL_NAME);
|
||||
if (!File.Exists(languageExcelPath))
|
||||
{
|
||||
Debug.LogWarning($"未找到语言表: {languageExcelPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(languageExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[LANGUAGE_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
Debug.LogWarning($"语言表中未找到Sheet: {LANGUAGE_SHEET_NAME}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找Key列和语言列
|
||||
int keyColumnIndex = -1;
|
||||
int zhCNColumnIndex = -1;
|
||||
int enUSColumnIndex = -1;
|
||||
int ptBRColumnIndex = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
if (header == "key")
|
||||
{
|
||||
keyColumnIndex = col;
|
||||
}
|
||||
else if (header == "zh_CN")
|
||||
{
|
||||
zhCNColumnIndex = col;
|
||||
}
|
||||
else if (header == "en_US")
|
||||
{
|
||||
enUSColumnIndex = col;
|
||||
}
|
||||
else if (header == "pt_BR")
|
||||
{
|
||||
ptBRColumnIndex = col;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyColumnIndex < 0)
|
||||
{
|
||||
Debug.LogWarning("语言表中未找到Key列");
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
for (int row = 3; row <= rowCount; row++)
|
||||
{
|
||||
string key = worksheet.Cells[row, keyColumnIndex].Text;
|
||||
if (string.IsNullOrEmpty(key)) continue;
|
||||
|
||||
if (!languageDict.ContainsKey(key))
|
||||
{
|
||||
languageDict[key] = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
if (zhCNColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["zh_CN"] = worksheet.Cells[row, zhCNColumnIndex].Text;
|
||||
}
|
||||
if (enUSColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["en_US"] = worksheet.Cells[row, enUSColumnIndex].Text;
|
||||
}
|
||||
if (ptBRColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["pt_BR"] = worksheet.Cells[row, ptBRColumnIndex].Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载Emoji.xlsx
|
||||
/// </summary>
|
||||
private void LoadEmojiExcel()
|
||||
{
|
||||
emojiDataList.Clear();
|
||||
|
||||
string emojiExcelPath = Path.Combine(docsRootPath, "config", EMOJI_EXCEL_NAME);
|
||||
if (!File.Exists(emojiExcelPath))
|
||||
{
|
||||
throw new Exception($"未找到Emoji配置文件: {emojiExcelPath}");
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(emojiExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[EMOJI_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
throw new Exception($"Emoji.xlsx中未找到Sheet: {EMOJI_SHEET_NAME}");
|
||||
}
|
||||
|
||||
// 读取表头(第1行)
|
||||
int idCol = -1, nameKeyCol = -1, initCol = -1, iconCol = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
switch (header)
|
||||
{
|
||||
case "Id":
|
||||
idCol = col;
|
||||
break;
|
||||
case "NameKey":
|
||||
nameKeyCol = col;
|
||||
break;
|
||||
case "Init":
|
||||
initCol = col;
|
||||
break;
|
||||
case "Icon":
|
||||
iconCol = col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idCol < 0 || nameKeyCol < 0 || initCol < 0 || iconCol < 0)
|
||||
{
|
||||
throw new Exception("Emoji.xlsx表结构不正确,缺少必要列(Id/NameKey/Init/Icon)");
|
||||
}
|
||||
|
||||
// 读取数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
for (int row = 3; row <= rowCount; row++)
|
||||
{
|
||||
string idText = worksheet.Cells[row, idCol].Text;
|
||||
if (string.IsNullOrEmpty(idText)) continue;
|
||||
|
||||
if (!int.TryParse(idText, out int id)) continue;
|
||||
|
||||
string nameKey = worksheet.Cells[row, nameKeyCol].Text;
|
||||
string initText = worksheet.Cells[row, initCol].Text;
|
||||
string iconText = worksheet.Cells[row, iconCol].Text;
|
||||
|
||||
int init = 0;
|
||||
if (!string.IsNullOrEmpty(initText))
|
||||
{
|
||||
int.TryParse(initText, out init);
|
||||
}
|
||||
|
||||
int iconId = -1;
|
||||
if (!string.IsNullOrEmpty(iconText))
|
||||
{
|
||||
if (!int.TryParse(iconText, out iconId))
|
||||
{
|
||||
iconId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
var emojiData = new EmojiData
|
||||
{
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
Init = init,
|
||||
IconId = iconId
|
||||
};
|
||||
|
||||
emojiDataList.Add(emojiData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制数据编辑器
|
||||
/// </summary>
|
||||
private void DrawDataEditor()
|
||||
{
|
||||
pendingTooltipText = "";
|
||||
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField($"表情数据列表(共 {emojiDataList.Count} 条)", EditorStyles.boldLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.backgroundColor = Color.cyan;
|
||||
if (GUILayout.Button("+ 添加表情", GUILayout.Height(25), GUILayout.Width(100)))
|
||||
{
|
||||
AddNewEmoji();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(3);
|
||||
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
// 表头
|
||||
EditorGUILayout.BeginHorizontal("box");
|
||||
EditorGUILayout.LabelField("Id", EditorStyles.boldLabel, GUILayout.Width(50));
|
||||
EditorGUILayout.LabelField("NameKey", EditorStyles.boldLabel, GUILayout.Width(150));
|
||||
EditorGUILayout.LabelField("中文名称", EditorStyles.boldLabel, GUILayout.Width(120));
|
||||
EditorGUILayout.LabelField("Init", EditorStyles.boldLabel, GUILayout.Width(50));
|
||||
EditorGUILayout.LabelField("Icon", EditorStyles.boldLabel, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField("预览", EditorStyles.boldLabel, GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField("操作", EditorStyles.boldLabel, GUILayout.Width(60));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// 数据行
|
||||
for (int i = 0; i < emojiDataList.Count; i++)
|
||||
{
|
||||
DrawEmojiDataRow(emojiDataList[i]);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// 保存按钮
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.backgroundColor = Color.green;
|
||||
if (GUILayout.Button("保存配置到Excel", GUILayout.Height(30), GUILayout.Width(200)))
|
||||
{
|
||||
SaveData();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// 在所有内容绘制完后,绘制tooltip(确保在最上层)
|
||||
if (!string.IsNullOrEmpty(pendingTooltipText))
|
||||
{
|
||||
Vector2 tooltipSize = GUI.skin.box.CalcSize(new GUIContent(pendingTooltipText));
|
||||
tooltipSize.x += 10;
|
||||
tooltipSize.y += 10;
|
||||
|
||||
Vector2 mousePos = Event.current.mousePosition;
|
||||
Rect tooltipRect = new Rect(
|
||||
mousePos.x + 1,
|
||||
mousePos.y + 1,
|
||||
tooltipSize.x,
|
||||
tooltipSize.y
|
||||
);
|
||||
|
||||
GUI.Box(tooltipRect, pendingTooltipText);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制单行数据
|
||||
/// </summary>
|
||||
private void DrawEmojiDataRow(EmojiData data)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal("box");
|
||||
|
||||
// Id(可编辑)
|
||||
data.Id = EditorGUILayout.IntField(data.Id, GUILayout.Width(50));
|
||||
|
||||
// NameKey(可编辑)
|
||||
data.NameKey = EditorGUILayout.TextField(data.NameKey, GUILayout.Width(150));
|
||||
|
||||
// 中文名称(只读预览,带即时多语言显示)
|
||||
GUI.enabled = false;
|
||||
string zhName = "未找到语言Key";
|
||||
|
||||
if (languageDict.ContainsKey(data.NameKey))
|
||||
{
|
||||
var langs = languageDict[data.NameKey];
|
||||
if (langs.ContainsKey("zh_CN"))
|
||||
{
|
||||
zhName = langs["zh_CN"];
|
||||
}
|
||||
}
|
||||
|
||||
Rect nameRect = GUILayoutUtility.GetRect(new GUIContent(zhName), GUI.skin.textField, GUILayout.Width(120));
|
||||
EditorGUI.TextField(nameRect, zhName);
|
||||
|
||||
// 检测鼠标悬停并准备tooltip内容
|
||||
if (nameRect.Contains(Event.current.mousePosition) && languageDict.ContainsKey(data.NameKey))
|
||||
{
|
||||
var langs = languageDict[data.NameKey];
|
||||
List<string> tooltipLines = new List<string>();
|
||||
|
||||
if (langs.ContainsKey("zh_CN") && !string.IsNullOrEmpty(langs["zh_CN"]))
|
||||
{
|
||||
tooltipLines.Add($"[中文] {langs["zh_CN"]}");
|
||||
}
|
||||
if (langs.ContainsKey("en_US") && !string.IsNullOrEmpty(langs["en_US"]))
|
||||
{
|
||||
tooltipLines.Add($"[English] {langs["en_US"]}");
|
||||
}
|
||||
if (langs.ContainsKey("pt_BR") && !string.IsNullOrEmpty(langs["pt_BR"]))
|
||||
{
|
||||
tooltipLines.Add($"[Português] {langs["pt_BR"]}");
|
||||
}
|
||||
|
||||
if (tooltipLines.Count > 0)
|
||||
{
|
||||
pendingTooltipText = string.Join("\n", tooltipLines);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
|
||||
// Init(Checkbox)
|
||||
bool initChecked = data.Init == 1;
|
||||
bool newInitChecked = EditorGUILayout.Toggle(initChecked, GUILayout.Width(50));
|
||||
data.Init = newInitChecked ? 1 : 0;
|
||||
|
||||
// Icon(下拉列表)
|
||||
var iconItems = emojiTableSO.Items;
|
||||
var iconNamesList = new List<string> { "未选择" };
|
||||
iconNamesList.AddRange(iconItems.Select(x => x.Name));
|
||||
var iconNames = iconNamesList.ToArray();
|
||||
|
||||
int currentIndex = 0;
|
||||
var currentItem = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (currentItem != null)
|
||||
{
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int newIndex = EditorGUILayout.Popup(currentIndex, iconNames, GUILayout.Width(200));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (newIndex == 0)
|
||||
{
|
||||
data.IconId = -1;
|
||||
}
|
||||
else if (newIndex > 0 && newIndex <= iconItems.Count)
|
||||
{
|
||||
data.IconId = iconItems[newIndex - 1].Id;
|
||||
}
|
||||
}
|
||||
|
||||
// 预览
|
||||
if (data.IconId >= 0)
|
||||
{
|
||||
var item = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (item != null)
|
||||
{
|
||||
Sprite sprite = item.Sprite;
|
||||
|
||||
if (sprite != null && sprite.texture != null)
|
||||
{
|
||||
Rect previewRect = GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
|
||||
EditorGUI.DrawRect(previewRect, new Color(0.5f, 0.5f, 0.5f, 1f));
|
||||
|
||||
Rect texCoords = sprite.textureRect;
|
||||
Texture2D tex = sprite.texture;
|
||||
|
||||
Rect normalizedCoords = new Rect(
|
||||
texCoords.x / tex.width,
|
||||
texCoords.y / tex.height,
|
||||
texCoords.width / tex.width,
|
||||
texCoords.height / tex.height
|
||||
);
|
||||
|
||||
float aspect = texCoords.width / texCoords.height;
|
||||
Rect drawRect = previewRect;
|
||||
if (aspect > 1f)
|
||||
{
|
||||
float height = drawRect.width / aspect;
|
||||
drawRect.y += (drawRect.height - height) * 0.5f;
|
||||
drawRect.height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
float width = drawRect.height * aspect;
|
||||
drawRect.x += (drawRect.width - width) * 0.5f;
|
||||
drawRect.width = width;
|
||||
}
|
||||
|
||||
GUI.DrawTextureWithTexCoords(drawRect, tex, normalizedCoords, true);
|
||||
|
||||
if (!string.IsNullOrEmpty(item.Desc))
|
||||
{
|
||||
GUI.Label(previewRect, new GUIContent("", item.Desc));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("无图片", GUILayout.Width(50));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("未选择", GUILayout.Width(50));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("未选择", GUILayout.Width(50));
|
||||
}
|
||||
|
||||
// 删除按钮
|
||||
GUI.backgroundColor = Color.red;
|
||||
if (GUILayout.Button("删除", GUILayout.Width(60)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("确认删除",
|
||||
$"确定要删除表情 {data.Id} ({data.NameKey}) 吗?",
|
||||
"删除", "取消"))
|
||||
{
|
||||
emojiDataList.Remove(data);
|
||||
}
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加新表情
|
||||
/// </summary>
|
||||
private void AddNewEmoji()
|
||||
{
|
||||
int newId = 1;
|
||||
if (emojiDataList.Count > 0)
|
||||
{
|
||||
newId = emojiDataList.Max(x => x.Id) + 1;
|
||||
}
|
||||
|
||||
var newEmoji = new EmojiData
|
||||
{
|
||||
Id = newId,
|
||||
NameKey = "",
|
||||
Init = 0,
|
||||
IconId = -1
|
||||
};
|
||||
|
||||
emojiDataList.Add(newEmoji);
|
||||
|
||||
scrollPosition = new Vector2(0, float.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存数据到Excel
|
||||
/// </summary>
|
||||
private void SaveData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string emojiExcelPath = Path.Combine(docsRootPath, "config", EMOJI_EXCEL_NAME);
|
||||
if (!File.Exists(emojiExcelPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"未找到Emoji配置文件: {emojiExcelPath}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(emojiExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[EMOJI_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"Emoji.xlsx中未找到Sheet: {EMOJI_SHEET_NAME}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找列索引
|
||||
int idCol = -1, nameKeyCol = -1, initCol = -1, iconCol = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
switch (header)
|
||||
{
|
||||
case "Id":
|
||||
idCol = col;
|
||||
break;
|
||||
case "NameKey":
|
||||
nameKeyCol = col;
|
||||
break;
|
||||
case "Init":
|
||||
initCol = col;
|
||||
break;
|
||||
case "Icon":
|
||||
iconCol = col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新和删除数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
var processedIds = new HashSet<int>();
|
||||
|
||||
// 第一遍:更新现有行或删除
|
||||
for (int row = rowCount; row >= 3; row--)
|
||||
{
|
||||
string idText = worksheet.Cells[row, idCol].Text;
|
||||
if (string.IsNullOrEmpty(idText)) continue;
|
||||
|
||||
if (!int.TryParse(idText, out int id)) continue;
|
||||
|
||||
var emojiData = emojiDataList.Find(x => x.Id == id);
|
||||
if (emojiData != null)
|
||||
{
|
||||
worksheet.Cells[row, nameKeyCol].Value = emojiData.NameKey;
|
||||
worksheet.Cells[row, initCol].Value = emojiData.Init;
|
||||
|
||||
if (emojiData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = emojiData.IconId;
|
||||
}
|
||||
|
||||
processedIds.Add(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.DeleteRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二遍:添加新行
|
||||
int currentRow = worksheet.Dimension?.Rows ?? 2;
|
||||
foreach (var emojiData in emojiDataList)
|
||||
{
|
||||
if (!processedIds.Contains(emojiData.Id))
|
||||
{
|
||||
currentRow++;
|
||||
worksheet.Cells[currentRow, idCol].Value = emojiData.Id;
|
||||
worksheet.Cells[currentRow, nameKeyCol].Value = emojiData.NameKey;
|
||||
worksheet.Cells[currentRow, initCol].Value = emojiData.Init;
|
||||
|
||||
if (emojiData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = emojiData.IconId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package.Save();
|
||||
}
|
||||
|
||||
// 提示成功并提醒推送
|
||||
bool understood = EditorUtility.DisplayDialog("保存成功",
|
||||
"配置已保存到Excel文件!\n\n" +
|
||||
"⚠️ 重要提醒:\n" +
|
||||
"请及时在SourceTree或GitHubDesktop中:\n" +
|
||||
"1. 提交(Commit)本次修改\n" +
|
||||
"2. 推送(Push)到远程仓库\n\n" +
|
||||
"避免与其他策划产生冲突!",
|
||||
"我知道了");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"保存数据失败: {e.Message}\n{e.StackTrace}", "确定");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表情数据类
|
||||
/// </summary>
|
||||
private class EmojiData
|
||||
{
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int Init;
|
||||
public int IconId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9ae11b153bc3454cab7be603efdb90b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
987
Scripts/Editor/Design_Tools/Collections/HeadConfigEditor.cs
Normal file
987
Scripts/Editor/Design_Tools/Collections/HeadConfigEditor.cs
Normal file
@ -0,0 +1,987 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using OfficeOpenXml;
|
||||
using ArtResource;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace DesignTools.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 头像配置Editor工具
|
||||
/// 读取Docs/config/Face.xlsx,关联Art_SO/HeadResources.asset
|
||||
/// </summary>
|
||||
public class HeadConfigEditor : EditorWindow
|
||||
{
|
||||
private const string HEAD_SO_PATH = "Assets/Art_SubModule/Art_SO";
|
||||
private const string FACE_EXCEL_NAME = "Face.xlsx";
|
||||
private const string LANGUAGE_EXCEL_NAME = "AllLanguage.xlsx";
|
||||
private const string FACE_SHEET_NAME = "Face";
|
||||
private const string LANGUAGE_SHEET_NAME = "client";
|
||||
private const string DOCS_PATH_PREF_KEY = "HeadConfigEditor_DocsPath";
|
||||
private const string DESIGN_SUBMODULE_PATH = "Assets/Design_SubModule";
|
||||
|
||||
private string docsRootPath = "";
|
||||
private List<FaceData> faceDataList = new List<FaceData>();
|
||||
private ArtTableSO headTableSO;
|
||||
private Dictionary<string, Dictionary<string, string>> languageDict = new Dictionary<string, Dictionary<string, string>>();
|
||||
private Vector2 scrollPosition;
|
||||
private bool isDataLoaded = false;
|
||||
private string pendingTooltipText = "";
|
||||
|
||||
[MenuItem("策划工具/收藏品/头像")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<HeadConfigEditor>("头像配置");
|
||||
window.minSize = new Vector2(900, 600);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// 设置EPPlus许可证
|
||||
// ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||
|
||||
// 读取上次保存的路径
|
||||
if (EditorPrefs.HasKey(DOCS_PATH_PREF_KEY))
|
||||
{
|
||||
docsRootPath = EditorPrefs.GetString(DOCS_PATH_PREF_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
EditorGUILayout.LabelField("头像配置工具", EditorStyles.boldLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Docs路径选择
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
EditorGUILayout.LabelField("Docs项目根目录", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
docsRootPath = EditorGUILayout.TextField("路径", docsRootPath);
|
||||
if (GUILayout.Button("选择文件夹", GUILayout.Width(100)))
|
||||
{
|
||||
string selectedPath = EditorUtility.OpenFolderPanel("选择Docs项目根目录", "", "");
|
||||
if (!string.IsNullOrEmpty(selectedPath))
|
||||
{
|
||||
docsRootPath = selectedPath;
|
||||
EditorPrefs.SetString(DOCS_PATH_PREF_KEY, docsRootPath);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (GUILayout.Button("加载配置数据", GUILayout.Height(30)))
|
||||
{
|
||||
LoadData();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// 数据编辑区域
|
||||
if (isDataLoaded)
|
||||
{
|
||||
DrawDataEditor();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("请先选择Docs根目录并加载配置数据", MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置数据
|
||||
/// </summary>
|
||||
private void LoadData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 校验路径
|
||||
if (string.IsNullOrEmpty(docsRootPath) || !Directory.Exists(docsRootPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "请选择有效的Docs根目录", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 检查Docs是否为Git仓库并更新
|
||||
if (!CheckAndUpdateDocsRepository())
|
||||
{
|
||||
return; // 检查失败,提示已在方法内显示
|
||||
}
|
||||
|
||||
// 2. 检查Design_SubModule分支
|
||||
if (!CheckAndSwitchDesignSubModuleBranch())
|
||||
{
|
||||
return; // 检查失败,提示已在方法内显示
|
||||
}
|
||||
|
||||
// 校验Head SO是否存在
|
||||
string[] soGuids = AssetDatabase.FindAssets("t:ArtTableSO", new[] { HEAD_SO_PATH });
|
||||
if (soGuids.Length == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"未在 {HEAD_SO_PATH} 目录下找到头像资源配置(ArtTableSO)\n请先在美术资源配置工具中创建", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找名称为"HeadResource"的SO
|
||||
ArtTableSO foundSO = null;
|
||||
foreach (var guid in soGuids)
|
||||
{
|
||||
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var so = AssetDatabase.LoadAssetAtPath<ArtTableSO>(path);
|
||||
if (so != null && so.TableName == "HeadResource")
|
||||
{
|
||||
foundSO = so;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (foundSO == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "无法加载头像资源配置", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
headTableSO = foundSO;
|
||||
|
||||
if (headTableSO.Items == null || headTableSO.Items.Count == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "头像资源配置数据为空", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载语言表
|
||||
LoadLanguageData();
|
||||
|
||||
// 加载Face.xlsx
|
||||
LoadFaceExcel();
|
||||
|
||||
isDataLoaded = true;
|
||||
EditorUtility.DisplayDialog("成功", "配置数据加载成功!", "确定");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"加载数据失败: {e.Message}\n{e.StackTrace}", "确定");
|
||||
isDataLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行Git命令
|
||||
/// </summary>
|
||||
private string ExecuteGitCommand(string workingDirectory, string arguments, out bool success)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "git",
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = workingDirectory,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process process = Process.Start(startInfo))
|
||||
{
|
||||
string output = process.StandardOutput.ReadToEnd();
|
||||
string error = process.StandardError.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
success = process.ExitCode == 0;
|
||||
return success ? output : error;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
success = false;
|
||||
return $"执行Git命令失败: {e.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查并更新Docs仓库
|
||||
/// </summary>
|
||||
private bool CheckAndUpdateDocsRepository()
|
||||
{
|
||||
// 检查是否是Git仓库
|
||||
string gitPath = Path.Combine(docsRootPath, ".git");
|
||||
if (!Directory.Exists(gitPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误",
|
||||
$"Docs目录不是Git仓库\n路径: {docsRootPath}\n\n请确保Docs项目已正确克隆",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查更新", "正在检查Docs仓库远程更新...", 0.3f);
|
||||
|
||||
// 执行git fetch检查远程更新
|
||||
string fetchResult = ExecuteGitCommand(docsRootPath, "fetch", out bool fetchSuccess);
|
||||
|
||||
if (!fetchSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Fetch失败",
|
||||
$"无法检查远程更新:\n{fetchResult}\n\n请在SourceTree或GitHubDesktop中检查网络连接和仓库状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查更新", "正在检查是否有新提交...", 0.6f);
|
||||
|
||||
// 检查本地和远程的差异
|
||||
string statusResult = ExecuteGitCommand(docsRootPath, "status -uno", out bool statusSuccess);
|
||||
|
||||
if (!statusSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Status失败",
|
||||
$"无法获取仓库状态:\n{statusResult}\n\n请在SourceTree或GitHubDesktop中检查仓库状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否有未提交的更改
|
||||
if (statusResult.Contains("Changes not staged") || statusResult.Contains("Changes to be committed"))
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
bool proceed = EditorUtility.DisplayDialog("警告:有未提交的更改",
|
||||
"Docs仓库中有未提交的更改,这可能导致拉取时产生冲突。\n\n建议先提交或暂存这些更改。\n\n是否继续加载配置?(不推荐)",
|
||||
"继续(不推荐)", "取消,前往处理");
|
||||
|
||||
if (!proceed)
|
||||
{
|
||||
Debug.Log("请在SourceTree或GitHubDesktop中处理未提交的更改");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否behind远程
|
||||
if (statusResult.Contains("Your branch is behind"))
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("更新中", "正在从远程拉取最新代码...", 0.8f);
|
||||
|
||||
string pullResult = ExecuteGitCommand(docsRootPath, "pull", out bool pullSuccess);
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (!pullSuccess)
|
||||
{
|
||||
// 检查是否是合并冲突
|
||||
if (pullResult.Contains("CONFLICT") || pullResult.Contains("conflict"))
|
||||
{
|
||||
EditorUtility.DisplayDialog("拉取失败:存在冲突",
|
||||
$"拉取远程更新时发生冲突:\n{pullResult}\n\n请在SourceTree或GitHubDesktop中解决冲突后再操作",
|
||||
"确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("拉取失败",
|
||||
$"无法拉取远程更新:\n{pullResult}\n\n请在SourceTree或GitHubDesktop中检查并解决问题",
|
||||
"确定");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Log($"Docs仓库已更新到最新版本:\n{pullResult}");
|
||||
EditorUtility.DisplayDialog("更新成功", "Docs仓库已更新到最新版本", "确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.Log("Docs仓库已是最新版本");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查并切换Design_SubModule到main分支
|
||||
/// </summary>
|
||||
private bool CheckAndSwitchDesignSubModuleBranch()
|
||||
{
|
||||
string designSubModulePath = Path.Combine(Application.dataPath, "..", DESIGN_SUBMODULE_PATH);
|
||||
designSubModulePath = Path.GetFullPath(designSubModulePath);
|
||||
|
||||
// 检查Design_SubModule是否存在
|
||||
if (!Directory.Exists(designSubModulePath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误",
|
||||
$"Design_SubModule目录不存在:\n{designSubModulePath}\n\n请确保子模块已正确初始化",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查分支", "正在检查Design_SubModule分支...", 0.5f);
|
||||
|
||||
// 检查当前分支
|
||||
string branchResult = ExecuteGitCommand(designSubModulePath, "branch --show-current", out bool branchSuccess);
|
||||
|
||||
if (!branchSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Branch失败",
|
||||
$"无法获取当前分支:\n{branchResult}\n\n请在SourceTree或GitHubDesktop中检查Design_SubModule状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
string currentBranch = branchResult.Trim();
|
||||
|
||||
if (currentBranch != "main")
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("切换分支", "正在切换到main分支...", 0.8f);
|
||||
|
||||
string checkoutResult = ExecuteGitCommand(designSubModulePath, "checkout main", out bool checkoutSuccess);
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (!checkoutSuccess)
|
||||
{
|
||||
EditorUtility.DisplayDialog("切换分支失败",
|
||||
$"无法切换到main分支:\n{checkoutResult}\n\n当前分支: {currentBranch}\n\n请在SourceTree或GitHubDesktop中手动切换到main分支",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Log($"Design_SubModule已从 {currentBranch} 切换到 main 分支");
|
||||
EditorUtility.DisplayDialog("分支切换成功",
|
||||
$"Design_SubModule已从 {currentBranch} 切换到 main 分支",
|
||||
"确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.Log("Design_SubModule已在main分支");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载语言数据
|
||||
/// </summary>
|
||||
private void LoadLanguageData()
|
||||
{
|
||||
languageDict.Clear();
|
||||
|
||||
string languageExcelPath = Path.Combine(docsRootPath, "config", LANGUAGE_EXCEL_NAME);
|
||||
if (!File.Exists(languageExcelPath))
|
||||
{
|
||||
Debug.LogWarning($"未找到语言表: {languageExcelPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(languageExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[LANGUAGE_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
Debug.LogWarning($"语言表中未找到Sheet: {LANGUAGE_SHEET_NAME}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找Key列和语言列
|
||||
int keyColumnIndex = -1;
|
||||
int zhCNColumnIndex = -1;
|
||||
int enUSColumnIndex = -1;
|
||||
int ptBRColumnIndex = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
if (header == "key")
|
||||
{
|
||||
keyColumnIndex = col;
|
||||
}
|
||||
else if (header == "zh_CN")
|
||||
{
|
||||
zhCNColumnIndex = col;
|
||||
}
|
||||
else if (header == "en_US")
|
||||
{
|
||||
enUSColumnIndex = col;
|
||||
}
|
||||
else if (header == "pt_BR")
|
||||
{
|
||||
ptBRColumnIndex = col;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyColumnIndex < 0)
|
||||
{
|
||||
Debug.LogWarning("语言表中未找到Key列");
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
for (int row = 3; row <= rowCount; row++)
|
||||
{
|
||||
string key = worksheet.Cells[row, keyColumnIndex].Text;
|
||||
if (string.IsNullOrEmpty(key)) continue;
|
||||
|
||||
if (!languageDict.ContainsKey(key))
|
||||
{
|
||||
languageDict[key] = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
if (zhCNColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["zh_CN"] = worksheet.Cells[row, zhCNColumnIndex].Text;
|
||||
}
|
||||
if (enUSColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["en_US"] = worksheet.Cells[row, enUSColumnIndex].Text;
|
||||
}
|
||||
if (ptBRColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["pt_BR"] = worksheet.Cells[row, ptBRColumnIndex].Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载Face.xlsx
|
||||
/// </summary>
|
||||
private void LoadFaceExcel()
|
||||
{
|
||||
faceDataList.Clear();
|
||||
|
||||
string faceExcelPath = Path.Combine(docsRootPath, "config", FACE_EXCEL_NAME);
|
||||
if (!File.Exists(faceExcelPath))
|
||||
{
|
||||
throw new Exception($"未找到Face配置文件: {faceExcelPath}");
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(faceExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[FACE_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
throw new Exception($"Face.xlsx中未找到Sheet: {FACE_SHEET_NAME}");
|
||||
}
|
||||
|
||||
// 读取表头(第1行)
|
||||
int idCol = -1, nameKeyCol = -1, initCol = -1, iconCol = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
switch (header)
|
||||
{
|
||||
case "Id":
|
||||
idCol = col;
|
||||
break;
|
||||
case "NameKey":
|
||||
nameKeyCol = col;
|
||||
break;
|
||||
case "Init":
|
||||
initCol = col;
|
||||
break;
|
||||
case "Icon":
|
||||
iconCol = col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idCol < 0 || nameKeyCol < 0 || initCol < 0 || iconCol < 0)
|
||||
{
|
||||
throw new Exception("Face.xlsx表结构不正确,缺少必要列(Id/NameKey/Init/Icon)");
|
||||
}
|
||||
|
||||
// 读取数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
for (int row = 3; row <= rowCount; row++)
|
||||
{
|
||||
string idText = worksheet.Cells[row, idCol].Text;
|
||||
if (string.IsNullOrEmpty(idText)) continue;
|
||||
|
||||
if (!int.TryParse(idText, out int id)) continue;
|
||||
|
||||
string nameKey = worksheet.Cells[row, nameKeyCol].Text;
|
||||
string initText = worksheet.Cells[row, initCol].Text;
|
||||
string iconText = worksheet.Cells[row, iconCol].Text;
|
||||
|
||||
int init = 0;
|
||||
if (!string.IsNullOrEmpty(initText))
|
||||
{
|
||||
int.TryParse(initText, out init);
|
||||
}
|
||||
|
||||
int iconId = -1; // 默认为未选择
|
||||
if (!string.IsNullOrEmpty(iconText))
|
||||
{
|
||||
if (!int.TryParse(iconText, out iconId))
|
||||
{
|
||||
iconId = -1; // 解析失败设为未选择
|
||||
}
|
||||
}
|
||||
|
||||
var faceData = new FaceData
|
||||
{
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
Init = init,
|
||||
IconId = iconId
|
||||
};
|
||||
|
||||
faceDataList.Add(faceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制数据编辑器
|
||||
/// </summary>
|
||||
private void DrawDataEditor()
|
||||
{
|
||||
pendingTooltipText = "";
|
||||
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField($"头像数据列表(共 {faceDataList.Count} 条)", EditorStyles.boldLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.backgroundColor = Color.cyan;
|
||||
if (GUILayout.Button("+ 添加头像", GUILayout.Height(25), GUILayout.Width(100)))
|
||||
{
|
||||
AddNewFace();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(3);
|
||||
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
// 表头
|
||||
EditorGUILayout.BeginHorizontal("box");
|
||||
EditorGUILayout.LabelField("Id", EditorStyles.boldLabel, GUILayout.Width(50));
|
||||
EditorGUILayout.LabelField("NameKey", EditorStyles.boldLabel, GUILayout.Width(150));
|
||||
EditorGUILayout.LabelField("中文名称", EditorStyles.boldLabel, GUILayout.Width(120));
|
||||
EditorGUILayout.LabelField("Init", EditorStyles.boldLabel, GUILayout.Width(50));
|
||||
EditorGUILayout.LabelField("Icon", EditorStyles.boldLabel, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField("预览", EditorStyles.boldLabel, GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField("操作", EditorStyles.boldLabel, GUILayout.Width(60));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// 数据行
|
||||
for (int i = 0; i < faceDataList.Count; i++)
|
||||
{
|
||||
DrawFaceDataRow(faceDataList[i]);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// 保存按钮
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.backgroundColor = Color.green;
|
||||
if (GUILayout.Button("保存配置到Excel", GUILayout.Height(30), GUILayout.Width(200)))
|
||||
{
|
||||
SaveData();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// 在所有内容绘制完后,绘制tooltip(确保在最上层)
|
||||
if (!string.IsNullOrEmpty(pendingTooltipText))
|
||||
{
|
||||
Vector2 tooltipSize = GUI.skin.box.CalcSize(new GUIContent(pendingTooltipText));
|
||||
tooltipSize.x += 10;
|
||||
tooltipSize.y += 10;
|
||||
|
||||
// 使用当前鼠标位置,而不是之前记录的位置
|
||||
Vector2 mousePos = Event.current.mousePosition;
|
||||
Rect tooltipRect = new Rect(
|
||||
mousePos.x + 1,
|
||||
mousePos.y + 1,
|
||||
tooltipSize.x,
|
||||
tooltipSize.y
|
||||
);
|
||||
|
||||
GUI.Box(tooltipRect, pendingTooltipText);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制单行数据
|
||||
/// </summary>
|
||||
private void DrawFaceDataRow(FaceData data)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal("box");
|
||||
|
||||
// Id(只读)
|
||||
GUI.enabled = false;
|
||||
EditorGUILayout.IntField(data.Id, GUILayout.Width(50));
|
||||
GUI.enabled = true;
|
||||
|
||||
// NameKey(可编辑)
|
||||
data.NameKey = EditorGUILayout.TextField(data.NameKey, GUILayout.Width(150));
|
||||
|
||||
// 中文名称(只读预览,带即时多语言显示)
|
||||
GUI.enabled = false;
|
||||
string zhName = "未找到语言Key";
|
||||
|
||||
if (languageDict.ContainsKey(data.NameKey))
|
||||
{
|
||||
var langs = languageDict[data.NameKey];
|
||||
if (langs.ContainsKey("zh_CN"))
|
||||
{
|
||||
zhName = langs["zh_CN"];
|
||||
}
|
||||
}
|
||||
|
||||
Rect nameRect = GUILayoutUtility.GetRect(new GUIContent(zhName), GUI.skin.textField, GUILayout.Width(120));
|
||||
EditorGUI.TextField(nameRect, zhName);
|
||||
|
||||
// 检测鼠标悬停并准备tooltip内容(不立即绘制)
|
||||
if (nameRect.Contains(Event.current.mousePosition) && languageDict.ContainsKey(data.NameKey))
|
||||
{
|
||||
var langs = languageDict[data.NameKey];
|
||||
List<string> tooltipLines = new List<string>();
|
||||
|
||||
if (langs.ContainsKey("zh_CN") && !string.IsNullOrEmpty(langs["zh_CN"]))
|
||||
{
|
||||
tooltipLines.Add($"[中文] {langs["zh_CN"]}");
|
||||
}
|
||||
if (langs.ContainsKey("en_US") && !string.IsNullOrEmpty(langs["en_US"]))
|
||||
{
|
||||
tooltipLines.Add($"[English] {langs["en_US"]}");
|
||||
}
|
||||
if (langs.ContainsKey("pt_BR") && !string.IsNullOrEmpty(langs["pt_BR"]))
|
||||
{
|
||||
tooltipLines.Add($"[Português] {langs["pt_BR"]}");
|
||||
}
|
||||
|
||||
if (tooltipLines.Count > 0)
|
||||
{
|
||||
pendingTooltipText = string.Join("\n", tooltipLines);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
|
||||
// Init(Checkbox)
|
||||
bool initChecked = data.Init == 1;
|
||||
bool newInitChecked = EditorGUILayout.Toggle(initChecked, GUILayout.Width(50));
|
||||
data.Init = newInitChecked ? 1 : 0;
|
||||
|
||||
// Icon(下拉列表,包含未选择选项)
|
||||
var iconItems = headTableSO.Items;
|
||||
var iconNamesList = new List<string> { "未选择" };
|
||||
iconNamesList.AddRange(iconItems.Select(x => x.Name));
|
||||
var iconNames = iconNamesList.ToArray();
|
||||
|
||||
// 查找当前选中的索引
|
||||
int currentIndex = 0; // 默认为"未选择"
|
||||
var currentItem = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (currentItem != null)
|
||||
{
|
||||
// 找到了对应的Item,索引需要+1(因为第0项是"未选择")
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int newIndex = EditorGUILayout.Popup(currentIndex, iconNames, GUILayout.Width(200));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (newIndex == 0)
|
||||
{
|
||||
// 选择了"未选择"
|
||||
data.IconId = -1;
|
||||
}
|
||||
else if (newIndex > 0 && newIndex <= iconItems.Count)
|
||||
{
|
||||
// 选择了具体的Icon(索引需要-1)
|
||||
data.IconId = iconItems[newIndex - 1].Id;
|
||||
}
|
||||
}
|
||||
|
||||
// 预览和Desc提示
|
||||
if (data.IconId >= 0)
|
||||
{
|
||||
var item = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (item != null)
|
||||
{
|
||||
// 直接使用Sprite引用
|
||||
Sprite sprite = item.Sprite;
|
||||
|
||||
if (sprite != null && sprite.texture != null)
|
||||
{
|
||||
// 正确处理图集sprite的预览
|
||||
Rect previewRect = GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
|
||||
// 绘制背景
|
||||
EditorGUI.DrawRect(previewRect, new Color(0.5f, 0.5f, 0.5f, 1f));
|
||||
|
||||
Rect texCoords = sprite.textureRect;
|
||||
Texture2D tex = sprite.texture;
|
||||
|
||||
// 归一化UV坐标
|
||||
Rect normalizedCoords = new Rect(
|
||||
texCoords.x / tex.width,
|
||||
texCoords.y / tex.height,
|
||||
texCoords.width / tex.width,
|
||||
texCoords.height / tex.height
|
||||
);
|
||||
|
||||
// 计算保持宽高比的显示区域
|
||||
float aspect = texCoords.width / texCoords.height;
|
||||
Rect drawRect = previewRect;
|
||||
if (aspect > 1f)
|
||||
{
|
||||
float height = drawRect.width / aspect;
|
||||
drawRect.y += (drawRect.height - height) * 0.5f;
|
||||
drawRect.height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
float width = drawRect.height * aspect;
|
||||
drawRect.x += (drawRect.width - width) * 0.5f;
|
||||
drawRect.width = width;
|
||||
}
|
||||
|
||||
GUI.DrawTextureWithTexCoords(drawRect, tex, normalizedCoords, true);
|
||||
|
||||
// 添加Tooltip
|
||||
if (!string.IsNullOrEmpty(item.Desc))
|
||||
{
|
||||
GUI.Label(previewRect, new GUIContent("", item.Desc));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("无图片", GUILayout.Width(50));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ID存在但找不到对应的Item
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("未选择", GUILayout.Width(50));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// IconId < 0,未选择状态
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("未选择", GUILayout.Width(50));
|
||||
}
|
||||
|
||||
// 删除按钮
|
||||
GUI.backgroundColor = Color.red;
|
||||
if (GUILayout.Button("删除", GUILayout.Width(60)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("确认删除",
|
||||
$"确定要删除头像 {data.Id} ({data.NameKey}) 吗?",
|
||||
"删除", "取消"))
|
||||
{
|
||||
faceDataList.Remove(data);
|
||||
}
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加新头像
|
||||
/// </summary>
|
||||
private void AddNewFace()
|
||||
{
|
||||
// 计算新ID(当前最大ID + 1)
|
||||
int newId = 1;
|
||||
if (faceDataList.Count > 0)
|
||||
{
|
||||
newId = faceDataList.Max(x => x.Id) + 1;
|
||||
}
|
||||
|
||||
var newFace = new FaceData
|
||||
{
|
||||
Id = newId,
|
||||
NameKey = "",
|
||||
Init = 0,
|
||||
IconId = -1
|
||||
};
|
||||
|
||||
faceDataList.Add(newFace);
|
||||
|
||||
// 滚动到底部
|
||||
scrollPosition = new Vector2(0, float.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存数据到Excel
|
||||
/// </summary>
|
||||
private void SaveData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string faceExcelPath = Path.Combine(docsRootPath, "config", FACE_EXCEL_NAME);
|
||||
if (!File.Exists(faceExcelPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"未找到Face配置文件: {faceExcelPath}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(faceExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[FACE_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"Face.xlsx中未找到Sheet: {FACE_SHEET_NAME}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找列索引
|
||||
int idCol = -1, nameKeyCol = -1, initCol = -1, iconCol = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
switch (header)
|
||||
{
|
||||
case "Id":
|
||||
idCol = col;
|
||||
break;
|
||||
case "NameKey":
|
||||
nameKeyCol = col;
|
||||
break;
|
||||
case "Init":
|
||||
initCol = col;
|
||||
break;
|
||||
case "Icon":
|
||||
iconCol = col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新和删除数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
var processedIds = new HashSet<int>();
|
||||
|
||||
// 第一遍:更新现有行或标记删除
|
||||
for (int row = rowCount; row >= 3; row--)
|
||||
{
|
||||
string idText = worksheet.Cells[row, idCol].Text;
|
||||
if (string.IsNullOrEmpty(idText)) continue;
|
||||
|
||||
if (!int.TryParse(idText, out int id)) continue;
|
||||
|
||||
// 查找对应的FaceData
|
||||
var faceData = faceDataList.Find(x => x.Id == id);
|
||||
if (faceData != null)
|
||||
{
|
||||
// 更新数据
|
||||
worksheet.Cells[row, nameKeyCol].Value = faceData.NameKey;
|
||||
worksheet.Cells[row, initCol].Value = faceData.Init;
|
||||
|
||||
// IconId为-1时写入空字符串,否则写入实际值
|
||||
if (faceData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = faceData.IconId;
|
||||
}
|
||||
|
||||
processedIds.Add(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 删除行
|
||||
worksheet.DeleteRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二遍:添加新行
|
||||
int currentRow = worksheet.Dimension?.Rows ?? 2;
|
||||
foreach (var faceData in faceDataList)
|
||||
{
|
||||
if (!processedIds.Contains(faceData.Id))
|
||||
{
|
||||
// 这是新增的数据
|
||||
currentRow++;
|
||||
worksheet.Cells[currentRow, idCol].Value = faceData.Id;
|
||||
worksheet.Cells[currentRow, nameKeyCol].Value = faceData.NameKey;
|
||||
worksheet.Cells[currentRow, initCol].Value = faceData.Init;
|
||||
|
||||
if (faceData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = faceData.IconId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
package.Save();
|
||||
}
|
||||
|
||||
// 提示成功并提醒推送
|
||||
bool understood = EditorUtility.DisplayDialog("保存成功",
|
||||
"配置已保存到Excel文件!\n\n" +
|
||||
"⚠️ 重要提醒:\n" +
|
||||
"请及时在SourceTree或GitHubDesktop中:\n" +
|
||||
"1. 提交(Commit)本次修改\n" +
|
||||
"2. 推送(Push)到远程仓库\n\n" +
|
||||
"避免与其他策划产生冲突!",
|
||||
"我知道了");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"保存数据失败: {e.Message}\n{e.StackTrace}", "确定");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 头像数据类
|
||||
/// </summary>
|
||||
private class FaceData
|
||||
{
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int Init;
|
||||
public int IconId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3c6d1239cdbf0748b5fb75f163c3121
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
965
Scripts/Editor/Design_Tools/Collections/HeadFrameConfigEditor.cs
Normal file
965
Scripts/Editor/Design_Tools/Collections/HeadFrameConfigEditor.cs
Normal file
@ -0,0 +1,965 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using OfficeOpenXml;
|
||||
using ArtResource;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace DesignTools.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 头像框配置Editor工具
|
||||
/// 读取Docs/config/Avatar.xlsx,关联Art_SO/Collections/HeadFrameResource.asset
|
||||
/// </summary>
|
||||
public class HeadFrameConfigEditor : EditorWindow
|
||||
{
|
||||
private const string HEADFRAME_SO_PATH = "Assets/Art_SubModule/Art_SO/Collections";
|
||||
private const string HEADFRAME_SO_NAME = "HeadFrameResource";
|
||||
private const string AVATAR_EXCEL_NAME = "Avatar.xlsx";
|
||||
private const string LANGUAGE_EXCEL_NAME = "AllLanguage.xlsx";
|
||||
private const string AVATAR_SHEET_NAME = "Avatar";
|
||||
private const string LANGUAGE_SHEET_NAME = "client";
|
||||
private const string DOCS_PATH_PREF_KEY = "HeadFrameConfigEditor_DocsPath";
|
||||
private const string DESIGN_SUBMODULE_PATH = "Assets/Design_SubModule";
|
||||
|
||||
private string docsRootPath = "";
|
||||
private List<HeadFrameData> headFrameDataList = new List<HeadFrameData>();
|
||||
private ArtTableSO headFrameTableSO;
|
||||
private Dictionary<string, Dictionary<string, string>> languageDict = new Dictionary<string, Dictionary<string, string>>();
|
||||
private Vector2 scrollPosition;
|
||||
private bool isDataLoaded = false;
|
||||
private string pendingTooltipText = "";
|
||||
|
||||
[MenuItem("策划工具/收藏品/头像框")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<HeadFrameConfigEditor>("头像框配置");
|
||||
window.minSize = new Vector2(1000, 600);
|
||||
window.Show();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// 读取上次保存的路径
|
||||
if (EditorPrefs.HasKey(DOCS_PATH_PREF_KEY))
|
||||
{
|
||||
docsRootPath = EditorPrefs.GetString(DOCS_PATH_PREF_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||
EditorGUILayout.LabelField("头像框配置工具", EditorStyles.boldLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Docs路径选择
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
EditorGUILayout.LabelField("Docs项目根目录", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
docsRootPath = EditorGUILayout.TextField("路径", docsRootPath);
|
||||
if (GUILayout.Button("选择文件夹", GUILayout.Width(100)))
|
||||
{
|
||||
string selectedPath = EditorUtility.OpenFolderPanel("选择Docs项目根目录", "", "");
|
||||
if (!string.IsNullOrEmpty(selectedPath))
|
||||
{
|
||||
docsRootPath = selectedPath;
|
||||
EditorPrefs.SetString(DOCS_PATH_PREF_KEY, docsRootPath);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (GUILayout.Button("加载配置数据", GUILayout.Height(30)))
|
||||
{
|
||||
LoadData();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// 数据编辑区域
|
||||
if (isDataLoaded)
|
||||
{
|
||||
DrawDataEditor();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("请先选择Docs根目录并加载配置数据", MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置数据
|
||||
/// </summary>
|
||||
private void LoadData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 校验路径
|
||||
if (string.IsNullOrEmpty(docsRootPath) || !Directory.Exists(docsRootPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "请选择有效的Docs根目录", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 检查Docs是否为Git仓库并更新
|
||||
if (!CheckAndUpdateDocsRepository())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 检查Design_SubModule分支
|
||||
if (!CheckAndSwitchDesignSubModuleBranch())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验HeadFrame SO是否存在
|
||||
string headFrameSOPath = Path.Combine(HEADFRAME_SO_PATH, $"{HEADFRAME_SO_NAME}.asset");
|
||||
headFrameTableSO = AssetDatabase.LoadAssetAtPath<ArtTableSO>(headFrameSOPath);
|
||||
|
||||
if (headFrameTableSO == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误",
|
||||
$"未找到头像框资源配置\n路径: {headFrameSOPath}\n\n请先在美术资源配置工具中创建",
|
||||
"确定");
|
||||
return;
|
||||
}
|
||||
|
||||
if (headFrameTableSO.Items == null || headFrameTableSO.Items.Count == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "头像框资源配置数据为空", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载语言表
|
||||
LoadLanguageData();
|
||||
|
||||
// 加载Avatar.xlsx
|
||||
LoadAvatarExcel();
|
||||
|
||||
isDataLoaded = true;
|
||||
EditorUtility.DisplayDialog("成功", "配置数据加载成功!", "确定");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"加载数据失败: {e.Message}\n{e.StackTrace}", "确定");
|
||||
isDataLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行Git命令
|
||||
/// </summary>
|
||||
private string ExecuteGitCommand(string workingDirectory, string arguments, out bool success)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "git",
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = workingDirectory,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process process = Process.Start(startInfo))
|
||||
{
|
||||
string output = process.StandardOutput.ReadToEnd();
|
||||
string error = process.StandardError.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
success = process.ExitCode == 0;
|
||||
return success ? output : error;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
success = false;
|
||||
return $"执行Git命令失败: {e.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查并更新Docs仓库
|
||||
/// </summary>
|
||||
private bool CheckAndUpdateDocsRepository()
|
||||
{
|
||||
string gitPath = Path.Combine(docsRootPath, ".git");
|
||||
if (!Directory.Exists(gitPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误",
|
||||
$"Docs目录不是Git仓库\n路径: {docsRootPath}\n\n请确保Docs项目已正确克隆",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查更新", "正在检查Docs仓库远程更新...", 0.3f);
|
||||
|
||||
string fetchResult = ExecuteGitCommand(docsRootPath, "fetch", out bool fetchSuccess);
|
||||
|
||||
if (!fetchSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Fetch失败",
|
||||
$"无法检查远程更新:\n{fetchResult}\n\n请在SourceTree或GitHubDesktop中检查网络连接和仓库状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查更新", "正在检查是否有新提交...", 0.6f);
|
||||
|
||||
string statusResult = ExecuteGitCommand(docsRootPath, "status -uno", out bool statusSuccess);
|
||||
|
||||
if (!statusSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Status失败",
|
||||
$"无法获取仓库状态:\n{statusResult}\n\n请在SourceTree或GitHubDesktop中检查仓库状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (statusResult.Contains("Changes not staged") || statusResult.Contains("Changes to be committed"))
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
bool proceed = EditorUtility.DisplayDialog("警告:有未提交的更改",
|
||||
"Docs仓库中有未提交的更改,这可能导致拉取时产生冲突。\n\n建议先提交或暂存这些更改。\n\n是否继续加载配置?(不推荐)",
|
||||
"继续(不推荐)", "取消,前往处理");
|
||||
|
||||
if (!proceed)
|
||||
{
|
||||
Debug.Log("请在SourceTree或GitHubDesktop中处理未提交的更改");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (statusResult.Contains("Your branch is behind"))
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("更新中", "正在从远程拉取最新代码...", 0.8f);
|
||||
|
||||
string pullResult = ExecuteGitCommand(docsRootPath, "pull", out bool pullSuccess);
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (!pullSuccess)
|
||||
{
|
||||
if (pullResult.Contains("CONFLICT") || pullResult.Contains("conflict"))
|
||||
{
|
||||
EditorUtility.DisplayDialog("拉取失败:存在冲突",
|
||||
$"拉取远程更新时发生冲突:\n{pullResult}\n\n请在SourceTree或GitHubDesktop中解决冲突后再操作",
|
||||
"确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("拉取失败",
|
||||
$"无法拉取远程更新:\n{pullResult}\n\n请在SourceTree或GitHubDesktop中检查并解决问题",
|
||||
"确定");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Log($"Docs仓库已更新到最新版本:\n{pullResult}");
|
||||
EditorUtility.DisplayDialog("更新成功", "Docs仓库已更新到最新版本", "确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.Log("Docs仓库已是最新版本");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查并切换Design_SubModule到main分支
|
||||
/// </summary>
|
||||
private bool CheckAndSwitchDesignSubModuleBranch()
|
||||
{
|
||||
string designSubModulePath = Path.Combine(Application.dataPath, "..", DESIGN_SUBMODULE_PATH);
|
||||
designSubModulePath = Path.GetFullPath(designSubModulePath);
|
||||
|
||||
if (!Directory.Exists(designSubModulePath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误",
|
||||
$"Design_SubModule目录不存在:\n{designSubModulePath}\n\n请确保子模块已正确初始化",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorUtility.DisplayProgressBar("检查分支", "正在检查Design_SubModule分支...", 0.5f);
|
||||
|
||||
string branchResult = ExecuteGitCommand(designSubModulePath, "branch --show-current", out bool branchSuccess);
|
||||
|
||||
if (!branchSuccess)
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
EditorUtility.DisplayDialog("Git Branch失败",
|
||||
$"无法获取当前分支:\n{branchResult}\n\n请在SourceTree或GitHubDesktop中检查Design_SubModule状态",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
string currentBranch = branchResult.Trim();
|
||||
|
||||
if (currentBranch != "main")
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("切换分支", "正在切换到main分支...", 0.8f);
|
||||
|
||||
string checkoutResult = ExecuteGitCommand(designSubModulePath, "checkout main", out bool checkoutSuccess);
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
|
||||
if (!checkoutSuccess)
|
||||
{
|
||||
EditorUtility.DisplayDialog("切换分支失败",
|
||||
$"无法切换到main分支:\n{checkoutResult}\n\n当前分支: {currentBranch}\n\n请在SourceTree或GitHubDesktop中手动切换到main分支",
|
||||
"确定");
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.Log($"Design_SubModule已从 {currentBranch} 切换到 main 分支");
|
||||
EditorUtility.DisplayDialog("分支切换成功",
|
||||
$"Design_SubModule已从 {currentBranch} 切换到 main 分支",
|
||||
"确定");
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
Debug.Log("Design_SubModule已在main分支");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载语言数据
|
||||
/// </summary>
|
||||
private void LoadLanguageData()
|
||||
{
|
||||
languageDict.Clear();
|
||||
|
||||
string languageExcelPath = Path.Combine(docsRootPath, "config", LANGUAGE_EXCEL_NAME);
|
||||
if (!File.Exists(languageExcelPath))
|
||||
{
|
||||
Debug.LogWarning($"未找到语言表: {languageExcelPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(languageExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[LANGUAGE_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
Debug.LogWarning($"语言表中未找到Sheet: {LANGUAGE_SHEET_NAME}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找Key列和语言列
|
||||
int keyColumnIndex = -1;
|
||||
int zhCNColumnIndex = -1;
|
||||
int enUSColumnIndex = -1;
|
||||
int ptBRColumnIndex = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
if (header == "key")
|
||||
{
|
||||
keyColumnIndex = col;
|
||||
}
|
||||
else if (header == "zh_CN")
|
||||
{
|
||||
zhCNColumnIndex = col;
|
||||
}
|
||||
else if (header == "en_US")
|
||||
{
|
||||
enUSColumnIndex = col;
|
||||
}
|
||||
else if (header == "pt_BR")
|
||||
{
|
||||
ptBRColumnIndex = col;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyColumnIndex < 0)
|
||||
{
|
||||
Debug.LogWarning("语言表中未找到Key列");
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
for (int row = 3; row <= rowCount; row++)
|
||||
{
|
||||
string key = worksheet.Cells[row, keyColumnIndex].Text;
|
||||
if (string.IsNullOrEmpty(key)) continue;
|
||||
|
||||
if (!languageDict.ContainsKey(key))
|
||||
{
|
||||
languageDict[key] = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
if (zhCNColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["zh_CN"] = worksheet.Cells[row, zhCNColumnIndex].Text;
|
||||
}
|
||||
if (enUSColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["en_US"] = worksheet.Cells[row, enUSColumnIndex].Text;
|
||||
}
|
||||
if (ptBRColumnIndex > 0)
|
||||
{
|
||||
languageDict[key]["pt_BR"] = worksheet.Cells[row, ptBRColumnIndex].Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载Avatar.xlsx
|
||||
/// </summary>
|
||||
private void LoadAvatarExcel()
|
||||
{
|
||||
headFrameDataList.Clear();
|
||||
|
||||
string avatarExcelPath = Path.Combine(docsRootPath, "config", AVATAR_EXCEL_NAME);
|
||||
if (!File.Exists(avatarExcelPath))
|
||||
{
|
||||
throw new Exception($"未找到Avatar配置文件: {avatarExcelPath}");
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(avatarExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[AVATAR_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
throw new Exception($"Avatar.xlsx中未找到Sheet: {AVATAR_SHEET_NAME}");
|
||||
}
|
||||
|
||||
// 读取表头(第1行)
|
||||
int idCol = -1, nameKeyCol = -1, initCol = -1, iconCol = -1, frameImageScaleCol = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
switch (header)
|
||||
{
|
||||
case "Id":
|
||||
idCol = col;
|
||||
break;
|
||||
case "NameKey":
|
||||
nameKeyCol = col;
|
||||
break;
|
||||
case "Init":
|
||||
initCol = col;
|
||||
break;
|
||||
case "Icon":
|
||||
iconCol = col;
|
||||
break;
|
||||
case "FrameImageScale":
|
||||
frameImageScaleCol = col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idCol < 0 || nameKeyCol < 0 || initCol < 0 || iconCol < 0 || frameImageScaleCol < 0)
|
||||
{
|
||||
throw new Exception("Avatar.xlsx表结构不正确,缺少必要列(Id/NameKey/Init/Icon/FrameImageScale)");
|
||||
}
|
||||
|
||||
// 读取数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
for (int row = 3; row <= rowCount; row++)
|
||||
{
|
||||
string idText = worksheet.Cells[row, idCol].Text;
|
||||
if (string.IsNullOrEmpty(idText)) continue;
|
||||
|
||||
if (!int.TryParse(idText, out int id)) continue;
|
||||
|
||||
string nameKey = worksheet.Cells[row, nameKeyCol].Text;
|
||||
string initText = worksheet.Cells[row, initCol].Text;
|
||||
string iconText = worksheet.Cells[row, iconCol].Text;
|
||||
string frameImageScaleText = worksheet.Cells[row, frameImageScaleCol].Text;
|
||||
|
||||
int init = 0;
|
||||
if (!string.IsNullOrEmpty(initText))
|
||||
{
|
||||
int.TryParse(initText, out init);
|
||||
}
|
||||
|
||||
int iconId = -1;
|
||||
if (!string.IsNullOrEmpty(iconText))
|
||||
{
|
||||
if (!int.TryParse(iconText, out iconId))
|
||||
{
|
||||
iconId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
float frameImageScale = 1.0f;
|
||||
if (!string.IsNullOrEmpty(frameImageScaleText))
|
||||
{
|
||||
if (!float.TryParse(frameImageScaleText, out frameImageScale))
|
||||
{
|
||||
frameImageScale = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
var frameData = new HeadFrameData
|
||||
{
|
||||
Id = id,
|
||||
NameKey = nameKey,
|
||||
Init = init,
|
||||
IconId = iconId,
|
||||
FrameImageScale = frameImageScale
|
||||
};
|
||||
|
||||
headFrameDataList.Add(frameData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制数据编辑器
|
||||
/// </summary>
|
||||
private void DrawDataEditor()
|
||||
{
|
||||
pendingTooltipText = "";
|
||||
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField($"头像框数据列表(共 {headFrameDataList.Count} 条)", EditorStyles.boldLabel);
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.backgroundColor = Color.cyan;
|
||||
if (GUILayout.Button("+ 添加头像框", GUILayout.Height(25), GUILayout.Width(100)))
|
||||
{
|
||||
AddNewHeadFrame();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(3);
|
||||
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
// 表头
|
||||
EditorGUILayout.BeginHorizontal("box");
|
||||
EditorGUILayout.LabelField("Id", EditorStyles.boldLabel, GUILayout.Width(50));
|
||||
EditorGUILayout.LabelField("NameKey", EditorStyles.boldLabel, GUILayout.Width(150));
|
||||
EditorGUILayout.LabelField("中文名称", EditorStyles.boldLabel, GUILayout.Width(120));
|
||||
EditorGUILayout.LabelField("Init", EditorStyles.boldLabel, GUILayout.Width(50));
|
||||
EditorGUILayout.LabelField("Icon", EditorStyles.boldLabel, GUILayout.Width(200));
|
||||
EditorGUILayout.LabelField("预览", EditorStyles.boldLabel, GUILayout.Width(100));
|
||||
EditorGUILayout.LabelField("缩放", EditorStyles.boldLabel, GUILayout.Width(80));
|
||||
EditorGUILayout.LabelField("操作", EditorStyles.boldLabel, GUILayout.Width(60));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// 数据行
|
||||
for (int i = 0; i < headFrameDataList.Count; i++)
|
||||
{
|
||||
DrawHeadFrameDataRow(headFrameDataList[i]);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// 保存按钮
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUI.backgroundColor = Color.green;
|
||||
if (GUILayout.Button("保存配置到Excel", GUILayout.Height(30), GUILayout.Width(200)))
|
||||
{
|
||||
SaveData();
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// 在所有内容绘制完后,绘制tooltip(确保在最上层)
|
||||
if (!string.IsNullOrEmpty(pendingTooltipText))
|
||||
{
|
||||
Vector2 tooltipSize = GUI.skin.box.CalcSize(new GUIContent(pendingTooltipText));
|
||||
tooltipSize.x += 10;
|
||||
tooltipSize.y += 10;
|
||||
|
||||
// 使用当前鼠标位置,而不是之前记录的位置
|
||||
Vector2 mousePos = Event.current.mousePosition;
|
||||
Rect tooltipRect = new Rect(
|
||||
mousePos.x + 1,
|
||||
mousePos.y + 1,
|
||||
tooltipSize.x,
|
||||
tooltipSize.y
|
||||
);
|
||||
|
||||
GUI.Box(tooltipRect, pendingTooltipText);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制单行数据
|
||||
/// </summary>
|
||||
private void DrawHeadFrameDataRow(HeadFrameData data)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal("box");
|
||||
|
||||
// Id(可编辑)
|
||||
data.Id = EditorGUILayout.IntField(data.Id, GUILayout.Width(50));
|
||||
|
||||
// NameKey(可编辑)
|
||||
data.NameKey = EditorGUILayout.TextField(data.NameKey, GUILayout.Width(150));
|
||||
|
||||
// 中文名称(只读预览,带即时多语言显示)
|
||||
GUI.enabled = false;
|
||||
string zhName = "未找到语言Key";
|
||||
|
||||
if (languageDict.ContainsKey(data.NameKey))
|
||||
{
|
||||
var langs = languageDict[data.NameKey];
|
||||
if (langs.ContainsKey("zh_CN"))
|
||||
{
|
||||
zhName = langs["zh_CN"];
|
||||
}
|
||||
}
|
||||
|
||||
Rect nameRect = GUILayoutUtility.GetRect(new GUIContent(zhName), GUI.skin.textField, GUILayout.Width(120));
|
||||
EditorGUI.TextField(nameRect, zhName);
|
||||
|
||||
// 检测鼠标悬停并准备tooltip内容(不立即绘制)
|
||||
if (nameRect.Contains(Event.current.mousePosition) && languageDict.ContainsKey(data.NameKey))
|
||||
{
|
||||
var langs = languageDict[data.NameKey];
|
||||
List<string> tooltipLines = new List<string>();
|
||||
|
||||
if (langs.ContainsKey("zh_CN") && !string.IsNullOrEmpty(langs["zh_CN"]))
|
||||
{
|
||||
tooltipLines.Add($"[中文] {langs["zh_CN"]}");
|
||||
}
|
||||
if (langs.ContainsKey("en_US") && !string.IsNullOrEmpty(langs["en_US"]))
|
||||
{
|
||||
tooltipLines.Add($"[English] {langs["en_US"]}");
|
||||
}
|
||||
if (langs.ContainsKey("pt_BR") && !string.IsNullOrEmpty(langs["pt_BR"]))
|
||||
{
|
||||
tooltipLines.Add($"[Português] {langs["pt_BR"]}");
|
||||
}
|
||||
|
||||
if (tooltipLines.Count > 0)
|
||||
{
|
||||
pendingTooltipText = string.Join("\n", tooltipLines);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
|
||||
// Init(Checkbox)
|
||||
bool initChecked = data.Init == 1;
|
||||
bool newInitChecked = EditorGUILayout.Toggle(initChecked, GUILayout.Width(50));
|
||||
data.Init = newInitChecked ? 1 : 0;
|
||||
|
||||
// Icon(下拉列表)
|
||||
var iconItems = headFrameTableSO.Items;
|
||||
var iconNamesList = new List<string> { "未选择" };
|
||||
iconNamesList.AddRange(iconItems.Select(x => x.Name));
|
||||
var iconNames = iconNamesList.ToArray();
|
||||
|
||||
int currentIndex = 0;
|
||||
var currentItem = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (currentItem != null)
|
||||
{
|
||||
int itemIndex = iconItems.IndexOf(currentItem);
|
||||
if (itemIndex >= 0)
|
||||
{
|
||||
currentIndex = itemIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
int newIndex = EditorGUILayout.Popup(currentIndex, iconNames, GUILayout.Width(200));
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
if (newIndex == 0)
|
||||
{
|
||||
data.IconId = -1;
|
||||
}
|
||||
else if (newIndex > 0 && newIndex <= iconItems.Count)
|
||||
{
|
||||
data.IconId = iconItems[newIndex - 1].Id;
|
||||
}
|
||||
}
|
||||
|
||||
// 预览
|
||||
if (data.IconId >= 0)
|
||||
{
|
||||
var item = iconItems.Find(x => x.Id == data.IconId);
|
||||
if (item != null)
|
||||
{
|
||||
Sprite sprite = item.Sprite;
|
||||
|
||||
if (sprite != null && sprite.texture != null)
|
||||
{
|
||||
Rect previewRect = GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
|
||||
EditorGUI.DrawRect(previewRect, new Color(0.5f, 0.5f, 0.5f, 1f));
|
||||
|
||||
Rect texCoords = sprite.textureRect;
|
||||
Texture2D tex = sprite.texture;
|
||||
|
||||
Rect normalizedCoords = new Rect(
|
||||
texCoords.x / tex.width,
|
||||
texCoords.y / tex.height,
|
||||
texCoords.width / tex.width,
|
||||
texCoords.height / tex.height
|
||||
);
|
||||
|
||||
float aspect = texCoords.width / texCoords.height;
|
||||
Rect drawRect = previewRect;
|
||||
if (aspect > 1f)
|
||||
{
|
||||
float height = drawRect.width / aspect;
|
||||
drawRect.y += (drawRect.height - height) * 0.5f;
|
||||
drawRect.height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
float width = drawRect.height * aspect;
|
||||
drawRect.x += (drawRect.width - width) * 0.5f;
|
||||
drawRect.width = width;
|
||||
}
|
||||
|
||||
GUI.DrawTextureWithTexCoords(drawRect, tex, normalizedCoords, true);
|
||||
|
||||
if (!string.IsNullOrEmpty(item.Desc))
|
||||
{
|
||||
GUI.Label(previewRect, new GUIContent("", item.Desc));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("无图片", GUILayout.Width(50));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("未选择", GUILayout.Width(50));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GUILayoutUtility.GetRect(50, 50, GUILayout.Width(50), GUILayout.Height(50));
|
||||
GUILayout.Space(-50);
|
||||
EditorGUILayout.LabelField("未选择", GUILayout.Width(50));
|
||||
}
|
||||
|
||||
// FrameImageScale(float输入框)
|
||||
data.FrameImageScale = EditorGUILayout.FloatField(data.FrameImageScale, GUILayout.Width(80));
|
||||
|
||||
// 删除按钮
|
||||
GUI.backgroundColor = Color.red;
|
||||
if (GUILayout.Button("删除", GUILayout.Width(60)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("确认删除",
|
||||
$"确定要删除头像框 {data.Id} ({data.NameKey}) 吗?",
|
||||
"删除", "取消"))
|
||||
{
|
||||
headFrameDataList.Remove(data);
|
||||
}
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加新头像框
|
||||
/// </summary>
|
||||
private void AddNewHeadFrame()
|
||||
{
|
||||
int newId = 1;
|
||||
if (headFrameDataList.Count > 0)
|
||||
{
|
||||
newId = headFrameDataList.Max(x => x.Id) + 1;
|
||||
}
|
||||
|
||||
var newFrame = new HeadFrameData
|
||||
{
|
||||
Id = newId,
|
||||
NameKey = "",
|
||||
Init = 0,
|
||||
IconId = -1,
|
||||
FrameImageScale = 1.0f
|
||||
};
|
||||
|
||||
headFrameDataList.Add(newFrame);
|
||||
|
||||
scrollPosition = new Vector2(0, float.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存数据到Excel
|
||||
/// </summary>
|
||||
private void SaveData()
|
||||
{
|
||||
try
|
||||
{
|
||||
string avatarExcelPath = Path.Combine(docsRootPath, "config", AVATAR_EXCEL_NAME);
|
||||
if (!File.Exists(avatarExcelPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"未找到Avatar配置文件: {avatarExcelPath}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
using (var package = new ExcelPackage(new FileInfo(avatarExcelPath)))
|
||||
{
|
||||
var worksheet = package.Workbook.Worksheets[AVATAR_SHEET_NAME];
|
||||
if (worksheet == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"Avatar.xlsx中未找到Sheet: {AVATAR_SHEET_NAME}", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找列索引
|
||||
int idCol = -1, nameKeyCol = -1, initCol = -1, iconCol = -1, frameImageScaleCol = -1;
|
||||
int columnCount = worksheet.Dimension.Columns;
|
||||
|
||||
for (int col = 1; col <= columnCount; col++)
|
||||
{
|
||||
string header = worksheet.Cells[1, col].Text;
|
||||
switch (header)
|
||||
{
|
||||
case "Id":
|
||||
idCol = col;
|
||||
break;
|
||||
case "NameKey":
|
||||
nameKeyCol = col;
|
||||
break;
|
||||
case "Init":
|
||||
initCol = col;
|
||||
break;
|
||||
case "Icon":
|
||||
iconCol = col;
|
||||
break;
|
||||
case "FrameImageScale":
|
||||
frameImageScaleCol = col;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新和删除数据(从第3行开始)
|
||||
int rowCount = worksheet.Dimension.Rows;
|
||||
var processedIds = new HashSet<int>();
|
||||
|
||||
// 第一遍:更新现有行或删除
|
||||
for (int row = rowCount; row >= 3; row--)
|
||||
{
|
||||
string idText = worksheet.Cells[row, idCol].Text;
|
||||
if (string.IsNullOrEmpty(idText)) continue;
|
||||
|
||||
if (!int.TryParse(idText, out int id)) continue;
|
||||
|
||||
var frameData = headFrameDataList.Find(x => x.Id == id);
|
||||
if (frameData != null)
|
||||
{
|
||||
worksheet.Cells[row, nameKeyCol].Value = frameData.NameKey;
|
||||
worksheet.Cells[row, initCol].Value = frameData.Init;
|
||||
|
||||
if (frameData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[row, iconCol].Value = frameData.IconId;
|
||||
}
|
||||
|
||||
worksheet.Cells[row, frameImageScaleCol].Value = frameData.FrameImageScale;
|
||||
|
||||
processedIds.Add(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.DeleteRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二遍:添加新行
|
||||
int currentRow = worksheet.Dimension?.Rows ?? 2;
|
||||
foreach (var frameData in headFrameDataList)
|
||||
{
|
||||
if (!processedIds.Contains(frameData.Id))
|
||||
{
|
||||
currentRow++;
|
||||
worksheet.Cells[currentRow, idCol].Value = frameData.Id;
|
||||
worksheet.Cells[currentRow, nameKeyCol].Value = frameData.NameKey;
|
||||
worksheet.Cells[currentRow, initCol].Value = frameData.Init;
|
||||
|
||||
if (frameData.IconId < 0)
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
worksheet.Cells[currentRow, iconCol].Value = frameData.IconId;
|
||||
}
|
||||
|
||||
worksheet.Cells[currentRow, frameImageScaleCol].Value = frameData.FrameImageScale;
|
||||
}
|
||||
}
|
||||
|
||||
package.Save();
|
||||
}
|
||||
|
||||
// 提示成功并提醒推送
|
||||
bool understood = EditorUtility.DisplayDialog("保存成功",
|
||||
"配置已保存到Excel文件!\n\n" +
|
||||
"⚠️ 重要提醒:\n" +
|
||||
"请及时在SourceTree或GitHubDesktop中:\n" +
|
||||
"1. 提交(Commit)本次修改\n" +
|
||||
"2. 推送(Push)到远程仓库\n\n" +
|
||||
"避免与其他策划产生冲突!",
|
||||
"我知道了");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", $"保存数据失败: {e.Message}\n{e.StackTrace}", "确定");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 头像框数据类
|
||||
/// </summary>
|
||||
private class HeadFrameData
|
||||
{
|
||||
public int Id;
|
||||
public string NameKey;
|
||||
public int Init;
|
||||
public int IconId;
|
||||
public float FrameImageScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 370961b2539ed9d49ade3ec27aedd25e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1806
Scripts/Editor/Design_Tools/ItemConfigEditor.cs
Normal file
1806
Scripts/Editor/Design_Tools/ItemConfigEditor.cs
Normal file
File diff suppressed because it is too large
Load Diff
11
Scripts/Editor/Design_Tools/ItemConfigEditor.cs.meta
Normal file
11
Scripts/Editor/Design_Tools/ItemConfigEditor.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cbfd5e054af7604e837e9ececb2a15c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Scripts/Editor/Design_Tools/Scene.meta
Normal file
8
Scripts/Editor/Design_Tools/Scene.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f94b023b059a5349a6ecd1811ad9334
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1585
Scripts/Editor/Design_Tools/Scene/IndoorProgressEditor.cs
Normal file
1585
Scripts/Editor/Design_Tools/Scene/IndoorProgressEditor.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15c94f7123a92774084366377ef07164
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Loading…
Reference in New Issue
Block a user