JSONStorables in Virt-A-Mate Plugin Development
JSONStorables provide a standardized way to store, expose, and manage parameters and actions in Virt-A-Mate (VaM). They support state persistence, UI binding, and inter-plugin communication.
JSONStorables let you:
- Persist plugin parameters (e.g., floats, booleans, strings).
- Expose plugin functionality (via actions) to the VaM UI and to other plugins.
- Enable external control by allowing one plugin to reference and trigger functionality in another.
- JSONStorables automatically persist their states with scene saves and restores, meaning plugin parameters maintain their values when users save and reload scenes.
Virt-A-Mate plugins automatically handle JSON serialization when you use standard JSONStorables (like JSONStorableFloat, JSONStorableBool, JSONStorableAction, etc.). You do not need to explicitly include the following line in your script if you’re only using standard JSONStorables provided by VaM:
using SimpleJSON;
However, you must include it explicitly if your plugin directly manipulates JSON data manually, for example:
public override JSONNode GetJSON()
{
JSONClass jc = new JSONClass();
jc["customValue"] = "My custom data";
return jc;
}
Types of JSONStorables
When you register a JSONStorable in Virt-A-Mate, the appropriate UI element is automatically generated:
JSONStorable Type | Automatically Generated UI Element |
---|---|
JSONStorableFloat |
Slider |
JSONStorableBool |
Checkbox (Toggle) |
JSONStorableString |
Text Field |
JSONStorableStringChooser |
Dropdown Menu (Popup) |
JSONStorableColor |
Color Picker |
JSONStorableAction |
Button |
JSONStorableFloat
Stores a floating-point number.
JSONStorableFloat myFloat = new JSONStorableFloat("MyFloat", 0.5f, v => {
// Callback when value changes
}, 0f, 1f);
RegisterFloat(myFloat);
JSONStorableBool
Stores a boolean value.
JSONStorableBool myBool = new JSONStorableBool("MyBool", false, v => {
// Callback when value changes
});
RegisterBool(myBool);
JSONStorableString
Stores a string value.
JSONStorableString myString = new JSONStorableString("MyString", "default", v => {
// Callback when value changes
});
RegisterString(myString);
JSONStorableAction
Exposes an action that can be triggered from the VaM UI or externally.
JSONStorableAction myAction = new JSONStorableAction("DoSomething", () => {
// Action code here
});
RegisterAction(myAction);
JSONStorableStringChooser
Provides a dropdown menu for selecting from a list of options.
List<string> options = new List<string> { "Option1", "Option2" };
JSONStorableStringChooser chooser = new JSONStorableStringChooser("MyChooser", options, options[0], "Label");
RegisterStringChooser(chooser);
Using JSONStorables
1. Declaration & Initialization
Define a JSONStorable for the parameter or action you want to expose.
Example (a float parameter for speed):
JSONStorableFloat speed = new JSONStorableFloat("Speed", 1.0f, v => {
// Update speed logic
}, 0f, 10f);
2. Registration
Register the storable with VaM to make it accessible.
RegisterFloat(speed);
3. UI Integration
VaM automatically binds registered storables to UI elements. For example, a JSONStorableFloat is typically represented as a slider.
4. External Communication
Other plugins can access and trigger functionality using the VaM API. For example, to trigger a play action:
JSONStorableAction playAction = timelineStorable.GetAction("Play");
if (playAction != null) {
playAction.actionCallback.Invoke();
}
Example Code
Exposing a Play Animation Action
Below is a simplified example of a Timeline plugin exposing a play action:
using SimpleJSON;
using UnityEngine;
public class TimelinePlugin : MVRScript {
private JSONStorableAction _playJSON;
public override void Init() {
// Initialize and register the play action
_playJSON = new JSONStorableAction("Play", () => {
PlayAnimation();
});
RegisterAction(_playJSON);
}
private void PlayAnimation() {
// Logic to play the animation
SuperController.LogMessage("Animation is playing!");
}
}
Accessing the Action from Another Plugin
Another plugin can trigger the play action by referencing the JSONStorableAction:
// Assuming timelinePlugin is a reference to the TimelinePlugin instance:
JSONStorableAction playAction = timelinePlugin.GetAction("Play");
if (playAction != null) {
playAction.actionCallback.Invoke();
}
One important rule in VaM plugin development is how JSONStorables are used to expose functionality across plugins. For example, the Timeline plugin declares a JSONStorableAction (commonly named something like
_playJSON
). Although this field is declared as a private member within the plugin, it is registered as a storable and becomes available via the VaM API.
This means that another plugin can obtain a reference to this action using methods like GetAction("Play " + animationName)
and trigger it externally. In other words, even though the field is private, its registration makes the action accessible to other parts of VaM, allowing plugins to communicate and control each other’s behavior in a decoupled and flexible manner.
JSONStorables Example Plugin
This plugin demonstrates using three common JSONStorables: JSONStorableFloat (slider), JSONStorableBool (toggle), JSONStorableAction (button).
using UnityEngine;
// This plugin demonstrates using three common JSONStorables:
// JSONStorableFloat (slider), JSONStorableBool (toggle), JSONStorableAction (button).
public class JSONStorablesExamplePlugin : MVRScript
{
// Declare a JSONStorableFloat named "mySpeed" to store a numeric value.
// It will automatically create a Slider UI element.
private JSONStorableFloat mySpeed;
// Declare a JSONStorableBool named "enableFeature" to store a true/false value.
// It will appear as a checkbox (toggle) in the UI.
private JSONStorableBool enableFeature;
// Declare a JSONStorableAction named "logAction" to create a clickable button in UI.
private JSONStorableAction logAction;
// The Init method is called once when your plugin is loaded.
public override void Init()
{
// Initialize "mySpeed" JSONStorableFloat with the following parameters:
// Name = "Speed", Default value = 1.0f, Min = 0, Max = 10.
// OnSpeedChanged method will be called whenever the slider is moved by the user.
mySpeed = new JSONStorableFloat("Speed Slider", 1.0f, OnSpeedChanged, 0f, 10f);
RegisterFloat(mySpeed); // Register "mySpeed" so Virt-A-Mate can persist and manage it.
CreateSlider(mySpeed, false); // Creates a slider UI element bound to "mySpeed".
// Initialize a checkbox (JSONStorableBool) to let users enable or disable a feature.
enableFeature = new JSONStorableBool("Enable Feature Checkbox", true, OnEnableFeatureChanged);
RegisterBool(enableFeature); // Register "enableFeature" for persistence.
CreateToggle(enableFeature, false); // Creates a toggle UI checkbox linked to "enableFeature".
// Initialize a button action named "LogCurrentSettings".
logAction = new JSONStorableAction("LogCurrentSettings", LogCurrentSettings);
RegisterAction(logAction); // Register "logAction" so Virt-A-Mate can handle the action.
CreateButton("Log Current Settings", false).button.onClick.AddListener(() => logAction.actionCallback.Invoke());
}
// This method is automatically called whenever the user adjusts the slider for "mySpeed".
private void OnSpeedChanged(float newSpeed)
{
// Log the new speed value into VaM's console to inform the user or for debugging.
SuperController.LogMessage("Speed changed to: " + newSpeed);
}
// This callback is automatically called when the checkbox "enableFeature" is toggled by the user.
private void OnEnableFeatureChanged(bool enabled)
{
// Log the current state of the toggle (enabled or disabled).
SuperController.LogMessage("Enable Feature changed to: " + enabled);
}
// This method runs when the user clicks the button created by "logAction".
private void LogCurrentSettings()
{
// Logs the current stored values of "mySpeed" and "enableFeature" to the Virt-A-Mate console.
SuperController.LogMessage("Current Speed: " + mySpeed.val + ", Feature Enabled: " + enableFeature.val);
}
}
Example Code that toggles light on/off
This plugin toggles the “Light On” setting in the Light tab of an atom named “InvisibleLight”.
using UnityEngine;
using UnityEngine.Events;
// This plugin toggles the "Light On" setting in the Light tab of an atom named "InvisibleLight".
// It demonstrates best practices for accessing an atom's parameters in Virt-A-Mate.
public class ToggleLightOn : MVRScript
{
// The Init() method is called when the plugin is loaded.
public override void Init()
{
// Create a UI button labeled "Toggle Light On" on the left side.
// When the button is clicked, the OnButtonPress() method is called.
SetupButton("Toggle Light On", OnButtonPress, false);
}
// This method is called when the UI button is pressed.
private void OnButtonPress()
{
// UID stands for "Unique Identifier". Every atom (scene object) in VaM has a UID that uniquely identifies it.
// In our scene, we have an atom that represents our light. Its UID is set to "InvisibleLight".
Atom lightAtom = GetAtomById("InvisibleLight");
// A "storable" is a container that holds parameters for an atom, such as booleans, floats, colors, etc.
// The light component (typically AdjustLightV2) registers its parameters under a specific storable ID.
// In this case, the light component has registered its container with the ID "Light".
JSONStorable lightStorable = lightAtom?.GetStorableByID("Light");
// Check if the storable was found.
if (lightStorable != null)
{
// "on" Toggle Explanation:
// Within the "Light" storable, there is a boolean parameter registered with the key "on".
// This "on" parameter corresponds to the "Light On" toggle seen in the Light tab of the InvisibleLight atom.
// Here we retrieve that boolean parameter.
JSONStorableBool lightOn = lightStorable.GetBoolJSONParam("on");
if (lightOn != null)
{
// Toggle the "Light On" state: if the light is currently on, this will turn it off, and vice versa.
lightOn.val = !lightOn.val;
}
else
{
// Log an error if the "on" parameter could not be found.
SuperController.LogError("Could not find the 'on' parameter (Light On toggle) in the Light storable.");
}
}
else
{
// Log an error if the storable with ID "Light" was not found on the atom.
SuperController.LogError("Could not find storable 'Light' on atom 'InvisibleLight'.");
}
}
// Helper method to create a UI button.
// label: the text displayed on the button.
// callback: the method to call when the button is clicked.
// rightSide: if true, the button appears on the right side; false for the left.
private void SetupButton(string label, UnityAction callback, bool rightSide)
{
UIDynamicButton button = CreateButton(label, rightSide);
button.button.onClick.AddListener(callback);
}
}
/*
Detailed Explanation:
---------------------
1. UID (Unique Identifier):
- Every atom in Virt-A-Mate has a UID that uniquely identifies it within the scene.
- In this code, "InvisibleLight" is the UID assigned to the light atom.
- This is similar to a name you give an object, ensuring you can reference it later.
2. Storable:
- A JSONStorable is a container for parameters associated with an atom.
- Components like AdjustLightV2 register their parameters (for example, the light's on/off state, intensity, etc.) inside a storable.
similar to:
this.onJSONParam = new JSONStorableBool("on", this._on, SyncOn);
this.RegisterBool(this.onJSONParam);
- The default registration for the light component uses the storable ID "Light".
- You retrieve this container by calling GetStorableByID("Light") on the atom.
3. "on" Toggle:
- Inside the "Light" storable, the light's on/off state is controlled by a boolean parameter registered as "on".
- This is what appears as the "Light On" toggle in the Light tab of the InvisibleLight atom.
- We access it with GetBoolJSONParam("on") and then toggle its value to switch the light on or off.
*/