Skip to content

AnhPham/Simple-Screen-Manager-for-Unity-aka-SS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Simple & Scalable UI Manager for Unity (aka SS)

Who is this for?

  • Developers looking for a very simple UI Manager / Navigator for Unity with enough features to build any type of game, even mid-core or more complex projects.
  • Those who want to learn the basics in 10 minutes.
  • Those who want to master advanced features in 30 minutes.

Concept

  • Regardless of fullscreen page or modal/modeless window, they are all considered Screen.
  • At any given time, only one Screen is visible to optimize performance. If a Screen is shown on top of another, the underlying Screen will be temporarily hidden, and it will reappear when the top Screen is closed.
  • A Screen is a freeform prefab that does not require any specific scripts, allowing it to be used as a child object anywhere.
  • A Scene is freeform and contains any object, including its own UI canvas. The Scene’s canvas will not be hidden when a Screen is displayed on top.
  • The package size is only 38 KB and does not depend on any external libraries.

Demo

In this demo, even though they share the same prefab, the Store can be displayed as a modal window or as the content of the Store Tab.

Basic Usage

1. Screen Settings

Set this to change the main canvas scaler and the game window size
From Menu: SS / Screen Settings / Input Screen Width & Height / Save

Demo

2. Create a screen

From Menu: SS / Screen Generator / Input Screen Name / Generate

Demo

3. Drag screen prefab to Resources/Screens folder

In case of not using Addressables

Demo

4. Add a screen on top with default animation

using SS.UI;
// Call this init once when your game starts, before any other calls from the Core class.
Core.Init();
Core.Add<Screen1Controller>(screenName: "Screen1");

Demo

5. Close a screen

Core.Close();

Demo

6. Load a scene with automatic fade

Core.Load<Scene1Controller>(sceneName: "Scene1");

Demo

Basic Usage Tutorial Video

https://youtu.be/mzCAjf7hye4


Advance Usage

1. Addressables

If you want to use Addressables, you do not need to drag screen prefabs to Resources/Screens

1.1. Install Addressables package:

From Menu: Window / Package Manager / Unity Registry / Addressables / Install

1.2. Add this Scripting Define Symbol: ADDRESSABLE

From Menu: Edit / Project Settings / Player / Other Settings / Scripting Define Symbols / + / ADDRESSABLE / Apply

Demo

1.3. Add screen prefab to addressables groups

Make sure the addressable name is the same as the screen name, not a path to the prefab.

Demo

2. Screen Animations

2.1. Default Screen Animations:

public enum ScreenAnimation
{
    None,       // No animation
    BottomHide, // The screen slides from the center to the bottom when hiding.
    BottomShow, // The screen slides from the bottom to the center when showing.
    FadeHide,   // The screen fades out when hiding.
    FadeShow,   // The screen fades in when showing.
    LeftHide,   // The screen slides from the center to the left when hiding.
    LeftShow,   // The screen slides from the left to the center when showing.
    RightHide,  // The screen slides from the center to the right when hiding.
    RightShow,  // The screen slides from the right to the center when showing.
    RotateHide, // The screen rotates clockwise when hiding.
    RotateShow, // The screen rotates counterclockwise when showing.
    ScaleHide,  // The screen scales down to 0 when hiding.
    ScaleShow,  // The screen scales up to 1 when showing.
    TopHide,    // The screen slides from the center to the top when hiding.
    TopShow     // The screen slides from the top to the center when showing.
}

2.2. Set Animations when adding screen:

The screen slides from the left to the center when showing.

Core.Add<Screen1Controller>(screenName: "Screen1", showAnimation: ScreenAnimation.LeftShow, hideAnimation: ScreenAnimation.LeftHide);

The screen slides from the center to the left when hiding. The 'hideAnimation' which is declared in the Add function will be used

Core.Close();

The screen fades out when hiding.

Core.Close(hideAnimation: ScreenAnimation.FadeHide);

2.3. Custom Screen Animations:

Put your custom animations (Unity legacy animations) in Resources/Animations

Demo

Add screen with custom animations

Core.Add<Screen1Controller>(screenName: "Screen1", showAnimation: "Custom1Show", hideAnimation: "Custom1Hide");

2.4. Custom Animation Object:

In default, animations will be added to the root object of screen

Core.Add<Screen1Controller>(screenName: "Screen1");

Demo

In case you only want to animate a few objects on the screen, the rest are static and not animated

Core.Add<Screen1Controller>(screenName: "Screen1", animationObjectName: "Animation");

Demo

2.5. Screen Animation Speed

In default, Screen Animation Speed is 1. You can change it.

Core.Set(screenAnimationSpeed: 1.5f);

2.6. Show Animation One Time

Indicate whether a screen play its show animation again when the screen above it closes. By default, showAnimationOneTime is false.

Core.Set(showAnimationOneTime: true);

2.7. None Screen Animation

For None Screen Animation, you can use an empty string for showAnimation & hideAnimation like this:

Core.Add<Screen1Controller>(screenName: "Screen1", showAnimation: "", hideAnimation: "");

3. Events

3.1. On Screen Loaded

Note: onScreenLoaded is called after Awake & OnEnable, before Start of scripts in screen

Core.Add<Screen1Controller>(screenName: "Screen1", onScreenLoad: (screen) => {
    // screen.Init();
});

3.2. On Scene Loaded

Note: onSceneLoaded is called after Awake & OnEnable, before Start of scripts in scene

Core.Load<Scene1Controller>(sceneName: "Scene1", onSceneLoaded: (scene1) =>
{
    // scene1.Init();
});

3.3. On Screen Closed

Note: onScreenClosed is called after the hideAnimation is ended (right after the screen is destroyed)

Core.Close(() =>
{
    // Code after closing this screen
});

3.4. On Key Back

If you create a screen by the Screen Generator, the screen controller will implement OnKeyBack of the IKeyBack interface by default. It means when players press the physics back button on Android (or ESC key on PC), the OnKeyBack() will be called. If you don't implement IKeyBack, Core.Close() will be called instead.

public class Screen1Controller : MonoBehaviour, IKeyBack
{
    public void OnKeyBack()
    {
        Core.Close();
    }
}

3.5. On Screen Added

Some projects require sending logs for analytics, indicating which screen is added, from which screen, and whether it was added manually (user click) or automatically.

// On Start of Main
Core.AddListener(onScreenAdded: (toScreen, fromScreen, manually) => {
    Debug.Log(string.Format("Add screen {0} from screen {1} ") + (manually ? "manually" : "automatically"));
});
Core.Load<Scene1Controller>(sceneName: "Scene1");
// On Screen1 Button Tap
Core.Add<Screen1Controller>(screenName: "Screen1", manually:true);
// On Start of Screen1Controller
Core.Add<Screen2Controller>(screenName: "Screen2", manually:false);

Output:

Added Screen1 from Scene1 manually
Added Screen2 from Screen1 automatically

3.6. On Screen Changed

Some projects require displaying an ads banner only when no screens are being shown.

void OnEnable()
{
    Core.AddListener(OnScreenChanged);
}
void OnDisable()
{
    Core.RemoveListener(OnScreenChanged);
}
void OnScreenChanged(int screenCount)
{
    if (screenCount > 0)
    {
        // Banner.Hide();
    }
    else
    {
        // Banner.Show();
    }
}

4. Set conditions to display screen

4.1. Wait Until No Screen

In this example, Screen2 will be shown when user closes Screen1, then Screen3 will be shown when user closes Screen2.

Core.Add<Screen1Controller>(screenName: "Screen1");
Core.Add<Screen2Controller>(screenName: "Screen2",  waitUntilNoScreen: true);
Core.Add<Screen3Controller>(screenName: "Screen3",  waitUntilNoScreen: true);

Demo

This example does not use waitUntilNoScreen, 3 Screens will appear consecutively.

