README.md
February 5, 2026 · View on GitHub
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.
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 sizeFrom Menu: SS / Screen Settings / Input Screen Width & Height / Save
2. Create a screen
From Menu: SS / Screen Generator / Input Screen Name / Generate
3. 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");
4. Close a screen
Core.Close();
5. Load a scene with automatic fade
Core.Load<Scene1Controller>(sceneName: "Scene1");
Basic Usage Tutorial Video
Advance Usage
1. Addressables
From version 2.0.0, we removed Addressables supports for screen prefabs because of duplicate memory problem.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
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");
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");
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);
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");
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");
Core.Add<Screen1Controller>(screenName:"Screen1", hasShield: false);
5.3. Show/Hide the Screen shield manually (with fade animation)
Core.ShowShield();
Core.HideShield();
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);
By default, this parameter is false, instantiate a new screen whenever Add is called
Core.Add<Screen2Controller>(screenName: "Screen2");
Core.Add<Screen1Controller>(screenName: "Screen1");
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);
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");
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");
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);
8. Tooltip
Show a tooltip with automatic screen padding.
From Menu, SS / Tooltip Generator, input Tooltip name, select Text type (Default or TextMeshPro), click Generate
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 = FindAnyObjectByType<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
:white_check_mark: Built-in
:white_check_mark: URP
:white_check_mark: 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