Skip to content

Tasks

Tasks define goals for players, commonly used for quests, missions, daily challenges, and achievements. Each task has an icon, description, reward, and a specific goal type that determines how progress is tracked.

Base Task

The parent component is Base Task. It doesn't have any goals on its own, so its child templates should be used to define specific goal types.

Parameter Type Description
Icon Image Displayed in UI
Description Localized String Goal description shown to the player
Reward List\<ItemWithAmount> Items added to Inventory when the task is claimed

Base Task

To define a game-specific goal, inherit a CMS template from Task Custom and add your parameters. Its SDK wrapper handles activation, progress, completion, failure, and cleanup; see Custom tasks. Inheriting directly from Base Task adds data fields but does not provide the custom-task lifecycle.

Built-in Task Types

The LiveOps package provides automatic task types for common goals, plus Task Custom for progress driven by your game.

Task Item

Track item interactions: collect, spend, or own a specific amount of a given Item.

Task Item

Parameter Type Description
Target Item Item Which item to track
Required Amount int Amount needed to complete
Type enum Collect, Spend, or Own

Type Behaviors:

Type Tracks Description
Collect Items gained Progress increases when items are added to Inventory. Spending doesn't affect progress.
Spend Items spent Progress increases when items are removed from Inventory. Gaining doesn't affect progress.
Own Current inventory count Progress equals current Inventory count. Can decrease if items are spent.

Own Type Can Revert

With the Own type, task completion is not permanent. If a player's inventory count drops below the required amount (e.g., by spending items), the task reverts from Completed back to InProgress.

Task Complete Levels

Complete a specified number of levels. Progress is tracked automatically via the Level user property from the System Profile.

Task Complete Levels

Parameter Type Description
Levels To Complete int Number of levels to finish

Task Complete Levels Streak

Complete a specified number of levels in a row without losing.

Task Complete Levels Streak

Parameter Type Description
Streak Length int Consecutive wins needed
Can Lose boolean Determines what happens when the player loses a level

Can Lose Behavior:

Can Lose On Level Failure
true Task fails completely (Status = Failed). Must be restored via API to retry.
false Streak resets to 0, but task remains active. Player can keep trying.

Task Condition

Use Task Condition when an existing Balancy Condition expresses the goal, such as reaching a player segment. Select the Condition in the task document. When the task activates, the SDK evaluates the condition and subscribes to its dependencies; it completes the task when the condition becomes true. A condition that is already true completes the task immediately. Completion is final; later changes do not reopen it.

Parameter Type Description
Condition Condition reference The condition the player must satisfy

Task Field Number

Use Task Field Number to watch a numeric field in a player profile, for example System.Level >= 10 or a custom score field. Select the profile and field path, a comparison, and a target value. The SDK reads the field on activation, observes changes, and completes the task once the comparison matches. A missing or non-numeric field does not complete it.

Parameter Type Description
Full Path Profile + field path Numeric profile field to observe
Comparison enum Equal, Greater, Greater Or Equal, Lower, Lower Or Equal, or Not Equal
Value number Target for the comparison

IsNullOrEmpty is not a numeric comparison and is not supported here. TaskInfo.Progress is a non-negative integer display value, truncated for fractions and capped at the 32-bit integer maximum. For the actual observed number, including fractions, read TaskInfo.NumericProgress: use TryGetNumericProgress(out double) in Unity or tryGetNumericProgress() in TypeScript. Completion does not reverse if the field later moves across the threshold.

Task Custom and game-defined task types

Use Task Custom for gameplay signals the SDK cannot observe directly, such as defeating an enemy. In the CMS, create a template inheriting Task Custom, add any game-specific parameters, and create a task document from that template. Set Count to a positive goal for automatic completion when progress reaches it. Set Count to 0 for a goal that your code finishes explicitly with Complete() / complete().

Register a wrapper for the exact CMS template name before Balancy loads the model. Override OnStart / onStart to attach gameplay handlers and OnStop / onStop to release them. The SDK calls Start on activation, restore, and profile reload; it calls Stop on completion, failure, deactivation, cleanup, or replacement. Keep the supplied CustomTaskContext in delayed handlers so updates from an older activation are rejected. A restored task receives a new RunId / runId.

using Balancy;
using Balancy.Models.LiveOps.Tasks;

public sealed class DefeatEnemiesTask : TaskCustom
{
    private CustomTaskContext _run;

