VAM API

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.
   */