Skip to content

Behavior Components

A Behavior Component is a reusable custom component authored as a project script. It uses the same registry, Inspector fields, Object Flow nodes, lifecycle, and runtime host as Opal's built-ins.

The workflow stays close to the object:

  1. Create or edit the definition in Scripts.
  2. Select an object in Arrange.
  3. Attach the component from the Inspector's Components card.
  4. Configure that object's field values.
  5. Use Edit Source on the attached component when you need to change its shared definition.

Components are not a separate workspace. The Inspector owns attachments and per-object settings; Script Studio owns reusable source.

Create a Behavior Component

Open Scripts, choose New → Behavior Component, and edit the generated opal.component(...) source:

js
export default opal.component("RageMeter", {
  label: "Rage Meter",
  category: "Combat",
  description: "Builds rage on hit and fires when full.",
  fields: {
    rage: { type: "number", default: 0 },
    max: { type: "number", default: 100 },
  },
  events: {
    full: {},
  },
  actions: {
    charge: {
      label: "Charge",
      params: {
        amount: { type: "number", default: 1 },
      },
      body: "self.rage = Math.min(self.max, self.rage + params.amount); if (self.rage >= self.max) emit('full');",
    },
  },
  conditions: {
    isFull: {
      label: "Is Full",
      body: "self.rage >= self.max",
    },
  },
});

Script Studio validates the source and keeps the last successfully compiled definition active when a new edit has an error. Once the script compiles, Rage Meter appears in the Inspector's Add Component menu.

Definition options

OptionPurpose
label, category, icon, descriptionIdentity and authoring UI
fieldsTyped, per-attachment values shown in the Inspector
eventsSignals the component can emit to Flow
actionsNamed operations exposed as Component Action nodes
conditionsNamed checks exposed as Component Condition nodes
requiresComponent IDs that should be attached first
onStart, onTick, onEndPlay lifecycle behavior

Field specs use the same types as built-ins, including number, boolean, string, enum, vector, color, objectRef, componentRef, assetRef, and eventKey. See Authoring components in code for the common field options.

Action and condition bodies

An action body can mutate fields, call sibling components, or emit declared events. Its available names include:

NameMeaning
selfThis attachment's field values
paramsValues passed by the caller or Flow node
statePrivate runtime scratch for this attachment
thingThe object that owns the component
get, run, checkRead, call, or query sibling components
emitFire a declared component event
source, target, eventCurrent event context when available

Condition bodies evaluate to a boolean. Keep action and condition bodies small; use Object Flow to orchestrate waits, scene transitions, and multi-step event logic.

Lifecycle behavior

Use lifecycle hooks when an object should run code without a Flow graph:

js
export default opal.component("Spinner", {
  label: "Spinner",
  category: "Behaviors",
  fields: {
    speed: { type: "number", default: 90, label: "Speed (deg/sec)" },
  },
  onStart: "state.elapsed = 0;",
  onTick: { spin: "self.speed" },
});
HookRuns
onStartOnce when Play begins
onTickEvery frame during Play; dt is available
onEndOnce when Play stops

Lifecycle string bodies can use state for per-instance scratch during the current Play session. Common onTick object forms such as { spin: "self.speed" } and { drift: { x: "60", y: "0" } } expand to Opal's motion-safe helpers. String bodies can also use helpers.spin, helpers.moveBy, and angle conversion helpers.

Attach and edit instances

  1. Select an object in Arrange.
  2. Open the Inspector's Components card.
  3. Choose Add Component and search by label, category, or component ID.
  4. Edit the generated field controls on the attached card.

Those values belong to that object. Attaching the same Behavior Component to two objects gives each its own field values and runtime scratch state.

Use Edit Source on a script-authored component card to open its project script. Definition edits apply to every attachment after a successful compile; instance field values remain object-owned.

Use from Object Flow

Behavior Components extend the selected object's Flow catalog exactly like built-ins:

  • Actions become Component Action nodes.
  • Conditions become Component Condition nodes.
  • Declared events can start or feed rules.
  • Fields can be exposed to component value nodes and expressions according to their field spec.

Attach the component before authoring its object graph so its entries appear near the top of the catalog.

Persistence and identity

The source definition is stored once in the project's script records. Scenes do not store a separate custom-component definition list. Each object serializes only its attachment:

json
{
  "type": "rageMeter",
  "version": 1,
  "fields": {
    "rage": 0,
    "max": 100
  }
}

Component IDs are the link between definitions and attachments. Renaming an ID changes that identity, so update or reattach existing instances deliberately. Removing the source removes the registered custom type; clean up attachments that still reference it.

Choosing the right tool

NeedUse
Reusable state and behavior attached to objectsBehavior Component
Event wiring, waits, and readable scene orchestrationObject Flow or Scene Flow
One reusable calculation or custom graph operationScript-authored Flow node
Engine-level capability shipped with OpalBuilt-in component in code