-
-
Save builder-main/680cc14cdaabb1cc2a7ec58992bdad29 to your computer and use it in GitHub Desktop.
Fix for unity layout groups dirting prefabs and having bad perf.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[TypeInfoBox("Help managing layouts and avoid dirty prefabs as well as runtime performance." + | |
"Add any rectTransform at Awake any content size fitter and layout group will be found from the filters." + | |
"You can also force offline search with 'PopulateManual' for prefab workflow.")] | |
public class LayoutGroupManualUpdater : MonoBehaviour | |
{ | |
/// <summary> | |
/// Layouts to enable / disable for layout updates and performances | |
/// </summary> | |
[SerializeField] | |
public List<LayoutGroup> AllEnabledLayouts = new List<LayoutGroup>(); | |
/// <summary> | |
/// Content size fitter to enable/ disable on layout updates | |
/// </summary> | |
[SerializeField] | |
public List<ContentSizeFitter> AllContentSizeFitters = new List<ContentSizeFitter>(); | |
[SerializeField] | |
public List<RectTransform> _roots = new List<RectTransform>(); | |
[Tooltip("If sequential will wait a frame between each root")] | |
public bool sequentialUpdate = false; | |
[Title("filter")] | |
public bool activeInHierarchy = true; | |
public bool isEnabled = true; | |
public void Awake() | |
{ | |
foreach (var rectTransform in _roots) | |
{ | |
AddAll(rectTransform); | |
} | |
} | |
[HorizontalGroup] | |
[Tooltip("Update all layouts and disable them at editor time, also populates the Layout and Content size fitter lists.")] | |
[Sirenix.OdinInspector.Button] | |
public void PopulateManual() | |
{ | |
Awake(); | |
DisableAll(); | |
} | |
[HorizontalGroup] | |
[Tooltip("Restore to initial state")] | |
[Sirenix.OdinInspector.Button] | |
public void ClearManual() | |
{ | |
EnableAll(); | |
AllEnabledLayouts.Clear(); | |
AllContentSizeFitters.Clear(); | |
} | |
[HorizontalGroup] | |
[Tooltip("If root is a prefab variant restore all layouts / fitter / child transform to parent prefab value")] | |
[Sirenix.OdinInspector.Button] | |
public void RevertVariant() | |
{ | |
#if UNITY_EDITOR | |
var stage = PrefabStageUtility.GetCurrentPrefabStage(); | |
if (!stage) | |
{ | |
IndusLogger.Error("LayoutGroupManualUpdater.RevertVariant()", $"Only works in Prefab Variant edit mode"); | |
return; | |
} | |
var nonVariant = AllEnabledLayouts.Where(l => stage.IsPartOfPrefabContents(l.gameObject).Not()).ToArray(); | |
if (nonVariant.Any()) | |
{ | |
IndusLogger.Error("LayoutGroupManualUpdater.RevertVariant()", | |
$"Security check : Some layouts are not from prefab variant and we cant revert {nonVariant.ToDelimitedString(";")}"); | |
return; | |
} | |
var nonVariantFitters = AllContentSizeFitters.Where(l => stage.IsPartOfPrefabContents(l.gameObject).Not()).ToArray(); | |
if (nonVariantFitters.Any()) | |
{ | |
IndusLogger.Error("LayoutGroupManualUpdater.RevertVariant()", | |
$"Security check : Some fitters are not from prefab variant and we cant revert {nonVariant.ToDelimitedString(";")}"); | |
return; | |
} | |
var revert = UnityEditor.EditorUtility.DisplayDialog("Warning", "This will revert the prefab variant values of ContentSizeFitter.enabled, LayoutGroup.enabled, LayoutGroup's rectTransform, and LayoutGroups' direct child's RectTransform", "Proceed", "Cancel"); | |
if (!revert) | |
{ | |
IndusLogger.Debug("LayoutGroupManualUpdater.RevertVariant()", | |
$"Canceled"); | |
return; | |
} | |
UnityEditor.Undo.RegisterFullObjectHierarchyUndo(gameObject, "Undo Revert of child layouts"); | |
foreach (var layout in AllEnabledLayouts) | |
{ | |
RevertEnabled(layout); | |
for (var i = 0; i < layout.transform.childCount; i++) | |
{ | |
var rectT = layout | |
.GetComponent<RectTransform>(); | |
if (rectT) UnityEditor.PrefabUtility.RevertObjectOverride(rectT, UnityEditor.InteractionMode.AutomatedAction); | |
layout.transform | |
.GetChild(i) | |
.AsOptional<RectTransform>() | |
.IfPresent(c => UnityEditor.PrefabUtility.RevertObjectOverride(c, UnityEditor.InteractionMode.AutomatedAction)); | |
} | |
} | |
foreach (var fitter in AllContentSizeFitters) | |
{ | |
RevertEnabled(fitter); | |
} | |
void RevertEnabled(Behaviour comp) | |
{ | |
var layoutSerialized = new UnityEditor.SerializedObject(comp); | |
var enabledProp = layoutSerialized.FindProperty("m_Enabled" /*Mandatory magic string*/); | |
UnityEditor.PrefabUtility.RevertPropertyOverride(enabledProp, UnityEditor.InteractionMode.AutomatedAction); | |
} | |
#endif | |
} | |
/// <summary> | |
/// Dynamically Add all enabled layout group and content size fitters from given root | |
/// </summary> | |
/// <param name="root"></param> | |
public void AddAll(RectTransform root) | |
{ | |
if(_roots.Contains(root).Not()) _roots.Add(root); | |
IndusLogger.Debug("LayoutGroupManualUpdater.AddAll()", $"Looking layouts in {root}"); | |
var toAddLayout = root.GetComponentsInChildren<LayoutGroup>(includeInactive: !activeInHierarchy) | |
.Where(l => !isEnabled || l.enabled) | |
.Where(l => AllEnabledLayouts.Contains(l).Not()); | |
AllEnabledLayouts.AddRange(toAddLayout); | |
var toAddFitters = root.GetComponentsInChildren<ContentSizeFitter>(includeInactive: !activeInHierarchy) | |
.Where(l => !isEnabled || l.enabled) | |
.Where(l => AllContentSizeFitters.Contains(l).Not()); | |
AllContentSizeFitters.AddRange(toAddFitters); | |
} | |
[Sirenix.OdinInspector.Button] | |
public void UpdateAsync() | |
{ | |
if(Application.isPlaying.Not()) return; | |
if(gameObject.activeInHierarchy) StartCoroutine(UpdateCoroutine()); | |
} | |
private IEnumerator UpdateCoroutine() | |
{ | |
EnableAll(); | |
foreach (var rectTransform in _roots) | |
{ | |
LayoutRebuilder.MarkLayoutForRebuild(rectTransform); | |
if (sequentialUpdate) yield return null; | |
} | |
yield return null; | |
DisableAll(); | |
} | |
/// <summary> | |
/// Disable all layouts through API | |
/// </summary> | |
[HorizontalGroup] | |
[Tooltip("Disable all elements")] | |
[Sirenix.OdinInspector.Button] | |
public void DisableAll() | |
{ | |
SetEnableElements(false); | |
} | |
/// <summary> | |
/// Enable all layouts through API. Useful if a continuous updating is needed such as in tween or animations. | |
/// </summary> | |
[HorizontalGroup] | |
[Tooltip("Restore to initial state")] | |
[Sirenix.OdinInspector.Button] | |
public void EnableAll() | |
{ | |
SetEnableElements(true); | |
} | |
private void SetEnableElements(bool isEnabled) | |
{ | |
foreach (var sizeFitter in AllContentSizeFitters) | |
{ | |
if(sizeFitter == null) continue; | |
sizeFitter.enabled = isEnabled; | |
} | |
foreach (var layout in AllEnabledLayouts) | |
{ | |
if(layout == null) continue; | |
layout.enabled = isEnabled; | |
} | |
} | |
// | |
// public LayoutGroupManualUpdater(List<LayoutGroup> allEnabledLayouts, List<ContentSizeFitter> allContentSizeFitters) | |
// { | |
// AllEnabledLayouts = allEnabledLayouts; | |
// AllContentSizeFitters = allContentSizeFitters; | |
// } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment