Skip to content

Components and properties ​

Components are reusable behavior blocks attached to objects — the Unity idea, inside Opal. An object can carry many components, each has typed fields, and those fields can drive runtime behavior and Flow graph nodes.

Manual vs Components guide: this page is using components in the editor (attach, configure, wire to Flow). For the full catalog and for authoring new ones, see the Components guide, Built-in reference, and Behavior Components.

When to use a component vs a graph ​

Your object needs…Reach for…Example
Durable state or a rule every instance sharesComponentHealth HP, Inventory, Team
Physics body or shapeComponentRigid Body 2D + Collider 2D
Input handling (tap/drag/drop)ComponentInteractable / Draggable / Drop Zone
Cooldown, timer, spawn waveComponentTimer, Spawner, Hazard
A one-off reaction to an eventObject FlowOn Tap → Play Sound
Global win/lose or scene transitionScene FlowOn Custom Event win → Go To Scene

Use components for capabilities you attach; use Flow for wiring those capabilities together.

Adding a component ​

  1. Select an object in Arrange.
  2. Open the Components card in the inspector (right panel).
  3. Click Add Component.
  4. Pick a built-in or a script-authored Behavior Component (they appear together once the script compiles).
  5. Configure its fields.

Some components pull dependencies automatically — adding Health Bar can also attach Health, because the bar needs a value to display. Problems will flag a missing dependency if you remove it.

Field types — what you actually edit ​

TypeLooks like in inspectorExample fields
NumberStepper + expression = toggleMax HP 100, Speed 320, Cooldown 1.5
BoolToggleEnabled, Starts Active, Is Sensor
TextInput + datalist autocompleteLabel, Tag, Event name coin_collected
ChoiceDropdownBody Type Dynamic/Fixed/Kinematic, Shape Box/Circle/Capsule
Object / Asset refPicker + searchTarget player, Prefab Slime, Sound pickup.wav, Clip Idle

Fields can be read by Flow graphs (get("health").hp, Get Variable nodes) and change at runtime through component actions.

Literal vs expression. A number field shows 100 by default (literal). Click = to enter =self.maxHp * 0.5 (expression). Flow expressions still read GameState.score. HUD labels use the UI Inspector Data bindings card (Source → GameState), not an = field. Use Get/Set variable nodes to change declared project state.

Components and Object Flow — the real payoff ​

When you attach a component, the object's Flow catalog grows automatically:

Component featureNew nodeExample
ActionDoes workhealth.damage with amount
ConditionBranches white wirehealth.isAlive → true/false
EventStarts a ruleOn Health Changed, On Pickup
FieldData you can readself.hp, get("inventory").count

Mini example — damage on tap:

text
On Tap → health.damage { amount: 10 } → health.isAlive?
                                     ├─ true  → Play Audio "hit.wav"
                                     └─ false → Spawn VFX "poof" → Destroy

Prefer component actions over manual variable math. If Health already models damage, use health.damage instead of score = score - 10.

Built-ins at a glance ​

ComponentOne-line usePair with
InteractableTap / double-tap / long-pressAny sprite that receives pointer input
Draggable / Drop ZonePick up and dropInventory puzzles, card games
Motion / Sprite Renderer / Sprite AnimatorMove, draw, animate framesPixel Studio / Art Canvas .ssb + Motion clips
Particle EmitterLooping FX on an objectParticle Canvas .pfx or a built-in name; Spawn VFX for one-shots
Health / Stats / Team / Health BarHP, damage, factions, barsCombat, bosses, squad
Inventory / PickupHold and collect itemsShop, keys, coins
Platformer / Top-Down / Side-Scroller ControllerPlayer movementInput and controls — set Controlled By
Spawner / Timer / Hazard / ProjectileWaves, clocks, damage zones, sticky arrowsPhysics — enable CCD on Projectile
Collider 2D / Rigid Body 2D / Joint 2D / RagdollPhysics shapes, bodies, constraintsPhysics and collisions
Scene CameraPlay viewport, follow, boundsOne per scene, usually on a marker object

Full catalog: Built-in reference. Authoring: Behavior Components + Object Flow integration.

Behavior Components and OpalScript ​

For logic that appears on several objects but should be maintained once, open Scripts.

New Behavior starts a typed OpalScript class — your first component. Public methods become Flow actions and conditions.

New → Behavior Component starts the JavaScript-shaped opal.component(...) form when you want Inspector fields without types:

js
export default opal.component("Spinner", {
  label: "Spinner",
  category: "Behaviors",
  fields: {
    speed: { type: "number", default: 90, label: "Speed (deg/sec)" },
    clockwise: { type: "boolean", default: true, label: "Clockwise" },
  },
  onTick: { spin: "self.speed" },
});

Save & Apply. Spinner (or your OpalScript class) now appears in Add Component alongside built-ins. Attach it, set fields per instance, press Play.

A Behavior Component can define fields, events, actions, conditions, and lifecycle hooks (onStart, onTick, onEnd). The short form onTick: { spin: "self.speed" } or onTick: { drift: { x: "60", y: "0" } } covers common motion; for custom logic use a string body with self, thing, dt, and helpers. Configure per instance in the Inspector; click Edit Source on an attached instance to jump back to its definition.

Typed classes, tasks, and native APIs: high-level scripting. The opal.component API: Behavior Components.

Inspector properties vs component fields ​

Inspector propertiesComponent fields
Describe the object itselfDescribe one capability on that object
Transform, name, art, visibility, parent, prefab linkHealth HP, Collider shape, Spawner prefab, Inventory capacity
One set per objectOne set per attached component

Keep data where it belongs: score → GameState; door's target scene → door object or its component; enemy HP → Health (not a loose number variable).

Troubleshooting components ​

SymptomFix
Component not in Add menuThe script failed to compile — open Scripts, read diagnostics, Save & Apply. A bad edit does not replace last-known-good; OpalScript classes and Behavior Components only appear after a clean apply
Health Bar shows —Missing Health dependency — re-add Health or check Problems
Condition always falseField is in literal mode, not = expression — or wrong scope (self vs get("player"))
On Health Changed never firesHealth never changed — verify a health.damage action actually ran (Flow overlay)