The objectives array awards points when a spacecraft reaches a state, rather than when an operator answers a question. An objective is the inverse of an event: an event runs on the clock and changes the spacecraft, while an objective watches the spacecraft and changes the score.

Objectives and questions both feed the same per-team score. Questions score what a team knows; objectives score what a team does. A scenario can use either, both, or neither.

Minimal objective

"objectives": [
  {
    "enabled": true,
    "name": "Battery Recovered",
    "description": "Restore the battery to at least 80% charge.",
    "type": "spacecraft",
    "assets": [],
    "target": "Battery",
    "target name": "",
    "variable": "Charge Fraction",
    "operation": ">=",
    "value": "0.8",
    "award": {
      "points": 25.0,
      "repeatable": false
    }
  }
]

Keys are case-insensitive on load. Studio writes objective keys in lowercase, unlike the PascalCase it uses inside events, so prefer lowercase here to keep hand-authored files looking like exports.

Fields

KeyTypeDefaultMeaning
enabledbooleantrueA disabled objective never fires and never pays out
namestring"Objective"Label shown on the timeline and used as the award reason when description is empty
descriptionstring""What the team must do; shown to operators unless hidden is true
typestring"spacecraft"Only spacecraft is implemented; treat as reserved
assetsstring[][]Asset IDs this applies to; empty means every spacecraft
targetstring""Component instance name or class alias to watch; required
target namestring""Narrows target to components with this name
variablestring""The reflected property compared; required
operationstring">="The comparison operator
valuestring""The value compared against, written as a string
min_timenumberomittedLower simulation-time bound for scoring
max_timenumberomittedUpper simulation-time bound for scoring
award.pointsnumber0.0Points added; negative subtracts, which is how a penalty is written
award.repeatablebooleanfalseWhether a team can earn it more than once per run
hiddenbooleanfalseHides the name and description until the team completes it

Do not author an id. Studio generates one on load to pair the file back up with what it builds.

NOTE

You author one objective. Studio expands it into one generated event per resolved component, on every spacecraft it applies to, each wired to the team that owns that spacecraft. You never write anything per team; teams are discovered from the spacecraft that resolve.

Choosing a target

target resolves exactly like an event Target:

  1. An exact component name (components[].name) is matched first, ignoring case.
  2. Otherwise the string is treated as a class alias such as Battery or Solar Panel, matching every component of that class.
  3. If target name is set, the matches are narrowed to components carrying that name.

A - suffix watches a model on the component, for example "Battery-BatteryLeakageErrorModel". Unlike an event, an objective never attaches a model; it only observes, so a component without that model is skipped.

In the Studio Objectives tab these keys appear as three separate fields: Target Class and Target Component together form target and target name, and Target Model adds the -Model suffix.

See Writing Spacecraft Events for the shared target and target-name rules, and Defining Components for the class aliases.

Choosing a variable

variable is the reflected name of a property on the component or model, not a Data key and not a telemetry field. Matching ignores case and strips spaces, so "Charge Fraction" and "chargefraction" reach the same property. Nothing else is normalized.

Read-only properties are allowed, which is the main difference from events: an event must write a settable property, while an objective only reads. The most useful state to score on is often read-only, such as Charge Fraction on a battery, Is Docked on a docking adapter, Amount on a fuel tank, or Allocated on storage.

Variable typeWatchableNotes
Float, IntYesThe usual cases
BoolYesCompared as 1 / 0
EnumYesCompared as its integer index
Vector2/3/4YesReduced to magnitude before comparison
String, Guid, DateTime, references, arraysNoCannot be watched

Studio’s objective editor lists every variable the chosen target exposes for the build you are running. Confirm names there before relying on them.

Writing the condition

operation accepts a symbol or a long name:

OperationFires while
>=value is at or above the threshold
<=value is at or below the threshold
>value is strictly above the threshold
<value is strictly below the threshold
==value matches within a tolerance of 1e-6
!=value differs by more than 1e-6
passesvalue crosses the threshold in either direction

Any other operator parses but never evaluates true, so the objective silently never scores.

value is always a string, decoded by the variable’s type:

Variable typeWriteExample
Floatthe number"0.8"
Intthe integer"3"
Bool"true" or "false""true"
Enumthe integer index"0"
Vectora single number, compared against magnitude"250.0"

WARNING

Do not use == on a continuously varying float; the 1e-6 tolerance means it almost never matches. Use >= or <= with a threshold. For a bool, the only string that means true is the literal text true; "1" reads as false.

The condition is evaluated every simulation step while running, and the objective fires on the step the condition becomes true. It re-arms once the condition stops holding.

Limiting by time

min_time and max_time gate the condition by elapsed simulation time:

{
  "name": "Recover During Contact",
  "target": "Battery",
  "variable": "Charge Fraction",
  "operation": ">=",
  "value": "0.9",
  "min_time": 300.0,
  "max_time": 900.0,
  "award": { "points": 25.0, "repeatable": false }
}
Keys presentPermitted time
NeitherAny time
min_time onlyStrictly after min_time
max_time onlyStrictly before max_time
BothBetween the two values, inclusive

0 is a valid time, so write "min_time": 0 only when that is intentional, and omit the key to disable the bound.

The award

Points go to the team that owns the spacecraft carrying the component.

  • award.repeatable: false pays the team at most once per run, scoped to the team rather than the component.
  • award.repeatable: true pays on every entry into the condition.
  • award.points can be negative to write a penalty.
{
  "name": "Battery Depleted",
  "description": "Do not let the battery fall below 5% charge.",
  "target": "Battery", "variable": "Charge Fraction",
  "operation": "<=", "value": "0.05",
  "award": { "points": -20.0, "repeatable": false }
}

A hidden objective still scores. Operators see that a secret objective exists and its point value, but its name and description are withheld until their team completes it. The award history clears on reset.

WARNING

Ask whether the condition already holds at t=0. A once-only objective whose condition is true at the start is claimed in the first step for doing nothing. Either invert the condition, use min_time, or pair it with an event that first puts the spacecraft into the state the team must recover from.

When an objective never scores

Work down this list, roughly ordered by how often each is the cause:

  1. the variable name is wrong
  2. the variable is not a watchable type
  3. the target resolves to nothing
  4. target name narrowed it to nothing
  5. the model is not already attached
  6. the spacecraft has no team to pay, such as a neutral asset
  7. assets does not match an assets.space[].id
  8. the operator never evaluates
  9. == was used on a float
  10. a bool value is not the literal text true
  11. the time window is inactive
  12. it already fired this run
  13. it is disabled, or the simulation is not running

Next, assemble and validate the complete scenario.