Skip to content

High-level scripting ​

This track teaches OpalScript by attaching small behaviors and pressing Play. If you opened scripting to learn scripting, you are in the right place — start with the spinner.

OpalScript looks like C#. It is not C# or .NET, and it cannot reach the browser or JavaScript.

Five minutes ​

  1. Open Scripts → New Behavior.
  2. Replace the starter class with the spinner.
  3. Save & Apply (Cmd/Ctrl+S).
  4. In Arrange, select a visible object → Add Component → Spinner.
  5. Press Play. It turns.
csharp
class Spinner : Component {
    void Update(float dt) {
        Transform.Rotate(90 * dt);
    }
}

Update runs every gameplay frame. dt is how many seconds passed, so 90 * dt is ninety degrees per second. Transform.Rotate uses degrees. Stage +Y is down.

The same loop — write, apply, attach, Play — is the whole high-level workflow.

Tiny recipes ​

Each recipe is a full class. New Behavior, paste, Save & Apply, attach, Play.

Inspector speed ​

[Property] fields show in the Inspector and save with the object. [Range] only limits the slider.

csharp
class Spinner : Component {
    [Property] [Range(0, 360)] float Speed = 90;

    void Update(float dt) {
        Transform.Rotate(Speed * dt);
    }
}

Attach it to two objects and give them different Speed values.

Drift to the right ​

csharp
class Drift : Component {
    [Property] float Speed = 80;

    void Update(float dt) {
        Transform.Move(Speed * dt, 0);
    }
}

Transform.Move is pixels this frame. Positive X is right — leave Play running and it will drift off the stage. That's the point.

Hop on a button ​

In Input, click Jump · Space. Add Rigid Body 2D (Dynamic, Gravity Scale 1) and Collider 2D to the object, then attach Hopper.

csharp
class Hopper : Component {
    [Property] float JumpSpeed = 420;

    void Update(float dt) {
        if (!Input.Pressed("Jump")) return;
        RigidBody2D body = GetComponent<RigidBody2D>();
        if (body != null) body.SetVelocity(new Vector2(body.Velocity.X, -JumpSpeed));
    }
}

Input.Pressed is true once per press. This sets upward velocity in pixels per second; gravity brings the body back down. This short example allows another hop in midair. For jumping only after landing, including soft bodies, follow Jump with Space.

Collect a coin ​

Declare a Number GameState variable named score in Variables. Attach this to a coin. Press Play and tap the coin.

csharp
class Coin : Component {
    void OnTap() {
        Game.SetNumber("score", Game.GetNumber("score") + 1);
        Scene.Destroy(Self);
    }
}

OnTap is an engine hook — tap this object during Play and this method runs. Self is this coin.

Want a score on screen? Open UI, add a Text element, and in Data bindings set Source to GameState, Value to score, Format to Integer (rounded), and Template to Score {value}. Tap, watch it count. Walkthrough: UI and HUD.

Wait, then turn ​

csharp
class WaitAndTurn : Component {
    async void Start() {
        await Time.Wait(1);
        Transform.Rotate(90);
    }
}

Time.Wait uses game time, so Pause holds the wait. Start runs once when Play begins — a nice place for a delayed kickoff.

Move with a stick ​

In the Input panel, click Move 2D · WASD. For a gamepad, add Left stick on that action.

csharp
class Mover : Component {
    [Property] [Range(0, 1000)] float Speed = 240;

    void Update(float dt) {
        Vector2 movement = Input.Vector("Move");
        Transform.Position += movement * Speed * dt;
    }
}

Input.Vector is a 2D stick for that action this frame. Vector2.Zero means nothing pressed. Hold a direction and it slides.

Pop on contact ​

Both objects need Collider 2D. At least one needs a moving Rigid Body 2D. Resting contact does not repeat OnCollisionStart.

csharp
class Pop : Component {
    void OnCollisionStart(Entity other) {
        if (other == null) return;
        Scene.Destroy(Self);
    }
}

A coin, a bubble, a crate that breaks — attach Pop, bump it.

Chase a named object ​

Name the player Player in the Inspector. Attach this to something that should follow.

csharp
class Chase : Component {
    [Property] float Speed = 120;
    [Property] string TargetName = "Player";

    void Update(float dt) {
        Entity target = Scene.FindByName(TargetName);
        if (target == null) return;
        Transform.Position = Vector2.MoveTowards(Transform.Position, target.Position, Speed * dt);
    }
}

Missing names return null. Check before using the reference — a typo in TargetName just means it stands still.

What you are writing ​

PieceMeaning
class Name : ComponentOne behavior per file, attached like a built-in
void Start()Once at Play, after physics is ready
void Update(float dt)Every gameplay frame
void OnTap()This object was tapped during Play
[Property]Inspector field, saved on the object
publicVisible to other scripts

A field without [Property] is runtime memory for this Play session — not in the Inspector, not saved.

New Behavior starts an OpalScript class. That is this track.

Built-in Health, Motion, and RigidBody2D are there to call, not to rebuild. Use GetComponent<Health>() when an object already has Health.

This track ​

  1. Script Studio — the Scripts workspace, Save & Apply, the docs drawer.
  2. Your first component — the spinner again, slower.
  3. Flap — a complete gravity-and-gap flyer (advanced lesson on this track).

Low-level (types, tasks, maps, native contracts): Low-level scripting. User Manual chapter: Scripts.