Unity runtime
The Unity runtime is a package, com.mythicbrain.envy-ui, shipped as C# source. The EnvyUI component reads a .envy.json document and rebuilds it in UI Toolkit at runtime, so there’s no generated UI to keep in sync. Buttons take clicks and taps, and the arrow keys or a gamepad move focus, with the Input System or the old Input Manager.
Requires Unity 6.3 or newer, the first version with the vector graphics module it draws SVG images with. It’s tested on Unity 6.6.
Install
- In Envy, export the Unity package (Hand off › Unity package…) and unzip it into the root of your Unity project, next to
Assets/. It addsPackages/com.mythicbrain.envy-ui, your document inAssets/UI/and a demo script,Assets/EnvyDemo.cs. Unity picks the package up on its own. .envy.jsonand.envyfiles import as EnvyDocument assets.- To try it, add the EnvyDemo component to an empty GameObject (its Document fills itself in) and press Play. In your own scenes, add an EnvyUI component, or let a script add it:
using Envy;
using UnityEngine;
public class GameUI : MonoBehaviour
{
public EnvyDocument document; // Assets/UI/my_ui.envy.json
EnvyUI ui;
void Awake()
{
ui = gameObject.AddComponent<EnvyUI>();
ui.UiEvent += OnUiEvent;
ui.LoadDocument(document);
ui.SetData("player.hp", 40);
ui.FocusFirstButton();
}
void OnUiEvent(string evt, string payload)
{
switch (evt)
{
case "new_hunt":
StartNewHunt();
break;
}
}
}
The AI handoff package also writes a controller like this one for your document, unity/<Name>UI.cs, with typed setters and a case for every event the UI sends.
Live reload
While the game runs in the Unity editor, save the document from Envy over its file in Assets/UI/ (see Projects and saving). The running UI reloads in place.
- The reload keeps the data the game set, the open screen and its overlays.
DocumentReloadedfires. - It watches
.envy.jsonfiles. Projects (.envy) aren’t watched. - Built players never reload.
hotReload = falseturns it off in the editor too.
Fonts
Fonts embedded in the document (Envy: Assets › Fonts, or Embed under the font picker) are used as they are, WOFF and WOFF2 included. For other families, map a font asset or a TTF/OTF Font in EnvyUI › Fonts, per family and weight. A mapping wins over an embedded font of the same family. A family with neither uses an installed font of that name, then Unity’s default.
Inspector fields
| Field | Meaning |
|---|---|
document |
An imported .envy.json or .envy (an EnvyDocument asset) |
documentJson |
Or the document as a TextAsset |
documentPath |
Or a file path, absolute or relative to StreamingAssets |
startScreen |
Screen to show first, by name or id. Empty shows the first screen |
scaleMode |
Fit (default) scales the design resolution into the panel and stretches to its aspect, like the web runtime |
safeAreaMargins |
Safe-area margins in pixels (left, top, right, bottom) for screens set to keep inside the safe area. -1 reads the device’s safe area on mobile |
autoplay |
Play autoplay animations when a screen opens (default on) |
useSampleData |
Start with the document’s sample data (default on) |
hotReload |
Reload the document when its file changes, in the editor (default on) |
locale |
Locale for translated text, like "de", or "pseudo". Empty shows the base text |
cancelClosesOverlay |
Cancel (Escape, gamepad B) closes the top overlay (default on) |
fonts |
Family and weight → font asset or Font, for fonts the document doesn’t embed |
panelSettings |
Panel settings for the UIDocument EnvyUI creates. Empty draws 1:1 pixels over the scene. EnvyUI uses the GameObject’s own UIDocument when it has one |
sortingOrder |
Draw order of that panel; higher draws on top (default 100) |
Methods
| Method | Does |
|---|---|
Goto(screen, transition = null) |
Shows a screen by name or id. transition overrides the screen’s own |
Push(screen, transition = null), Pop() |
Opens an overlay, closes the top one |
Cancel() |
Closes the top overlay as Cancel does: presses its cancel button, else pops |
GetStack(), GetScreenNames() |
The open screens (base first), the game’s screens |
SetData(path, value), MergeData(patch) |
Changes data |
GetData(path = "") |
A value, or all data with no path |
Play(anim), Stop(anim = ""), IsPlaying(anim) |
Animations. Stop() with no name stops them all |
SetLocale(locale), GetLocales() |
Language. SetLocale returns the locale it matched |
GetElement(nameOrId) |
The VisualElement built for a layer |
FocusFirstButton() |
Starts keyboard and gamepad navigation on the initial button, else the first one |
Press(button) |
Presses a button from code, by name or id |
LoadDocument(asset), LoadFile(path), LoadJson(text) |
Loads another document |
Reload() |
Loads the assigned document again |
Goto, Push, Pop, Play and the loaders return true when they worked. CurrentScreenName is the name of the screen on top.
Events
| Event | When |
|---|---|
UiEvent(evt, payload) |
A button emitted an event. onUiEvent is the same as a UnityEvent, to wire in the Inspector |
ScreenChanged(name) |
A new screen shows: Goto, Push or Pop ran. onScreenChanged is the UnityEvent |
DataChanged(data) |
Data changed |
DocumentReloaded |
The document changed on disk and was reloaded |
How it’s drawn
| Envy | UI Toolkit |
|---|---|
| Anchors and offsets | Absolute left / top / right / bottom, in % and px |
| Auto layout (and wrap) | Flexbox |
| Fill, border, radius | Background color, borders, border radii |
| Shadows, gradients | Meshes from generateVisualContent; a shape’s shadow is a drop-shadow filter |
| Shapes | Painter2D |
| Text | TextElement with TextCore font assets |
| Images (fit, nine-slice, tint), SVG | Background image, nine-slice, tint; VectorImage |
| Layer and background blur | Blur filters |
Text is drawn from signed distance fields, so glyphs look about 10% lighter than in Chrome at the same size and weight; weights still match each other. See known differences for the rest.
Tests
The package has EditMode and PlayMode tests, and they read the same fixtures as the Godot tests:
Unity -batchmode -nographics -projectPath runtimes/unity -runTests -testPlatform EditMode -testResults edit.xml
Unity -batchmode -projectPath runtimes/unity -runTests -testPlatform PlayMode -testResults play.xml
PlayMode draws, so it runs without -nographics.