├── Window
├── WindowPriority.cs
├── WindowHistoryEntry.cs
├── WindowParaLayer.cs
├── AWindowController.cs
├── WindowProperties.cs
└── WindowUILayer.cs
├── Panel
├── PanelProperties.cs
├── APanelController.cs
├── PanelUILayer.cs
└── PanelPriority.cs
├── ScreenTransitions
├── ATransitionComponent.cs
├── SimpleFadeTransition.cs
└── LegacyAnimationScreenTransition.cs
├── Core
├── ScreenPropertyInterfaces.cs
├── ScreenControllerInterfaces.cs
├── AUILayer.cs
└── AUIScreenController.cs
├── LICENSE
├── UISettings.cs
├── README.md
├── Editor
└── UIFrameworkTools.cs
├── MANUAL.md
└── UIFrame.cs
/Window/WindowPriority.cs:
--------------------------------------------------------------------------------
1 | namespace deVoid.UIFramework {
2 | ///
3 | /// Enum to define behaviour of Windows
4 | /// upon opening, in the history and queue
5 | ///
6 | public enum WindowPriority {
7 | ForceForeground = 0,
8 | Enqueue = 1,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Window/WindowHistoryEntry.cs:
--------------------------------------------------------------------------------
1 | namespace deVoid.UIFramework {
2 | ///
3 | /// An entry for controlling window history and queue
4 | ///
5 | public struct WindowHistoryEntry
6 | {
7 | public readonly IWindowController Screen;
8 | public readonly IWindowProperties Properties;
9 |
10 | public WindowHistoryEntry(IWindowController screen, IWindowProperties properties) {
11 | Screen = screen;
12 | Properties = properties;
13 | }
14 |
15 | public void Show() {
16 | Screen.Show(Properties);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Panel/PanelProperties.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace deVoid.UIFramework {
4 | ///
5 | /// Properties common to all panels
6 | ///
7 | [System.Serializable]
8 | public class PanelProperties : IPanelProperties {
9 | [SerializeField]
10 | [Tooltip("Panels go to different para-layers depending on their priority. You can set up para-layers in the Panel Layer.")]
11 | private PanelPriority priority;
12 |
13 | public PanelPriority Priority {
14 | get { return priority; }
15 | set { priority = value; }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ScreenTransitions/ATransitionComponent.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System;
3 |
4 | namespace deVoid.UIFramework {
5 | ///
6 | /// Screens use ATransitionComponents to animate their in and out transitions.
7 | /// This can be extended to use Lerps, animations etc.
8 | ///
9 | public abstract class ATransitionComponent : MonoBehaviour {
10 | ///
11 | /// Animate the specified target transform and execute CallWhenFinished when the animation is done.
12 | ///
13 | /// Target transform.
14 | /// Delegate to be called when animation is finished.
15 | public abstract void Animate(Transform target, Action callWhenFinished);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Core/ScreenPropertyInterfaces.cs:
--------------------------------------------------------------------------------
1 | namespace deVoid.UIFramework
2 | {
3 | ///
4 | /// Base interface for all the screen properties
5 | ///
6 | public interface IScreenProperties { }
7 |
8 | ///
9 | /// Base interface for all Panel properties
10 | ///
11 | public interface IPanelProperties : IScreenProperties
12 | {
13 | PanelPriority Priority { get; set; }
14 | }
15 |
16 | ///
17 | /// Base interface for Window properties.
18 | ///
19 | public interface IWindowProperties : IScreenProperties
20 | {
21 | WindowPriority WindowQueuePriority { get; set; }
22 | bool HideOnForegroundLost { get; set; }
23 | bool IsPopup { get; set; }
24 | bool SuppressPrefabProperties { get; set; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Panel/APanelController.cs:
--------------------------------------------------------------------------------
1 | namespace deVoid.UIFramework {
2 | ///
3 | /// Base class for panels that need no special Properties
4 | ///
5 | public abstract class APanelController : APanelController { }
6 |
7 | ///
8 | /// Base class for Panels
9 | ///
10 | public abstract class APanelController : AUIScreenController, IPanelController where T : IPanelProperties {
11 | public PanelPriority Priority {
12 | get {
13 | if (Properties != null) {
14 | return Properties.Priority;
15 | }
16 | else {
17 | return PanelPriority.None;
18 | }
19 | }
20 | }
21 |
22 | protected sealed override void SetProperties(T props) {
23 | base.SetProperties(props);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Yanko Oliveira
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Core/ScreenControllerInterfaces.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace deVoid.UIFramework {
4 | ///
5 | /// Interface that all UI Screens must implement directly or indirectly
6 | ///
7 | public interface IUIScreenController {
8 | string ScreenId { get; set; }
9 | bool IsVisible { get; }
10 |
11 | void Show(IScreenProperties props = null);
12 | void Hide(bool animate = true);
13 |
14 | Action InTransitionFinished { get; set; }
15 | Action OutTransitionFinished { get; set; }
16 | Action CloseRequest { get; set; }
17 | Action ScreenDestroyed { get; set; }
18 | }
19 |
20 | ///
21 | /// Interface that all Windows must implement
22 | ///
23 | public interface IWindowController : IUIScreenController {
24 | bool HideOnForegroundLost { get; }
25 | bool IsPopup { get; }
26 | WindowPriority WindowPriority { get; }
27 | }
28 |
29 | ///
30 | /// Interface that all Panels must implement
31 | ///
32 | public interface IPanelController : IUIScreenController {
33 | PanelPriority Priority { get; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Window/WindowParaLayer.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections.Generic;
3 |
4 | namespace deVoid.UIFramework {
5 | ///
6 | /// This is a "helper" layer so Windows with higher priority can be displayed.
7 | /// By default, it contains any window tagged as a Popup. It is controlled by the WindowUILayer.
8 | ///
9 | public class WindowParaLayer : MonoBehaviour {
10 | [SerializeField]
11 | private GameObject darkenBgObject = null;
12 |
13 | private List containedScreens = new List();
14 |
15 | public void AddScreen(Transform screenRectTransform) {
16 | screenRectTransform.SetParent(transform, false);
17 | containedScreens.Add(screenRectTransform.gameObject);
18 | }
19 |
20 | public void RefreshDarken() {
21 | for (int i = 0; i < containedScreens.Count; i++) {
22 | if (containedScreens[i] != null) {
23 | if (containedScreens[i].activeSelf) {
24 | darkenBgObject.SetActive(true);
25 | return;
26 | }
27 | }
28 | }
29 |
30 | darkenBgObject.SetActive(false);
31 | }
32 |
33 | public void DarkenBG() {
34 | darkenBgObject.SetActive(true);
35 | darkenBgObject.transform.SetAsLastSibling();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Panel/PanelUILayer.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace deVoid.UIFramework {
6 | ///
7 | /// This Layer controls Panels.
8 | /// Panels are Screens that have no history or queuing,
9 | /// they are simply shown and hidden in the Frame
10 | /// eg: a HUD, an energy bar, a mini map etc.
11 | ///
12 | public class PanelUILayer : AUILayer {
13 | [SerializeField]
14 | [Tooltip("Settings for the priority para-layers. A Panel registered to this layer will be reparented to a different para-layer object depending on its Priority.")]
15 | private PanelPriorityLayerList priorityLayers = null;
16 |
17 | public override void ReparentScreen(IUIScreenController controller, Transform screenTransform) {
18 | var ctl = controller as IPanelController;
19 | if (ctl != null) {
20 | ReparentToParaLayer(ctl.Priority, screenTransform);
21 | }
22 | else {
23 | base.ReparentScreen(controller, screenTransform);
24 | }
25 | }
26 |
27 | public override void ShowScreen(IPanelController screen) {
28 | screen.Show();
29 | }
30 |
31 | public override void ShowScreen(IPanelController screen, TProps properties) {
32 | screen.Show(properties);
33 | }
34 |
35 | public override void HideScreen(IPanelController screen) {
36 | screen.Hide();
37 | }
38 |
39 | public bool IsPanelVisible(string panelId) {
40 | IPanelController panel;
41 | if (registeredScreens.TryGetValue(panelId, out panel)) {
42 | return panel.IsVisible;
43 | }
44 |
45 | return false;
46 | }
47 |
48 | private void ReparentToParaLayer(PanelPriority priority, Transform screenTransform) {
49 | Transform trans;
50 | if (!priorityLayers.ParaLayerLookup.TryGetValue(priority, out trans)) {
51 | trans = transform;
52 | }
53 |
54 | screenTransform.SetParent(trans, false);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Panel/PanelPriority.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | namespace deVoid.UIFramework
5 | {
6 | ///
7 | /// Defines to which para-layer
8 | /// the panel is going to be parented to
9 | ///
10 | public enum PanelPriority {
11 | None = 0,
12 | Prioritary = 1,
13 | Tutorial = 2,
14 | Blocker = 3,
15 | }
16 |
17 | [System.Serializable]
18 | public class PanelPriorityLayerListEntry {
19 | [SerializeField]
20 | [Tooltip("The panel priority type for a given target para-layer")]
21 | private PanelPriority priority;
22 | [SerializeField]
23 | [Tooltip("The GameObject that should house all Panels tagged with this priority")]
24 | private Transform targetParent;
25 |
26 | public Transform TargetParent {
27 | get { return targetParent; }
28 | set { targetParent = value; }
29 | }
30 |
31 | public PanelPriority Priority {
32 | get { return priority; }
33 | set { priority = value; }
34 | }
35 |
36 | public PanelPriorityLayerListEntry(PanelPriority prio, Transform parent) {
37 | priority = prio;
38 | targetParent = parent;
39 | }
40 | }
41 |
42 | [System.Serializable]
43 | public class PanelPriorityLayerList {
44 | [SerializeField]
45 | [Tooltip("A lookup of GameObjects to store panels depending on their Priority. Render priority is set by the hierarchy order of these GameObjects")]
46 | private List paraLayers = null;
47 |
48 | private Dictionary lookup;
49 |
50 | public Dictionary ParaLayerLookup {
51 | get {
52 | if (lookup == null || lookup.Count == 0) {
53 | CacheLookup();
54 | }
55 |
56 | return lookup;
57 | }
58 | }
59 |
60 | private void CacheLookup() {
61 | lookup = new Dictionary();
62 | for (int i = 0; i < paraLayers.Count; i++) {
63 | lookup.Add(paraLayers[i].Priority, paraLayers[i].TargetParent);
64 | }
65 | }
66 |
67 | public PanelPriorityLayerList(List entries) {
68 | paraLayers = entries;
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/ScreenTransitions/SimpleFadeTransition.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace deVoid.UIFramework
5 | {
6 | ///
7 | /// This is a simple fade transition implemented as a built-in example.
8 | /// I recommend using a free tweening library like DOTween (http://dotween.demigiant.com/)
9 | /// or rolling out your own.
10 | /// Check the Examples project for more robust and battle-tested options:
11 | /// https://github.com/yankooliveira/uiframework_examples
12 | ///
13 | public class SimpleFadeTransition : ATransitionComponent
14 | {
15 | [SerializeField] private float fadeDuration = 0.5f;
16 | [SerializeField] private bool fadeOut = false;
17 |
18 | private CanvasGroup canvasGroup;
19 | private float timer;
20 | private Action currentAction;
21 | private Transform currentTarget;
22 |
23 | private float startValue;
24 | private float endValue;
25 |
26 | private bool shouldAnimate;
27 |
28 | public override void Animate(Transform target, Action callWhenFinished) {
29 | if (currentAction != null) {
30 | canvasGroup.alpha = endValue;
31 | currentAction();
32 | }
33 |
34 | canvasGroup = target.GetComponent();
35 | if (canvasGroup == null) {
36 | canvasGroup = target.gameObject.AddComponent();
37 | }
38 |
39 | if (fadeOut) {
40 | startValue = 1f;
41 | endValue = 0f;
42 | }
43 | else {
44 | startValue = 0f;
45 | endValue = 1f;
46 | }
47 |
48 | currentAction = callWhenFinished;
49 | timer = fadeDuration;
50 |
51 | canvasGroup.alpha = startValue;
52 | shouldAnimate = true;
53 | }
54 |
55 | private void Update() {
56 | if (!shouldAnimate) {
57 | return;
58 | }
59 |
60 | if (timer > 0f) {
61 | timer -= Time.deltaTime;
62 | canvasGroup.alpha = Mathf.Lerp(endValue, startValue, timer / fadeDuration);
63 | }
64 | else {
65 | canvasGroup.alpha = 1f;
66 | if (currentAction != null) {
67 | currentAction();
68 | }
69 |
70 | currentAction = null;
71 | shouldAnimate = false;
72 | }
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/Window/AWindowController.cs:
--------------------------------------------------------------------------------
1 | namespace deVoid.UIFramework
2 | {
3 | ///
4 | /// Base implementation for Window ScreenControllers that need no special Properties
5 | ///
6 | public abstract class AWindowController : AWindowController { }
7 |
8 | ///
9 | /// Base implementation for Window ScreenControllers. Its parameter is a specific type of IWindowProperties.
10 | /// In case your window doesn't need special properties, inherit from AWindowScreenController, without Generic param.
11 | ///
12 | ///
13 | ///
14 | public abstract class AWindowController : AUIScreenController, IWindowController
15 | where TProps : IWindowProperties
16 | {
17 | public bool HideOnForegroundLost {
18 | get { return Properties.HideOnForegroundLost; }
19 | }
20 |
21 | public bool IsPopup {
22 | get { return Properties.IsPopup; }
23 | }
24 |
25 | public WindowPriority WindowPriority {
26 | get { return Properties.WindowQueuePriority; }
27 | }
28 |
29 | ///
30 | /// Requests this Window to be closed, handy for rigging it directly in the Editor.
31 | /// I use the UI_ prefix to group all the methods that should be rigged in the Editor so that it's
32 | /// easy to find the screen-specific methods. It breaks naming convention, but does more good than harm as
33 | /// the amount of methods grow.
34 | /// This is *not* called every time it is closed, just upon user input - for that behaviour, see
35 | /// WhileHiding();
36 | ///
37 | public virtual void UI_Close() {
38 | CloseRequest(this);
39 | }
40 |
41 | protected sealed override void SetProperties(TProps props) {
42 | if (props != null) {
43 | // If the Properties set on the prefab should not be overwritten,
44 | // copy the default values to the passed in properties
45 | if (!props.SuppressPrefabProperties) {
46 | props.HideOnForegroundLost = Properties.HideOnForegroundLost;
47 | props.WindowQueuePriority = Properties.WindowQueuePriority;
48 | props.IsPopup = Properties.IsPopup;
49 | }
50 |
51 | Properties = props;
52 | }
53 | }
54 |
55 | protected override void HierarchyFixOnShow() {
56 | transform.SetAsLastSibling();
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ScreenTransitions/LegacyAnimationScreenTransition.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using UnityEngine;
4 |
5 | namespace deVoid.UIFramework.Examples
6 | {
7 | ///
8 | /// I have avoided using the Legacy Animation system for ages, but since I know people
9 | /// will want to have hand-authored animations on their UI and I highly recommend
10 | /// *not* using Animator for that, both for workflow and performance reasons
11 | /// (ref: https://www.youtube.com/watch?v=_wxitgdx-UI&t=2883s ),
12 | /// I decided to add this example using the Legacy system. An alternative you can
13 | /// look into is the SimpleAnimationComponent
14 | /// (ref: https://blogs.unity3d.com/2017/11/28/introducing-the-simple-animation-component/ )
15 | /// Although it still runs on top of Animator, at least it might have a simpler workflow.
16 | ///
17 | /// Word of warning: this seems to work, but was barely tested. Be careful if taking it into
18 | /// production :D
19 | ///
20 | public class LegacyAnimationScreenTransition : ATransitionComponent
21 | {
22 | [SerializeField] private AnimationClip clip = null;
23 | [SerializeField] private bool playReverse = false;
24 |
25 | private Action previousCallbackWhenFinished;
26 |
27 | public override void Animate(Transform target, Action callWhenFinished) {
28 | FinishPrevious();
29 | var targetAnimation = target.GetComponent();
30 | if (targetAnimation == null) {
31 | Debug.LogError("[LegacyAnimationScreenTransition] No Animation component in " + target);
32 | if (callWhenFinished != null) {
33 | callWhenFinished();
34 | }
35 |
36 | return;
37 | }
38 |
39 | targetAnimation.clip = clip;
40 | StartCoroutine(PlayAnimationRoutine(targetAnimation, callWhenFinished));
41 | }
42 |
43 | private IEnumerator PlayAnimationRoutine(Animation targetAnimation, Action callWhenFinished) {
44 | previousCallbackWhenFinished = callWhenFinished;
45 | foreach (AnimationState state in targetAnimation) {
46 | state.time = playReverse ? state.clip.length : 0f;
47 | state.speed = playReverse ? -1f : 1f;
48 | }
49 |
50 | targetAnimation.Play(PlayMode.StopAll);
51 | yield return new WaitForSeconds(targetAnimation.clip.length);
52 | FinishPrevious();
53 | }
54 |
55 | private void FinishPrevious() {
56 | if (previousCallbackWhenFinished != null) {
57 | previousCallbackWhenFinished();
58 | previousCallbackWhenFinished = null;
59 | }
60 |
61 | StopAllCoroutines();
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Window/WindowProperties.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace deVoid.UIFramework {
4 | ///
5 | /// Properties common to all windows
6 | ///
7 | [System.Serializable]
8 | public class WindowProperties : IWindowProperties {
9 | [SerializeField]
10 | protected bool hideOnForegroundLost = true;
11 |
12 | [SerializeField]
13 | protected WindowPriority windowQueuePriority = WindowPriority.ForceForeground;
14 |
15 | [SerializeField]
16 | protected bool isPopup = false;
17 |
18 | public WindowProperties() {
19 | hideOnForegroundLost = true;
20 | windowQueuePriority = WindowPriority.ForceForeground;
21 | isPopup = false;
22 | }
23 |
24 | ///
25 | /// How should this window behave in case another window
26 | /// is already opened?
27 | ///
28 | /// Force Foreground opens it immediately, Enqueue queues it so that it's opened as soon as
29 | /// the current one is closed.
30 | public WindowPriority WindowQueuePriority {
31 | get { return windowQueuePriority; }
32 | set { windowQueuePriority = value; }
33 | }
34 |
35 | ///
36 | /// Should this window be hidden when other window takes its foreground?
37 | ///
38 | /// true if hide on foreground lost; otherwise, false.
39 | public bool HideOnForegroundLost {
40 | get { return hideOnForegroundLost; }
41 | set { hideOnForegroundLost = value; }
42 | }
43 |
44 | ///
45 | /// When properties are passed in the Open() call, should the ones
46 | /// configured in the viewPrefab be overwritten?
47 | ///
48 | /// true if suppress viewPrefab properties; otherwise, false.
49 | public bool SuppressPrefabProperties { get; set; }
50 |
51 | ///
52 | /// Popups are displayed with a black background behind them and
53 | /// in front of all other Windows
54 | ///
55 | /// true if this window is a popup; otherwise, false.
56 | public bool IsPopup {
57 | get { return isPopup; }
58 | set { isPopup = value; }
59 | }
60 |
61 | public WindowProperties(bool suppressPrefabProperties = false) {
62 | WindowQueuePriority = WindowPriority.ForceForeground;
63 | HideOnForegroundLost = false;
64 | SuppressPrefabProperties = suppressPrefabProperties;
65 | }
66 |
67 | public WindowProperties(WindowPriority priority, bool hideOnForegroundLost = false, bool suppressPrefabProperties = false) {
68 | WindowQueuePriority = priority;
69 | HideOnForegroundLost = hideOnForegroundLost;
70 | SuppressPrefabProperties = suppressPrefabProperties;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/UISettings.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | namespace deVoid.UIFramework
5 | {
6 | ///
7 | /// Template for an UI. You can rig the prefab for the UI Frame itself and all the screens that should
8 | /// be instanced and registered upon instantiating a new UI Frame.
9 | ///
10 |
11 | [CreateAssetMenu(fileName = "UISettings", menuName = "deVoid UI/UI Settings")]
12 | public class UISettings : ScriptableObject
13 | {
14 | [Tooltip("Prefab for the UI Frame structure itself")]
15 | [SerializeField] private UIFrame templateUIPrefab = null;
16 | [Tooltip("Prefabs for all the screens (both Panels and Windows) that are to be instanced and registered when the UI is instantiated")]
17 | [SerializeField] private List screensToRegister = null;
18 | [Tooltip("In case a screen prefab is not deactivated, should the system automatically deactivate its GameObject upon instantiation? If false, the screen will be at a visible state upon instantiation.")]
19 | [SerializeField] private bool deactivateScreenGOs = true;
20 |
21 | ///
22 | /// Creates an instance of the UI Frame Prefab. By default, also instantiates
23 | /// all the screens listed and registers them. If the deactivateScreenGOs flag is
24 | /// true, it will deactivate all Screen GameObjects in case they're active.
25 | ///
26 | /// Should the screens listed in the Settings file be instanced and registered?
27 | /// A new UI Frame
28 | public UIFrame CreateUIInstance(bool instanceAndRegisterScreens = true) {
29 | var newUI = Instantiate(templateUIPrefab);
30 |
31 | if (instanceAndRegisterScreens) {
32 | foreach (var screen in screensToRegister) {
33 | var screenInstance = Instantiate(screen);
34 | var screenController = screenInstance.GetComponent();
35 |
36 | if (screenController != null) {
37 | newUI.RegisterScreen(screen.name, screenController, screenInstance.transform);
38 | if (deactivateScreenGOs && screenInstance.activeSelf) {
39 | screenInstance.SetActive(false);
40 | }
41 | }
42 | else {
43 | Debug.LogError("[UIConfig] Screen doesn't contain a ScreenController! Skipping " + screen.name);
44 | }
45 | }
46 | }
47 |
48 | return newUI;
49 | }
50 |
51 | private void OnValidate() {
52 | List objectsToRemove = new List();
53 | for(int i = 0; i < screensToRegister.Count; i++) {
54 | var screenCtl = screensToRegister[i].GetComponent();
55 | if (screenCtl == null) {
56 | objectsToRemove.Add(screensToRegister[i]);
57 | }
58 | }
59 |
60 | if (objectsToRemove.Count > 0) {
61 | Debug.LogError("[UISettings] Some GameObjects that were added to the Screen Prefab List didn't have ScreenControllers attached to them! Removing.");
62 | foreach (var obj in objectsToRemove) {
63 | Debug.LogError("[UISettings] Removed " + obj.name + " from " + name + " as it has no Screen Controller attached!");
64 | screensToRegister.Remove(obj);
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # deVoid UI Framework
2 | 
3 |
4 | *You can see a live demo of the [the examples repo](https://github.com/yankooliveira/uiframework_examples) at my [itch.io page](https://yanko.itch.io/devui)*
5 |
6 | **TL;DR:**
7 | *Step 1:*
8 |
9 | Right click on project view
10 | `Create -> deVoid UI -> UIFrame Prefab`
11 | then drag the Prefab onto your scene
12 |
13 | *Step 2:*
14 | Get a reference to your UI Frame and register some screens
15 | ```c#
16 | uiFrame.RegisterScreen("YourScreenId", yourScreenPrefab);
17 | ```
18 |
19 | *Step 3:*
20 | Show your screens
21 | ```c#
22 | uiFrame.OpenWindow("YourWindowId");
23 | uiFrame.ShowPanel("YourPanelId");
24 | ```
25 |
26 | Or, if you have some data payload
27 |
28 | ```c#
29 | uiFrame.OpenWindow("YourWindowId", yourWindowProperties);
30 | uiFrame.ShowPanel("YourPanelId", yourPanelProperties);
31 | ```
32 |
33 | *Step 4:*
34 | Now that you're familiar with the API, right click on project view
35 | `Create -> deVoid UI -> UI Settings`
36 |
37 | Rig your UI Frame prefab as the UI Template, drag all your screens in the Screens To Register list and simply do
38 |
39 | ```c#
40 | uiFrame = yourUiSettings.CreateUIInstance();
41 | ```
42 |
43 | Which will give you a new UI Frame instance and automatically do *Step 2* for you.
44 |
45 | Make sure to check out [the examples repo](https://github.com/yankooliveira/uiframework_examples) and read [the manual](https://github.com/yankooliveira/uiframework/blob/master/MANUAL.md)!
46 |
47 | ## What?
48 | The *deVoid UI Framework* (or **devUI** for short) is a simple architecture for UI handling and navigation in Unity. It enforces one simple rule: *you can **never** directly access the internals of your UI code from external code*, but anything else is fair game. You want to read data from Singletons from inside your UI? Sure. You want to sandwich mediators, views and controllers and do MVC by the book? Go for it. You want to skip the data passing functionalities and use your own MVVM framework? Power to you. Do whatever floats your boat and works best for your needs.
49 |
50 | ## Why?
51 | Having worked with mobile F2P games for years, I've done my fair share of UI. And I *hate* doing UI.
52 | So I figured the more people learn how to do it, the smaller the chances that I ever have to do it again 🌈
53 |
54 | *(Also, sharing is caring and I'm secretly a hippie)*
55 |
56 | ### Features
57 | * Simple to extend and customize
58 | * Support for navigation history and queuing
59 | * Transition animations
60 | * Blocking user input while screens transition (especially handy for touch input)
61 | * Priority and layering
62 | * Focus on type safety and flexibility
63 |
64 | ### Known Limitations
65 | * No built in support for controller navigation
66 |
67 | ### Disclaimer:
68 | This is **A** solution to work with UI - I don't believe in perfect, one-size-fits-all solutions (and nor should you in anyone who tells you otherwise). But this architecture was battle tested in more than one game over the last few years (from game jams to medium and bigger sized games), and it ticked all the boxes for me so far.
69 |
70 | While I tried to keep this it as flexible and as easy to deal with as possible, there is still some structure to be followed. I recommend checking out [the examples repo](https://github.com/yankooliveira/uiframework_examples) and taking a look at [the manual](https://github.com/yankooliveira/uiframework/blob/master/MANUAL.md) before using.
71 |
72 | Although the architecture itself is sound and was tested in live environments, this implementation was made from scratch on my free time. I have been using it for a few months now and did some cleanup and bugfixes for the public release, so it *Should Work(TM)*.
73 |
74 | ### Acknowledgements
75 | A lot of the structure is inspired by a design used by [Renan Rennó](https://www.linkedin.com/in/renanrenno/) when we worked together. Special thanks to everyone who I made use this architecture through the years and to [Sylvain Cornillon](https://www.bossastudios.com/the-team/), who allowed me to open source it and helped me find more proper names for the classes, or I'd be still calling it *"UIManagerOrWhatevs"* cause I'm a rebel.
76 |
77 | You can read my original (even more) verbose post about this architecture [in my blog](http://yankooliveira.com/index.php/2017/12/27/uisystem/) and poke me on twitter [@yankooliveira](https://twitter.com/yankooliveira).
78 |
--------------------------------------------------------------------------------
/Editor/UIFrameworkTools.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using UnityEditor;
5 | using UnityEngine;
6 | using UnityEngine.EventSystems;
7 | using UnityEngine.UI;
8 |
9 | namespace deVoid.UIFramework.Editor
10 | {
11 | public static class UIFrameworkTools
12 | {
13 | [MenuItem("Assets/Create/deVoid UI/UI Frame in Scene", priority = 2)]
14 | public static void CreateUIFrameInScene() {
15 | CreateUIFrame();
16 | }
17 |
18 | [MenuItem("Assets/Create/deVoid UI/UI Frame Prefab", priority = 1)]
19 | public static void CreateUIFramePrefab() {
20 | var frame = CreateUIFrame();
21 |
22 | string prefabPath = GetCurrentPath();
23 | prefabPath = EditorUtility.SaveFilePanel("UI Frame Prefab", prefabPath,"UIFrame", "prefab");
24 |
25 | if (prefabPath.StartsWith(Application.dataPath)) {
26 | prefabPath = "Assets" + prefabPath.Substring(Application.dataPath.Length);
27 | }
28 |
29 | if (!string.IsNullOrEmpty(prefabPath)) {
30 | CreateNewPrefab(frame, prefabPath);
31 | }
32 |
33 | Object.DestroyImmediate(frame);
34 | }
35 |
36 | private static GameObject CreateUIFrame() {
37 | var uiLayer = LayerMask.NameToLayer("UI");
38 | var root = new GameObject("UIFrame");
39 | var camera = new GameObject("UICamera");
40 |
41 | var cam = camera.AddComponent();
42 | cam.clearFlags = CameraClearFlags.Depth;
43 | cam.cullingMask = LayerMask.GetMask("UI");
44 | cam.orthographic = true;
45 | cam.farClipPlane = 25;
46 |
47 | root.AddComponent();
48 | var canvas = root.AddComponent