diff --git a/.PhotoshopScript/JSZXPsd2Unity/PSD导出Unity.js b/.PhotoshopScript/JSZXPsd2Unity/PSD导出Unity.js index 92bb350..3cedf96 100644 --- a/.PhotoshopScript/JSZXPsd2Unity/PSD导出Unity.js +++ b/.PhotoshopScript/JSZXPsd2Unity/PSD导出Unity.js @@ -107,6 +107,13 @@ function s2t(t) { return stringIDToTypeID(t) } return; } + // 判断文件名是否有中文 + if (Util.haveChinese(app.activeDocument.name)) + { + alert("\"" + app.activeDocument.name + "\" 名字不合法,出现中文") + return; + } + this.documentName = app.activeDocument.name.slice(0, -4); var i, j, k, len, ref1, removeFiles; @@ -445,12 +452,6 @@ function s2t(t) { return stringIDToTypeID(t) } return true } - function isChinese(temp) { - var re = /[^\u4E00-\u9FA5]/; - if (re.test(temp)) return false; - return true; - } - function isNumber(val) { var regPos = /^\d+(\.\d+)?$/; //非负浮点数 var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/; //负浮点数 @@ -1221,6 +1222,12 @@ function s2t(t) { return stringIDToTypeID(t) } return imageName + layer.name.split("@")[0].replace('_', '').replace(' ', '-').toLowerCase(); }; + Util.haveChinese = function (temp) { + var re = /[\u4E00-\u9FA5]/; + if (re.test(temp)) return true; + return false; + } + Util.getLastSnapshotID = function (doc) { var hsLength, hsObj, i, j, ref1; hsObj = doc.historyStates; diff --git a/.PhotoshopScript/JSZXPsd2Unity/run_step2.bat b/.PhotoshopScript/JSZXPsd2Unity/run_step2.bat index 5cc919b..eed521a 100644 --- a/.PhotoshopScript/JSZXPsd2Unity/run_step2.bat +++ b/.PhotoshopScript/JSZXPsd2Unity/run_step2.bat @@ -1,2 +1,2 @@ cd /d %~dp0 -start step2.exe -src C:\Users\biaosong\Desktop\c2main_panel -dst C:\Users\biaosong\Desktop\c2main_panel_output +start step2.exe -src C:\Users\biaosong\Desktop\shijiezhishi -dst C:\Users\biaosong\Desktop\shijiezhishi_output diff --git a/.PhotoshopScript/JSZXPsd2Unity/step2.exe b/.PhotoshopScript/JSZXPsd2Unity/step2.exe index e39a9d6..9060648 100644 Binary files a/.PhotoshopScript/JSZXPsd2Unity/step2.exe and b/.PhotoshopScript/JSZXPsd2Unity/step2.exe differ diff --git a/.res/创建text.png b/.res/创建text.png index 943408a..fa9d268 100644 Binary files a/.res/创建text.png and b/.res/创建text.png differ diff --git a/.res/创建text后的结果.png b/.res/创建text后的结果.png index 9814476..26eefc4 100644 Binary files a/.res/创建text后的结果.png and b/.res/创建text后的结果.png differ diff --git a/.res/创建预制体.png b/.res/创建预制体.png new file mode 100644 index 0000000..e79673a Binary files /dev/null and b/.res/创建预制体.png differ diff --git a/.res/吸附展示.gif b/.res/吸附展示.gif index eb48696..20e7083 100644 Binary files a/.res/吸附展示.gif and b/.res/吸附展示.gif differ diff --git a/.res/复原子节点位置.gif b/.res/复原子节点位置.gif new file mode 100644 index 0000000..e14d4cb Binary files /dev/null and b/.res/复原子节点位置.gif differ diff --git a/.res/展示.gif b/.res/展示.gif new file mode 100644 index 0000000..6f52723 Binary files /dev/null and b/.res/展示.gif differ diff --git a/.res/开启助手.png b/.res/开启助手.png index 08ca582..f5b6289 100644 Binary files a/.res/开启助手.png and b/.res/开启助手.png differ diff --git a/.res/父节点应用子节点变换.gif b/.res/父节点应用子节点变换.gif new file mode 100644 index 0000000..d12d1a4 Binary files /dev/null and b/.res/父节点应用子节点变换.gif differ diff --git a/Assets/Editor/Entity/IEntity.cs b/Assets/Editor/Entity/IEntity.cs index 3abd2e7..4f5347c 100644 --- a/Assets/Editor/Entity/IEntity.cs +++ b/Assets/Editor/Entity/IEntity.cs @@ -2,6 +2,7 @@ using Sirenix.OdinInspector; using System; using Unity.Mathematics; +using UnityEditor; using UnityEngine; using UnityEngine.UIElements; @@ -44,19 +45,27 @@ namespace UguiToolkit.Editor public virtual void ApplyTransformByParent(Transform parentTf, Transform tf) { - var position = lastMoveMatrix.GetColumn(3); // 提取位置 - var rotation = Quaternion.LookRotation(lastMoveMatrix.GetColumn(2), lastMoveMatrix.GetColumn(1)); // 提取旋转 - Debug.Log($"[I] ApplyTransformByParent {position}, {rotation.eulerAngles}"); - parentTf.position += new Vector3(position.x, position.y, position.z); - parentTf.rotation *= rotation; + Undo.RecordObject(parentTf, "ApplyTransformByParent"); + var resultMatrix = lastMoveMatrix * parentTf.localToWorldMatrix; + parentTf.position = resultMatrix.GetColumn(3); + parentTf.rotation = Quaternion.LookRotation(resultMatrix.GetColumn(2), resultMatrix.GetColumn(1)); + parentTf.localScale = new Vector3( + resultMatrix.GetColumn(0).magnitude, + resultMatrix.GetColumn(1).magnitude, + resultMatrix.GetColumn(2).magnitude + ); if (tf) { - var lastTransformMatrixInverse = lastMoveMatrix.inverse; - position = lastTransformMatrixInverse.GetColumn(3); // 提取位置 - rotation = Quaternion.LookRotation(lastTransformMatrixInverse.GetColumn(2), lastTransformMatrixInverse.GetColumn(1)); // 提取旋转 - tf.position += new Vector3(position.x, position.y, position.z); - tf.rotation *= rotation; + Undo.RecordObject(tf, "ApplyTransformByParent"); + resultMatrix = lastMoveMatrix.inverse * tf.localToWorldMatrix; + tf.position = resultMatrix.GetColumn(3); + tf.rotation = Quaternion.LookRotation(resultMatrix.GetColumn(2), resultMatrix.GetColumn(1)); + tf.localScale = new Vector3( + resultMatrix.GetColumn(0).magnitude, + resultMatrix.GetColumn(1).magnitude, + resultMatrix.GetColumn(2).magnitude + ); } } @@ -72,8 +81,9 @@ namespace UguiToolkit.Editor public void ApplyTransform(Transform tf) { - Debug.Log($"[I] ApplyTransform {tf.name}"); + Undo.RecordObject(tf, "ApplyTransform"); OnApplyTransform(tf); + Matrix4x4 newTransformMatrix = Matrix4x4.TRS(tf.position, tf.rotation, tf.localScale); lastMoveMatrix = newTransformMatrix * oldTransformMatrix.inverse; } @@ -94,6 +104,7 @@ namespace UguiToolkit.Editor public void ApplyData(T ui) where T : Component { + Undo.RecordObject(ui, "ApplyData"); OnApplyData(ui as T1); } diff --git a/Assets/Editor/Entity/ImageEntity.cs b/Assets/Editor/Entity/ImageEntity.cs index 57d3469..dc54a01 100644 --- a/Assets/Editor/Entity/ImageEntity.cs +++ b/Assets/Editor/Entity/ImageEntity.cs @@ -69,6 +69,7 @@ namespace UguiToolkit.Editor if (!TryGetComponent(out m_previewImage)) { m_previewImage = gameObject.AddComponent(); + m_previewImage.type = Image.Type.Simple; } LoadImageFromFile(ElementInfo.imgPath); @@ -83,13 +84,11 @@ namespace UguiToolkit.Editor { var pos = ElementInfo.Position; var worldPos = StageManager.Instance.PrefabContentsRoot.transform.TransformPoint(new Vector3(pos.x, pos.y, 0)); - var anchorMin = rt.anchorMin; - var anchorMax = rt.anchorMax; var oldPiovt = rt.pivot; rt.pivot = new Vector2(0.5f, 0.5f); - Vector2 size = new Vector2(ElementInfo.w, ElementInfo.h); ; + Vector2 size = new Vector2(ElementInfo.w, ElementInfo.h); Quaternion rotation = Quaternion.identity; if (!similarityCalc) @@ -106,12 +105,8 @@ namespace UguiToolkit.Editor } // С - rt.anchorMin = rt.pivot; - rt.anchorMax = rt.pivot; - var oldSizeDelta = rt.sizeDelta; - rt.sizeDelta = new Vector2(size.x, size.y); - rt.anchorMin = anchorMin; - rt.anchorMax = anchorMax; + rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x); + rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y); // rt.position = worldPos; @@ -119,7 +114,9 @@ namespace UguiToolkit.Editor //ĵ var oldRect = rt.rect; + var oldLocalPosition = rt.localPosition; rt.pivot = oldPiovt; + rt.localPosition = oldLocalPosition; var offsetRectPos = oldRect.position - rt.rect.position; rt.Translate(offsetRectPos); } diff --git a/Assets/Editor/Entity/TextMeshProEntity.cs b/Assets/Editor/Entity/TextMeshProEntity.cs index 81ea71b..f3fe363 100644 --- a/Assets/Editor/Entity/TextMeshProEntity.cs +++ b/Assets/Editor/Entity/TextMeshProEntity.cs @@ -16,23 +16,28 @@ namespace UguiToolkit.Editor protected override void OnApplyTransform(Transform tf) { var rt = tf as RectTransform; - var pos = ElementInfo.Position; + Vector2 size = new Vector2(ElementInfo.w, ElementInfo.h); var worldPos = StageManager.Instance.PrefabContentsRoot.transform.TransformPoint(new Vector3(pos.x, pos.y, 0)); Quaternion rotation = Quaternion.identity; var oldPiovt = rt.pivot; rt.pivot = new Vector2(0.5f, 0.5f); + // С + rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x); + rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y); + // rt.position = worldPos; rt.rotation = rotation; //ĵ var oldRect = rt.rect; + var oldLocalPosition = rt.localPosition; rt.pivot = oldPiovt; + rt.localPosition = oldLocalPosition; var offsetRectPos = oldRect.position - rt.rect.position; rt.Translate(offsetRectPos); - } protected override void OnApplyData(TextMeshProUGUI ui) @@ -47,22 +52,45 @@ namespace UguiToolkit.Editor if (!TryGetComponent(out m_previewText)) { m_previewText = gameObject.AddComponent(); + m_previewText.alignment = TextAlignmentOptions.Center; } OnApplyData(m_previewText); ApplyTransform(transform); } + // ڱ֤ı겻 뷽ʽ + private static void SetAlignment(TextMeshProUGUI textMeshPro, TextAlignmentOptions newAlignment) + { + textMeshPro.ForceMeshUpdate(); + + // ȡǰıı߽ + Bounds originalBounds = textMeshPro.textBounds; + // µĶ뷽ʽ + textMeshPro.alignment = newAlignment; + // ² + textMeshPro.ForceMeshUpdate(); + // ȡµı߽ + Bounds newBounds = textMeshPro.textBounds; + // ƫ + Vector3 offset = originalBounds.center - newBounds.center; + // TextMeshProλ + textMeshPro.transform.position += offset; + } + private static void SetTMPByTextInfo(TextMeshProUGUI ui, LayoutInfo.TextInfo textInfo) { // TODO: չ + // 浱ǰĶ뷽ʽ + TextAlignmentOptions originalAlignment = ui.alignment; float2 sizeOffset = float2.zero; + ui.alignment = TextAlignmentOptions.Center; + if (!ui.hasStringID) ui.text = textInfo.text; ui.fontSize = (int)textInfo.size; ui.color = textInfo.color; - ui.alignment = TextAlignmentOptions.Center; if (GetTextFontPreset(textInfo, out var textFontPreset)) { @@ -86,21 +114,15 @@ namespace UguiToolkit.Editor sizeOffset = textFontPreset.sizeOffset; } - var rt = ui.rectTransform; - var anchorMin = rt.anchorMin; - var anchorMax = rt.anchorMax; - // С - rt.anchorMin = new Vector2(0.5f, 0.5f); - rt.anchorMax = anchorMin; - rt.sizeDelta = new Vector2(textInfo.w + sizeOffset.x, textInfo.h + sizeOffset.y); - rt.anchorMin = anchorMin; - rt.anchorMax = anchorMax; - // margins ui.margin = Vector4.zero; // ּ ui.characterSpacing = 0f; + + // ö뷽ʽ + if (originalAlignment != TextAlignmentOptions.Center) + SetAlignment(ui, originalAlignment); } private static bool GetTextFontPreset(LayoutInfo.TextInfo textInfo, out TextFontPresetOfUnity textFontPreset) diff --git a/Assets/Editor/Helper/ImageUtils.cs b/Assets/Editor/Helper/ImageUtils.cs index ad5066a..98749f3 100644 --- a/Assets/Editor/Helper/ImageUtils.cs +++ b/Assets/Editor/Helper/ImageUtils.cs @@ -28,30 +28,163 @@ public static class ImageUtils return Path.GetRelativePath(projectPath, imgFilePath).Replace("\\", "/"); } - public static void LoadPngImagesFromFolder(string folderPath, List images, List imagePaths) + public static void LoadPngImagesFromFolder(string folderPath, List images, List imagePaths, Func filterCallback) { - foreach (string file in Directory.GetFiles(folderPath, "*.png")) + foreach (string filePath in Directory.GetFiles(folderPath, "*.png")) { - Mat img = Imgcodecs.imread(file); + if (filterCallback(filePath)) continue; + + Mat img = Imgcodecs.imread(filePath, Imgcodecs.IMREAD_UNCHANGED); if (!img.empty()) { images.Add(img); - imagePaths.Add(FormatImgFilePath(file)); + imagePaths.Add(FormatImgFilePath(filePath)); } } } + #region SliceTexture + + static List hashListOfSlice; + + static int ToHashCode(in Color color) + { + int a = Mathf.RoundToInt(color.a * 255); + if (a == 0) return 0; + + int r = Mathf.RoundToInt(color.r * 255); + int g = Mathf.RoundToInt(color.g * 255); + int b = Mathf.RoundToInt(color.b * 255); + + return (a << 24) + (r << 16) + (g << 8) + b; + } + + static (int, int) CalcLine(List hashList) + { + int start = 0, end = 0; + int tmpStart = 0, tmpEnd = 0; + int tmpHash = hashList[0]; + + for (int i = 0; i < hashList.Count; i++) + { + if (tmpHash == hashList[i]) + { + tmpEnd = i; + } + else + { + if (end - start < tmpEnd - tmpStart) + { + start = tmpStart; + end = tmpEnd; + } + tmpStart = i; + tmpEnd = i; + tmpHash = hashList[i]; + } + } + + if (end - start < tmpEnd - tmpStart) + { + start = tmpStart; + end = tmpEnd; + } + + return (start, end); + } + + static List CreateHashList(Mat image, in char axis) + { + if (hashListOfSlice == null) hashListOfSlice = new (); + var hashList = hashListOfSlice; + hashList.Clear(); + + if (axis == 'x') + { + for (int i = 0; i < image.cols(); i++) + { + int hash = 0; + for (int j = 0; j < image.rows(); j++) + { + double[] color = image.get(j, i); + hash += ToHashCode(new Color((float)color[2] / 255, (float)color[1] / 255, (float)color[0] / 255, (float)color[3] / 255)); + } + hashList.Add(hash); + } + } + else + { + for (int j = 0; j < image.rows(); j++) + { + int hash = 0; + for (int i = 0; i < image.cols(); i++) + { + double[] color = image.get(j, i); + hash += ToHashCode(new Color((float)color[2] / 255, (float)color[1] / 255, (float)color[0] / 255, (float)color[3] / 255)); + } + hashList.Add(hash); + } + } + + return hashList; + } + + public static void SliceTexture(string imagePath, string outputPath) + { + var mat = SliceTexture(imagePath); + Imgcodecs.imwrite(outputPath, mat, new MatOfInt(Imgcodecs.IMWRITE_PNG_COMPRESSION, 9)); + } + + public static Mat SliceTexture(string imagePath) + { + Mat image = Imgcodecs.imread(imagePath, Imgcodecs.IMREAD_UNCHANGED); // ʹOpenCVͼ + + int width = image.cols(); + int height = image.rows(); + + List hashListX = CreateHashList(image, 'x'); + (int xStart, int xEnd) = CalcLine(hashListX); + + List hashListY = CreateHashList(image, 'y'); + (int yStart, int yEnd) = CalcLine(hashListY); + + int outputWidth = width - (xEnd - xStart); + int outputHeight = height - (yEnd - yStart); + + var outputMat = new Mat(outputHeight, outputWidth, CvType.CV_8UC4); + for (int x = 0; x < outputWidth; x++) + { + int originalX = x < xStart ? x : x + (xEnd - xStart); + for (int y = 0; y < outputHeight; y++) + { + int originalY = y < yStart ? y : y + (yEnd - yStart); + double[] color = image.get(originalY, originalX); + outputMat.put(y, x, color); + } + } + + return outputMat; + } + #endregion + static List> tasks; static ObjectPool detectorPool; public static async Task ProcessFolderAsync(List images, string targetFilePath, double distanceDifference, Action callback, - Action endCallback) + Action endCallback, bool isSliceTexture = false) { if (tasks == null) tasks = new (images.Count); if (detectorPool == null) detectorPool = new (images.Count); - Mat targetImage = Imgcodecs.imread(targetFilePath); + Mat targetImage = null; + if (isSliceTexture) + { + targetImage = SliceTexture(targetFilePath); + } + else { + targetImage = Imgcodecs.imread(targetFilePath, Imgcodecs.IMREAD_UNCHANGED); + } List detectors = new(images.Count); tasks.Clear(); diff --git a/Assets/Editor/Helper/PanelHelper.cs b/Assets/Editor/Helper/PanelHelper.cs index 2edd89d..0e76c43 100644 --- a/Assets/Editor/Helper/PanelHelper.cs +++ b/Assets/Editor/Helper/PanelHelper.cs @@ -1,5 +1,6 @@ #if UNITY_EDITOR using Sirenix.OdinInspector.Editor; +using System; using UguiToolkit.Editor.Windows; using UnityEditor; using UnityEditor.SceneManagement; @@ -9,6 +10,9 @@ namespace UguiToolkit.Editor { public static class PanelHelper { + public static event Action jumpOfGameCameraByCurPos; + public static event Action recoverDataSelectionObjCache; + [InitializeOnLoadMethod] public static void AddListener() { @@ -45,11 +49,26 @@ namespace UguiToolkit.Editor { if (StageManager.Instance) { - var entityMng = StageManager.Instance.GetManager(); - if (entityMng && Selection.activeGameObject) - { - entityMng.EffectLastApplyTransform(Selection.activeGameObject.transform); - } + if (Selection.activeGameObject) + jumpOfGameCameraByCurPos.Invoke(Selection.activeGameObject.transform); + + Debug.Log("JumpOfGameCameraByCurPos"); + } + } + + /// + /// 创建热键 CTRL SPACE + /// + /// + [MenuItem("GameObject/拼接助手/恢复子节点位置 %SPACE", false, 6)] + public static void RecoverDataSelectionObjCache(MenuCommand menuCommand) + { + if (StageManager.Instance) + { + if (Selection.activeGameObject) + recoverDataSelectionObjCache.Invoke(Selection.activeGameObject.transform); + + Debug.Log("RecoverDataSelectionObjCache"); } } } diff --git a/Assets/Editor/Manager/EntityManager.cs b/Assets/Editor/Manager/EntityManager.cs index 2005e34..29ff448 100644 --- a/Assets/Editor/Manager/EntityManager.cs +++ b/Assets/Editor/Manager/EntityManager.cs @@ -12,18 +12,25 @@ using UnityEngine.UI; using UguiToolkit.Editor.Windows; using System.Threading.Tasks; using Unity.Mathematics; +using Securify.ShellLink.Flags; namespace UguiToolkit.Editor { [ExecuteAlways] public class EntityManager : MonoBehaviour, IManager { + private const string m_backgroundFileName= "__background__"; + private string m_BackgroundFileAllName= m_backgroundFileName + ".png"; + private PanelCache m_panelCache; private Transform m_entityRoot; private Transform m_background; - private GameObject m_lastSelectionGo; - private IEntity m_lastSelectionEntity; - private GameObject m_curSelectionGo; + private GameObject m_lastApplyDataGo; // 上一次应用的游戏对象 + private IEntity m_lastApplyDataEntity; // 上一次应用的entity + private GameObject m_curSelectionEntityGo; // 当前选中的entity游戏对象 + private GameObject m_curSelectionGo; // 当前选中的游戏对象 + private SelectionObjCache m_curSelectionObjCache = new (); // 当前选中的游戏对象的缓存数据 + private Color m_curSelectionObjLabelColor = Color.yellow; private List m_imageEntities; private List m_textEntities; @@ -60,9 +67,12 @@ namespace UguiToolkit.Editor editWindow.createAllTextEntity += CreateAllTextEntity; editWindow.createAllPrefabEntity += CreateAllPrefabEntity; editWindow.effectLastApplyTransform += EffectLastApplyTransform; + editWindow.recoverDataSelectionObjCache += OnRecoverDataSelectionObjCache; } EditorApplication.hierarchyWindowItemOnGUI += HandleHierarchyWindowItemOnGUI; + PanelHelper.jumpOfGameCameraByCurPos += EffectLastApplyTransform; + PanelHelper.recoverDataSelectionObjCache += RecoverDataSelectionObjCache; ImageUtils.SetDebugMode(true); } @@ -78,9 +88,12 @@ namespace UguiToolkit.Editor editWindow.createAllTextEntity -= CreateAllTextEntity; editWindow.createAllPrefabEntity -= CreateAllPrefabEntity; editWindow.effectLastApplyTransform -= EffectLastApplyTransform; + editWindow.recoverDataSelectionObjCache -= OnRecoverDataSelectionObjCache; } EditorApplication.hierarchyWindowItemOnGUI -= HandleHierarchyWindowItemOnGUI; + PanelHelper.jumpOfGameCameraByCurPos -= EffectLastApplyTransform; + PanelHelper.recoverDataSelectionObjCache -= RecoverDataSelectionObjCache; ImageUtils.SetDebugMode(false); if (m_stageManager.PrefabAsset || m_panelCache != null) @@ -90,21 +103,21 @@ namespace UguiToolkit.Editor private void Update() { // 检测是否到达可选实例矩形内部 - if (m_selectionEntities != null && m_curSelectionGo) + if (m_selectionEntities != null && m_curSelectionEntityGo) { - if (m_lastSelectionGo && m_lastSelectionGo == m_curSelectionGo) + if (m_lastApplyDataGo && m_lastApplyDataGo == m_curSelectionEntityGo) { - if (m_lastSelectionEntity != null && !m_lastSelectionEntity.IsInside(m_lastSelectionGo.transform)) + if (m_lastApplyDataEntity != null && !m_lastApplyDataEntity.IsInside(m_lastApplyDataGo.transform)) { - m_lastSelectionGo = null; - m_lastSelectionEntity = null; + m_lastApplyDataGo = null; + m_lastApplyDataEntity = null; } return; } foreach (var entity in m_selectionEntities) { - var tf = m_curSelectionGo.transform; + var tf = m_curSelectionEntityGo.transform; if (entity.IsInside(tf)) { entity.ApplyTransform(tf); @@ -125,8 +138,8 @@ namespace UguiToolkit.Editor entity.ApplyData(temp); } - m_lastSelectionGo = m_curSelectionGo; - m_lastSelectionEntity = entity; + m_lastApplyDataGo = m_curSelectionEntityGo; + m_lastApplyDataEntity = entity; Selection.activeGameObject = null; break; @@ -135,11 +148,36 @@ namespace UguiToolkit.Editor } } + private void UpdateSelectionObjLabelColor() + { + m_curSelectionObjLabelColor = m_curSelectionObjLabelColor == Color.yellow ? Color.blue : Color.yellow; + } + + private void OnRecoverDataSelectionObjCache() + { + if (m_curSelectionObjCache.Transform) + m_curSelectionObjCache.RecoverData(); + } + + private void RecoverDataSelectionObjCache(Transform parent) + { + if (m_curSelectionObjCache.Transform && m_curSelectionObjCache.Transform == parent) + { + m_curSelectionObjCache.RecoverData(); + } + } + public void EffectLastApplyTransform(Transform parent) { - if (m_lastSelectionEntity != null) + if (m_lastApplyDataEntity != null) { - m_lastSelectionEntity.ApplyTransformByParent(parent, m_lastSelectionGo ? m_lastSelectionGo.transform: null); + m_lastApplyDataEntity.ApplyTransformByParent(parent, m_lastApplyDataGo ? m_lastApplyDataGo.transform: null); + + if (m_curSelectionObjCache.Transform) + { + m_curSelectionObjCache.SetData(m_curSelectionObjCache.Transform); + UpdateSelectionObjLabelColor(); + } } } @@ -148,11 +186,12 @@ namespace UguiToolkit.Editor if (!m_stageManager) return; var go = EditorUtility.InstanceIDToObject(instanceID) as GameObject; if (!go) return; - if (go == m_lastSelectionGo) + var goLabel = new GUIContent(go.name); + var goSize = EditorStyles.linkLabel.CalcSize(goLabel) + new Vector2(20, 0); + + if (go == m_lastApplyDataGo) { - var goLabel = new GUIContent(go.name); - var goSize = EditorStyles.linkLabel.CalcSize(goLabel) + new Vector2(20, 0); - var label = new GUIContent(" <-----"); + var label = new GUIContent(" <---------锚点"); var size = EditorStyles.linkLabel.CalcSize(label) + new Vector2(10, 0); var offsetRect = new Rect(selectionRect.position + new Vector2(goSize.x, 0), size); @@ -161,6 +200,24 @@ namespace UguiToolkit.Editor normal = new GUIStyleState() { textColor = Color.green }, }); } + + if (go.transform == m_curSelectionObjCache.Transform) + { + var label = new GUIContent(" <---------复原子节点"); + var size = EditorStyles.linkLabel.CalcSize(label) + new Vector2(10, 0); + var offsetRect = + new Rect(selectionRect.position + new Vector2(goSize.x, 0), size); + EditorGUI.LabelField(offsetRect, label, new GUIStyle() + { + normal = new GUIStyleState() { textColor = m_curSelectionObjLabelColor }, + }); + } + } + + [Button("TestSliceTexture")] + private void TestSliceTexture(string imagePath, string outputPath) + { + ImageUtils.SliceTexture(imagePath, outputPath); } private void CreateAllTextEntity() @@ -208,7 +265,7 @@ namespace UguiToolkit.Editor { if (m_background) DestroyImmediate(m_background.gameObject); - var go = new GameObject("__background__", typeof(RectTransform)); + var go = new GameObject(m_backgroundFileName, typeof(RectTransform)); UpdateHierarchyOfEntity(false, go); m_background = go.transform; m_background.SetParent(transform); @@ -228,7 +285,7 @@ namespace UguiToolkit.Editor imgTf.localRotation = Quaternion.identity; imgTf.localScale = Vector3.one; var img = imgGo.AddComponent(); - var imgPath = System.IO.Path.Join(m_panelCache.TargetImgDirPath, "__background__.png"); + var imgPath = System.IO.Path.Join(m_panelCache.TargetImgDirPath, m_BackgroundFileAllName); if (System.IO.File.Exists(imgPath)) { byte[] fileData = System.IO.File.ReadAllBytes(imgPath); @@ -244,7 +301,7 @@ namespace UguiToolkit.Editor UpdateHierarchyOfEntity(false, imgGo); } - private void UpdatePanelCache(string srcImgPath) + private void UpdatePanelCache(string srcImgPath, bool isSliceTexture = false) { float distanceDifference = GlobalManager.Instance.setting.distanceDifference; isCalcRotationScaleRunning = true; @@ -269,18 +326,22 @@ namespace UguiToolkit.Editor m_panelCache.AddRotScaleInfo(srcImgPath, rotScaleItemList); if (rotScaleItemList.Count > 0) OnSelectionChanged(); - }); + }, isSliceTexture: isSliceTexture); } private void OnSelectionChanged() { if (m_noSelection || !Selection.activeGameObject || Selection.gameObjects.Length > 1) return; - if (Selection.activeGameObject == m_curSelectionGo || !Selection.activeGameObject.activeSelf) return; + if (Selection.activeGameObject == m_curSelectionEntityGo || !Selection.activeGameObject.activeSelf) return; var activeGameObject = Selection.activeGameObject; if (activeGameObject.transform.parent == m_entityRoot || activeGameObject.transform.parent == m_background) return; - m_curSelectionGo = null; + m_curSelectionGo = activeGameObject; + m_curSelectionObjCache.SetData(activeGameObject.transform); + UpdateSelectionObjLabelColor(); + + m_curSelectionEntityGo = null; m_selectionEntities.Clear(); m_entityRoot.gameObject.SetActive(false); @@ -304,14 +365,14 @@ namespace UguiToolkit.Editor if (IsInside) { - if (m_lastSelectionGo && m_lastSelectionGo == activeGameObject) + if (m_lastApplyDataGo && m_lastApplyDataGo == activeGameObject) { - m_curSelectionGo = activeGameObject; + m_curSelectionEntityGo = activeGameObject; } } else { - m_curSelectionGo = activeGameObject; + m_curSelectionEntityGo = activeGameObject; } foreach (var imgEntity in m_imageEntities) @@ -367,14 +428,14 @@ namespace UguiToolkit.Editor if (IsInside) { - if (m_lastSelectionGo && m_lastSelectionGo == activeGameObject) + if (m_lastApplyDataGo && m_lastApplyDataGo == activeGameObject) { - m_curSelectionGo = activeGameObject; + m_curSelectionEntityGo = activeGameObject; } } else { - m_curSelectionGo = activeGameObject; + m_curSelectionEntityGo = activeGameObject; } foreach (var textEntity in m_textEntities) @@ -406,14 +467,14 @@ namespace UguiToolkit.Editor if (IsInside) { - if (m_lastSelectionGo && m_lastSelectionGo == activeGameObject) + if (m_lastApplyDataGo && m_lastApplyDataGo == activeGameObject) { - m_curSelectionGo = activeGameObject; + m_curSelectionEntityGo = activeGameObject; } } else { - m_curSelectionGo = activeGameObject; + m_curSelectionEntityGo = activeGameObject; } foreach (var imgEntity in m_imageEntities) @@ -431,11 +492,12 @@ namespace UguiToolkit.Editor { if (activeGameObject.TryGetComponent(out var image) && image.IsActive() && image.sprite) { - var srcImgPath = AssetDatabase.GetAssetPath(image.sprite); + var sprite = image.sprite; + var srcImgPath = AssetDatabase.GetAssetPath(sprite); if (!string.IsNullOrEmpty(srcImgPath) && !m_panelCache.HaveRotScaleInfo(srcImgPath) && !isCalcRotationScaleRunning) { - UpdatePanelCache(srcImgPath); + UpdatePanelCache(srcImgPath, sprite.border != Vector4.zero); //var srcImgDirPath = System.IO.Path.GetDirectoryName(srcImgPath); //AddCheckImgDirPath(srcImgDirPath); @@ -453,9 +515,28 @@ namespace UguiToolkit.Editor CreateAllEntity(); InitBackground(); + + HashSet slicePaths = new HashSet(); + foreach (var entity in m_imageEntities) + { + var elementInfo = entity.ElementInfo; + if (elementInfo.HaveSlice) + { + var imageName = Path.GetFileName(elementInfo.imgPath); + slicePaths.Add(imageName); + } + } + targetImages = new (); targetPaths = new (); - ImageUtils.LoadPngImagesFromFolder(panelCache.TargetImgDirPath, targetImages, targetPaths); + ImageUtils.LoadPngImagesFromFolder(panelCache.TargetImgDirPath, targetImages, targetPaths, + (imagePath) => { + var imageName = Path.GetFileName(imagePath); + if (m_BackgroundFileAllName == imageName) return true; + if (slicePaths.Contains(imageName)) return true; + + return false; + }); } private void OnUpdateBackgroundShow(bool show) @@ -574,6 +655,48 @@ namespace UguiToolkit.Editor m_entityRoot.gameObject.SetActive(false); } + + class SelectionObjCache + { + Transform m_transform; + + List childrens = new (); + List originalPositions = new(); + + public Transform Transform => m_transform; + + public void SetData(Transform tf) + { + ClearData(); + m_transform = tf; + + for (int i = 0; i < tf.childCount; i++) + { + var rt = tf.GetChild(i) as RectTransform; + childrens.Add(rt); + originalPositions.Add(rt.position); + } + } + + public void ClearData() + { + childrens.Clear(); + originalPositions.Clear(); + originalPositions.Clear(); + + m_transform = null; + } + + public void RecoverData() + { + // 恢复子节点的全局位置和大小 + for (int i = 0; i < childrens.Count; i++) + { + Undo.RecordObject(childrens[i], "RecoverData"); + childrens[i].position = originalPositions[i]; + } + } + } } } #endif \ No newline at end of file diff --git a/Assets/Editor/Windows/EditWindow.cs b/Assets/Editor/Windows/EditWindow.cs index 5f3e56b..466112c 100644 --- a/Assets/Editor/Windows/EditWindow.cs +++ b/Assets/Editor/Windows/EditWindow.cs @@ -34,6 +34,11 @@ namespace UguiToolkit.Editor.Windows /// public event Action effectLastApplyTransform; + /// + /// Ӧϴα任 + /// + public event Action recoverDataSelectionObjCache; + [SerializeField, HideInInspector] private bool m_showHierarchyOfEntityChange = false; @@ -80,7 +85,7 @@ namespace UguiToolkit.Editor.Windows [LabelText("ҪӦñ任Ķ"), SerializeField] private Transform m_targetTransform; - [Button("Ӧϴα任 (SPACE)")] + [Button("Ӧϴα任 (SPACE)")] private void EffectLastApplyTransform() { if (m_targetTransform) @@ -89,6 +94,12 @@ namespace UguiToolkit.Editor.Windows } } + [Button("ָӽڵλ (CTRL + SPACE)")] + private void RecoverDataSelectionObjCache() + { + recoverDataSelectionObjCache?.Invoke(); + } + public override string GettitleContent() { return "ֱ༭"; diff --git a/README.md b/README.md index 9f5ee2f..a66980f 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,34 @@ # C1 Ugui拼接助手 ## 简介 -> 该工具主要作用是优化工作流,提高重复劳动的工作效率,以下是当前版本已经实现的功能: -> 1. psd 导出自动切图,并处理九宫格 + +> 该工具主要作用是提高效果图还原度和提高拼接效率,以下是当前版本已经实现的功能: +> 1. psd 端自动切图,并处理九宫格 > 2. unity拼接时,对psd上所标记的像素图层进行吸附,设置位置和旋转、缩放 > 3. unity拼接时,对psd上所标记的文本图层进行吸附,设置位置和旋转、缩放,并设置字体、字体大小、字体颜色、描边等 > 4. unity拼接时,对公共组件预制体进行设置位置和旋转、缩放 +## 展示 + +![GIF 2024-12-17 10-59-36](F:\c1workspace\svn\__workspace__dev__\client\PackagesSource\com.txcombo.c1.ugui-toolkit\.res\展示.gif) + ## 工作流 + ![](./.res/流程图.png) ## 如何操作 ### psd数据导出 #### 对图层名使用关键字标记 -1. `$<图层名>` : 图片切图导出 例如:$img_tools -2. `%<图层名>` : 图片吸附用 例如:%img_tools -3. `%<文本图层名>` : 文本吸附用 例如:%text_tools -4. `<图层名>@九宫格` : 九宫格切图 例如:$img_tools@九宫格 -4. `<图层名>@透明度=100` : 透明度属性 例如:$img_tools@透明度=50 +1. `$<图层名>` : 图片切图导出并吸附 例如:$img_tools ,参数:九宫格:`<图层名>@九宫格` +2. `%<图层名>` : 仅图片吸附用 例如:%img_tools,参数:九宫格:`<图层名>@九宫格` +3. `%<文本图层名>` : 仅文本吸附用 例如:%text_tools 5. `<图层名>@预制体=<资源唯一名称>` : 引用通用组件预制体 例如:领取龙币@预制体=btn_common_yellow_large #### 导出数据 -> psd文件名不支持中文 - 运行指定脚本进行导出 ![](./.res/ps导出入口.png) 选中项目中脚本 -`client\PackagesSource\com.txcombo.c1.ugui-toolkit\.PhotoshopScript\JSZXPsd2Unity\PSD导出Unity.js` +`client\toolchains\c1_ugui_toolkit\.PhotoshopScript\JSZXPsd2Unity\PSD导出Unity.js` 执行完毕后会自动弹出目录 ![](./.res/弹出目录.png) @@ -39,13 +41,25 @@ ![img](F:\c1workspace\svn\__workspace__dev__\client\PackagesSource\com.txcombo.c1.ugui-toolkit\.res\开启助手.png) -#### 如何吸附 +#### 选中image和text、预制体后,自动吸附 ##### 图片 - ![](./.res/吸附展示.gif) -##### 文本 +##### 一键创建所有文本 ![](./.res/创建text.png) ![](./.res/创建text后的结果.png) -##### 预制体 -1. 选中预制体节点后,会显示界面所有可供创建的预制体预览 -2. 将鼠标光标放入预制体预览框,会自动创建预制体 \ No newline at end of file + +##### 一键创建所有预制体 +![img](F:\c1workspace\svn\__workspace__dev__\client\PackagesSource\com.txcombo.c1.ugui-toolkit\.res\创建预制体.png) + +#### 进阶操作 +##### 父节点应用子节点变换 + +吸附完成后,选中父节点按下“空格” + +![GIF 2024-12-12 10-19-28](F:\c1workspace\svn\__workspace__dev__\client\PackagesSource\com.txcombo.c1.ugui-toolkit\.res\父节点应用子节点变换.gif) + +##### 修改父节点边框后复原子节点位置 + +对父节点边框进行调整后,按下“Ctrl + 空格” + +![GIF 2024-12-12 10-25-00](F:\c1workspace\svn\__workspace__dev__\client\PackagesSource\com.txcombo.c1.ugui-toolkit\.res\复原子节点位置.gif)