Skip to content

Scripts

In Visual Scripting, a script is similar to any scripts in other languages: it is a sequence of instructions or commands that are visually constructed using Nodes and Links. These commands are then executed during runtime on the end user's device. Scripts are typically used to determine which Game Offers should be shown to an end user.

Creating a Script

Scripts are created in the Scripts page, which can be found under the LiveOps section of the Navigation panel. Upon creating a Script, it can be opened by pressing the View button next to the Script name, which will bring you to the Script View.

Using a Script

Activation By Game Event

After creating a Script, it can be activated in-game by first attaching it to a Game Event. The Script activates as soon as the Game Event itself activates when its conditions are met.

Note: If a Game Event has no conditions, it will immediately activate upon starting the game.

If the Script's Input Port is named exactly 'GameEvent' and is of Document type, it will automatically receive Game Event document which has activated this Script. It is useful to get access to Game Event's parameters.

Activation From Code

There are two ways to launch scripts from code: the SDK API and ScriptRef.

SDK API

Launch a script by its CMS ID or name, optionally passing input parameters and a launcher identifier. All methods return an instanceId that you can use to stop the script later.

// Run by name with input parameters
var instanceId = Balancy.API.VisualScripting.RunScriptByName(
    "MyRewardScript",
    "my-launcher-id",
    "{\"rewardAmount\": 100, \"rewardType\": \"gold\"}"
);

// Run by CMS ID
var instanceId = Balancy.API.VisualScripting.RunScriptById(
    "script-cms-id", "my-launcher-id", null
);

// Run by CMS ID with completion callback
var instanceId = Balancy.API.VisualScripting.RunScriptById(
    "script-cms-id",
    "my-launcher-id",
    "{\"rewardAmount\": 100}",
    onComplete: (exitPort, resultJson) =>
    {
        Debug.Log($"Script finished via: {exitPort}");
        Debug.Log($"Outputs: {resultJson}");
    }
);

// Stop a running script
Balancy.API.VisualScripting.StopScript(instanceId);
// Run by name with input parameters
const instanceId = API.Scripts.runScriptByName(
    "MyRewardScript",
    "my-launcher-id",
    { rewardAmount: 100, rewardType: "gold" }
);

// Run by CMS ID
const instanceId = API.Scripts.runScriptById(
    "script-cms-id", "my-launcher-id"
);

// Run by CMS ID with completion callback
const instanceId = API.Scripts.runScriptById(
    "script-cms-id",
    "my-launcher-id",
    { rewardAmount: 100 },
    (exitPort, outputs) => {
        console.log("Script finished via:", exitPort);
        console.log("Outputs:", outputs);
    }
);

// Stop a running script
API.Scripts.stopScript(instanceId);
Method Description
RunScriptById / runScriptById Look up a script by its CMS ID and run it
RunScriptByName / runScriptByName Look up a script by name and run it
StopScript / stopScript Stop a running script by instanceId. Returns true if found.

Input parameters are passed as a JSON string in C# or a plain object in TypeScript. The keys must match the script's input port names exactly (case-sensitive).

All launch methods accept an optional onComplete callback that fires when the script's final node exits. See Completion Callbacks below.

ScriptRef

Documents can have parameters of Script type. Check Dynamic Reward script param of Store Item for example. When a CMS model has a Script field, the generated code exposes it as a ScriptRef — a lightweight wrapper with Launch and Stop methods.

var adConfig = Balancy.CMS.GetModelByUnnyId<AdConfig>("some-unny-id");

if (adConfig.Script != null && adConfig.Script.HasValue)
{
    var instanceId = adConfig.Script.Launch(
        "ad-reward",
        "{\"rewardAmount\": 100}"
    );

    // With completion callback
    var instanceId = adConfig.Script.Launch(
        "ad-reward",
        "{\"rewardAmount\": 100}",
        onComplete: (exitPort, resultJson) =>
        {
            Debug.Log($"Script exited via: {exitPort}");
            Debug.Log($"Outputs: {resultJson}");
        }
    );

    // Stop later
    ScriptRef.Stop(instanceId);
}
const adConfig = CMS.getModelByUnnyId<AdConfig>("some-unny-id");

if (adConfig.script?.hasValue) {
    const instanceId = adConfig.script.launch(
        "ad-reward",
        { rewardAmount: 100 }
    );

    // With completion callback
    const instanceId = adConfig.script.launch(
        "ad-reward",
        { rewardAmount: 100 },
        (exitPort, outputs) => {
            console.log("Script exited via:", exitPort);
            console.log("Outputs:", outputs);
        }
    );

    // Stop later
    ScriptRef.stop(instanceId);
}
Property / Method Description
ScriptId / scriptId The script's CMS ID
HasValue / hasValue true if a script is assigned to this field
Launch / launch Launch the script, returns instanceId. Accepts optional onComplete callback.
Stop / stop (static) Stop a running script by instanceId

Completion Callbacks

When launching a script (via SDK API or ScriptRef), you can pass an optional onComplete callback. It fires when the script's final node exits, carrying the exit port name and all output port values.

Balancy.API.VisualScripting.RunScriptByName(
    "CalculateReward",
    "launcher-id",
    "{\"level\": 5}",
    onComplete: (exitPort, resultJson) =>
    {
        // exitPort: name of the exit port that triggered (e.g. "Success", "Fail")
        // resultJson: JSON string with output port values, e.g. {"reward": 100}
        Debug.Log($"Script finished via '{exitPort}', outputs: {resultJson}");
    }
);
API.Scripts.runScriptByName(
    "CalculateReward",
    "launcher-id",
    { level: 5 },
    (exitPort, outputs) => {
        // exitPort: name of the exit port that triggered
        // outputs: Record<string, unknown> with output port values
        console.log(`Script finished via '${exitPort}', outputs:`, outputs);
    }
);
Parameter C# Type TypeScript Type Description
exitPort string string Name of the exit port that triggered
resultJson / outputs string (JSON) Record<string, unknown> Script's final output port values

Timing

The callback fires at least one engine frame after Launch() / launch() returns, so there is no race condition even for synchronous scripts. If a script is stopped via StopScript / stopScript, the callback does not fire.

Inner Scripts

Scripts can also be used within other Scripts. This Script will act like any other Node and can be connected to other Nodes using Links. This allows you to reuse any Scripts you've created previously, so you won't have to recreate any logic you find yourself using often.

Below is a normal Script and how it could be used inside other Scripts.

Inner Script

Using an Inner Script

Tip: Try to make your Scripts do only one thing. This helps prevent redundant logic if you ever need to use the same logic in another Script. It also makes updating a particular logic easier, since you will only need to do the updates in one Script.