原理:
用相机拍摄预制体,生成一张预览图
需要注意:
1) GameObject.Instantiate实例化预制体后,要等待几帧才能获取到正确的Transform参数,刚创建就获取可能是不对的。
2) 预制体预览有两种实现方式,一种是继承Editor然后重写OnPreviewGUI;另一种是继承ObjectPreview然后重写OnPreviewGUI;
这边选择后面这种,因为第1种,对于场景下的嵌套预制体,预览界面不起作用。
3) 如果预制体的根节点有Image组件,只有在Project窗口中选择预制体时才能预览;其他方式只有Image图片的预览,目前未找到解决方式,猜测可能ImageEditor的OnPreviewGUI优先级更高?
#if UNITY_EDITOR using System.IO; using UnityEditor; using UnityEditor.Experimental.SceneManagement; using UnityEngine; [CustomPreview(typeof(GameObject))] public class PrefabPreview : ObjectPreview { private string m_UGUIPrefabPath; private int m_Frame = -1; private string m_PreviewFilePath; private void OnEditorUpdate() { if (m_Frame > 0) { m_Frame--; if (0 == m_Frame) { var previewGo = GameObject.Find("___preview"); if (null != previewGo) { if (string.IsNullOrEmpty(m_PreviewFilePath)) { Debug.LogError($"m_PreviewFilePath none, PreviewEditor Change?"); GameObject.DestroyImmediate(previewGo); } else { previewGo.name = "preview_ready"; var tex = AssetPreviewHelper.GetAssetPreview(previewGo); AssetPreviewHelper.SaveTexture2D(tex, m_PreviewFilePath); } } m_Frame = -1; EditorApplication.update -= OnEditorUpdate;
AssetPreviewHelper.RepaintInspector(); } } } private bool IsUGUIPrefab() { m_UGUIPrefabPath = ""; var prefabPath = AssetDatabase.GetAssetPath(target); var targetGo = (GameObject)target; if (string.IsNullOrEmpty(prefabPath)) { if (PrefabUtility.IsAnyPrefabInstanceRoot(targetGo)) //场景中的嵌套预制体 prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(targetGo); else { var stage = PrefabStageUtility.GetCurrentPrefabStage(); if (null != stage) //预制体编辑界面 prefabPath = stage.prefabAssetPath; } } if (string.IsNullOrEmpty(prefabPath)) return false; var rtf = targetGo.GetComponent<RectTransform>(); if (null != rtf) { m_UGUIPrefabPath = prefabPath; return true; } return false; } public override bool HasPreviewGUI() { if (IsUGUIPrefab()) return true; return base.HasPreviewGUI(); } public override void OnPreviewGUI(Rect r, GUIStyle background) { if (string.IsNullOrEmpty(m_UGUIPrefabPath)) { base.OnPreviewGUI(r, background); return; } var preview = AssetPreview.GetAssetPreview(target); if (null == preview) { var guid = AssetDatabase.AssetPathToGUID(m_UGUIPrefabPath); var previewFilePath = Path.Combine("_Preview", $"{guid}.png"); if (File.Exists(previewFilePath)) { var imgBytes = File.ReadAllBytes(previewFilePath); Texture2D tex = new Texture2D(2, 2); tex.LoadImage(imgBytes); preview = tex; } } if (preview) PreviewEditor.OnGUI_PreviewImage(r, preview); } //生成预览图 private void RefreshPreviewImage(string previewFilePath) { var previewGo = GameObject.Find("___preview"); if (null != previewGo) { Debug.LogWarning("last preview not finish, force terminate!"); GameObject.DestroyImmediate(previewGo); } previewGo = GameObject.Instantiate((GameObject)target); //previewGo.hideFlags = HideFlags.HideAndDontSave; previewGo.name = "___preview"; previewGo.transform.position = new Vector3(-1000, -1000, -1000); //远一点, 这样才看不到 m_Frame = 2; //等待几帧 m_PreviewFilePath = previewFilePath; EditorApplication.update -= OnEditorUpdate; EditorApplication.update += OnEditorUpdate; } public override void OnPreviewSettings() { base.OnPreviewSettings(); if (GUILayout.Button("刷新", "preButton")) { var guid = AssetDatabase.AssetPathToGUID(m_UGUIPrefabPath); var previewFilePath = Path.Combine("_Preview", $"{guid}.png"); RefreshPreviewImage(previewFilePath); } } } #endif
场景嵌套预制体
预制体编辑界面
资源管理其中选择预制体时
#if UNITY_EDITOR using System; using System.IO; using System.Reflection; using UnityEngine; using UnityEngine.UI; public static class AssetPreviewHelper { public static Texture2D GetAssetPreview(GameObject cloneGo) { GameObject rootGo = null; var cameraGo = new GameObject("render camera", typeof(Camera)); var cameraTransform = cameraGo.transform; var renderCamera = cameraGo.GetComponent<Camera>(); renderCamera.backgroundColor = new Color(0.8f, 0.8f, 0.8f, 0.1f); renderCamera.clearFlags = CameraClearFlags.Color; renderCamera.cameraType = CameraType.SceneView; renderCamera.cullingMask = 1 << 31; Bounds bounds; var cloneTransform = cloneGo.transform; bool isUINode = false; if (cloneTransform is RectTransform) { //如果是UGUI节点的话就要把它们放在Canvas下了 var canvasGo = new GameObject("render canvas", typeof(Canvas)); var canvasRtf = canvasGo.GetComponent<RectTransform>(); SetAsDesignSize(canvasRtf); canvasRtf.position = new Vector3(-1000, -1000, -1000); //远一点, 这样才看不到 canvasGo.layer = 31;//放在31层,摄像机也只渲染此层的,避免混入了奇怪的东西 rootGo = canvasGo; var canvas = canvasGo.GetComponent<Canvas>(); canvas.renderMode = RenderMode.WorldSpace; canvas.worldCamera = renderCamera; cameraTransform.SetParent(canvasRtf, false); cloneTransform.SetParent(canvasRtf, false); cloneTransform.localPosition = Vector3.zero; if (cloneTransform.GetComponent<Canvas>()) //根节点有Canvas组件, 只考虑根节点的大小 { var cloneRtf = (RectTransform)cloneTransform; var anchorDiff = cloneRtf.anchorMax - cloneRtf.anchorMin; bool haveStretch = !Mathf.Approximately(anchorDiff.sqrMagnitude, float.Epsilon); if (haveStretch) { var cavs = cloneTransform.GetComponent<CanvasScaler>(); if (cavs && cavs.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize) { var size = cavs.referenceResolution; cloneRtf.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, size.x); cloneRtf.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, size.y); } } var tempWorldCorners = new Vector3[4]; cloneRtf.GetWorldCorners(tempWorldCorners); bounds = new Bounds(tempWorldCorners[0], Vector3.zero); //左下 bounds.Encapsulate(tempWorldCorners[2]); //右上 } else { bounds = GetBounds(cloneGo); } isUINode = true; } else { rootGo = cloneGo; cameraTransform.SetParent(cloneTransform, false); bounds = GetBounds(cloneGo); } var allTransforms = cloneGo.GetComponentsInChildren<Transform>(); foreach (Transform trans in allTransforms) trans.gameObject.layer = 31; var vMin = bounds.min; var vMax = bounds.max; float texWidth = 128; float texHeight = 128; var center = bounds.center; var pos = cameraTransform.position; pos.x = center.x; pos.y = center.y; if (isUINode) { pos.z -= 100; cameraTransform.position = pos; renderCamera.orthographic = true; float width = vMax.x - vMin.x; float height = vMax.y - vMin.y; if (width > height) //宽不变, 调整高 { if (width < 64) //原来就比64小, 直接用原大小 texWidth = width; else texWidth = Mathf.Max(64, width * 0.5f); //最小64 texWidth = Mathf.Min(768, texWidth); //最大768 renderCamera.orthographicSize = height * 0.5f; texHeight = texWidth * height / width; } else { if (height < 64) //原来就比64小, 直接用原大小 texHeight = height; else texHeight = Mathf.Max(64, height * 0.5f); //最小64 texHeight = Mathf.Min(768, texHeight); //最大768 renderCamera.orthographicSize = height * 0.5f; texWidth = texHeight * width / height; } Debug.Log($"ui:{width}x{height}, tex:{texWidth}x{texHeight}"); } else { pos.z -= vMax.z + (vMax.z - vMin.z); //z往前一点 cameraTransform.position = pos; cameraTransform.LookAt(center); var size = bounds.size; int angle = (int)(Mathf.Atan2(size.y * 0.5f, size.z) * Mathf.Rad2Deg * 2); //Camera在(0,0,0)处, 拍摄Bounds包围盒 renderCamera.fieldOfView = angle; } var camRT = new RenderTexture((int)texWidth, (int)texHeight, 0, RenderTextureFormat.Default); renderCamera.targetTexture = camRT; renderCamera.Render(); var tex = RTToTexture2D(camRT); UnityEngine.Object.DestroyImmediate(rootGo); return tex; } public static Texture2D RTToTexture2D(RenderTexture rt) { var oldActiveRT = RenderTexture.active; RenderTexture.active = rt; var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBAHalf, false); tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); RenderTexture.active = oldActiveRT; return tex; } public static Bounds GetBounds(GameObject go) { var vMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); var vMax = new Vector3(float.MinValue, float.MinValue, float.MinValue); var renders = go.GetComponentsInChildren<MeshRenderer>(); if (renders.Length > 0) { for (int i = 0; i < renders.Length; i++) { var bounds = renders[i].bounds; vMin = Vector3.Min(vMin, bounds.min); vMax = Vector3.Max(vMax, bounds.max); } } else { var rectTrans = go.GetComponentsInChildren<RectTransform>(); var tempWorldCorners = new Vector3[4]; for (int i = 0; i < rectTrans.Length; i++) { var rtf = rectTrans[i]; var s = rtf.lossyScale; //if (s.x <= 3 && s.y <= 3 && s.z <= 3) { //获取节点的四个角的世界坐标,分别按顺序为左下, 左上, 右上, 右下 rtf.GetWorldCorners(tempWorldCorners); vMin = Vector3.Min(vMin, tempWorldCorners[0]); vMax = Vector3.Max(vMax, tempWorldCorners[2]); } } } var bounds_2 = new Bounds(vMin, Vector3.zero); bounds_2.Encapsulate(vMax); //Debug.Log($"vMin:{vMin}, vMax:{vMax}, size:{bounds_2.size}"); return bounds_2; } public static void SaveTexture2D(Texture2D tex, string filePath) { var dirPath = Path.GetDirectoryName(filePath); if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath); byte[] texBytes = tex.EncodeToPNG(); File.WriteAllBytes(filePath, texBytes); } //设置为设计分辨率 private static void SetAsDesignSize(RectTransform rtf) { var cavs = GameObject.FindObjectOfType<CanvasScaler>(); if (cavs && cavs.uiScaleMode == CanvasScaler.ScaleMode.ScaleWithScreenSize) { var size = cavs.referenceResolution; rtf.sizeDelta = size; return; } rtf.sizeDelta = GetGameViewSize(); } public static Vector2 GetGameViewSize() { try { var type_GameView = System.Type.GetType("UnityEditor.GameView,UnityEditor"); var mi_GetMainGameView = type_GameView.GetMethod("GetMainGameView", BindingFlags.NonPublic | BindingFlags.Static); var gameView = (UnityEditor.EditorWindow)mi_GetMainGameView.Invoke(null, null); var prop_currentGameViewSize = gameView.GetType().GetProperty("currentGameViewSize", BindingFlags.NonPublic | BindingFlags.Instance); object[] emptyObjArr = null; // new object[0] { }; var gameViewSize = prop_currentGameViewSize.GetValue(gameView, emptyObjArr); var type_GameViewSize = gameViewSize.GetType(); var w = (int)type_GameViewSize.GetProperty("width", BindingFlags.Public | BindingFlags.Instance).GetValue(gameViewSize, emptyObjArr); var h = (int)type_GameViewSize.GetProperty("height", BindingFlags.Public | BindingFlags.Instance).GetValue(gameViewSize, emptyObjArr); return new Vector2(w, h); } catch (Exception ex) { } return new Vector2(Screen.width, Screen.height); } public static void RepaintInspector() { var windows = UnityEngine.Resources.FindObjectsOfTypeAll<UnityEditor.EditorWindow>(); foreach (var win in windows) { if (win.titleContent.text == "Inspector") win.Repaint(); } } } #endif
参考
[Unity 3d] UGUI-Editor(UGUI编辑器增强工具) - GitHub - 简书 (jianshu.com)
GitHub - liuhaopen/UGUI-Editor: Unity UGUI editor tools,improve the efficiency of ui development.
获取unity prefab的预览图像 - 露夕逝 - 博客园 (cnblogs.com)
为Unity的新版ugui的Prefab生成预览图_unity ui prefab 预览图-CSDN博客
Unity3D 在 Inspector 中预览场景_unity3d怎么预览-CSDN博客
GitHub - liuhaopen/UGUI-Editor: Unity UGUI editor tools,improve the efficiency of ui development.
标签:预览,GameObject,height,tex,var,预制,new,Inspector From: https://www.cnblogs.com/sailJs/p/17746235.html