    public override void OnStart(CustomTaskContext context) => _run = context;
    public override void OnStop(CustomTaskContext context)
    {
        if (_run?.RunId == context.RunId) _run = null;
    }

    // Call from your game's enemy-defeated handler.
    public bool RecordEnemyDefeated() => _run?.AddProgress() ?? false;
}

// Register before initialization/model loading; preserve other custom models.
var previous = Balancy.CMS.OnTypeRequested;
Balancy.CMS.OnTypeRequested = name =>
    name == "Game.DefeatEnemies" ? new DefeatEnemiesTask() : previous?.Invoke(name);

// Once the model is loaded, call its method from game code:
var task = Balancy.CMS.GetModelByUnnyId<DefeatEnemiesTask>("task-unny-id");
task?.RecordEnemyDefeated();
import { CMS, CustomTaskContext, LiveOpsTasksTaskCustom } from '@balancy/core';

class DefeatEnemiesTask extends LiveOpsTasksTaskCustom {
    private run: CustomTaskContext | null = null;
    override onStart(context: CustomTaskContext): void { this.run = context; }
    override onStop(context: CustomTaskContext): void {
        if (this.run?.runId === context.runId) this.run = null;
    }
    // Call from your game's enemy-defeated handler.
    recordEnemyDefeated(): boolean { return this.run?.addProgress() ?? false; }
}

// The name must equal the CMS template name; register before model loading.
CMS.register('Game.DefeatEnemies', DefeatEnemiesTask);

const task = CMS.getModelByUnnyId<DefeatEnemiesTask>('task-unny-id');
task?.recordEnemyDefeated();

You can also update an active custom task through Balancy.API.Tasks.SetProgress(taskInfo, value), AddProgress(taskInfo, amount), CompleteTask(taskInfo), and FailTask(taskInfo) in Unity, or Balancy.API.Tasks.setProgress(...), addProgress(...), completeTask(...), and failTask(...) in TypeScript. These helpers use the task's current run. For asynchronous work, capture taskInfo.CreateCustomContext() / taskInfo.createCustomContext() when the work starts and call its methods later; context updates return false for an invalid or stale run, while keeping a mutable TaskInfo can accidentally target a later run. Only Task Custom accepts these manual progress operations. Progress and additions must be non-negative integers.

Task Lifecycle

Every task goes through a lifecycle of states:

Inactive --> Activated --> InProgress --> Completed --> Claimed
                              |
                           Failed (streak tasks or custom tasks)
                              |
                           Restored --> InProgress

TaskStatus values:

Value Status Description
0 None Not active
1 InProgress Active, player working toward goal
2 Completed Goal reached, reward available to claim
3 Claimed Reward collected
4 Failed Failed (streak tasks or custom tasks)

Using Tasks with Game Events

Tasks are typically activated through Game Events. A Game Event can have a Visual Script attachment that activates tasks when the event triggers.

Typical flow:

  1. Create task documents in the CMS (using Base Task child templates)
  2. Create a Visual Script that activates the tasks
  3. Create a Game Event with conditions (e.g., Level >= 5, specific day of week)
  4. Assign the script as the event's attachment
  5. Deploy

When the event's conditions are met, the script runs and the tasks become active for the player.

Integration

Accessing Task Data

Task data is stored on the System Profile in the TasksInfo container.

var tasksInfo = Balancy.Profiles.System.TasksInfo;

// Find a specific task by its UnnyId
var taskInfo = tasksInfo.FindTaskInfoByTaskUnnyId("task-unny-id");
if (taskInfo != null)
{
    Debug.Log($"Status: {taskInfo.Status}, Progress: {taskInfo.Progress}");
}

// Get all tasks for a specific Game Event
var eventTasks = tasksInfo.GetTasksForEvent(myGameEvent);
foreach (var task in eventTasks)
{
    Debug.Log($"Task: {task.Task?.Description}, Status: {task.Status}");
}
const tasksInfo = Balancy.Profiles.system.tasksInfo;

// Find a specific task by its UnnyId
const taskInfo = tasksInfo.findTaskInfoByTaskUnnyId("task-unny-id");
if (taskInfo) {
    console.log(`Status: ${taskInfo.status}, Progress: ${taskInfo.progress}`);
}

// Get all tasks for a specific Game Event by UnnyId
const eventTasks = tasksInfo.getTasksForEventById("event-unny-id");

TaskInfo fields:

Field Type Description
task BaseTask The task template this instance is based on
taskUnnyId string UnnyId of the BaseTask model
parent GameEvent The GameEvent this task belongs to (if any)
parentUnnyId string UnnyId of the parent GameEvent
progress int Current progress value
numericProgress string Exact observed value for Task Field Number; parse with the SDK helper
runId string Activation token used to reject stale custom-task updates
status TaskStatus Current status
startTime int Timestamp when task was activated
completeTime int Timestamp when task was completed

TasksInfo helper methods:

Method Description
FindTaskInfo(task) Find by task reference
FindTaskInfoByTaskUnnyId(unnyId) Find by the BaseTask model's UnnyId
GetTasksForEvent(gameEvent) Get all tasks for a specific GameEvent
GetTasksForEvent(gameEventUnnyId) Get all tasks for a GameEvent by its UnnyId

Task API Methods

The SDK provides four lifecycle/reward methods to manage any task, plus manual progress methods for Task Custom.

ActivateTask

Activates a task, optionally associating it with a GameEvent.

var baseTask = Balancy.CMS.GetModelByUnnyId<BaseTask>("task-unny-id");
Balancy.API.Tasks.ActivateTask(baseTask, myGameEvent);  // gameEvent is optional
Balancy.API.Tasks.activateTask(baseTask, myGameEvent); // parent is optional

DeactivateTask

Deactivates a currently active task.

Balancy.API.Tasks.DeactivateTask(baseTask);
Balancy.API.Tasks.deactivateTask(baseTask);

ClaimReward

Claims the reward for a completed task. Returns true if successful. The reward items are automatically added to the player's Inventory.

bool success = Balancy.API.Tasks.ClaimReward(baseTask);
if (success)
{
    Debug.Log("Reward claimed!");
}
const success: boolean = Balancy.API.Tasks.claimReward(baseTask);

RestoreFailedTask

Restores a task with Failed status back to active state. This applies to streak tasks with CanLose = true and to custom tasks failed by game code. A restored custom task starts a new run.

Balancy.API.Tasks.RestoreFailedTask(baseTask);
Balancy.API.Tasks.restoreFailedTask(baseTask);

Complete Example

var tasksInfo = Balancy.Profiles.System.TasksInfo;
var eventTasks = tasksInfo.GetTasksForEvent(myGameEvent);

foreach (var taskInfo in eventTasks)
{
    switch (taskInfo.Status)
    {
        case TaskStatus.InProgress:
            // Show progress UI
            UpdateProgressBar(taskInfo.Task, taskInfo.Progress);
            break;

        case TaskStatus.Completed:
            // Reward ready to claim
            bool claimed = Balancy.API.Tasks.ClaimReward(taskInfo.Task);
            if (claimed)
                ShowRewardAnimation(taskInfo.Task.Reward);
            break;

        case TaskStatus.Failed:
            // Offer to restore a failed streak or custom task
            ShowRestoreDialog(taskInfo.Task);
            break;
    }
}
const tasksInfo = Balancy.Profiles.system.tasksInfo;
const eventTasks = tasksInfo.getTasksForEventById("event-unny-id");

for (let i = 0; i < eventTasks.length; i++) {
    const taskInfo = eventTasks[i];
    console.log(`Task: ${taskInfo.taskUnnyId}`);
    console.log(`  Status: ${taskInfo.status}`);
    console.log(`  Progress: ${taskInfo.progress}`);
}

UI Builder (WebView) API

If you are displaying tasks inside UI Builder views, use the JavaScript bridge API instead of the SDK methods above. The bridge provides the same functionality through a different interface optimized for WebView context.

See the full API reference at Balancy JavaScript API - Tasks & Events.

In View bridge examples, eventInfo is the runtime info passed to init(eventInfo). Pass eventInfo.instanceId, not its configuration unnyIdGameEvent. Host C#/TypeScript APIs are unchanged.

// Get all tasks
const tasks = await balancy.getTasks(eventInfo.instanceId);

// Activate tasks
await balancy.activateTasks(['quest-1', 'quest-2'], eventInfo.instanceId);

// Deactivate tasks
await balancy.deactivateTasks(['quest-1', 'quest-2'], eventInfo.instanceId);

// Claim reward
const success = await balancy.claimTaskReward('daily-quest-1');

// Restore a failed task
const failedTask = tasks.find(t => t.status === balancy.TaskStatus.Failed);
if (failedTask) {
    await balancy.restoreFailedTask(failedTask);
}

Examples

Tasks are used for setting up events in Starter Kit package - Car Race and Tower Climb.

Car Race:

Car Race Event

Tower Climb:

Tower Climb Event