Core.Add<Screen1Controller>(screenName: "Screen1");
Core.Add<Screen2Controller>(screenName: "Screen2");
Core.Add<Screen3Controller>(screenName: "Screen3");

Demo

4.2. Custom Add-Condition

In this example, Screen1 will be shown when the bool something variable becomes true

bool something = false;
Core.Add<Screen1Controller>(screenName: "Screen1", addCondition: WaitSomething);
bool WaitSomething()
{
    return something;
}

4.3. Wait until no screen to do other things

In some cases, you have to wait until there is no more Screen displayed before doing something to avoid being covered by a Screen.

IEnumerator WaitUntilNoScreenToDoSomething()
{
    while (!Core.IsNoMoreScreen())
    {
        yield return 0;
    }

    // Do somethings
}

5. Screen Shield

The Screen shield is an image with customizable color and transparency, located between the current Scene (with its UI canvas) and the top Screen. By default, the Screen shield will be shown when a Screen is displayed.

5.1. Set Screen Shield Color

Core.Set(screenShieldColor: new Color(0, 0, 0, 0.8f));

5.2. Display a Screen with/without a Shield

Core.Add<Screen1Controller>(screenName:"Screen1");

Demo

Core.Add<Screen1Controller>(screenName:"Screen1", hasShield: false);

Demo

5.3. Show/Hide the Screen shield manually (with fade animation)

Core.ShowShield();
Core.HideShield();

Demo

5.4. Close on tapping Shield

Indicate whether close the top screen when users tap the shield. By default, closeOnTappingShield is false.

Core.Set(closeOnTappingShield: true);

5.5. IShieldBehavior

Implement the IShieldBehavior interface to control OnShieldTap, OnShieldHold, OnShieldRelease.

public class Screen1Controller : MonoBehaviour, IShieldBehavior
{
    public void OnShieldTap()
    {
        Core.Close();
    }

    public void OnShieldHold()
    {
        Core.HideShield();
        GetComponent<CanvasGroup>().alpha = 0;
    }

    public void OnShieldRelease()
    {
        Core.ShowShield();
        GetComponent<CanvasGroup>().alpha = 1;
    }
}

If screen does not implement IShieldBehavior and closeOnTappingShield is true, the system will check if it implements IKeyBack.

  • If yes, call OnKeyBack() when user tap the shield.
  • If no, call Core.Close() when user tap the shield.
public class Screen1Controller : MonoBehaviour, IKeyBack
{
    public void OnKeyBack()
    {
        // Do something
        Core.Close();
    }
}

6. Other parameters of adding a screen

6.1. Use Existing Screen

If this parameter is true, check if the screen is existing, bring it to the top. If not found, instantiate a new one

Core.Add<Screen2Controller>(screenName: "Screen2", useExistingScreen: true);
Core.Add<Screen1Controller>(screenName: "Screen1", useExistingScreen: true);

Demo

By default, this parameter is false, instantiate a new screen whenever Add is called

Core.Add<Screen2Controller>(screenName: "Screen2");
Core.Add<Screen1Controller>(screenName: "Screen1");

Demo

6.2. Destroy Top Screen

If this parameter is true, destroy the top screen after adding a screen (right after OnScreenLoaded is called).

Core.Add<Screen2Controller>(screenName: "Screen2", destroyTopScreen: true);

Demo

By default, this parameter is false, temporary hide the top screen when add the Screen2, and show it again after closing the Screen2

Core.Add<Screen2Controller>(screenName: "Screen2");

Demo

6.3. Hide Top Screen

If this parameter is false, the system will not hide the top screen when add other screen. By default it is true.

Core.Add<Screen2Controller>(screenName: "Screen2", hideTopScreen: false);

6.4. Shield Alpha

We can set alpha of shield when add a screen. By default it is -1, means use screenShieldColor (set by Core.Set) and do not change alpha.

Core.Add<Screen2Controller>(screenName: "Screen2", shieldAlpha: 0.9f);

7. Loading

7.1. Scene Loading

Show a loading UI while loading a Scene.

From Menu, SS / Screen Generator, create a Screen named SceneLoading. Use Core.asyncOperationProgress to get progress of scene loading, like below example

public class SceneLoadingController : MonoBehaviour
{
    [SerializeField] RectTransform m_Progress;

    private void Update()
    {
        m_Progress.sizeDelta = new Vector2(Core.asyncOperationProgress * 500, 50);
    }
}

You also can implement ISceneLoading to add Show/Hide animations for the SceneLoadingController.

using SS.UI;

public class SceneLoadingController : MonoBehaviour, ISceneLoading
{
    [SerializeField] RectTransform m_Progress;
    [SerializeField] RectTransform m_ProgressBG;
    [SerializeField] Animation m_Animation;

    public void Show()
    {
        m_Animation.Play("SceneLoadingShow");
    }

    public void Hide()
    {
        m_Animation.Play("SceneLoadingHide");
    }

    public float ShowDuration()
    {
        return m_Animation["SceneLoadingShow"].length;
    }

    public float HideDuration()
    {
        return m_Animation["SceneLoadingHide"].length;
    }

    private void OnEnable()
    {
        m_Progress.sizeDelta = new Vector2(0, m_ProgressBG.sizeDelta.y);
    }

    private void Update()
    {
        m_Progress.sizeDelta = new Vector2(Core.asyncOperationProgress * m_ProgressBG.sizeDelta.x, m_ProgressBG.sizeDelta.y);
    }
}

Do not forget to set the Scene Loading name on App Launch

Core.Set(sceneLoadingName: "SceneLoading");

Demo

7.2. Loading on Top

Show a loading UI on top of all screens

From Menu, SS / Screen Generator, create a Screen named Loading. You should add a loop loading animation to it.

Do not forget to set the Loading name on App Launch

Core.Set(loadingName: "Loading");

Show Loading

Core.Loading(true);

Hide Loading

Core.Loading(false);

Demo

8. Tooltip

Show a tooltip with automatic screen padding.

Demo

From Menu, SS / Tooltip Generator, input Tooltip name, select Text type (Default or TextMeshPro), click Generate

Demo

Edit the Tooltip prefab as you want, drag it to Resources/Screens folder (or drag to Addressable Group in case of using Addressables). You can also edit the Tooltip showing animation

Do not forget to set the Tooltip name on App Launch

Core.Set(tooltipName: "Tooltip");

Show the tooltip

public Transform button;
// targetY is the distance from the start position along the Y axis
Core.ShowTooltip(text: "Tooltip Text", worldPosition: button.position, targetY: 100f);

Hide the tooltip

Core.HideTooltip();

9. Other useful methods of Core

9.1. Top

In some cases, you want to add somethings to the top of all Screens (like some flying coins)

// Coin.cs
transform.SetParent(Core.Top);

9.2. Destroy

Destroy immediately the screen which is at the top of all screens, without playing animation.

Core.Destroy();

9.3. DestroyAll

Destroy immediately all screens, without playing animation.

Core.DestroyAll();

9.4. Destroy or Close a specific screen

var screen1 = FindObjectOfType<Screen1Controller>(true);
Core.Destroy(screen: screen1);
Core.Close(screen: screen1);

9.5. Init

Init this system using default managers or customized managers. Call this init once when your game starts, before any other calls from the Core class.

Core.Init(screenManagerPath: "Managers/MyScreenManager");

Render pipeline compatibility

✅ Built-in
✅ URP
✅ HDRP

Unity Version

2022.3.30 or higher. In fact this package can work on most versions of Unity because it is very compact.

License

This software is released under the MIT License.
You are free to use it within the scope of the license.
However, the following copyright and license notices are required for use.

https://github.com/AnhPham/Simple-Screen-Manager-for-Unity-aka-SS?tab=MIT-1-ov-file

About

A simple and scalable Unity library for managing and navigating UI with high performance and reusability.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages