Lifecycle and collisions
The engine calls these methods. They are not Flow nodes.
| Method | When |
|---|---|
void Start() | After native initialization for the whole creation cohort |
void Update(float dt) | Outer gameplay frame |
void FixedUpdate(float dt) | Immediately before each fixed physics step |
void End() | Stop Play; enabled components only; synchronous |
Calling another object from Start cannot launch work that later initialization will erase. Update, FixedUpdate, and End are synchronous — they cannot await. See gameplay time and Tasks.
Self is this object’s Entity. Transform is this object’s pose.
Collision callbacks
class Hazard : Component {
[Property] int Damage = 10;
void OnCollisionStart(Entity other) {
Damageable target = other.GetComponent<Damageable>();
if (target != null) {
target.TakeDamage(Damage);
}
}
}Both objects need Collider 2D; at least one needs a moving Rigid Body 2D. Resting contact does not repeat OnCollisionStart.
| Callback | Pair |
|---|---|
OnCollisionStart(Entity other) | Solid colliders |
OnCollisionEnd(Entity other) | Solid colliders |
OnSensorEnter(Entity other) | Sensor involved |
OnSensorExit(Entity other) | Sensor involved |
All four return void. A pair involving a sensor uses the sensor callbacks. The engine delivers each transition once per participating object, before the first-class Flow event. Disabled script components and objects outside Play do not run these callbacks. They describe Rapier contacts — they do not replace pointer, overlap, or custom Flow events.
The Collision Components tutorial is a complete single-script setup.
GetComponent
GetComponent<T>() looks on this object. other.GetComponent<T>() looks on another. Missing or disabled components return null. Unguarded null access reports a script error.
T is an enabled OpalScript class in the project (exact class name) or a native type: Health, RigidBody2D, SoftBody2D, Collider2D, Joint2D, Ragdoll, Motion, Spawner, SpriteAnimator, ParticleEmitter, SceneCamera. opal.component declarations are not typed lookup targets.
HasComponent<T>() is the boolean check. See Component.
Entity.Id and Entity.Name are read-only. Entity.Position (pixels) and Entity.Rotation (degrees) read and write that entity’s local transform and keep Motion targets in sync, like Transform.Position.
Stored references compare equal across compatible live reload. Changes that break a caller’s types or public member access prevent the whole library from applying.
References can be private/runtime fields, locals, and private helper parameters or return values. They cannot be [Property] fields or public Flow method parameters. Only public component fields and methods are accessible through a reference; [Property] alone does not grant public access. Cross-component calls share the same execution budget and recursion limit.
Deleted, detached, or otherwise invalid targets read as null.
Older saved scripts migrate from Thing to Entity at type locations. New scripts use Entity; Thing is not an alias. The internal JavaScript engine class remains Thing.
Component names and existing source
Native type names such as Health and native component ids such as health are reserved. This applies to ordinary scripts, explicit [ComponentId("...")] identities, legacy component declarations, recovery source, and script packages. A source-record id is separate from the component identity it declares.
Older colliding source is retained and reported as a compile error. Opal applies a valid last-known-good revision when available; an invalid candidate cannot replace the running library. If an id conflict has no valid recovery, scene loading stops before constructing objects so saved custom fields cannot be converted under a native schema. Rename a conflicting class and give it an unused component id, then update attachments and Flow references deliberately. Opal does not automatically remap an ambiguous native/custom component id in saved data.