Created
November 10, 2015 04:59
-
-
Save LordNed/454d4e3f0104e179d400 to your computer and use it in GitHub Desktop.
Simple UI panel manager for use in bringing a more state-like approach to the UI. Only one panel can be active at a time, but multiple overlays can be active. To use, derive a class from either UIBasePanel, or UIBaseOverlay and put them on an object in the scene. Also put an instance of UIPanelManager, preferably on the root of your UI.
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
using UnityEngine; | |
using System.Collections; | |
public class UIBaseOverlay : MonoBehaviour | |
{ | |
public virtual IEnumerator OnOverlayActivated() | |
{ | |
yield break; | |
} | |
public virtual IEnumerator OnOverlayDeactivated() | |
{ | |
yield break; | |
} | |
} |
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
using UnityEngine; | |
using System.Collections; | |
public class UIBasePanel : MonoBehaviour | |
{ | |
public virtual IEnumerator OnPanelActivated() | |
{ | |
yield break; | |
} | |
public virtual IEnumerator OnPanelDeactivated() | |
{ | |
yield break; | |
} | |
} |
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
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class UIPanelManager : MonoBehaviour | |
{ | |
private List<UIBasePanel> m_cachedPanelList; | |
private List<UIBaseOverlay> m_cachedOverlayList; | |
private Stack<UIBasePanel> m_panelStack; | |
private List<UIBaseOverlay> m_overlayList; | |
private bool m_isTransitioning; | |
public static UIPanelManager Instance; | |
void Awake() | |
{ | |
m_panelStack = new Stack<UIBasePanel>(); | |
m_overlayList = new List<UIBaseOverlay>(); | |
m_cachedPanelList = new List<UIBasePanel>(); | |
m_cachedOverlayList = new List<UIBaseOverlay>(); | |
Instance = this; | |
UpdateTrackedChildren(); | |
} | |
private void OnLevelWasLoaded(int levelIndex) | |
{ | |
Debug.Log("[LuxelOperation.UIController] OnLevelWasLoaded triggered, updating tracked children."); | |
UpdateTrackedChildren(); | |
} | |
public void UpdateTrackedChildren() | |
{ | |
// Clear our list of previously tracked children (panels and overlays) | |
m_cachedPanelList.Clear(); | |
m_cachedOverlayList.Clear(); | |
// Find all UIControllerRoots in the scene. | |
Canvas[] roots = FindObjectsOfType<Canvas>(); | |
foreach (var root in roots) | |
{ | |
m_cachedPanelList.AddRange(root.GetComponentsInChildren<UIBasePanel>(true)); | |
m_cachedOverlayList.AddRange(root.GetComponentsInChildren<UIBaseOverlay>(true)); | |
} | |
// Ensure all panels start disabled so it doesn't matter what the state of the panels is (during dev). | |
foreach (var panel in m_cachedPanelList) | |
panel.gameObject.SetActive(false); | |
foreach (var overlay in m_cachedOverlayList) | |
overlay.gameObject.SetActive(false); | |
} | |
#region Panels | |
private IEnumerator PopPanelCoroutine(Action onComplete = null) | |
{ | |
m_isTransitioning = true; | |
// Deactivate the top panel, pop it, then activate the new top (again, due to it transitioning out before). | |
UIBasePanel topPanel = m_panelStack.Peek(); | |
yield return StartCoroutine(DeactivatePanelCoroutine(topPanel)); | |
// Update stack state to match... | |
m_panelStack.Pop(); | |
if (m_panelStack.Count > 0) | |
{ | |
yield return StartCoroutine(ActivatePanelCoroutine(m_panelStack.Peek())); | |
} | |
if (onComplete != null) | |
onComplete(); | |
m_isTransitioning = false; | |
} | |
private IEnumerator PushPanelCoroutine<T>(Action onComplete = null) where T : UIBasePanel | |
{ | |
m_isTransitioning = true; | |
// Deactivate the top panel, wait for it to finish, then push our own. | |
if (m_panelStack.Count > 0) | |
yield return StartCoroutine(DeactivatePanelCoroutine(m_panelStack.Peek())); | |
// Find the new panel that we want to push to the top. | |
foreach (var panel in m_cachedPanelList) | |
{ | |
if (panel.GetType() == typeof(T)) | |
{ | |
m_panelStack.Push(panel); | |
yield return StartCoroutine(ActivatePanelCoroutine(panel)); | |
m_isTransitioning = false; | |
if (onComplete != null) | |
onComplete(); | |
// Leave the function early, we're done here. | |
yield break; | |
} | |
} | |
// If we didn't find the panel, ensure we break the transition lock | |
// and warn user. | |
m_isTransitioning = false; | |
Debug.LogWarning(string.Format("[Helios.UIPanelManager] Failed to find panel of type {0} in UIPanelManager!", typeof(T))); | |
if (onComplete != null) | |
onComplete(); | |
} | |
private IEnumerator PushPanelCoroutine<T>(bool overrideYield = false, Action onComplete = null) where T : UIBasePanel | |
{ | |
m_isTransitioning = true; | |
// Deactivate the top panel, wait for it to finish, then push our own. | |
if (m_panelStack.Count > 0) | |
{ | |
if (overrideYield) | |
{ | |
StartCoroutine(DeactivatePanelCoroutine(m_panelStack.Peek())); | |
} | |
else | |
{ | |
yield return StartCoroutine(DeactivatePanelCoroutine(m_panelStack.Peek())); | |
} | |
} | |
// Find the new panel that we want to push to the top. | |
foreach (var panel in m_cachedPanelList) | |
{ | |
if (panel.GetType() == typeof(T)) | |
{ | |
m_panelStack.Push(panel); | |
yield return StartCoroutine(ActivatePanelCoroutine(panel)); | |
m_isTransitioning = false; | |
if (onComplete != null) | |
onComplete(); | |
// Leave the function early, we're done here. | |
yield break; | |
} | |
} | |
// If we didn't find the panel, ensure we break the transition lock | |
// and warn user. | |
m_isTransitioning = false; | |
Debug.LogWarning(string.Format("[Helios.UIPanelManager] Failed to find panel of type {0} in UIPanelManager!", typeof(T))); | |
if (onComplete != null) | |
onComplete(); | |
} | |
public IEnumerator DeactivatePanelCoroutine(UIBasePanel panel) | |
{ | |
if (panel.gameObject.activeSelf) | |
{ | |
yield return StartCoroutine(panel.OnPanelDeactivated()); | |
panel.gameObject.SetActive(false); | |
} | |
} | |
private IEnumerator ActivatePanelCoroutine(UIBasePanel panel) | |
{ | |
panel.gameObject.SetActive(true); | |
yield return StartCoroutine(panel.OnPanelActivated()); | |
} | |
private T GetPanelOfType<T>() where T : UIBasePanel | |
{ | |
foreach (var panel in m_cachedPanelList) | |
{ | |
if (panel.GetType() == typeof(T)) | |
return (T)panel; | |
} | |
// Silently fail as PushPanel already warns user of failure. | |
return null; | |
} | |
#endregion | |
#region Overlays | |
private IEnumerator PopOverlayCoroutine(UIBaseOverlay overlay, Action onComplete = null) | |
{ | |
if (!m_overlayList.Contains(overlay)) | |
{ | |
Debug.Log(string.Format("[Helios.UIPanelManager] Attempted to pop overlay of type {0} in UIPanelManager and could not find!", overlay.GetType())); | |
if (onComplete != null) | |
onComplete(); | |
yield break; | |
} | |
// Remove from the list immediately, and then turn it off. | |
m_overlayList.Remove(overlay); | |
StartCoroutine(DeactivateOverlayCoroutine(overlay)); | |
if (onComplete != null) | |
onComplete(); | |
yield break; | |
} | |
private IEnumerator PushOverlayCoroutine<T>(Action onComplete = null) where T : UIBaseOverlay | |
{ | |
// Find the new overlay that we want to enable | |
foreach (var overlay in m_cachedOverlayList) | |
{ | |
if (overlay.GetType() == typeof(T)) | |
{ | |
if (!m_overlayList.Contains(overlay)) | |
{ | |
m_overlayList.Add(overlay); | |
StartCoroutine(ActivateOverlayCoroutine(overlay)); | |
} | |
if (onComplete != null) | |
onComplete(); | |
// Leave the function early, we're done here. | |
yield break; | |
} | |
} | |
// If we didn't find the overlay, warn the user. | |
Debug.LogWarning(string.Format("[Helios.UIPanelManager] Failed to find overlay of type {0} in UIPanelManager!", typeof(T))); | |
if (onComplete != null) | |
onComplete(); | |
} | |
private IEnumerator DeactivateOverlayCoroutine(UIBaseOverlay overlay) | |
{ | |
yield return StartCoroutine(overlay.OnOverlayDeactivated()); | |
overlay.gameObject.SetActive(false); | |
} | |
private IEnumerator ActivateOverlayCoroutine(UIBaseOverlay overlay) | |
{ | |
overlay.gameObject.SetActive(true); | |
yield return StartCoroutine(overlay.OnOverlayActivated()); | |
} | |
private T GetOverlayOfType<T>() where T : UIBaseOverlay | |
{ | |
foreach (var panel in m_cachedOverlayList) | |
{ | |
if (panel.GetType() == typeof(T)) | |
return (T)panel; | |
} | |
// Silently fail as PushOverlay already warns user of failure. | |
return null; | |
} | |
#endregion | |
#region Public API | |
public T PushPanel<T>(Action onComplete = null) where T : UIBasePanel | |
{ | |
StartCoroutine(PushPanelCoroutine<T>(onComplete)); | |
// Then return a reference to the panel of that type. | |
return GetPanelOfType<T>(); | |
} | |
public T PushPanel<T>(bool overrideYield, Action onComplete = null) where T : UIBasePanel | |
{ | |
StartCoroutine(PushPanelCoroutine<T>(overrideYield, onComplete)); | |
// Then return a reference to the panel of that type. | |
return GetPanelOfType<T>(); | |
} | |
public IEnumerator PushPanelAndWait<T>() where T : UIBasePanel | |
{ | |
yield return StartCoroutine(PushPanelCoroutine<T>(null)); | |
} | |
public void PopPanel(Action onComplete = null) | |
{ | |
StartCoroutine(PopPanelCoroutine(onComplete)); | |
} | |
public T GetPanel<T>() where T : UIBasePanel | |
{ | |
return GetPanelOfType<T>(); | |
} | |
public T PushOverlay<T>(Action onComplete = null) where T : UIBaseOverlay | |
{ | |
StartCoroutine(PushOverlayCoroutine<T>(onComplete)); | |
// Then return a reference to the panel of that type. | |
return GetOverlayOfType<T>(); | |
} | |
public void PopOverlay(UIBaseOverlay overlay, Action onComplete = null) | |
{ | |
StartCoroutine(PopOverlayCoroutine(overlay, onComplete)); | |
} | |
public T GetOverlay<T>() where T : UIBaseOverlay | |
{ | |
return GetOverlayOfType<T>(); | |
} | |
public IEnumerator PushOverlayAndWait<T>() where T : UIBaseOverlay | |
{ | |
yield return StartCoroutine(PushOverlayCoroutine<T>(null)); | |
} | |
public bool IsInTransition() | |
{ | |
return m_isTransitioning; | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment