PurrUI ↗
dev.purrnet.ui Procedural UI and a stack based screen manager for Unity's UI.
PurrUI
A Unity UI framework by the PurrNet team featuring procedural UI rendering, view management, and UI pooling.
Features
- Procedural UI - SDF-based rendering with
RectangleGraphicandGlowGraphic(for glow/shadows) - Material Icons - Simple to use material icon support for textmeshpro
- View Management -
ViewStackandViewCollectionfor managing UI views with transition support - Sounds2D - Lightweight 2D audio system with a fluent API for fire-and-forget sound effects
Installation
Unity Package Manager (Git URL)
Latest release:
https://github.com/PurrNet/PurrUI.git?path=Assets/PurrUI#release
Latest development:
https://github.com/PurrNet/PurrUI.git?path=Assets/PurrUI#dev
Procedural UI
Material Icons
Hello world
<icon=reddit><icon=gamepad_circle><icon=gamepad_variant>
<icon=gamepad_square><icon=keyboard_f1><icon=keyboard_return>
To use Material Icons you simply need to follow the example above <icon=your_icon_name> and you also need to add the MaterialIconProcessor component to your text.
This MaterialIconProcessor allows the icons to work but it also gives you a handy little search bar to search for icons so you never get lost.
The Copy button will give you something like this directly: <icon=account_alert>.
View System Documentation
PurrUI provides a stack-based view navigation system for Unity. Views are managed through a ViewStack that handles pushing, popping, ordering, visibility, and animated transitions.
All classes live in the PurrNet.UI namespace.
Core Concepts
| Class | Type | Purpose |
|---|---|---|
ViewStack |
MonoBehaviour | Navigation controller that manages a stack of views |
MonoView |
MonoBehaviour | Base class for all views |
ViewCollection |
ScriptableObject | Asset that holds references to view prefabs |
ViewTransitions |
Static class | Built-in transition animations (fade, slide, etc.) |
Setup
1. Create a ViewCollection
Right-click in your Project window and select Create > PurrNet > View Collection.
A ViewCollection is a ScriptableObject that stores references to your view prefabs. It has two modes:
- Auto Generate (default): Assign folders to watch and the collection automatically discovers all
MonoViewprefabs inside them. It refreshes whenever assets change. - Manual: Disable
autoGenerateand drag prefabs into theviewsarray yourself.
2. Create View Prefabs
Each view is a prefab with a MonoView (or subclass) component on its root GameObject. MonoView automatically requires a Canvas and CanvasGroup, so those are added for you.
3. Add a ViewStack to Your Scene
Add a ViewStack component to a GameObject. In the inspector, configure:
- Parent: The transform where instantiated views will be parented (defaults to the ViewStack's own transform).
- Prefabs: Your
ViewCollectionasset. - Push On Start (optional): A view to automatically push when the scene starts.
- Order Offset: An offset applied to canvas sorting order values.
Creating Custom Views
Subclass MonoView to create your own views:
using PurrNet.UI;
using UnityEngine;
public class ProfileView : MonoView
{
[SerializeField] private TMPro.TMP_Text _username;
[SerializeField] private TMPro.TMP_Text _bio;
public void Setup(string username, string bio)
{
_username.text = username;
_bio.text = bio;
}
}
The Setup Convention
We follow a convention of adding a Setup method to views that need initialization data. This is not enforced by the base class, it's simply a pattern. The idea is that Setup is called immediately after pushing:
_stack.Push<ProfileView>().Setup("Jane", "Hello world!");
All built-in views provided by PurrUI follow this pattern. You are encouraged to do the same for consistency, but it is not required.
Pushing and Popping Views
Pushing
There are two ways to push a view onto the stack:
// Generic push: looks up the prefab by type from the ViewCollection
var view = _stack.Push<DialogView>();
// Direct prefab push
var view = _stack.Push(someViewPrefab);
When a view is pushed:
- The current top view (if any) is moved to the background (raycasts disabled).
- The prefab is instantiated under the configured parent transform.
- The view is initialized and assigned a sorting order.
- If the view defines an enter transition, it plays (raycasts are blocked until it completes).
Popping
// Pop the top view
_stack.Pop();
// Pop a specific view instance (regardless of position in the stack)
_stack.Pop(viewInstance);
// From inside a view, call CloseMe() (can be wired to a button in the inspector)
CloseMe();
When a view is popped:
- If the view defines an exit transition, it plays before the view is destroyed.
- The new top view is moved to the foreground (raycasts re-enabled,
OnBecomeForegroundcalled).
Other Stack Operations
// Remove all views from the stack (exit transitions play for each)
_stack.Clear();
// Move an existing view to the top of the stack
_stack.MoveToTop(viewInstance);
_stack.MoveToTop<DialogView>();
Transitions
Override OnEnterTransition and OnExitTransition in your MonoView subclass to define animated transitions. They return IEnumerator coroutines.
public class MyView : MonoView
{
[SerializeField] private RectTransform _content;
protected override IEnumerator OnEnterTransition()
{
return ViewTransitions.FadeIn(this);
}
protected override IEnumerator OnExitTransition()
{
return ViewTransitions.FadeOut(this);
}
}
Built-in Transitions
ViewTransitions provides ready-to-use animations. All use smoothstep easing and unscaled time (unaffected by Time.timeScale). Default duration is 0.2s.
Fade:
ViewTransitions.FadeIn(view, duration)
ViewTransitions.FadeOut(view, duration)
ViewTransitions.Fade(canvasGroup, fromAlpha, toAlpha, duration)
Slide:
ViewTransitions.SlideFromLeft(content, duration)
ViewTransitions.SlideToLeft(content, duration)
ViewTransitions.SlideFromRight(content, duration)
ViewTransitions.SlideToRight(content, duration)
ViewTransitions.SlideFromTop(content, duration)
ViewTransitions.SlideToTop(content, duration)
ViewTransitions.SlideFromBottom(content, duration)
ViewTransitions.SlideToBottom(content, duration)
Slide methods operate on a RectTransform (typically a child content container, not the root view), using anchoredPosition and the rect's own width/height to calculate offsets.
Combining Transitions
Use ViewTransitions.Parallel to run multiple transitions at the same time:
protected override IEnumerator OnEnterTransition()
{
var fade = ViewTransitions.FadeIn(this);
var slide = ViewTransitions.SlideFromLeft(_content);
return ViewTransitions.Parallel(fade, slide);
}
protected override IEnumerator OnExitTransition()
{
var fade = ViewTransitions.FadeOut(this);
var slide = ViewTransitions.SlideToRight(_content);
return ViewTransitions.Parallel(fade, slide);
}
View Lifecycle & Visibility
- Sorting Order: Each view in the stack is assigned an incrementing sorting order (plus
_orderOffset), so views higher in the stack render on top. - Raycasts: Only the top view receives raycasts. Views below the top have raycasts disabled.
- Cull Windows Behind: If a view has
cullWindowsBehindenabled, all views below it in the stack have theirCanvasdisabled entirely. Useful for fullscreen views where nothing behind them is visible. - OnBecomeForeground: Override this virtual method in your
MonoViewsubclass to react when the view returns to the top of the stack (e.g., after the view above it is popped).
Sounds2D
A lightweight, fire-and-forget 2D audio system. It manages a small pool of AudioSource components (up to 5) on a DontDestroyOnLoad GameObject, so you never have to wire up audio sources yourself.
Quick Start
using PurrNet.UI;
using UnityEngine;
public class ButtonSFX : MonoBehaviour
{
[SerializeField] private AudioClip _clickSound;
public void OnClick()
{
Sounds2D.Play(new AudioSession(_clickSound));
}
}
AudioSession
AudioSession is a struct that describes what to play and how. It uses a fluent builder API:
// Simple: play a clip at default volume and pitch
Sounds2D.Play(new AudioSession(clip));
// With volume and pitch
Sounds2D.Play(new AudioSession(clip).WithVolume(0.8f).WithPitch(1.2f));
// Random variation: adds ± randomRange to the base value each time
Sounds2D.Play(new AudioSession(clip).WithVolume(0.7f, 0.1f).WithPitch(1f, 0.15f));
// Random clip from an array (great for footsteps, impacts, etc.)
AudioClip[] hitSounds = { hit1, hit2, hit3 };
Sounds2D.Play(new AudioSession(hitSounds).WithVolume(0.9f));
Master Volume
// Set a global master volume (0-1) that scales all sounds
Sounds2D.masterVolume = 0.5f;
Install via Git URL
https://github.com/PurrNet/PurrUI.git?path=Assets/PurrUI#release https://github.com/PurrNet/PurrUI.git?path=Assets/PurrUI#dev