using System.Globalization; using UnityEditor; using UnityEngine; using UnityEngine.UI; namespace AppUI.Editor { /// /// 设计稿尺寸与 Canvas Reference Resolution 之间的换算工具窗口。 /// public class DesignResolutionConverterWindow : EditorWindow { private const string PrefDesignW = "AppUI.DesignResolutionConverter.DesignW"; private const string PrefDesignH = "AppUI.DesignResolutionConverter.DesignH"; private const string PrefCanvasW = "AppUI.DesignResolutionConverter.CanvasW"; private const string PrefCanvasH = "AppUI.DesignResolutionConverter.CanvasH"; private Vector2 _designArtboard = new Vector2(DesignResolutionConverter.DefaultDesignWidth, DesignResolutionConverter.DefaultDesignHeight); private Vector2 _canvasReference = new Vector2(DesignResolutionConverter.DefaultDesignWidth, DesignResolutionConverter.DefaultDesignHeight); private Vector2 _sampleDesignSize = new Vector2(100f, 40f); private Vector2 _sampleCanvasSize = new Vector2(100f, 40f); private const string PrefScalarValue = "AppUI.DesignResolutionConverter.ScalarValue"; private const string PrefScalarDir = "AppUI.DesignResolutionConverter.ScalarDir"; private const string PrefScalarAxis = "AppUI.DesignResolutionConverter.ScalarAxis"; private float _scalarInput = 100f; private int _scalarDirection; // 0: 设计稿→Canvas, 1: Canvas→设计稿 private int _scalarAxis; // 0: 宽度轴 scaleX, 1: 高度轴 scaleY private Vector2 _scroll; [MenuItem("Tools/AppUI/设计稿与 Canvas 分辨率换算")] public static void Open() { var win = GetWindow(); win.titleContent = new GUIContent("设计稿 ↔ Canvas 换算"); win.minSize = new Vector2(380f, 420f); } private void OnEnable() { _designArtboard.x = EditorPrefs.GetFloat(PrefDesignW, DesignResolutionConverter.DefaultDesignWidth); _designArtboard.y = EditorPrefs.GetFloat(PrefDesignH, DesignResolutionConverter.DefaultDesignHeight); _canvasReference.x = EditorPrefs.GetFloat(PrefCanvasW, DesignResolutionConverter.DefaultDesignWidth); _canvasReference.y = EditorPrefs.GetFloat(PrefCanvasH, DesignResolutionConverter.DefaultDesignHeight); _scalarInput = EditorPrefs.GetFloat(PrefScalarValue, 100f); _scalarDirection = EditorPrefs.GetInt(PrefScalarDir, 0); _scalarAxis = EditorPrefs.GetInt(PrefScalarAxis, 0); } private void SavePrefs() { EditorPrefs.SetFloat(PrefDesignW, _designArtboard.x); EditorPrefs.SetFloat(PrefDesignH, _designArtboard.y); EditorPrefs.SetFloat(PrefCanvasW, _canvasReference.x); EditorPrefs.SetFloat(PrefCanvasH, _canvasReference.y); } private void OnGUI() { _scroll = EditorGUILayout.BeginScrollView(_scroll); EditorGUILayout.LabelField("基准尺寸", EditorStyles.boldLabel); EditorGUILayout.HelpBox( "设计稿宽高:你在 Figma / Sketch 等中的画板像素尺寸(默认 402×874)。\n" + "Canvas 参考分辨率:当前场景 CanvasScaler 的 Reference Resolution。", MessageType.Info); EditorGUI.BeginChangeCheck(); _designArtboard = EditorGUILayout.Vector2Field("设计稿宽高 (px)", _designArtboard); _canvasReference = EditorGUILayout.Vector2Field("Canvas Reference Resolution", _canvasReference); if (EditorGUI.EndChangeCheck()) SavePrefs(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("从选中对象读取 CanvasScaler")) PullFromSelection(); if (GUILayout.Button("将设计稿尺寸填到 Canvas(与左栏相同)")) { _canvasReference = _designArtboard; SavePrefs(); } EditorGUILayout.EndHorizontal(); bool valid = _designArtboard.x > 0f && _designArtboard.y > 0f && _canvasReference.x > 0f && _canvasReference.y > 0f; if (!valid) EditorGUILayout.HelpBox("设计稿与 Canvas 的宽高均需大于 0。", MessageType.Warning); Vector2 scale = valid ? DesignResolutionConverter.GetScaleFactors(_designArtboard, _canvasReference) : Vector2.one; EditorGUILayout.Space(6f); EditorGUILayout.LabelField("轴向缩放系数", EditorStyles.boldLabel); if (valid) { EditorGUILayout.LabelField($"scaleX = {_canvasReference.x:F4} / {_designArtboard.x:F4} = {scale.x:F6}"); EditorGUILayout.LabelField($"scaleY = {_canvasReference.y:F4} / {_designArtboard.y:F4} = {scale.y:F6}"); } EditorGUILayout.Space(10f); EditorGUILayout.LabelField("单值换算", EditorStyles.boldLabel); EditorGUILayout.HelpBox("输入一个数,按所选轴向换算;结果可复制到剪贴板(Inspector / 代码中粘贴)。", MessageType.None); EditorGUI.BeginChangeCheck(); _scalarDirection = GUILayout.Toolbar(_scalarDirection, new[] { "设计稿 → Canvas", "Canvas → 设计稿" }); _scalarAxis = GUILayout.Toolbar(_scalarAxis, new[] { "按宽度轴 (scaleX)", "按高度轴 (scaleY)" }); _scalarInput = EditorGUILayout.FloatField(_scalarDirection == 0 ? "输入值 (设计稿 px)" : "输入值 (Canvas 单位)", _scalarInput); if (EditorGUI.EndChangeCheck()) { EditorPrefs.SetFloat(PrefScalarValue, _scalarInput); EditorPrefs.SetInt(PrefScalarDir, _scalarDirection); EditorPrefs.SetInt(PrefScalarAxis, _scalarAxis); } if (valid) { bool useWidthAxis = _scalarAxis == 0; float scalarResult = _scalarDirection == 0 ? DesignResolutionConverter.DesignLengthToCanvas(_scalarInput, useWidthAxis, _designArtboard, _canvasReference) : DesignResolutionConverter.CanvasLengthToDesign(_scalarInput, useWidthAxis, _designArtboard, _canvasReference); string resultLabel = _scalarDirection == 0 ? "Canvas 单位" : "设计稿 px"; EditorGUILayout.LabelField($"换算结果 ({resultLabel})", EditorStyles.boldLabel); string resultText = scalarResult.ToString("G9", CultureInfo.InvariantCulture); EditorGUILayout.SelectableLabel(resultText, GUILayout.Height(18f)); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("复制结果")) { EditorGUIUtility.systemCopyBuffer = resultText; ShowNotification(new GUIContent("已复制到剪贴板")); } if (GUILayout.Button("复制(保留 4 位小数)")) { string rounded = scalarResult.ToString("F4", CultureInfo.InvariantCulture); EditorGUIUtility.systemCopyBuffer = rounded; ShowNotification(new GUIContent("已复制(F4)")); } EditorGUILayout.EndHorizontal(); } EditorGUILayout.Space(10f); EditorGUILayout.LabelField("尺寸换算", EditorStyles.boldLabel); _sampleDesignSize = EditorGUILayout.Vector2Field("设计稿上的宽×高 (px)", _sampleDesignSize); if (valid) { Vector2 toCanvas = DesignResolutionConverter.DesignSizeToCanvasUnits(_sampleDesignSize, _designArtboard, _canvasReference); EditorGUILayout.SelectableLabel($"→ Canvas 单位: ({toCanvas.x:F4}, {toCanvas.y:F4})", GUILayout.Height(18f)); if (GUILayout.Button("将换算结果应用到选中 RectTransform(宽×高)")) ApplyCanvasSizeToSelection(toCanvas); } EditorGUILayout.Space(6f); _sampleCanvasSize = EditorGUILayout.Vector2Field("Canvas 上的宽×高", _sampleCanvasSize); if (valid) { Vector2 toDesign = DesignResolutionConverter.CanvasUnitsToDesignSize(_sampleCanvasSize, _designArtboard, _canvasReference); EditorGUILayout.SelectableLabel($"→ 设计稿 px: ({toDesign.x:F4}, {toDesign.y:F4})", GUILayout.Height(18f)); if (GUILayout.Button("将当前 Canvas 宽×高应用到选中 RectTransform")) ApplyCanvasSizeToSelection(_sampleCanvasSize); } EditorGUILayout.Space(10f); EditorGUILayout.LabelField("文档", EditorStyles.boldLabel); if (GUILayout.Button("打开说明文档 (AppUI/Docs)")) { const string docPath = "Assets/AppUI/Docs/UI设计稿与Canvas分辨率换算.md"; var obj = AssetDatabase.LoadAssetAtPath(docPath); if (obj != null) AssetDatabase.OpenAsset(obj); else EditorUtility.DisplayDialog("未找到", $"请确认文件存在:{docPath}", "确定"); } EditorGUILayout.EndScrollView(); } private void PullFromSelection() { GameObject go = Selection.activeGameObject; if (go == null) { EditorUtility.DisplayDialog("未选中", "请选中带有 CanvasScaler 的 GameObject(通常为 Canvas)。", "确定"); return; } var scaler = go.GetComponent(); if (scaler == null) scaler = go.GetComponentInParent(); if (scaler == null) { EditorUtility.DisplayDialog("未找到 CanvasScaler", "选中对象及其父级上都没有 CanvasScaler。", "确定"); return; } _canvasReference = scaler.referenceResolution; SavePrefs(); ShowNotification(new GUIContent("已读取 Reference Resolution")); } /// /// 使用 RectTransform.SetSizeWithCurrentAnchors,在保持当前锚点的前提下写入宽、高。 /// private void ApplyCanvasSizeToSelection(Vector2 sizeCanvasUnits) { GameObject[] gos = Selection.gameObjects; if (gos == null || gos.Length == 0) { EditorUtility.DisplayDialog("未选中", "请选中一个或多个带 RectTransform 的 UI 对象。", "确定"); return; } int applied = 0; foreach (GameObject go in gos) { var rt = go.GetComponent(); if (rt == null) continue; Undo.RecordObject(rt, "Apply UI size from design converter"); rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, sizeCanvasUnits.x); rt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, sizeCanvasUnits.y); EditorUtility.SetDirty(rt); if (PrefabUtility.IsPartOfPrefabInstance(rt)) PrefabUtility.RecordPrefabInstancePropertyModifications(rt); applied++; } if (applied == 0) EditorUtility.DisplayDialog("无 RectTransform", "选中对象中没有任何 RectTransform 组件。", "确定"); else ShowNotification(new GUIContent($"已更新 {applied} 个对象的宽×高")); } } }