#if UNITY_EDITOR using Sirenix.OdinInspector; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using TMPro; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; 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_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; private List m_prefabEntities; private List m_selectionEntities; [LabelText("脱离选择控制"), ShowInInspector, SerializeField] private bool m_noSelection; private StageManager m_stageManager; private bool m_useTMP; private EditWindow editWindow; private bool isCalcRotationScaleRunning = false; private List targetImages; private List targetPaths; public Transform Background => m_background; private void OnEnable() { var globalMng = GlobalManager.Instance; m_useTMP = globalMng.setting.useTMP; Selection.selectionChanged += OnSelectionChanged; m_stageManager = StageManager.Instance; editWindow = EditWindow.Window; if (editWindow != null) { editWindow.showHierarchyOfEntityChanged += OnUpdateHierarchyOfEntityAllEntity; editWindow.showBackgroundChanged += OnUpdateBackgroundShow; editWindow.createAllTextEntity += CreateAllTextEntity; editWindow.createAllPrefabEntity += CreateAllPrefabEntity; editWindow.effectLastApplyTransform += EffectLastApplyTransform; editWindow.recoverDataSelectionObjCache += OnRecoverDataSelectionObjCache; } EditorApplication.hierarchyWindowItemOnGUI += HandleHierarchyWindowItemOnGUI; PanelHelper.jumpOfGameCameraByCurPos += EffectLastApplyTransform; PanelHelper.recoverDataSelectionObjCache += RecoverDataSelectionObjCache; ImageUtils.SetDebugMode(true); } private void OnDisable() { Selection.selectionChanged -= OnSelectionChanged; if (editWindow != null) { editWindow.showHierarchyOfEntityChanged -= OnUpdateHierarchyOfEntityAllEntity; editWindow.showBackgroundChanged -= OnUpdateBackgroundShow; 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) GlobalManager.Instance.SaveCache(m_stageManager.PrefabAsset, m_panelCache); } private void Update() { // 检测是否到达可选实例矩形内部 if (m_selectionEntities != null && m_curSelectionEntityGo) { if (m_lastApplyDataGo && m_lastApplyDataGo == m_curSelectionEntityGo) { if (m_lastApplyDataEntity != null && !m_lastApplyDataEntity.IsInside(m_lastApplyDataGo.transform)) { m_lastApplyDataGo = null; m_lastApplyDataEntity = null; } return; } foreach (var entity in m_selectionEntities) { var tf = m_curSelectionEntityGo.transform; if (entity.IsInside(tf)) { entity.ApplyTransform(tf); if (PrefabEntity.IsPrefab(tf.gameObject)) { entity.ApplyData(tf); } else if (tf.TryGetComponent(out var image)) { entity.ApplyData(image); } else if (tf.TryGetComponent(out var text)) { entity.ApplyData(text); } else if (tf.TryGetComponent(out var temp)) { entity.ApplyData(temp); } m_lastApplyDataGo = m_curSelectionEntityGo; m_lastApplyDataEntity = entity; Selection.activeGameObject = null; break; } } } } 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_lastApplyDataEntity != null) { m_lastApplyDataEntity.ApplyTransformByParent(parent, m_lastApplyDataGo ? m_lastApplyDataGo.transform: null); if (m_curSelectionObjCache.Transform) { m_curSelectionObjCache.SetData(m_curSelectionObjCache.Transform); UpdateSelectionObjLabelColor(); } } } private void HandleHierarchyWindowItemOnGUI(int instanceID, Rect selectionRect) { if (!m_stageManager) return; var go = EditorUtility.InstanceIDToObject(instanceID) as GameObject; if (!go) return; var goLabel = new GUIContent(go.name); var goSize = EditorStyles.linkLabel.CalcSize(goLabel) + new Vector2(20, 0); if (go == m_lastApplyDataGo) { 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 = 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() { var root = m_stageManager.PrefabContentsRoot; var textsTf = root.transform.Find("__texts__"); if (textsTf) DestroyImmediate(textsTf.gameObject); if (m_textEntities == null) return; var textsGo = new GameObject("__texts__", typeof(RectTransform)); textsGo.transform.parent = root.transform; Type textType = m_useTMP ? typeof(TextMeshProUGUI) : typeof(Text); foreach (var textEntity in m_textEntities) { var newText = new GameObject(textEntity.gameObject.name, typeof(RectTransform), textType); newText.transform.parent = textsGo.transform; textEntity.ApplyData(newText.GetComponent(textType)); textEntity.ApplyTransform(newText.transform); } } private void CreateAllPrefabEntity() { var root = m_stageManager.PrefabContentsRoot; var prefabsTf = root.transform.Find("__prefabs__"); if (prefabsTf) DestroyImmediate(prefabsTf.gameObject); if (m_textEntities == null) return; var prefabsGo = new GameObject("__prefabs__", typeof(RectTransform)); prefabsGo.transform.parent = root.transform; foreach (var prefabEntity in m_prefabEntities) { var asset = prefabEntity.GetPrefabAsset(PrefabEntity.GetCommonDirPath()); if (asset != null) { var go = GameObject.Instantiate(asset, prefabsGo.transform); go.name = asset.name; prefabEntity.ApplyData(go.transform); } } } private void InitBackground() { if (m_background) DestroyImmediate(m_background.gameObject); var go = new GameObject(m_backgroundFileName, typeof(RectTransform)); UpdateHierarchyOfEntity(false, go); m_background = go.transform; m_background.SetParent(transform); m_background.localPosition = Vector3.zero; m_background.localRotation = Quaternion.identity; m_background.localScale = Vector3.one; var canvas = m_background.gameObject.AddComponent(); canvas.pixelPerfect = true; canvas.overrideSorting = true; canvas.sortingOrder = -100; var imgGo = new GameObject("_", typeof(RectTransform)); var imgTf = imgGo.transform; imgTf.SetParent(m_background); imgTf.localPosition = Vector3.zero; imgTf.localRotation = Quaternion.identity; imgTf.localScale = Vector3.one; var img = imgGo.AddComponent(); var imgPath = System.IO.Path.Join(m_panelCache.TargetImgDirPath, m_BackgroundFileAllName); if (System.IO.File.Exists(imgPath)) { byte[] fileData = System.IO.File.ReadAllBytes(imgPath); Texture2D texture = new Texture2D(2, 2); texture.LoadImage(fileData); // 加载图片数据到Texture2D Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); img.sprite = sprite; img.color = new Color(1, 1, 1, 0.2f); img.SetNativeSize(); } UpdateHierarchyOfEntity(false, imgGo); } private void UpdatePanelCache(string srcImgPath, bool isSliceTexture = false) { float distanceDifference = GlobalManager.Instance.setting.distanceDifference; isCalcRotationScaleRunning = true; List rotScaleItemList = new(); Debug.Log("GetRotationScaleAsync: " + srcImgPath); _ = ImageUtils.ProcessFolderAsync(targetImages, srcImgPath, distanceDifference, (index, result) => { if (!m_stageManager) return; rotScaleItemList.Add(new() { imgPath = ImageUtils.FormatImgFilePath(targetPaths[index]), rotiation = (float)result.Item1, scale = new float2((float)result.Item2.Item1, (float)result.Item2.Item1), similarityCalc = result.Item3 }); }, () => { Debug.Log("End GetRotationScaleAsync: " + srcImgPath); isCalcRotationScaleRunning = false; if (!m_stageManager) return; 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_curSelectionEntityGo || !Selection.activeGameObject.activeSelf) return; var activeGameObject = Selection.activeGameObject; if (activeGameObject.transform.parent == m_entityRoot || activeGameObject.transform.parent == m_background) return; m_curSelectionGo = activeGameObject; m_curSelectionObjCache.SetData(activeGameObject.transform); UpdateSelectionObjLabelColor(); m_curSelectionEntityGo = null; m_selectionEntities.Clear(); m_entityRoot.gameObject.SetActive(false); if (Selection.activeGameObject != null && m_panelCache != null) { if (PrefabEntity.IsCommonPrefab(activeGameObject)) { bool IsInside = false; m_entityRoot.gameObject.SetActive(true); foreach (var prefabEntity in m_prefabEntities) { prefabEntity.ShowSelectionImg(true); prefabEntity.gameObject.SetActive(true); prefabEntity.SetOriginalMatrix(activeGameObject.transform); m_selectionEntities.Add(prefabEntity); if (!IsInside && prefabEntity.IsInside(activeGameObject.transform)) IsInside = true; } if (IsInside) { if (m_lastApplyDataGo && m_lastApplyDataGo == activeGameObject) { m_curSelectionEntityGo = activeGameObject; } } else { m_curSelectionEntityGo = activeGameObject; } foreach (var imgEntity in m_imageEntities) { imgEntity.gameObject.SetActive(false); } foreach (var textEntity in m_textEntities) { textEntity.gameObject.SetActive(false); } } else if (activeGameObject.TryGetComponent(out var image)) { if (image.sprite) { var srcImgPath = AssetDatabase.GetAssetPath(image.sprite); if (!string.IsNullOrEmpty(srcImgPath) && m_panelCache.HaveRotScaleInfo(srcImgPath, out var rotScaleInfoItems)) { m_entityRoot.gameObject.SetActive(true); bool isFind; bool IsInside = false; foreach (var imgEntity in m_imageEntities) { isFind = false; foreach (var rotScale in rotScaleInfoItems) { var imgInfo = imgEntity.ElementInfo; if ((imgInfo.HaveSlice && imgInfo.imgSlicePath == rotScale.imgPath) || imgInfo.imgPath == rotScale.imgPath) { if (rotScale.similarityCalc) { imgEntity.SetTransform(0, new Unity.Mathematics.float2(1, 1), true); } else { imgEntity.SetTransform(rotScale.rotiation, rotScale.scale, false); } imgEntity.InternalApplyTransform(imgEntity.transform); imgEntity.ShowSelectionImg(true); imgEntity.SetOriginalMatrix(activeGameObject.transform); m_selectionEntities.Add(imgEntity); if (!IsInside && imgEntity.IsInside(activeGameObject.transform)) IsInside = true; isFind = true; break; } } imgEntity.gameObject.SetActive(isFind); } if (IsInside) { if (m_lastApplyDataGo && m_lastApplyDataGo == activeGameObject) { m_curSelectionEntityGo = activeGameObject; } } else { m_curSelectionEntityGo = activeGameObject; } foreach (var textEntity in m_textEntities) { textEntity.gameObject.SetActive(false); } foreach (var prefabEntity in m_prefabEntities) { prefabEntity.gameObject.SetActive(false); } } } } else if (activeGameObject.TryGetComponent(out var _) || activeGameObject.TryGetComponent(out var _)) { bool IsInside = false; m_entityRoot.gameObject.SetActive(true); foreach (var textEntity in m_textEntities) { textEntity.ShowSelectionImg(true); textEntity.gameObject.SetActive(true); textEntity.SetOriginalMatrix(activeGameObject.transform); m_selectionEntities.Add(textEntity); if (!IsInside && textEntity.IsInside(activeGameObject.transform)) IsInside = true; } if (IsInside) { if (m_lastApplyDataGo && m_lastApplyDataGo == activeGameObject) { m_curSelectionEntityGo = activeGameObject; } } else { m_curSelectionEntityGo = activeGameObject; } foreach (var imgEntity in m_imageEntities) { imgEntity.gameObject.SetActive(false); } foreach (var prefabEntity in m_prefabEntities) { prefabEntity.gameObject.SetActive(false); } } } if (activeGameObject.transform.parent != m_entityRoot && activeGameObject.transform.parent != m_background) { if (activeGameObject.TryGetComponent(out var image) && image.IsActive() && image.sprite) { var sprite = image.sprite; var srcImgPath = AssetDatabase.GetAssetPath(sprite); if (!string.IsNullOrEmpty(srcImgPath) && !m_panelCache.HaveRotScaleInfo(srcImgPath) && !isCalcRotationScaleRunning) { UpdatePanelCache(srcImgPath, sprite.border != Vector4.zero); //var srcImgDirPath = System.IO.Path.GetDirectoryName(srcImgPath); //AddCheckImgDirPath(srcImgDirPath); } } } } public void Init(PanelCache panelCache) { this.m_panelCache = panelCache; this.m_stageManager = StageManager.Instance; // 创建所有实例 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, (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) { if (m_background) { m_background.gameObject.SetActive(show); } } private void OnUpdateHierarchyOfEntityAllEntity(bool show) { UpdateHierarchyOfEntity(show, m_entityRoot.gameObject); UpdateHierarchyOfEntity(show, m_background.gameObject); foreach (Transform entity in m_background.transform) UpdateHierarchyOfEntity(show, entity.gameObject); foreach (var entity in m_imageEntities) UpdateHierarchyOfEntity(show, entity.gameObject); foreach (var entity in m_textEntities) UpdateHierarchyOfEntity(show, entity.gameObject); foreach (var entity in m_prefabEntities) UpdateHierarchyOfEntity(show, entity.gameObject); } private void UpdateHierarchyOfEntity(in bool show, in GameObject entity) { EntityHelper.UpdateHierarchyAndSceneVisibilityOfEntity(show, entity.gameObject); } private void CreateAllEntity() { if (this.m_panelCache == null) return; var go = new GameObject("__entityRoot__", typeof(RectTransform)); UpdateHierarchyOfEntity(false, go); m_entityRoot = go.transform; m_entityRoot.SetParent(transform); m_entityRoot.localPosition = Vector3.zero; m_entityRoot.localRotation = Quaternion.identity; m_entityRoot.localScale = Vector3.one; var canvas = m_entityRoot.gameObject.AddComponent(); canvas.pixelPerfect = true; canvas.overrideSorting = true; canvas.sortingOrder = 200; m_imageEntities = new(m_panelCache.layoutInfo.Count); m_textEntities = new(m_panelCache.layoutInfo.Count); m_selectionEntities = new(m_panelCache.layoutInfo.Count); m_prefabEntities = new(m_panelCache.layoutInfo.Count); foreach (var elementInfo in m_panelCache.GetLayoutElementInfos()) { var imgInfo = elementInfo as LayoutInfo.ImageInfo; if (imgInfo != null) // Image { go = new GameObject(imgInfo.name, typeof(RectTransform)); var entity = go.AddComponent(); entity.transform.SetParent(m_entityRoot); entity.transform.SetSiblingIndex(0); entity.SetData(imgInfo); entity.InitPreview(); m_imageEntities.Add(entity); UpdateHierarchyOfEntity(false, entity.gameObject); continue; } var prefabInfo = elementInfo as LayoutInfo.PrefabInfo; if (prefabInfo != null) // 预制体 { go = new GameObject(prefabInfo.name, typeof(RectTransform)); var entity = go.AddComponent(); entity.transform.SetParent(m_entityRoot); entity.transform.SetSiblingIndex(0); entity.SetData(prefabInfo); entity.InitPreview(); m_prefabEntities.Add(entity); UpdateHierarchyOfEntity(false, entity.gameObject); continue; } var textInfo = elementInfo as LayoutInfo.TextInfo; if (textInfo != null) // Text { if (m_useTMP) { go = new GameObject(textInfo.text, typeof(RectTransform)); var entity = go.AddComponent(); entity.transform.SetParent(m_entityRoot); entity.transform.SetSiblingIndex(0); entity.SetData(textInfo); entity.InitPreview(); m_textEntities.Add(entity); UpdateHierarchyOfEntity(false, entity.gameObject); continue; } else { go = new GameObject(textInfo.text, typeof(RectTransform)); var entity = go.AddComponent(); entity.transform.SetParent(m_entityRoot); entity.transform.SetSiblingIndex(0); entity.SetData(textInfo); entity.InitPreview(); m_textEntities.Add(entity); UpdateHierarchyOfEntity(false, entity.gameObject); continue; } } } 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