Docs Scripting API

Scripting API

PHP 8.5+ class reference for the Lenga scripting runtime.

Attributes

AddComponentMenu

Applies to: **classes**

Namespace:

namespace Lenga\Engine\Attributes;

Registers a Behaviour subclass in the editor's Add Component menu under the specified path. Without this attribute, the Behaviour class can still be used by code, but it will not appear in the Inspector's component picker.

Parameters

Parameter Type Default Description
$menuName string Menu path shown in the component picker, e.g. "AI/Pathfinding".
$order int\|null null Optional sort order within the menu group.

Example

use Lenga\Engine\Attributes\AddComponentMenu;
use Lenga\Engine\Core\Behaviour;

#[AddComponentMenu('AI/Pathfinding Agent')]
class PathfindingAgent extends Behaviour
{
    public float $speed = 3.5;

    public function update(): void
    {
        // move towards target...
    }
}
Attributes

Header

Applies to: **properties**

Namespace:

namespace Lenga\Engine\Attributes;

Adds a bold label above a serialized property in the Inspector window. Use it to visually group related fields without adding any runtime overhead.

Parameters

Parameter Type Description
$headerText string The label text to display above the field.

Example

use Lenga\Engine\Attributes\Header;
use Lenga\Engine\Core\Behaviour;

class PlayerController extends Behaviour
{
    #[Header('Movement')]
    public float $speed = 5.0;
    public float $jumpHeight = 2.0;

    #[Header('Health')]
    public int $maxHealth = 100;
    public int $currentHealth = 100;
}
Attributes

HideInInspector

Applies to: **properties**

Namespace:

namespace Lenga\Engine\Attributes;

Hides a public property from the Inspector window. Useful when a field must be public for inter-script access but should not be exposed or edited through the editor.

Example

use Lenga\Engine\Attributes\HideInInspector;
use Lenga\Engine\Core\Behaviour;

class Enemy extends Behaviour
{
    // Configurable in the Inspector
    public int $maxHealth = 100;

    // Runtime-only — hidden from the Inspector
    #[HideInInspector]
    public bool $isDead = false;

    #[HideInInspector]
    public int $currentHealth = 0;

    public function start(): void
    {
        $this->currentHealth = $this->maxHealth;
    }
}
Attributes

Icon

Applies to: **classes**

Namespace:

namespace Lenga\Engine\Attributes;

Sets a custom icon for a Behaviour class in the editor. The icon is displayed next to the component's name in the Inspector and the scene hierarchy.

Parameters

Parameter Type Description
$iconPath string Path to the icon file, relative to the project's Assets/ folder.

Example

use Lenga\Engine\Attributes\Icon;
use Lenga\Engine\Core\Behaviour;

#[Icon('Icons/enemy-icon.png')]
class Enemy extends Behaviour
{
    public int $health = 50;

    public function update(): void
    {
        // enemy logic...
    }
}
Attributes

ListOf

Applies to: **properties** typed as `array`

Namespace:

namespace Lenga\Engine\Attributes;

Describes the element type of an Inspector-visible array. Use it when an array should be edited as an expandable list instead of an untyped JSON value.

Parameters

Parameter Type Description
$listType class-string The type used for each list element.

Example

use Lenga\Engine\Attributes\ListOf;
use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\GameObject;
use Lenga\Engine\Core\Vector3;

final class Waypoint
{
    public string $label = '';
    public Vector3 $position;
}

final class PatrolRoute extends Behaviour
{
    /** @var array<int, GameObject> */
    #[ListOf(GameObject::class)]
    public array $targets = [];

    /** @var array<int, Waypoint> */
    #[ListOf(Waypoint::class)]
    public array $waypoints = [];
}

targets renders as a list of scene-object pickers. waypoints renders as a list of nested object editors, with each Waypoint exposing its own serialized fields.

Runtime Shape

Lenga hydrates supported list elements before Behaviour lifecycle methods run:

  • engine value types such as Vector2, Vector3, Vector4, and Color
  • scene references such as GameObject, components, UI elements, AudioClip, and AudioMixer
  • custom PHP classes with serialized/public fields

Keep the PHP property typed as array, and add a docblock such as /** @var array<int, Waypoint> */ so tools understand the expected element type.

Attributes

Min

Applies to: **properties** (numeric)

Namespace:

namespace Lenga\Engine\Attributes;

Clamps a serialized numeric property to a minimum value in the Inspector. The editor prevents the value from being set below the specified minimum. Use Range when you need both a minimum and maximum.

Parameters

Parameter Type Description
$minValue float\|int The minimum value the field can hold.

Example

use Lenga\Engine\Attributes\Min;
use Lenga\Engine\Core\Behaviour;

class Enemy extends Behaviour
{
    // Health can never be set below 1 in the Inspector
    #[Min(1)]
    public int $maxHealth = 100;

    // Speed cannot go negative
    #[Min(0.0)]
    public float $movementSpeed = 3.5;
}
Attributes

Range

Applies to: **properties** (numeric)

Namespace:

namespace Lenga\Engine\Attributes;

Constrains a serialized numeric property to a minimum/maximum range in the Inspector. The field is displayed as a slider instead of a plain number input, making it easy to tune values visually.

Parameters

Parameter Type Description
$minValue float\|int The minimum value of the slider.
$maxValue float\|int The maximum value of the slider.

Example

use Lenga\Engine\Attributes\Range;
use Lenga\Engine\Core\Behaviour;

class AudioManager extends Behaviour
{
    #[Range(0.0, 1.0)]
    public float $masterVolume = 0.8;

    #[Range(0, 10)]
    public int $reverb = 3;

    public function update(): void
    {
        // apply masterVolume to audio output...
    }
}
Attributes

RequireComponent

Applies to: **classes** (repeatable)

Namespace:

namespace Lenga\Engine\Attributes;

Declares that this Behaviour depends on one or more other components being present on the same GameObject.

Use this when a Behaviour cannot work correctly unless another component exists beside it. For example, a player movement Behaviour might require a Rigidbody2D, or a pickup Behaviour might require a renderer and collider.

When Lenga attaches the Behaviour, it checks the required components and adds any missing ones before the Behaviour lifecycle methods run. This applies both when the Behaviour is authored in the editor and when the Behaviour is added from code.

Multiple types can be listed in a single attribute or by stacking multiple RequireComponent attributes.

Parameters

Parameter Type Description
...$componentTypes string One or more fully-qualified component class names.

Example

use Lenga\Engine\Attributes\RequireComponent;
use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Rigidbody2D;
use Lenga\Engine\Core\BoxCollider2D;

// Ensure a 2D physics body and collider are always present.
#[RequireComponent(Rigidbody2D::class, BoxCollider2D::class)]
class PlayerController extends Behaviour
{
    public float $speed = 5.0;

    public function update(): void
    {
        $body = $this->getComponent(Rigidbody2D::class);
        // Move using the rigidbody...
    }
}
Attributes

SerializeField

Applies to: **properties**

Namespace:

namespace Lenga\Engine\Attributes;

Exposes a private or protected property to the Inspector so it can be set without making the property part of the class's public API. This keeps encapsulation intact while still allowing values to be configured from the editor.

Public properties are serialized automatically. Use SerializeField only on private or protected ones.

Example

use Lenga\Engine\Attributes\SerializeField;
use Lenga\Engine\Core\Behaviour;

class PlayerController extends Behaviour
{
    // Editable in the Inspector, but private to this class
    #[SerializeField]
    private float $speed = 5.0;

    #[SerializeField]
    private int $maxJumps = 2;

    private int $jumpsRemaining = 0;

    public function start(): void
    {
        $this->jumpsRemaining = $this->maxJumps;
    }

    public function update(): void
    {
        // use $this->speed...
    }
}
Attributes

SerializeReference

Applies to: **properties**

Namespace:

namespace Lenga\Engine\Attributes;

Marks a property for managed-reference serialization. Unlike SerializeField, which serializes by value, SerializeReference preserves the concrete type of the object when saving and loading, making it suitable for polymorphic fields (interfaces, abstract classes, or class hierarchies).

Example

use Lenga\Engine\Attributes\SerializeReference;
use Lenga\Engine\Core\Behaviour;

interface IWeapon
{
    public function attack(): void;
}

class Sword implements IWeapon
{
    public float $damage = 25.0;

    public function attack(): void { /* swing */ }
}

class Player extends Behaviour
{
    // The actual concrete type (Sword, Bow, etc.) is preserved on save/load
    #[SerializeReference]
    public ?IWeapon $equippedWeapon = null;

    public function update(): void
    {
        $this->equippedWeapon?->attack();
    }
}
Attributes

Space

Applies to: **properties**

Namespace:

namespace Lenga\Engine\Attributes;

Inserts a vertical gap above a field in the Inspector window, adding breathing room between groups of properties without a visible label. For a labeled separator, use Header instead.

Parameters

Parameter Type Default Description
$height float 8.0 Height of the gap in pixels.

Example

use Lenga\Engine\Attributes\Header;
use Lenga\Engine\Attributes\Space;
use Lenga\Engine\Core\Behaviour;

class PlayerController extends Behaviour
{
    public float $speed = 5.0;
    public float $jumpHeight = 2.0;

    // Extra gap before the next property group
    #[Space(16.0)]
    #[Header('Stamina')]
    public float $maxStamina = 100.0;
    public float $staminaRegenRate = 10.0;
}
Attributes

TextArea

Applies to: **properties** (string)

Namespace:

namespace Lenga\Engine\Attributes;

Renders a multi-line text area in the Inspector for a serialized string property. Useful for longer text values such as dialogue lines, descriptions, or configuration blobs.

Parameters

Parameter Type Default Description
$minLines int 3 Minimum number of visible lines in the editor.
$maxLines int 10 Maximum number of visible lines before scrolling.

Example

use Lenga\Engine\Attributes\TextArea;
use Lenga\Engine\Core\Behaviour;

class DialogueTrigger extends Behaviour
{
    #[TextArea(minLines: 3, maxLines: 8)]
    public string $dialogueText = 'Enter dialogue here...';

    public function onTriggerEnter(): void
    {
        // display $this->dialogueText in the UI...
    }
}
Attributes

Tooltip

Applies to: **properties**

Namespace:

namespace Lenga\Engine\Attributes;

Attaches a descriptive tooltip to a serialized property in the Inspector. When the user hovers over the field label, the tooltip text is shown as a pop-up hint, improving discoverability without cluttering the layout.

Parameters

Parameter Type Description
$tooltipText string The text shown when hovering over the field in the Inspector.

Example

use Lenga\Engine\Attributes\Tooltip;
use Lenga\Engine\Core\Behaviour;

class PlayerController extends Behaviour
{
    #[Tooltip('Movement speed in units per second.')]
    public float $speed = 5.0;

    #[Tooltip('Multiplier applied to speed when sprinting. Values above 2 may feel unresponsive.')]
    public float $sprintMultiplier = 1.5;
}
Audio

AudioClip

Namespace:

namespace Lenga\Engine\Audio;

class AudioClip

AudioClip is a script-facing reference to an imported audio asset.

You normally assign clips in the Inspector by exposing an AudioClip field on a Behaviour, then pass that clip to an AudioSource when you want to play it.

Properties

name

public string $name

Display name derived from the assigned asset.

channels

public int $channels

Number of channels in the clip.

frequency

public int $frequency

Sample rate in hertz.

length

public float $length

Clip duration in seconds.

samples

public int $samples

Number of sample frames in the clip.

loadState

public AudioDataLoadState $loadState

Whether the clip can currently be read by the runtime.

Methods

getData

public function getData(int $offsetSamples, ?int $sampleCount = null): array|false

Reads interleaved sample values from the clip, normalized from -1.0 to 1.0. Returns false if the clip data cannot be read.

Example

use Lenga\Engine\Audio\AudioClip;
use Lenga\Engine\Audio\AudioSource;
use Lenga\Engine\Core\Behaviour;

final class LevelAudio extends Behaviour
{
    public ?AudioClip $winSound = null;

    private ?AudioSource $audio = null;

    public function start(): void
    {
        $this->audio = $this->gameObject->getComponent(AudioSource::class);
    }

    public function playWinSound(): void
    {
        if ($this->winSound === null || $this->audio === null) {
            return;
        }

        $this->audio->clip = $this->winSound;
        $this->audio->play();
    }
}
Audio

AudioMixer

Namespace:

namespace Lenga\Engine\Audio;

class AudioMixer

AudioMixer is a script-facing reference to an audio mixer asset.

Use mixer references when a Behaviour needs to route an AudioSource into a specific mix channel. Prefer assigning the mixer and output channel in the Inspector when the routing is static.

Properties

name

public string $name

Display name derived from the assigned mixer asset.

assetPath

public string $assetPath

Project-relative path to the mixer asset.

Methods

matches

public function matches(?AudioMixer $other): bool

Returns true when both mixer handles point to the same asset.

Example

use Lenga\Engine\Audio\AudioMixer;
use Lenga\Engine\Audio\AudioSource;
use Lenga\Engine\Core\Behaviour;

final class MenuAudio extends Behaviour
{
    public ?AudioMixer $mixer = null;

    private ?AudioSource $source = null;

    public function start(): void
    {
        $this->source = $this->gameObject->getComponent(AudioSource::class);
        $this->source?->setOutput($this->mixer, 'channel-1');
    }
}

Assign the mixer in the Inspector, then route the source to the channel that matches the sound's role.

Notes

  • AudioMixer values are asset references. Assign them in the Inspector instead of typing file paths in gameplay code.
  • New mixer assets start with a Master channel. Add more channels in the Audio Mixer window as your project needs them.
  • If an AudioSource has no mixer assigned, it plays through the default output.
Core

Application

Engine/application-level utilities.

Namespace:

namespace Lenga\Engine\Core;

class Application

Methods

quit

public static function quit()

Engine/application-level utilities.

pause

public static function pause()

resume

public static function resume()

togglePause

public static function togglePause()

isPaused

public static function isPaused()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Application;

class QuitButton extends Behaviour
{
    public function update(): void
    {
        if (Input::getKeyDown(KeyCode::ESCAPE)) {
            Application::quit();
        }
    }
}
Audio

AudioSource

Namespace:

namespace Lenga\Engine\Audio;

class AudioSource

AudioSource plays an assigned AudioClip from a GameObject.

Use it for background music, UI sounds, pickup sounds, impacts, jump sounds, and other one-shot or looping audio. In the editor, add an Audio Source component, assign an audio asset to Clip, then tune Play On Awake, Loop, Volume, Pitch, and mixer routing.

Properties

clip

public ?AudioClip $clip

The audio clip currently assigned to this source.

Assign clips through the Inspector for authored defaults. Scripts can also swap between serialized AudioClip references exposed on a Behaviour.

playOnAwake

public bool $playOnAwake

When true, the source starts playing automatically when the component starts. When false, the clip only plays when you call play() from code.

loop

public bool $loop

When true, the source restarts after the clip finishes. Use this for background music and ambience.

volume

public float $volume

Playback volume from 0.0 to 1.0.

pitch

public float $pitch

Playback pitch. 1.0 is the normal pitch. Values above 1.0 play higher and faster. Values below 1.0 play lower and slower.

outputAudioMixer

public ?AudioMixer $outputAudioMixer

Mixer asset that receives this source's output. Leave this null to play through the default output.

outputChannelId

public string $outputChannelId

Channel id inside the assigned mixer. New mixer assets start with master. Additional channels receive generated ids when you add them to the mixer asset.

isPlaying

public bool $isPlaying

Returns whether this source is currently playing and not paused.

isPaused

public bool $isPaused

Returns true when this source is currently paused.

This includes script-level pauses made with pause(). It also reports true while an active source is paused by the engine-wide application pause state.

isStopped

public bool $isStopped

Returns true when this source is stopped and not paused. This includes sources that have never played, sources that have finished playing and are not set to loop, and sources that have been stopped with stop().

Methods

play

public function play(): bool

Starts the assigned clip. Returns false if the clip cannot be played.

Calling play() starts from the beginning of the clip. If the source was paused with pause(), play() clears that paused state and restarts playback.

pause

public function pause(): bool

Pauses playback without resetting the clip position. Returns false if there is no active clip playback to pause.

Use this when you want to temporarily hold a music track, ambience loop, or long sound effect and later continue from the same position.

resume

public function resume(): bool

Resumes playback after pause(). Returns false if the source cannot resume because the clip is no longer available.

If the engine is globally paused, resume() clears the source-level pause but the sound remains silent until the engine resumes.

stop

public function stop(): bool

Stops playback and resets the source. Use pause() instead when you need to continue from the current clip position later.

isPlaying

public function isPlaying(): bool

Deprecated compatibility method. Use the isPlaying property instead:

if ($audio->isPlaying) {
    $audio->pause();
}

setOutput

public function setOutput(?AudioMixer $mixer, string $channelId = 'master'): bool

Routes the source to a mixer channel in one call. Returns false if Lenga cannot apply the change at runtime.

The source volume and pitch are multiplied by the resolved mixer channel path. For example, lowering the assigned channel volume affects every AudioSource routed through that channel.

Example: Choose a Sound from Script

use Lenga\Engine\Audio\AudioClip;
use Lenga\Engine\Audio\AudioMixer;
use Lenga\Engine\Audio\AudioSource;
use Lenga\Engine\Core\Behaviour;

final class ImpactAudio extends Behaviour
{
    public ?AudioClip $softHit = null;
    public ?AudioClip $hardHit = null;
    public ?AudioMixer $mixer = null;

    private ?AudioSource $audio = null;

    public function start(): void
    {
        $this->audio = $this->gameObject->getComponent(AudioSource::class);
        $this->audio?->setOutput($this->mixer, 'channel-1');
    }

    public function playImpact(bool $hard): void
    {
        $clip = $hard ? $this->hardHit : $this->softHit;
        if ($clip === null || $this->audio === null) {
            return;
        }

        $this->audio->clip = $clip;
        $this->audio->play();
    }
}

Assign Soft Hit and Hard Hit in the Inspector, then choose which one to play from gameplay code.

Notes

  • Play On Awake only controls automatic playback. A script can still call play() explicitly.
  • Active audio sources pause and resume with the engine-wide pause state.
  • Source-level pause() is separate from engine pause. If a script pauses a source, resuming the engine does not automatically resume that source.
  • Use looping sources for music and ambience. Use explicit play() calls for short gameplay sounds.
  • Use AudioClip references instead of hand-written paths in gameplay code.
  • Use AudioMixer routing when several sources should share music, SFX, UI, or ambience controls.
Core

Behaviour

Namespace:

namespace Lenga\Engine\Core;

class Behaviour

Behaviour is the base class for component logic that belongs on a GameObject. Use it when code needs scene ownership, lifecycle methods, Inspector fields, or nearby components. Use ordinary PHP classes, enums, and utilities for code that does not need to live on a GameObject.

Event Helpers

createSignal

protected function createSignal(): Signal

Creates an owner-scoped Signal and registers it for cleanup when the behaviour is destroyed.

Use this when a specific instance intentionally exposes a local callback surface. For most cross-system gameplay events, prefer emitEvent(...) and onEvent(...).

emitEvent

protected function emitEvent(string $eventName, mixed $payload = null): void

Emits a global EventBus event.

Use this when listeners should react without holding a reference to the dispatcher.

dispatchEvent

protected function dispatchEvent(string $eventName, mixed $payload = null): void

Alias for emitEvent(...).

onEvent

protected function onEvent(string $eventName, callable $listener, bool $disposeOnDisable = true): SignalSubscription

Subscribes this behaviour to a global EventBus event and automatically tracks the returned subscription.

By default the subscription is disposed when the behaviour disables.

onceEvent

protected function onceEvent(string $eventName, callable $listener, bool $disposeOnDisable = true): SignalSubscription

Like onEvent(...), but the listener is automatically removed after the first matching event.

trackSubscription

protected function trackSubscription(SignalSubscription $subscription, bool $disposeOnDisable = true): SignalSubscription

Tracks an existing subscription handle so the behaviour lifecycle can clean it up.

This is useful when you subscribe directly through EventBus::on(...) or another API that returns a SignalSubscription.

If disposeOnDisable is false, the subscription stays alive across disable and is only released on destroy.

Methods

startCoroutine

public function startCoroutine(\Generator $routine)

stopCoroutine

public function stopCoroutine(Coroutine|\Generator $routine)

stopAllCoroutines

public function stopAllCoroutines()

awake

public function awake()

Called when the Behaviour instance is being loaded.

onEnable

public function onEnable()

Called when the object becomes enabled and active.

start

public function start()

Called before the first frame update, after all Awake calls.

fixedUpdate

public function fixedUpdate()

Called on a fixed timestep, used for physics-like updates.

update

public function update()

Called once per frame.

lateUpdate

public function lateUpdate()

Called once per frame, after all Update calls.

onDisable

public function onDisable()

Called when the object becomes disabled or inactive.

onDestroy

public function onDestroy()

Called when the behaviour is destroyed.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Signal;

final class Door extends Behaviour
{
    public Signal $onOpened;

    public function awake(): void
    {
        $this->onOpened = $this->createSignal();
    }

    public function open(): void
    {
        $this->onOpened->dispatch($this);

        $this->emitEvent('game.door.opened', [
            'source' => $this,
        ]);
    }
}
Core

BoxCollider2D

Namespace:

namespace Lenga\Engine\Core;

class BoxCollider2D

Methods

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\BoxCollider2D;
use Lenga\Engine\Core\Collision2D;

class PlayerCollisionHandler extends Behaviour
{
    public function start(): void
    {
        $collider = $this->getComponent(BoxCollider2D::class);
        $collider->size = [1.0, 2.0];
    }

    public function onCollisionEnter(Collision2D $collision): void
    {
        Debug::log('Collided with: ' . $collision->gameObject->name);
    }
}
Core

BoxCollider3D

Namespace:

namespace Lenga\Engine\Core;

class BoxCollider3D

Properties

  • size: the local box size.
  • offset: local offset from the GameObject transform.
  • isTrigger: detects overlap without producing a solid collision response.
  • materialPath: project-relative path to a Physics Material 3D asset.
  • material: a PhysicsMaterial3D object representing the collider material.

Methods

isTouching

public function isTouching(bool $includeTriggers = true, ?int $layerMask = null): bool

Returns true when this box collider is touching another matching 3D collider.

Set $includeTriggers to false to ignore trigger contacts. Pass a $layerMask to limit the check to colliders on matching GameObject layers.

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null): array

Returns the current Collision3D contacts involving this box collider.

Set $includeTriggers to false to return only solid contacts. Pass a $layerMask to limit results to matching GameObject layers.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\BoxCollider3D;
use Lenga\Engine\Core\Collision3D;
use Lenga\Engine\Core\Debug;
use Lenga\Engine\Core\PhysicsMaterial3D;
use Lenga\Engine\Core\Vector3;

class TriggerZone extends Behaviour
{
    public function start(): void
    {
        $collider = $this->getComponent(BoxCollider3D::class);
        $collider->size = new Vector3(2.0, 2.0, 2.0);
        $collider->isTrigger = true;
        $collider->material = new PhysicsMaterial3D(staticFriction: 0.9);
    }

    public function onTriggerEnter(Collision3D $collision): void
    {
        Debug::log('Entered trigger zone');
    }
}
Core

Camera

Namespace:

namespace Lenga\Engine\Core;

class Camera

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Camera;

class CameraController extends Behaviour
{
    public function start(): void
    {
        $camera = $this->getComponent(Camera::class);
        $camera->fieldOfView = 60.0;
    }

    public function update(): void
    {
        $camera = $this->getComponent(Camera::class);
        // Use camera to perform raycasts or screen-to-world conversions
    }
}
Core

CapsuleCollider3D

Namespace:

namespace Lenga\Engine\Core;

class CapsuleCollider3D

Properties

  • radius: local capsule radius.
  • height: local capsule height.
  • offset: local offset from the GameObject transform.
  • isTrigger: detects overlap without producing a solid collision response.
  • materialPath: project-relative path to a Physics Material 3D asset.
  • material: a PhysicsMaterial3D object representing the collider material.

Methods

isTouching

public function isTouching(bool $includeTriggers = true, ?int $layerMask = null): bool

Returns true when this capsule collider is touching another matching 3D collider.

Set $includeTriggers to false to ignore trigger contacts. Pass a $layerMask to limit the check to colliders on matching GameObject layers.

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null): array

Returns the current Collision3D contacts involving this capsule collider.

Set $includeTriggers to false to return only solid contacts. Pass a $layerMask to limit results to matching GameObject layers.

moveAndSlide

public function moveAndSlide(
    Vector3 $delta,
    float $skinWidth = 0.05,
    bool $includeTriggers = false,
    ?int $layerMask = null,
): KinematicMove3DResult

Moves the GameObject by $delta using capsule collision and slides along blocking 3D colliders.

This method is deprecated for new character code. Prefer CharacterController::move() for kinematic movement.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CapsuleCollider3D;
use Lenga\Engine\Core\Debug;

class CharacterCollider extends Behaviour
{
    public function start(): void
    {
        $collider = $this->getComponent(CapsuleCollider3D::class);
        $collider->radius = 0.5;
        $collider->height = 2.0;
    }

    public function update(): void
    {
        $collider = $this->getComponent(CapsuleCollider3D::class);
        if ($collider->isTouching()) {
            Debug::log('Character is touching something');
        }
    }
}
Core

CapsuleRenderer

Namespace:

namespace Lenga\Engine\Core;

class CapsuleRenderer

Properties

center

public Vector3 $center

radius

public float $radius

height

public float $height

totalSlices

public int $totalSlices

totalRings

public int $totalRings

materialPath

public string $materialPath

Methods

getColor

public function getColor()

setColor

public function setColor(int $red, int $green, int $blue, int $alpha = 255): void

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CapsuleRenderer;

class CapsuleVisual extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(CapsuleRenderer::class);
        $renderer->radius = 0.5;
        $renderer->height = 2.0;
        $renderer->setColor(0, 200, 255, 255);
    }
}
Core

CharacterController

Namespace:

namespace Lenga\Engine\Core;

class CharacterController

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CharacterController;
use Lenga\Engine\Core\Vector3;
use Lenga\Engine\Core\Time;

class PlayerMovement extends Behaviour
{
    public float $speed = 5.0;

    public function update(): void
    {
        $controller = $this->getComponent(CharacterController::class);

        $moveDirection = new Vector3(0, 0, 0);
        if (Input::getKey(KeyCode::W)) {
            $moveDirection->z += 1;
        }
        if (Input::getKey(KeyCode::S)) {
            $moveDirection->z -= 1;
        }

        $delta = Vector3::scale($moveDirection, $this->speed * Time::deltaTime());
        $collisionFlags = $controller->move($delta);
    }
}
Core

CircleCollider2D

Namespace:

namespace Lenga\Engine\Core;

class CircleCollider2D

Methods

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CircleCollider2D;

class BallCollider extends Behaviour
{
    public function start(): void
    {
        $collider = $this->getComponent(CircleCollider2D::class);
        $collider->radius = 0.5;
    }

    public function update(): void
    {
        $collider = $this->getComponent(CircleCollider2D::class);
        $contacts = $collider->getContacts();
        if (count($contacts) > 0) {
            Debug::log('Ball is in contact with ' . count($contacts) . ' object(s)');
        }
    }
}
Core

Collision2D

Namespace:

namespace Lenga\Engine\Core;

class Collision2D

Methods

fromNativeData

public static function fromNativeData(array $data)

Properties

gameObject

public ?GameObject $gameObject { get; }

The GameObject that contacted the callback receiver.

collider

public ?Component $collider { get; }

The collider that contacted the callback receiver.

selfGameObject

public ?GameObject $selfGameObject { get; }

The GameObject that received the callback.

selfCollider

public ?Component $selfCollider { get; }

The receiver-side collider involved in the contact.

otherGameObject and otherCollider

These remain available as aliases for the contacted object and collider.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Collision2D;

class DamageOnCollision extends Behaviour
{
    public function onCollisionEnter(Collision2D $collision): void
    {
        Debug::log('Collided with: ' . $collision->gameObject?->name);
    }

    public function onCollisionStay(Collision2D $collision): void
    {
        // Called while contact continues.
    }

    public function onCollisionExit(Collision2D $collision): void
    {
        Debug::log('Stopped colliding with: ' . $collision->gameObject?->name);
    }
}
Core

Collision3D

Namespace:

namespace Lenga\Engine\Core;

class Collision3D

Methods

fromNativeData

public static function fromNativeData(array $data)

Properties

gameObject

public ?GameObject $gameObject { get; }

The GameObject that contacted the callback receiver.

collider

public ?Component $collider { get; }

The collider that contacted the callback receiver.

selfGameObject

public ?GameObject $selfGameObject { get; }

The GameObject that received the callback.

selfCollider

public ?Component $selfCollider { get; }

The receiver-side collider involved in the contact.

otherGameObject and otherCollider

These remain available as aliases for the contacted object and collider.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Collision3D;

class ExplosiveObject extends Behaviour
{
    public function onCollisionEnter(Collision3D $collision): void
    {
        Debug::log('Touched by: ' . $collision->gameObject?->name);
        $this->explode();
    }

    private function explode(): void
    {
    }
}
Core

Component

Namespace:

namespace Lenga\Engine\Core;

class Component

Component is the base class for engine components attached to a GameObject.

Inspector-assigned component fields can reference live scene components or compatible components stored inside prefab assets. When a component field points at a prefab asset, pass that component reference to GameObject::instantiate() to clone the prefab and receive the matching component on the clone.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Component;
use Lenga\Engine\Core\GameObject;
use Lenga\Engine\Core\InstantiateOptions;
use Lenga\Engine\Core\ParticleSystem;
use Lenga\Engine\Core\Rigidbody3D;
use Lenga\Engine\Core\Vector3;

class ComponentExample extends Behaviour
{
    public ?ParticleSystem $hitSparkPrefab = null;

    public function start(): void
    {
        // Get a component from this GameObject
        $rb = $this->getComponent(Rigidbody3D::class);
        $instanceId = $rb->getInstanceId();

        // Access the GameObject that owns this component
        $gameObject = $this->gameObject;

        Debug::log('Component instance ID: ' . $instanceId);
    }

    public function spawnHitSpark(Vector3 $hitPoint): void
    {
        if ($this->hitSparkPrefab === null) {
            return;
        }

        $spark = GameObject::instantiate(
            $this->hitSparkPrefab,
            InstantiateOptions::at($hitPoint, name: 'Hit Spark'),
        );
        $spark->play();
    }
}
Core

Coroutine

Namespace:

namespace Lenga\Engine\Core;

class Coroutine

Methods

getId

public function getId()

isFinished

public function isFinished()

stop

public function stop()

matchesGenerator

public function matchesGenerator(\Generator $generator)

prime

public function prime()

tick

public function tick()

tickFixedUpdate

public function tickFixedUpdate()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\WaitForSeconds;

class SequenceController extends Behaviour
{
    public function start(): void
    {
        // Start a coroutine that will be managed automatically
        $this->startCoroutine($this->playSequence());
    }

    private function playSequence(): \Generator
    {
        Debug::log('Sequence started');

        yield new WaitForSeconds(1.0);
        Debug::log('1 second has passed');

        yield new WaitForSeconds(2.0);
        Debug::log('3 seconds total have passed');

        // Coroutine automatically finishes when generator ends
    }
}
Core

CubeRenderer

Namespace:

namespace Lenga\Engine\Core;

class CubeRenderer

Methods

setSize

public function setSize(float $width, float $height, float $length)

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CubeRenderer;

class CubeVisual extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(CubeRenderer::class);
        $renderer->setSize(1.0, 1.0, 1.0);
        $renderer->setColor(255, 0, 0, 255); // Red cube
    }
}
Core

CylinderCollider3D

Namespace:

namespace Lenga\Engine\Core;

class CylinderCollider3D

Properties

  • radius: sets the top and bottom radius to the same value.
  • topRadius: local radius of the top cap. Use 0.0 for cone-like shapes.
  • bottomRadius: local radius of the bottom cap.
  • height: local collider height.
  • offset: local offset from the GameObject transform.
  • isTrigger: detects overlap without producing a solid collision response.
  • materialPath: project-relative path to a Physics Material 3D asset.
  • material: a PhysicsMaterial3D object representing the collider material.

Methods

isTouching

public function isTouching(bool $includeTriggers = true, ?int $layerMask = null): bool

Returns true when this cylinder collider is touching another matching 3D collider.

Set $includeTriggers to false to ignore trigger contacts. Pass a $layerMask to limit the check to colliders on matching GameObject layers.

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null): array

Returns the current Collision3D contacts involving this cylinder collider.

Set $includeTriggers to false to return only solid contacts. Pass a $layerMask to limit results to matching GameObject layers.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CylinderCollider3D;

final class PillarCollider extends Behaviour
{
    public function start(): void
    {
        $collider = $this->getComponent(CylinderCollider3D::class);
        $collider->radius = 0.5;
        $collider->height = 3.0;
    }
}
Core

CylinderRenderer

Namespace:

namespace Lenga\Engine\Core;

class CylinderRenderer

Methods

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CylinderRenderer;

class CylinderPillar extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(CylinderRenderer::class);
        $renderer->setSize(0.5, 3.0); // radius, height
        $renderer->setColor(100, 100, 100, 255); // Gray cylinder
    }
}
Core

Debug

Namespace:

namespace Lenga\Engine\Core;

class Debug

Methods

log

public static function log(mixed $message)

info

public static function info(mixed $message)

warn

public static function warn(mixed $message)

error

public static function error(mixed $message)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Debug;

class DebugExample extends Behaviour
{
    public function update(): void
    {
        Debug::log('This is a log message');
        Debug::info('This is info');
        Debug::warn('This is a warning');

        if (Input::getKeyDown(KeyCode::SPACE)) {
            Debug::error('Player jumped!');
        }
    }
}
Core

DirectionalLight

Namespace:

namespace Lenga\Engine\Core;

class DirectionalLight

Methods

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\DirectionalLight;

class LightController extends Behaviour
{
    public function start(): void
    {
        $light = $this->getComponent(DirectionalLight::class);
        $light->intensity = 1.0;
        $light->setColor(255, 255, 255, 255); // White light
    }

    public function update(): void
    {
        $light = $this->getComponent(DirectionalLight::class);
        // Rotate the light source
        $this->transform->rotateY(10 * Time::deltaTime());
    }
}
Core

EventBus

Namespace:

namespace Lenga\Engine\Core;

final class EventBus

EventBus is the process-wide decoupled event system.

For gameplay events, start here when listeners should react without holding a reference to the dispatcher. Use Signal only when a specific owner instance intentionally exposes an owner-scoped callback surface.

Inside Behaviours, prefer the Behaviour helpers emitEvent(...), onEvent(...), and onceEvent(...) unless you specifically need the raw static API.

Constants

UI_SELECTION_CHANGED

public const UI_SELECTION_CHANGED = 'ui.selection_changed';

Emitted by the engine when UI selection changes through the built-in navigation system.

The payload is an array with:

  • name: the event name string
  • viaPointer: whether pointer input caused the selection
  • canvas: data for the canvas that owns the selection
  • previous: data for the previously selected UI element, or null
  • current: data for the selected UI element, or null

Methods

on

public static function on(string $eventName, callable $listener): SignalSubscription

Subscribes a listener and returns a SignalSubscription.

The listener receives the payload first and the event name second:

EventBus::on('game.round_started', function (array $payload, string $eventName): void {
    // ...
});

If you are inside a Behaviour, prefer onEvent(...) so lifecycle cleanup is automatic.

addListener

public static function addListener(string $eventName, callable $listener): SignalSubscription

Alias for on(...).

subscribe

public static function subscribe(string $eventName, callable $listener): SignalSubscription

Alias for on(...).

once

public static function once(string $eventName, callable $listener): SignalSubscription

Subscribes a listener that automatically removes itself after the first event.

onceListener

public static function onceListener(string $eventName, callable $listener): SignalSubscription

Alias for once(...).

off

public static function off(string $eventName, SignalSubscription|callable $listener): void

Removes a subscription or callable listener from the event.

removeListener

public static function removeListener(string $eventName, SignalSubscription|callable $listener): void

Alias for off(...).

emit

public static function emit(string $eventName, mixed $payload = null): void

Dispatches a global event with one payload value.

dispatch

public static function dispatch(string $eventName, mixed $payload = null): void

Alias for emit(...).

clear

public static function clear(?string $eventName = null): void

Clears listeners for one event. If no event name is provided, clears all global event listeners.

Use this carefully. Most gameplay code should dispose the specific SignalSubscription it owns instead.

hasListeners

public static function hasListeners(string $eventName): bool

Returns whether the event currently has listeners.

listenerCount

public static function listenerCount(string $eventName): int

Returns the number of listeners for the event.

Example

use Lenga\Engine\Core\Behaviour;

final class Health extends Behaviour
{
    public int $current = 100;
    public string $kind = 'enemy';

    public function applyDamage(int $amount): void
    {
        $this->current -= $amount;

        if ($this->current <= 0) {
            $this->emitEvent('game.health.died', [
                'source' => $this,
                'kind' => $this->kind,
            ]);
        }
    }
}
use Lenga\Engine\Core\Behaviour;

final class GameManager extends Behaviour
{
    public function onEnable(): void
    {
        $this->onEvent('game.health.died', function (?array $payload): void {
            if (($payload['kind'] ?? null) === 'player') {
                $this->beginGameOverFlow();
            }
        });
    }

    private function beginGameOverFlow(): void
    {
    }
}

This pattern lets unrelated systems react to the same event without first finding the dispatcher.

Core

GameObject

Namespace:

namespace Lenga\Engine\Core;

class GameObject

GameObject represents an object in a scene hierarchy. Every GameObject has a Transform, can contain components, and can have child GameObjects.

Parenting affects transforms in the usual hierarchy-friendly way: moving a parent moves its children in world space, but moving a child does not move the parent. Child objects keep their own local position, rotation, and scale relative to the parent.

Properties

prefabAssetPath

public string $prefabAssetPath

Contains the project-relative prefab asset path when this GameObject is an inspector-assigned prefab reference rather than a live scene object. Scene objects return an empty string.

Methods

setActive

public function setActive(bool $value)

isActiveSelf

public function isActiveSelf()

isActiveInHierarchy

public function isActiveInHierarchy()

compareTag

public function compareTag(string $tag)

getScene

public function getScene()

getChildren

public function getChildren()

setParent

public function setParent(?GameObject $parent, bool $worldPositionStays = true): bool

Moves this GameObject under another GameObject, or back to the scene root when $parent is null. When $worldPositionStays is true, Lenga preserves the object's world-space placement and recalculates its local transform. When it is false, Lenga keeps the current local transform relative to the new parent.

getComponents

public function getComponents(?string $type = null)

Pass a concrete component class such as Rigidbody2D::class when you want clear return types and better editor support. String type names remain available for advanced dynamic lookups.

getComponentsInChildren

public function getComponentsInChildren(?string $type = null, bool $includeInactive = false)

getComponentInChildren

public function getComponentInChildren(string $type, bool $includeInactive = false)

getComponentsInParent

public function getComponentsInParent(?string $type = null, bool $includeInactive = false)

getComponentInParent

public function getComponentInParent(string $type, bool $includeInactive = false)

addComponent

public function addComponent(string $type)

Accepts concrete component class names and dynamic type strings. Behaviour components must be added by passing the concrete script class.

registerComponentWrapper

public static function registerComponentWrapper(string $nativeType, string $componentClass): void

Registers a component wrapper class when an advanced package does not follow Lenga's default namespace conventions.

clone

public function clone(?string $name = null)

instantiate

public static function instantiate(
    GameObject|Transform|Component|Behaviour $original,
    string|Vector3|Transform|GameObject|InstantiateOptions|array|null $optionsOrPositionOrParentOrName = null,
    Vector3|Quaternion|bool|string|null $rotationOrWorldPositionStaysOrName = null,
    Transform|GameObject|string|null $parentOrName = null,
    ?string $name = null,
): object

Creates a new scene instance from an existing scene object, a prefab asset reference, or a component reference. When the source is a component, Lenga clones the owning GameObject and returns the matching component on the clone.

Common forms:

$copy = GameObject::instantiate($source);
$copy = GameObject::instantiate($source, 'Display Name');
$copy = GameObject::instantiate($source, $position, $rotation);
$copy = GameObject::instantiate($source, $position, $rotation, $parent);
$copy = GameObject::instantiate($source, InstantiateOptions::under($parent, worldPositionStays: true));
$copy = GameObject::instantiate($source, InstantiateOptions::at($position, name: 'Hit Spark'));

find

public static function find(string $name)

findGameObjectsWithTag

public static function findGameObjectsWithTag(string $tag)

fromPrefabAssetPath

public static function fromPrefabAssetPath(string $assetPath, ?string $name = null)

Creates a prefab asset reference from a project-relative asset path. Most gameplay scripts receive these references through exposed GameObject fields in the inspector.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\GameObject;
use Lenga\Engine\Core\InstantiateOptions;

class GameObjectInteraction extends Behaviour
{
    public function start(): void
    {
        // Access the GameObject this Behaviour is attached to
        $gameObject = $this->gameObject;

        // Get information about this GameObject
        Debug::log('Name: ' . $gameObject->name);
        Debug::log('Active: ' . ($gameObject->isActiveInHierarchy() ? 'yes' : 'no'));

        // Find other game objects by tag
        $enemies = GameObject::findGameObjectsWithTag('Enemy');
        foreach ($enemies as $enemy) {
            Debug::log('Found enemy: ' . $enemy->name);
        }
    }
}

Prefab Field Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\GameObject;

final class ProjectileLauncher extends Behaviour
{
    public ?GameObject $projectilePrefab = null;

    public function fire(): void
    {
        if ($this->projectilePrefab === null) {
            return;
        }

        $projectile = GameObject::instantiate(
            $this->projectilePrefab,
            InstantiateOptions::at($this->transform->position),
        );
    }
}
Core

Input

Class Input

Namespace:

namespace Lenga\Engine\Core;

class Input

Static Properties

touchCount

public static int $touchCount

Number of touch snapshots captured for the current frame. The value is refreshed by the native runtime once per frame.

touches

/** @var list<Touch> */
public static array $touches

Touch snapshots captured for the current frame. Each entry is a Touch object.

touchPressureSupported

public static bool $touchPressureSupported

Whether the active backend reports real touch pressure values. Current raylib-backed builds report false.

touchSupported

public static bool $touchSupported

Whether the active backend reports touch input support.

Methods

getAxis

public static function getAxis(string $axis)

Returns the smoothed value of a named project input axis.

getAxisRaw

public static function getAxisRaw(string $axis)

Returns the raw value of a named project input axis before smoothing.

getKey

public static function getKey(int|string|KeyCode $key)

Checks if the specified key is currently pressed.

getKeyDown

public static function getKeyDown(int|string|KeyCode $key)

Checks if the specified key was pressed down this frame.

getKeyUp

public static function getKeyUp(int|string|KeyCode $key)

Checks if the specified key was released this frame.

getButton

public static function getButton(string $axis)

Returns true while the named project input axis is pressed past its button threshold.

getButtonDown

public static function getButtonDown(string $axis)

Returns true on the frame the named project input axis crosses its button threshold.

getButtonUp

public static function getButtonUp(string $axis)

Returns true on the frame the named project input axis is released below its button threshold.

getMouseButton

public static function getMouseButton(int $button)

Returns whether the given mouse button is currently held.

getMouseButtonDown

public static function getMouseButtonDown(int $button)

Returns true during the frame the user pressed the given mouse button.

getMouseButtonUp

public static function getMouseButtonUp(int $button)

Returns true during the frame the user released the given mouse button.

getTouch

public static function getTouch(int $index)

Returns the Touch snapshot at the given frame-stable index.

isKeyPressed

public static function isKeyPressed(int|string|KeyCode $key)

Returns true on the frame the key is pressed.

isKeyDown

public static function isKeyDown(int|string|KeyCode $key)

Returns true while the key is held.

isKeyReleased

public static function isKeyReleased(int|string|KeyCode $key)

Returns true on the frame the key is released.

isKeyUp

public static function isKeyUp(int|string|KeyCode $key)

Returns true while the key is not held.

isGamepadAvailable

public static function isGamepadAvailable(int $gamepad)

Returns true when the gamepad slot has a connected gamepad.

getGamepadName

public static function getGamepadName(int $gamepad)

Returns the display name reported for the gamepad slot, or an empty string.

isGamepadButtonPressed

public static function isGamepadButtonPressed(int $gamepad, int|GamepadButton $button)

Returns true on the frame the gamepad button is pressed.

isGamepadButtonDown

public static function isGamepadButtonDown(int $gamepad, int|GamepadButton $button)

Returns true while the gamepad button is held.

isGamepadButtonReleased

public static function isGamepadButtonReleased(int $gamepad, int|GamepadButton $button)

Returns true on the frame the gamepad button is released.

isGamepadButtonUp

public static function isGamepadButtonUp(int $gamepad, int|GamepadButton $button)

Returns true while the gamepad button is not held.

getGamepadButtonPressed

public static function getGamepadButtonPressed()

Returns the most recent gamepad button pressed, or GamepadButton::UNKNOWN.

getGamepadAxisCount

public static function getGamepadAxisCount(int $gamepad)

Returns the number of axes exposed by the gamepad.

getGamepadAxisMovement

public static function getGamepadAxisMovement(int $gamepad, int|GamepadAxis $axis)

Returns the current movement value for a gamepad axis.

setGamepadMappings

public static function setGamepadMappings(string $mappings)

Loads additional gamepad mapping definitions and returns the number accepted.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Core\KeyCode;

class PlayerController extends Behaviour
{
    public function update(): void
    {
        // Check for key presses
        if (Input::getKeyDown(KeyCode::W)) {
            Debug::log('W key pressed');
        }

        // Check if key is held down
        if (Input::getKey(KeyCode::SPACE)) {
            // Jump logic
        }

        // Use input axes for smooth movement
        $horizontalInput = Input::getAxis('Horizontal');
        $verticalInput = Input::getAxis('Vertical');
        $lookX = Input::getAxis('Mouse X');
        $lookY = Input::getAxis('Mouse Y');

        if (Input::getButtonDown('Fire')) {
            // Fire can be bound to a mouse button in Project > Input Settings.
        }

        // Check gamepad input
        if (Input::isGamepadAvailable(0)) {
            $gamepadAxis = Input::getGamepadAxisMovement(0, 0);
        }
    }
}
Core

InstantiateOptions

Namespace:

namespace Lenga\Engine\Core;

class InstantiateOptions

InstantiateOptions describes how a prefab, GameObject, or component source should be instantiated.

Properties

name

public ?string $name

Optional display name for the created instance.

position

public ?Vector3 $position

Optional world-space position for the created instance.

rotation

public ?Quaternion $rotation

Optional world-space rotation for the created instance.

parent

public GameObject|Transform|null $parent

Optional parent for the created instance.

worldPositionStays

public bool $worldPositionStays

Controls how a parent-only instantiate keeps transform values. Use false to keep the source local transform under the new parent. Use true to preserve the source world transform.

Methods

named

public static function named(string $name): self

Creates options that only rename the created instance.

at

public static function at(
    Vector3 $position,
    Vector3|Quaternion|null $rotation = null,
    GameObject|Transform|null $parent = null,
    ?string $name = null,
): self

Creates options for spawning at a world position, with optional world rotation, parent, and name. Passing a Vector3 as the rotation treats it as Euler angles in degrees.

under

public static function under(
    GameObject|Transform|null $parent,
    bool $worldPositionStays = false,
    ?string $name = null,
): self

Creates options for instantiating under a parent.

Example

use Lenga\Engine\Core\GameObject;
use Lenga\Engine\Core\InstantiateOptions;
use Lenga\Engine\Core\Vector3;

$spark = GameObject::instantiate(
    $this->hitSparkPrefab,
    InstantiateOptions::at(
        position: $hitPoint,
        rotation: new Vector3(0, 45, 0),
        name: 'Hit Spark',
    ),
);
Core

KinematicMove3DResult

Namespace:

namespace Lenga\Engine\Core;

class KinematicMove3DResult

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CharacterController;
use Lenga\Engine\Core\Vector3;

class CharacterMovement extends Behaviour
{
    public function update(): void
    {
        $controller = $this->getComponent(CharacterController::class);

        // Move the character
        $moveDirection = new Vector3(1, 0, 0);
        $result = $controller->move($moveDirection);

        // Check the move result
        if ($result->collided) {
            Debug::log('Character hit something');

            if ($result->hit !== null) {
                $hitObject = $result->hit->gameObject;
                $hitDistance = $result->hit->distance;
                Debug::log('Hit object: ' . $hitObject->name);
            }
        }

        if ($result->usedSlide) {
            Debug::log('Character slid along a surface');
        }
    }
}
Core

MathUtil

A collection of common math functions.

Namespace:

namespace Lenga\Engine\Core;

class MathUtil

Methods

abs

public static function abs(float $f)

Returns the absolute value of f.

ceil

public static function ceil(float $f)

Returns the smallest integer greater than or equal to f.

ceilToInt

public static function ceilToInt(float $f)

Returns the smallest integer greater than or equal to f (as int).

floor

public static function floor(float $f)

Returns the largest integer smaller than or equal to f.

floorToInt

public static function floorToInt(float $f)

Returns the largest integer smaller than or equal to f (as int).

round

public static function round(float $f)

Returns f rounded to the nearest integer.

roundToInt

public static function roundToInt(float $f)

Returns f rounded to the nearest integer (as int).

sign

public static function sign(float $f)

Returns the mathematical sign of f: -1, 0 or 1.

sqrt

public static function sqrt(float $f)

Returns the square root of f.

pow

public static function pow(float $f, float $p)

Returns f raised to the power p.

exp

public static function exp(float $power)

Returns e raised to the power power.

log

public static function log(float $f, float $base = M_E)

Returns the logarithm of f in a specified base.

log10

public static function log10(float $f)

Returns the base-10 logarithm of f.

acos

public static function acos(float $f)

Returns the arc-cosine of f (radians).

asin

public static function asin(float $f)

Returns the arc-sine of f (radians).

atan

public static function atan(float $f)

Returns the arc-tangent of f (radians).

atan2

public static function atan2(float $y, float $x)

Returns the angle in radians whose tangent is y/x.

cos

public static function cos(float $f)

Returns the cosine of angle f (radians).

sin

public static function sin(float $f)

Returns the sine of angle f (radians).

tan

public static function tan(float $f)

Returns the tangent of angle f (radians).

clamp

public static function clamp(float $value, float $min, float $max)

Clamps value between min and max and returns the clamped value.

clamp01

public static function clamp01(float $value)

Clamps value between 0 and 1.

min

public static function min(float $a, float ...$rest)

Returns the minimum of two or more values.

max

public static function max(float $a, float ...$rest)

Returns the maximum of two or more values.

repeat

public static function repeat(float $t, float $length)

Loops the value t so that it is never larger than length and never

pingPong

public static function pingPong(float $t, float $length)

PingPongs the value t between 0 and length.

wrap

public static function wrap(float $value, float $min, float $max)

Wraps a value into the [min, max) range.

lerp

public static function lerp(float $a, float $b, float $t)

Linearly interpolates between a and b by t (clamped to [0, 1]).

lerpUnclamped

public static function lerpUnclamped(float $a, float $b, float $t)

Linearly interpolates between a and b by t with no clamping.

lerpAngle

public static function lerpAngle(float $a, float $b, float $t)

Same as {@see lerp()} but interpolates correctly when values wrap

inverseLerp

public static function inverseLerp(float $a, float $b, float $t)

Determines the interpolation factor of value between a and b

smoothStep

public static function smoothStep(float $from, float $to, float $t)

Interpolates between from and to with smoothing at the limits.

smoothDamp

public static function smoothDamp(float $current,
        float $target,
        float &$currentVelocity,
        float $smoothTime,
        float $deltaTime,
        float $maxSpeed = self::INFINITY,)

Gradually moves the current value towards a target value, over a

smoothDampAngle

public static function smoothDampAngle(float $current,
        float $target,
        float &$currentVelocity,
        float $smoothTime,
        float $deltaTime,
        float $maxSpeed = self::INFINITY,)

Gradually changes an angle given in degrees towards a desired goal

moveTowards

public static function moveTowards(float $current, float $target, float $maxDelta)

Moves a value current towards target.

moveTowardsAngle

public static function moveTowardsAngle(float $current, float $target, float $maxDelta)

Same as {@see moveTowards()} but handles angles wrapping around

approximately

public static function approximately(float $a, float $b)

Compares two floating-point values and returns true if they are

deltaAngle

public static function deltaAngle(float $current, float $target)

Calculates the shortest difference between two angles in degrees.

closestPowerOfTwo

public static function closestPowerOfTwo(int $value)

Returns the closest power of two to the given value.

isPowerOfTwo

public static function isPowerOfTwo(int $value)

Returns true if the value is a power of two.

nextPowerOfTwo

public static function nextPowerOfTwo(int $value)

Returns the next power of two that is equal to or greater than the

perlinNoise

public static function perlinNoise(float $x, float $y)

Generates 2D Perlin noise for the given coordinates.

perlinNoise1D

public static function perlinNoise1D(float $x)

Generates 1D pseudo-random pattern of float values across a 2D

floatToHalf

public static function floatToHalf(float $f)

Encode a 32-bit float into its IEEE 754 half-precision (16-bit)

halfToFloat

public static function halfToFloat(int $half)

Convert a half-precision float (16-bit int) to a 32-bit float.

gammaToLinearSpace

public static function gammaToLinearSpace(float $value)

Converts a value from gamma (sRGB) to linear colour space.

linearToGammaSpace

public static function linearToGammaSpace(float $value)

Converts a value from linear to gamma (sRGB) colour space.

correlatedColorTemperatureToRGB

public static function correlatedColorTemperatureToRGB(float $kelvin)

Converts a colour temperature in Kelvin to an approximate RGB

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\MathUtil;

class MovementController extends Behaviour
{
    public function update(): void
    {
        // Clamp a value between min and max
        $speed = MathUtil::clamp($desiredSpeed, 0.0, 10.0);

        // Linear interpolation for smooth transitions
        $currentAlpha = MathUtil::lerp($currentAlpha, $targetAlpha, 0.1);

        // Wrap angle values
        $angle = MathUtil::wrap($angle, 0.0, 360.0);

        // Smooth movement towards a target
        $velocity = 0.0;
        $currentValue = MathUtil::smoothDamp(
            $currentValue,
            $targetValue,
            $velocity,
            0.3,
            Time::deltaTime()
        );
    }
}
Core

MeshCollider3D

Namespace:

namespace Lenga\Engine\Core;

class MeshCollider3D

MeshCollider3D uses a mesh or model asset as static triangle collision geometry. It is intended for authored environment pieces such as floors, ramps, terrain, race tracks, and level shells.

Properties

  • meshPath: project-relative path to the mesh or model asset used for collision.
  • meshIndex: zero-based mesh index when the asset contains multiple meshes.
  • offset: local offset from the GameObject transform.
  • isTrigger: detects overlap without producing a solid collision response.
  • materialPath: project-relative path to a Physics Material 3D asset.
  • material: a PhysicsMaterial3D object representing the collider material.

Methods

getMeshCount

public function getMeshCount(): int

Returns the number of meshes loaded from the assigned mesh or model asset.

The value is 0 when no asset is assigned, the asset cannot be loaded, or the runtime has not resolved mesh data for this collider yet.

isTouching

public function isTouching(bool $includeTriggers = true, ?int $layerMask = null): bool

Returns true when this mesh collider is touching another matching 3D collider.

Set $includeTriggers to false to ignore trigger contacts. Pass a $layerMask to limit the check to colliders on matching GameObject layers.

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null): array

Returns the current Collision3D contacts involving this mesh collider.

Set $includeTriggers to false to return only solid contacts. Pass a $layerMask to limit results to matching GameObject layers.

Notes

For v1, use MeshCollider3D on static scene geometry. Moving actors should use primitive 3D colliders such as BoxCollider3D, SphereCollider3D, CapsuleCollider3D, or CylinderCollider3D.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\MeshCollider3D;

final class TrackCollision extends Behaviour
{
    public function start(): void
    {
        $collider = $this->getComponent(MeshCollider3D::class);
        $collider->meshPath = 'Assets/Models/Track.glb';
        $collider->meshIndex = 0;
    }
}
Core

MeshRenderer

Namespace:

namespace Lenga\Engine\Core;

class MeshRenderer

Methods

loadMesh

public function loadMesh(string $meshPath)

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\MeshRenderer;

class CustomMeshDisplay extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(MeshRenderer::class);
        $renderer->loadMesh('assets/models/character.obj');
        $renderer->setColor(200, 200, 200, 255);
    }
}
Core

ModelRenderer

Namespace:

namespace Lenga\Engine\Core;

class ModelRenderer

Methods

loadModel

public function loadModel(string $modelPath)

loadAnimations

public function loadAnimations(string $animationPath)

getAnimationCount

public function getAnimationCount()

getAnimationNames

public function getAnimationNames()

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\ModelRenderer;

class AnimatedCharacter extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(ModelRenderer::class);
        $renderer->loadModel('assets/models/player.gltf');
        $renderer->loadAnimations('assets/animations/player.anim');

        $animCount = $renderer->getAnimationCount();
        $animNames = $renderer->getAnimationNames();
        Debug::log('Available animations: ' . implode(', ', $animNames));
    }
}
Core

ParticleSystem

Namespace:

namespace Lenga\Engine\Core;

final class ParticleSystem extends Component

Each ParticleSystem controls one native 2D or 3D particle emitter attached to a GameObject. A GameObject may have multiple ParticleSystem components for layered effects.

Author detailed modules, curves, gradients, bursts, renderer settings, and reusable .particle.json profiles in the editor. Use PHP for playback, burst emission, profile swaps, texture swaps, and simple runtime tuning.

Sub Emitters are authored in the editor or in particle profiles. At runtime, Lenga currently supports Birth and Death burst slots that emit from their target ParticleSystem automatically, including per-slot Emit Probability; there is no separate PHP method to trigger or configure a sub-emitter slot yet. Unity-style Birth-over-lifetime, Manual, Collision, Trigger, and Inherit behavior is planned but should not be treated as available runtime API.

For authoring workflows, start with Particle Systems. For scripted control patterns, see Script Particle Systems.

When an editor-authored field uses a curve, gradient, or random range, the simple PHP properties return a deterministic preview value. Use profiles for detailed authoring and PHP setters for straightforward runtime tuning.

Properties

public bool $isPlaying { get; }
public int $aliveParticleCount { get; }
public string $dimension { get; set; }
public string $simulationSpace { get; set; }
public string $customSimulationSpaceId { get; set; }
public string $deltaTime { get; set; }
public string $scalingMode { get; set; }
public string $emitterVelocityMode { get; set; }
public string $stopAction { get; set; }
public string $ringBufferMode { get; set; }
public float $ringBufferLoopRangeMin { get; set; }
public float $ringBufferLoopRangeMax { get; set; }
public string $sortingLayer { get; set; }
public int $orderInLayer { get; set; }
public string $texturePath { get; }
public string $profilePath { get; }
public float $emissionRate { get; set; }
public float $lifetime { get; set; }
public float $startSpeed { get; set; }
public float $startSize { get; set; }
public float $endSize { get; set; }
public string $shapeType { get; set; }
public Color $startColor { get; set; }
public Color $endColor { get; set; }
public float $simulationTimeMs { get; }
public int $renderSubmissions { get; }

dimension is 2D or 3D. shapeType accepts values such as Point, Line, Circle, Rectangle, Cone2D, Sphere, Hemisphere, Box, and Cone3D.

simulationSpace is Local, World, or Custom (simulating in the Transform of the scene object referenced by customSimulationSpaceId). deltaTime is Scaled or Unscaled. scalingMode is Hierarchy, Local, or Shape. emitterVelocityMode is Transform or Rigidbody. stopAction is None, Disable, or Destroy and applies to the GameObject once a stopped system has no live particles. ringBufferMode is Disabled, PauseUntilReplaced, or LoopUntilReplaced with the loop range expressed as normalized 0..1 lifetimes.

simulationTimeMs and renderSubmissions are read-only aggregate profiling values; the bridge never exchanges per-particle data.

The properties sortingLayer, orderInLayer, texturePath, startColor, and endColor remain supported for existing scripts. Setting startColor or endColor from PHP writes a simple constant color setup; use the editor profile when an effect needs random colors or gradients.

Methods

play

public function play(): void

Starts the emitter.

pause

public function pause(): void

Pauses simulation without clearing living particles.

stop

public function stop(bool $clear = false): void

Stops emission. Pass true to clear living particles immediately.

restart

public function restart(bool $keepSeed = false): void

Clears and starts the system. Pass true to keep the current deterministic seed sequence.

clear

public function clear(): void

Removes all living particles.

simulate

public function simulate(float $seconds, bool $restart = false): void

Advances the particle simulation manually. This is useful for warmup, scripted previews, and deterministic setup.

emit

public function emit(int $count, array $options = []): void

Creates a burst immediately. The optional $options parameter is reserved for future per-burst overrides.

getCollisionEvents

public function getCollisionEvents(): array

Returns this frame's batched particle collision events. Recording requires the Collision module's Send Messages option (or a Collision sub-emitter slot). Each event carries particleId, point, normal, velocity (approach velocity), approachSpeed, colliderId, and colliderName. The buffer is bounded per frame and rebuilt every update, so poll once per update loop.

getTriggerEvents

public function getTriggerEvents(): array

Returns this frame's batched trigger events for conditions whose action is Callback. Each event carries particleId, position, velocity, condition (Inside, Outside, Enter, or Exit), and colliderMask (bits into the Trigger module's collider list).

triggerSubEmitter

public function triggerSubEmitter(int $slotIndex = -1): void

Fires the system's Manual sub-emitter slots once for every live particle, spawning each slot's emit count into its target system with the slot's inheritance flags and emit probability applied. Pass a slot index to fire one slot, or -1 to fire every Manual slot. Slots with other event types are ignored.

loadTexture

public function loadTexture(string $texturePath): bool

Loads a particle texture from a project-relative path.

loadProfile

public function loadProfile(string $profilePath): bool

Loads a reusable .particle.json profile from a project-relative path.

getStartColor

public function getStartColor(): array

Returns byte-channel RGBA values for the particle start color.

setStartColor

public function setStartColor(Color|array|int $red, ?int $green = null, ?int $blue = null, int $alpha = 255): void

Sets the color used when particles spawn. Pass a Color, an RGBA array, or individual byte-channel values.

getEndColor

public function getEndColor(): array

Returns byte-channel RGBA values for the particle end color.

setEndColor

public function setEndColor(Color|array|int $red, ?int $green = null, ?int $blue = null, int $alpha = 255): void

Sets the color particles blend toward over their lifetime.

getState

public function getState(): array

Returns particle state for debugging overlays, runtime UI, or advanced tools. Common keys include isPlaying, aliveParticleCount, dimension, maxParticles, emissionRate, lifetime, startSpeed, startSize, startRotation, flipRotation, texturePath, profilePath, shapeType, shapeRadius, shapeRadiusThickness, shapeArc, shapeArcMode, shapeEmitFrom, shapeAlignToDirection, renderMode, sortingLayer, and orderInLayer.

renderMode reports the editor-authored renderer mode, such as Billboard, StretchedBillboard, HorizontalBillboard, VerticalBillboard, or Mesh. Author detailed renderer settings in the editor or in a particle profile; PHP is best used for playback, bursts, profile swaps, and simple runtime tuning.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\ParticleSystem;

final class LandingDust extends Behaviour
{
    private ?ParticleSystem $dust = null;

    public function start(): void
    {
        $this->dust = $this->getComponent(ParticleSystem::class);
        $this->dust?->loadProfile('Assets/Particles/LandingDust.particle.json');
    }

    public function onLanded(): void
    {
        $this->dust?->restart(true);
        $this->dust?->emit(16);
    }
}

Related Guides

Core

Physics2D

Namespace:

namespace Lenga\Engine\Core;

class Physics2D

Methods

getGravity

public static function getGravity(): Vector2

Returns the global 2D gravity vector applied to dynamic Rigidbody2D components that have gravity enabled.

setGravity

public static function setGravity(Vector2 $gravity): void

Sets the global 2D gravity vector. Individual bodies still use useGravity and gravityScale to opt in and scale this value.

layerMask

public static function layerMask(int ...$layers)

raycast

public static function raycast(Vector3 $origin,
        Vector3 $direction,
        float $distance,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

raycastAll

public static function raycastAll(Vector3 $origin,
        Vector3 $direction,
        float $distance,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

circleCast

public static function circleCast(Vector3 $origin,
        float $radius,
        Vector3 $direction,
        float $distance,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

circleCastAll

public static function circleCastAll(Vector3 $origin,
        float $radius,
        Vector3 $direction,
        float $distance,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

boxCast

public static function boxCast(Vector3 $origin,
        Vector3 $size,
        Vector3 $direction,
        float $distance,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

boxCastAll

public static function boxCastAll(Vector3 $origin,
        Vector3 $size,
        Vector3 $direction,
        float $distance,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

overlapPoint

public static function overlapPoint(Vector3 $point,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

overlapCircleAll

public static function overlapCircleAll(Vector3 $center,
        float $radius,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

overlapBoxAll

public static function overlapBoxAll(Vector3 $center,
        Vector3 $size,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Debug;
use Lenga\Engine\Core\Physics2D;
use Lenga\Engine\Core\Vector2;
use Lenga\Engine\Core\Vector3;

class PhysicsQueries extends Behaviour
{
    public function update(): void
    {
        Physics2D::setGravity(new Vector2(0, -9.8));
        Debug::log('Gravity: ' . Physics2D::getGravity());

        // Raycast in 2D
        $origin = new Vector3(0, 0, 0);
        $direction = new Vector3(1, 0, 0);
        $hit = Physics2D::raycast($origin, $direction, 100.0);

        if ($hit !== null) {
            Debug::log('Hit: ' . $hit->gameObject->name);
        }

        // Overlap circle
        $circle = Physics2D::overlapCircleAll(new Vector3(0, 0, 0), 5.0);
        Debug::log('Objects in circle: ' . count($circle));

        // Overlap box
        $box = Physics2D::overlapBoxAll(new Vector3(0, 0, 0), new Vector3(2, 2, 0));
        foreach ($box as $collider) {
            Debug::log('Overlapping: ' . $collider->gameObject->name);
        }
    }
}
Core

Physics3D

Namespace:

namespace Lenga\Engine\Core;

class Physics3D

Methods

getGravity

public static function getGravity(): Vector3

Returns the global 3D gravity vector applied to dynamic Rigidbody3D components that have gravity enabled.

setGravity

public static function setGravity(Vector3 $gravity): void

Sets the global 3D gravity vector. Individual bodies still use useGravity and gravityScale to opt in and scale this value.

moveAndSlideSphere

public static function moveAndSlideSphere(Vector3 $position,
        float $radius,
        Vector3 $delta,
        ?GameObject $ignoreGameObject = null,
        ?Vector3 $offset = null,
        float $skinWidth = 0.05,
        bool $includeTriggers = false,
        ?int $layerMask = null,)

@deprecated Use CharacterController::move() for the current kinematic movement workflow.

moveAndSlideCapsule

public static function moveAndSlideCapsule(Vector3 $position,
        float $radius,
        float $height,
        Vector3 $delta,
        ?GameObject $ignoreGameObject = null,
        ?Vector3 $offset = null,
        float $skinWidth = 0.05,
        bool $includeTriggers = false,
        ?int $layerMask = null,)

@deprecated Use CharacterController::move() for the current kinematic movement workflow.

layerMask

public static function layerMask(int ...$layers)

raycast

public static function raycast(Vector3 $origin,
        Vector3 $direction,
        float $distance,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

raycastAll

public static function raycastAll(Vector3 $origin,
        Vector3 $direction,
        float $distance,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

overlapSphereAll

public static function overlapSphereAll(Vector3 $center,
        float $radius,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

overlapBoxAll

public static function overlapBoxAll(Vector3 $center,
        Vector3 $size,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

overlapCapsuleAll

public static function overlapCapsuleAll(Vector3 $center,
        float $radius,
        float $height,
        bool $includeTriggers = true,
        ?int $layerMask = null,)

@return list

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Debug;
use Lenga\Engine\Core\Physics3D;
use Lenga\Engine\Core\Vector3;

class PhysicsQueries3D extends Behaviour
{
    public function update(): void
    {
        Physics3D::setGravity(new Vector3(0, -9.8, 0));
        Debug::log('Gravity: ' . Physics3D::getGravity());

        // Raycast in 3D
        $origin = $this->transform->position;
        $direction = $this->transform->forward;
        $hit = Physics3D::raycast($origin, $direction, 100.0);

        if ($hit !== null) {
            Debug::log('Raycast hit: ' . $hit->gameObject->name);
            Debug::log('Distance: ' . $hit->distance);
        }

        // Overlap sphere
        $sphereColliders = Physics3D::overlapSphereAll(
            new Vector3(0, 1, 0),
            5.0
        );
        Debug::log('Objects in sphere: ' . count($sphereColliders));

        // Overlap box
        $boxColliders = Physics3D::overlapBoxAll(
            new Vector3(0, 0, 0),
            new Vector3(2, 2, 2)
        );
        Debug::log('Objects in box: ' . count($boxColliders));
    }
}
Core

PhysicsMaterial2D

Namespace:

namespace Lenga\Engine\Core;

class PhysicsMaterial2D

Methods

fromArray

public static function fromArray(array $state, string $assetPath = '')

@param array{

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Rigidbody2D;

class BouncyBall extends Behaviour
{
    public function start(): void
    {
        $rb = $this->getComponent(Rigidbody2D::class);

        // Create and configure physics material
        $material = new PhysicsMaterial2D();
        $material->density = 1.0;
        $material->friction = 0.4;
        $material->restitution = 0.8; // Bouncy!

        // Apply material to collider
        $rb->material = $material;
    }
}
Core

PhysicsMaterial3D

Namespace:

namespace Lenga\Engine\Core;

class PhysicsMaterial3D

PhysicsMaterial3D describes how a 3D collider responds when it contacts another collider.

Use it for surfaces that need distinct friction or bounce behavior, such as ice, rubber, stone, ramps, or collectible bumpers.

Properties

  • dynamicFriction: friction while bodies are already moving across each other.
  • staticFriction: friction used when bodies are trying to start sliding.
  • bounciness: restitution applied to contacts.
  • frictionCombine: how friction should combine with the other collider's material.
  • bounceCombine: how bounciness should combine with the other collider's material.
  • assetPath: the project-relative asset path when the material came from the editor asset workflow.

Methods

fromArray

public static function fromArray(array $state, string $assetPath = ''): self

Creates a PhysicsMaterial3D from collider state data.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\PhysicsMaterial3D;
use Lenga\Engine\Core\SphereCollider3D;

final class BouncyBall extends Behaviour
{
    public function start(): void
    {
        $collider = $this->getComponent(SphereCollider3D::class);

        $collider->material = new PhysicsMaterial3D(
            dynamicFriction: 0.2,
            staticFriction: 0.2,
            bounciness: 0.85,
        );
    }
}

For authored projects, prefer creating a Physics Material 3D asset in the editor and assigning it to the collider's Material field.

Core

PlaneRenderer

Namespace:

namespace Lenga\Engine\Core;

class PlaneRenderer

Methods

setSize

public function setSize(float $width, float $length)

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\PlaneRenderer;

class GroundPlane extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(PlaneRenderer::class);
        $renderer->setSize(10.0, 10.0);
        $renderer->setColor(100, 150, 100, 255); // Green ground
    }
}
Core

PointLight

Namespace:

namespace Lenga\Engine\Core;

class PointLight

Methods

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\PointLight;

class TorchLight extends Behaviour
{
    public function start(): void
    {
        $light = $this->getComponent(PointLight::class);
        $light->intensity = 1.5;
        $light->range = 10.0;
        $light->setColor(255, 200, 100, 255); // Warm orange light
    }

    public function update(): void
    {
        $light = $this->getComponent(PointLight::class);
        // Flicker effect
        $light->intensity = 1.5 + MathUtil::sin(Time::time() * 5) * 0.3;
    }
}
Core

Prefab

Namespace:

namespace Lenga\Engine\Core;

class Prefab

Methods

instantiate

public static function instantiate(
    string $assetPath,
    string|InstantiateOptions|array|null $options = null,
): GameObject

Instantiates the prefab at the project-relative asset path into the active scene. Pass a string to rename the instance, or pass InstantiateOptions when the instance should spawn at a position, rotation, or parent.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Core\InstantiateOptions;
use Lenga\Engine\Core\Prefab;
use Lenga\Engine\Enumerations\KeyCode;

class SpawnManager extends Behaviour
{
    public function start(): void
    {
        // Instantiate a prefab at this GameObject's position
        $enemyInstance = Prefab::instantiate(
            'Assets/Prefabs/Enemy.prefab.json',
            InstantiateOptions::at($this->gameObject->transform->position, name: 'Enemy_1'),
        );
    }

    public function update(): void
    {
        if (Input::getKeyDown(KeyCode::SPACE)) {
            // Spawn a bullet from a prefab
            $bullet = Prefab::instantiate(
                'Assets/Prefabs/Bullet.prefab.json',
                InstantiateOptions::at($this->transform->position),
            );
        }
    }
}
Core

Preferences

`Preferences` is the PHP API for small engine-managed local settings.

Namespace:

namespace Lenga\Engine\Core;

final class Preferences

Use Preferences for values such as volume, language, fullscreen, mouse sensitivity, and tiny non-critical flags. Do not use it for full save-game data or sensitive information.

Preferences are loaded from an engine-managed per-user location. Script code reads and writes values through the Preferences API instead of choosing an arbitrary file path.

Methods

getInt

public static function getInt(string $key, int $defaultValue = 0): int

Returns the saved integer value for $key.

If the key does not exist, Lenga returns $defaultValue.

setInt

public static function setInt(string $key, int $value): void

Sets an integer preference in memory.

Call save() when you want to write the change to disk.

getFloat

public static function getFloat(string $key, float $defaultValue = 0.0): float

Returns the saved float value for $key, or $defaultValue when the key does not exist.

setFloat

public static function setFloat(string $key, float $value): void

Sets a float preference in memory.

getString

public static function getString(string $key, string $defaultValue = ''): string

Returns the saved string value for $key, or $defaultValue when the key does not exist.

setString

public static function setString(string $key, string $value): void

Sets a string preference in memory.

getBool

public static function getBool(string $key, bool $defaultValue = false): bool

Returns the saved boolean value for $key, or $defaultValue when the key does not exist.

setBool

public static function setBool(string $key, bool $value): void

Sets a boolean preference in memory.

hasKey

public static function hasKey(string $key): bool

Returns true when a preference exists for $key.

deleteKey

public static function deleteKey(string $key): void

Removes one preference from memory.

Call save() to persist the removal.

deleteAll

public static function deleteAll(): void

Removes all preferences from memory.

Call save() to persist the reset.

save

public static function save(): bool

Writes the current in-memory preferences to disk.

Returns true when the save succeeds.

Example

The code below belongs in a Behaviour class attached to a normal GameObject.

<?php

namespace Game\Scripts;

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Preferences;

final class SettingsController extends Behaviour
{
    public function start(): void
    {
        $volume = Preferences::getFloat('audio.masterVolume', 0.8);

        // Apply $volume to your audio settings here.
    }

    public function setMasterVolume(float $volume): void
    {
        Preferences::setFloat('audio.masterVolume', $volume);
        Preferences::save();
    }
}

Usage Notes

Preferences is for small local settings and simple scalar state.

It is not a save-game system, a local database, or a secure secret store.

Core

Core Project Structure

Lenga expects a project folder to contain these key pieces:

MyGame/
  Assets/
  ProjectSettings/
    lenga.json
  bootstrap.php
  composer.json

If one of the important files is missing, the editor shows a validation error instead of opening the project in a broken state.

composer.json declares the project's PHP autoload rules and package dependencies. Projects created by Lenga include the matching PHP gameplay API and can run without a manual Composer install.

Core

Quaternion

Namespace:

namespace Lenga\Engine\Core;

class Quaternion

Quaternion is Lenga's 3D rotation value type. Use it for world and local Transform rotations, smooth rotation interpolation, direction-to-direction alignment, and rotating direction vectors without relying on Euler-angle addition.

Lenga's PHP API uses lower camelCase method names.

Properties

x

public float $x

X component of the quaternion.

y

public float $y

Y component of the quaternion.

z

public float $z

Z component of the quaternion.

w

public float $w

W component of the quaternion.

sqrMagnitude

public float $sqrMagnitude

Squared length of the quaternion.

magnitude

public float $magnitude

Length of the quaternion.

normalized

public Quaternion $normalized

Returns a normalized copy of the quaternion.

eulerAngles

public Vector3 $eulerAngles

Gets or sets the Euler-angle representation in degrees.

Constructors

public function __construct(float $x = 0.0, float $y = 0.0, float $z = 0.0, float $w = 1.0)

Creates a quaternion from raw x, y, z, and w components.

Static Methods

identity

public static function identity(): self

Returns the identity rotation.

euler

public static function euler(Vector3|float $x, ?float $y = null, ?float $z = null): self

Creates a quaternion from Euler angles in degrees. Pass either a Vector3 or three degree values.

fromEulerAngles

public static function fromEulerAngles(Vector3 $degrees): self

Creates a quaternion from Euler angles in degrees.

angleAxis

public static function angleAxis(float $angleDegrees, Vector3 $axis): self

Creates a rotation around an axis by an angle in degrees.

fromAxisAngle

public static function fromAxisAngle(Vector3 $axis, float $angleDegrees): self

Creates a rotation around an axis by an angle in degrees.

dot

public static function dot(Quaternion $a, Quaternion $b): float

Returns the dot product between two rotations.

angle

public static function angle(Quaternion $a, Quaternion $b): float

Returns the shortest angle in degrees between two rotations.

fromToRotation

public static function fromToRotation(Vector3 $fromDirection, Vector3 $toDirection): self

Creates a rotation that turns one direction into another direction.

lookRotation

public static function lookRotation(Vector3 $forward, ?Vector3 $upwards = null): self

Creates a rotation using a forward direction and optional upwards direction. In Lenga, Vector3::back() is the default forward direction used by Transform::$forward.

inverseOf

public static function inverseOf(Quaternion $rotation): self

Returns the inverse of a rotation.

normalizeRotation

public static function normalizeRotation(Quaternion $rotation): self

Returns a normalized copy of a rotation.

lerp

public static function lerp(Quaternion $a, Quaternion $b, float $t): self

Linearly interpolates between two rotations, clamps t to 0..1, and normalizes the result.

lerpUnclamped

public static function lerpUnclamped(Quaternion $a, Quaternion $b, float $t): self

Linearly interpolates between two rotations without clamping t, and normalizes the result.

slerp

public static function slerp(Quaternion $a, Quaternion $b, float $t): self

Spherically interpolates between two rotations and clamps t to 0..1.

slerpUnclamped

public static function slerpUnclamped(Quaternion $a, Quaternion $b, float $t): self

Spherically interpolates between two rotations without clamping t.

rotateTowards

public static function rotateTowards(Quaternion $from, Quaternion $to, float $maxDegreesDelta): self

Rotates from one orientation toward another by up to maxDegreesDelta.

Instance Methods

toEulerAngles

public function toEulerAngles(): Vector3

Converts the quaternion to Euler angles in degrees.

multiply

public function multiply(Quaternion $other): self

Combines this rotation with another rotation.

set

public function set(float $x, float $y, float $z, float $w): void

Sets the quaternion components.

setFromToRotation

public function setFromToRotation(Vector3 $fromDirection, Vector3 $toDirection): void

Replaces this quaternion with a rotation that turns one direction into another direction.

setLookRotation

public function setLookRotation(Vector3 $forward, ?Vector3 $upwards = null): void

Replaces this quaternion with a look rotation.

toAngleAxis

public function toAngleAxis(): array

Returns an array with angle in degrees and axis as a Vector3.

inverse

public function inverse(): self

Returns the inverse rotation.

rotateVector

public function rotateVector(Vector3 $vector): Vector3

Rotates a direction or offset vector by this quaternion.

normalize

public function normalize(): self

Normalizes this quaternion in place and returns it.

equals

public function equals(Quaternion $other): bool

Performs exact component equality.

__toString

public function __toString(): string

Returns a readable component string.

Index Access

Quaternion supports array-style access:

$rotation[0];     // x
$rotation[1];     // y
$rotation[2];     // z
$rotation[3];     // w
$rotation['w'];   // w

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Quaternion;
use Lenga\Engine\Core\Time;
use Lenga\Engine\Core\Vector3;

final class TurretAim extends Behaviour
{
    public function update(): void
    {
        $targetRotation = Quaternion::lookRotation($this->targetDirection());

        $this->transform->rotation = Quaternion::rotateTowards(
            $this->transform->rotation,
            $targetRotation,
            120.0 * Time::deltaTime(),
        );
    }

    private function targetDirection(): Vector3
    {
        return Vector3::back();
    }
}
Core

RaycastHit2D

Namespace:

namespace Lenga\Engine\Core;

class RaycastHit2D

Methods

fromNativeData

public static function fromNativeData(array $data)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Physics2D;
use Lenga\Engine\Core\Vector3;

class RaycastExample extends Behaviour
{
    public function update(): void
    {
        $origin = $this->transform->position;
        $direction = new Vector3(1, 0, 0);

        // Cast a ray and get hit information
        $hit = Physics2D::raycast($origin, $direction, 100.0);

        if ($hit !== null) {
            // Access hit information
            $hitObject = $hit->gameObject;
            $hitDistance = $hit->distance;
            $hitPoint = $hit->point;
            $hitNormal = $hit->normal;

            Debug::log('Hit object: ' . $hitObject->name);
            Debug::log('Distance: ' . $hitDistance);
        }
    }
}
Core

RaycastHit3D

Namespace:

namespace Lenga\Engine\Core;

class RaycastHit3D

Methods

fromNativeData

public static function fromNativeData(array $data)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Physics3D;
use Lenga\Engine\Core\Vector3;

class WeaponRaycast extends Behaviour
{
    public function update(): void
    {
        if (Input::getMouseButtonDown(0)) {
            // Cast a ray from camera forward
            $camera = $this->getComponent(Camera::class);
            $origin = $camera->transform->position;
            $direction = $camera->transform->forward;

            // Raycast from camera
            $hit = Physics3D::raycast($origin, $direction, 1000.0);

            if ($hit !== null) {
                $targetObject = $hit->gameObject;
                $impactPoint = $hit->point;

                Debug::log('Shot ' . $targetObject->name);
                // Apply damage, create impact effect, etc.
            }
        }
    }
}
Core

RectangleRenderer

Namespace:

namespace Lenga\Engine\Core;

class RectangleRenderer

Methods

setSize

public function setSize(float $width, float $height)

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\RectangleRenderer;

class UIPanel extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(RectangleRenderer::class);
        $renderer->setSize(2.0, 3.0);
        $renderer->setColor(50, 50, 200, 200); // Semi-transparent blue
    }
}
Core

Rigidbody2D

2D rigid body physics component used by the Lenga physics runtime.

Namespace:

namespace Lenga\Engine\Core;

class Rigidbody2D

Methods

isTouching

public function isTouching(bool $includeTriggers = true, ?int $layerMask = null)

Rigid body mode used by the physics solver (for example: Dynamic, Kinematic, Static).

isGrounded

public function isGrounded(float $minSupportDot = 0.5,
        bool $includeTriggers = false,
        ?int $layerMask = null,)

Returns true when this body is resting on a supporting surface.

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null)

Returns all current contacts that match the query.

addForce

public function addForce(Vector2 $force, ForceMode2D $mode = ForceMode2D::Force)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Rigidbody2D;
use Lenga\Engine\Core\Vector2;
use Lenga\Engine\Core\ForceMode2D;

class PlayerMovement2D extends Behaviour
{
    public float $jumpForce = 10.0;

    public function update(): void
    {
        $rb = $this->getComponent(Rigidbody2D::class);

        // Apply horizontal movement
        $moveInput = Input::getAxis('Horizontal');
        $rb->velocity = new Vector2($moveInput * 5.0, $rb->velocity->y);

        // Jump when grounded
        if (Input::getKeyDown(KeyCode::SPACE) && $rb->isGrounded()) {
            $rb->addForce(new Vector2(0, $this->jumpForce), ForceMode2D::Impulse);
        }
    }
}
Core

Rigidbody3D

3D rigid body physics component bound to the Lenga runtime.

Namespace:

namespace Lenga\Engine\Core;

class Rigidbody3D

Use Rigidbody3D when a GameObject should be moved by 3D physics, gravity, velocity, and forces.

Properties

Property Type Description
bodyType string Dynamic, Kinematic, or Static. Dynamic bodies are moved by physics.
velocity Vector3 World-space linear velocity.
angularVelocity Vector3 World-space angular velocity.
useGravity bool Enables or disables gravity for this body.
gravityScale float Multiplier applied to world gravity.
linearDrag float Damps translational velocity.
angularDrag float Damps rotational velocity.
freezeRotation bool Prevents simulated rotation.
collisionDetection string Discrete or Continuous. Use continuous detection for fast-moving bodies.

Methods

addForce

public function addForce(Vector3 $force, ForceMode $mode = ForceMode::Force): void

Applies a force to this body.

Force and Acceleration are applied over the next simulation step. Impulse and VelocityChange apply an immediate velocity change.

isTouching

public function isTouching(bool $includeTriggers = true, ?int $layerMask = null): bool

Returns true when any collider on this Rigidbody's GameObject is touching another matching collider.

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null): array

Returns current 3D contacts for colliders owned by this Rigidbody's GameObject.

Example

<?php

declare(strict_types=1);

namespace Game\Scripts;

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Core\Rigidbody3D;
use Lenga\Engine\Core\Vector3;
use Lenga\Engine\Enumerations\ForceMode;
use Lenga\Engine\Enumerations\KeyCode;

final class LaunchPad extends Behaviour
{
    public float $launchStrength = 12.0;

    private ?Rigidbody3D $body = null;

    public function start(): void
    {
        $this->body = $this->getComponent(Rigidbody3D::class);
    }

    public function update(): void
    {
        if (!$this->body instanceof Rigidbody3D || !Input::getKeyDown(KeyCode::SPACE)) {
            return;
        }

        $this->body->addForce(new Vector3(0.0, $this->launchStrength, 0.0), ForceMode::Impulse);
    }
}
Core

Signal

Namespace:

namespace Lenga\Engine\Core;

class Signal

Signal is an owner-scoped callback surface for one specific behaviour or system instance.

Subscribers still need a reference to the owner in order to subscribe. If you want decoupled gameplay communication where listeners react without knowing the dispatcher ahead of time, use EventBus instead.

If one known object can just call one known collaborator directly, a public method is often simpler than a Signal. Signal is most useful when an owner wants to expose a one-to-many callback point with add(...), once(...), and disposal semantics.

Methods

add

public function add(callable $listener)

addListener

public function addListener(callable $listener)

once

public function once(callable $listener)

onceListener

public function onceListener(callable $listener)

remove

public function remove(SignalSubscription|callable $listener)

removeListener

public function removeListener(SignalSubscription|callable $listener)

clear

public function clear()

hasListeners

public function hasListeners()

count

public function count()

dispatch

public function dispatch(mixed ...$arguments)

invoke

public function invoke(mixed ...$arguments)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Signal;
use Lenga\Engine\Core\SignalSubscription;

final class Door extends Behaviour
{
    public Signal $onOpened;

    public function awake(): void
    {
        $this->onOpened = $this->createSignal();
    }

    public function open(): void
    {
        $this->onOpened->dispatch($this);
    }
}

final class DoorIndicator extends Behaviour
{
    public ?Door $door = null;
    private ?SignalSubscription $subscription = null;

    public function onEnable(): void
    {
        $this->subscription = $this->door?->onOpened->add(function (Door $door): void {
            $this->flashIndicator();
        });
    }

    public function onDisable(): void
    {
        $this->subscription?->dispose();
        $this->subscription = null;
    }

    private function flashIndicator(): void
    {
    }
}

This pattern fits when the listener already knows which owner instance it wants to observe.

Core

SignalSubscription

Namespace:

namespace Lenga\Engine\Core;

class SignalSubscription

Methods

dispose

public function dispose()

isDisposed

public function isDisposed()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Signal;
use Lenga\Engine\Core\SignalSubscription;

final class Door extends Behaviour
{
    public Signal $onOpened;

    public function awake(): void
    {
        $this->onOpened = $this->createSignal();
    }

    public function open(): void
    {
        $this->onOpened->dispatch($this);
    }
}

final class DoorIndicator extends Behaviour
{
    public ?Door $door = null;
    private ?SignalSubscription $subscription = null;

    public function onEnable(): void
    {
        $this->subscription = $this->door?->onOpened->add(function (Door $door): void {
            $this->flashIndicator();
        });
    }

    public function onDisable(): void
    {
        if ($this->subscription !== null && !$this->subscription->isDisposed()) {
            $this->subscription->dispose();
        }

        $this->subscription = null;
    }

    private function flashIndicator(): void
    {
    }
}
Core

SphereCollider3D

Namespace:

namespace Lenga\Engine\Core;

class SphereCollider3D

Properties

  • radius: local sphere radius.
  • offset: local offset from the GameObject transform.
  • isTrigger: detects overlap without producing a solid collision response.
  • materialPath: project-relative path to a Physics Material 3D asset.
  • material: a PhysicsMaterial3D object representing the collider material.

Methods

isTouching

public function isTouching(bool $includeTriggers = true, ?int $layerMask = null): bool

Returns true when this sphere collider is touching another matching 3D collider.

Set $includeTriggers to false to ignore trigger contacts. Pass a $layerMask to limit the check to colliders on matching GameObject layers.

getContacts

public function getContacts(bool $includeTriggers = true, ?int $layerMask = null): array

Returns the current Collision3D contacts involving this sphere collider.

Set $includeTriggers to false to return only solid contacts. Pass a $layerMask to limit results to matching GameObject layers.

moveAndSlide

public function moveAndSlide(
    Vector3 $delta,
    float $skinWidth = 0.05,
    bool $includeTriggers = false,
    ?int $layerMask = null,
): KinematicMove3DResult

Moves the GameObject by $delta using sphere collision and slides along blocking 3D colliders.

This method is deprecated for new character code. Prefer CharacterController::move() for kinematic movement.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Debug;
use Lenga\Engine\Core\SphereCollider3D;
use Lenga\Engine\Core\Vector3;

class BallPhysics extends Behaviour
{
    public function start(): void
    {
        $collider = $this->getComponent(SphereCollider3D::class);
        $collider->radius = 0.5;
        $collider->offset = new Vector3(0.0, 0.0, 0.0);
    }

    public function update(): void
    {
        $collider = $this->getComponent(SphereCollider3D::class);

        // Check if touching any objects
        if ($collider->isTouching()) {
            $contacts = $collider->getContacts();
            foreach ($contacts as $contact) {
                Debug::log('In contact with: ' . $contact->gameObject->name);
            }
        }
    }
}
Core

SphereRenderer

Namespace:

namespace Lenga\Engine\Core;

class SphereRenderer

Methods

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\SphereRenderer;

class SphereVisual extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(SphereRenderer::class);
        $renderer->setRadius(0.5);
        $renderer->setColor(255, 0, 0, 255); // Red sphere
    }
}
Core

SpriteAnimation

Namespace:

namespace Lenga\Engine\Core;

final class SpriteAnimation extends Component

SpriteAnimation plays a single clip or drives a sprite animation controller on a GameObject.

Use it when gameplay code needs to:

  • play or stop a clip directly
  • switch the active controller state
  • set animator parameters from gameplay code
  • fire one-shot animation triggers

Getting the Component

The usual workflow is:

  1. Add Sprite Renderer to a GameObject.
  2. Add Sprite Animation to the same GameObject.
  3. Assign either a clip or a controller in the Inspector.
  4. From PHP, call $this->getComponent(SpriteAnimation::class).
use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\SpriteAnimation;

final class PlayerAnimator extends Behaviour
{
    private ?SpriteAnimation $animator = null;

    public function start(): void
    {
        $this->animator = $this->getComponent(SpriteAnimation::class);
    }
}

Properties

clipPath

public string $clipPath

Project-relative path to the clip that should play when you are not using a controller.

controllerPath

public string $controllerPath

Project-relative path to the animation controller asset.

state

public string $state

Gets or sets the currently selected state name.

This is most useful when you want to force a state directly instead of waiting for controller conditions.

playOnAwake

public bool $playOnAwake

Controls whether the animation starts automatically when the component starts.

speed

public float $speed

Playback speed multiplier for the current clip or controller output.

Methods

isPlaying

public function isPlaying(): bool

Returns whether the animation is currently playing.

play

public function play(): bool

Starts playback.

stop

public function stop(): bool

Stops playback.

getBool

public function getBool(string $parameterName): bool

Returns the current value of a bool controller parameter.

setBool

public function setBool(string $parameterName, bool $value): bool

Sets a bool controller parameter.

getInt

public function getInt(string $parameterName): int

Returns the current value of an int controller parameter.

setInt

public function setInt(string $parameterName, int $value): bool

Sets an int controller parameter.

getFloat

public function getFloat(string $parameterName): float

Returns the current value of a float controller parameter.

setFloat

public function setFloat(string $parameterName, float $value): bool

Sets a float controller parameter.

setTrigger

public function setTrigger(string $parameterName): bool

Fires a trigger parameter for one-shot transitions such as attacks, hits, and jumps.

resetTrigger

public function resetTrigger(string $parameterName): bool

Clears a trigger parameter.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Core\SpriteAnimation;

class CharacterAnimator extends Behaviour
{
    private ?SpriteAnimation $animator = null;

    public function start(): void
    {
        $this->animator = $this->getComponent(SpriteAnimation::class);
    }

    public function update(): void
    {
        if ($this->animator === null) {
            return;
        }

        $move = Input::getAxis('Horizontal');
        $speed = abs($move) * 8.0;

        $this->animator->setBool('isWalking', $speed > 0.05);
        $this->animator->setBool('isRunning', $speed > 6.0);
        $this->animator->setFloat('speed', $speed);

        if (Input::getButtonDown('Attack')) {
            $this->animator->setInt('comboStep', $this->animator->getInt('comboStep') + 1);
            $this->animator->setTrigger('lightAttack');
        }
    }
}

Parameter Notes

  • Parameter names must match the controller exactly.
  • Use bools for persistent conditions such as isGrounded.
  • Use ints for discrete steps such as comboStep.
  • Use floats for threshold- or comparison-based motion such as speed.
  • Use triggers for one-shot transitions such as lightAttack.
Core

SpriteRenderer

Namespace:

namespace Lenga\Engine\Core;

class SpriteRenderer

Methods

setSize

public function setSize(float $width, float $height)

getColor

public function getColor()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\SpriteRenderer;

class PlayerSprite extends Behaviour
{
    public function start(): void
    {
        $renderer = $this->getComponent(SpriteRenderer::class);
        $renderer->loadSprite('assets/sprites/player.png');
        $renderer->setSize(1.0, 1.0);
        $renderer->setColor(255, 255, 255, 255);
    }

    public function update(): void
    {
        $renderer = $this->getComponent(SpriteRenderer::class);
        // Fade effect
        $alpha = (int)(255 * (0.5 + 0.5 * MathUtil::sin(Time::time() * 2)));
        $color = $renderer->getColor();
        $renderer->setColor($color['r'], $color['g'], $color['b'], $alpha);
    }
}
Core

Time

Read frame timing and pause state from gameplay scripts. Use `Time` when you need movement, cooldowns, animations, timers, or physics-related code to behave consistently across different frame rates.

Namespace:

namespace Lenga\Engine\Core;

class Time

Methods

time

public static function time()

Returns the total game time in seconds since the current run started.

unscaledTime

public static function unscaledTime()

Returns the real time elapsed since application start, unaffected by pause.

deltaTime

public static function deltaTime()

Returns the time elapsed since the last frame.

unscaledDeltaTime

public static function unscaledDeltaTime()

Returns the real frame delta time, unaffected by pause.

fixedDeltaTime

public static function fixedDeltaTime()

Returns the fixed delta time value.

fixedStepCount

public static function fixedStepCount()

isPaused

public static function isPaused()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Time;

class MovementController extends Behaviour
{
    public float $speed = 5.0;

    public function update(): void
    {
        // Frame-rate-independent movement
        $moveDistance = $this->speed * Time::deltaTime();
        $this->transform->translate($moveDistance, 0, 0);

        // Get time since game started
        $elapsedTime = Time::time();
        Debug::log('Time elapsed: ' . $elapsedTime);
    }

    public function fixedUpdate(): void
    {
        // Physics updates use fixed delta time
        $fixedDelta = Time::fixedDeltaTime();
        // Apply physics calculations with consistent timestep
    }
}
Core

Touch

Snapshot of a single touch point for the current input frame.

Namespace:

namespace Lenga\Engine\Core;

class Touch

Properties

fingerId

public readonly int $fingerId

Stable identifier for the touch point while the backend reports it.

position

public readonly Vector2 $position

Touch position in screen coordinates.

rawPosition

public readonly Vector2 $rawPosition

Raw touch position. Current raylib-backed builds use the same value as position.

deltaPosition

public readonly Vector2 $deltaPosition

Position delta since the previous frame for this touch id.

deltaTime

public readonly float $deltaTime

Unscaled frame delta used to produce this touch snapshot.

phase

public readonly TouchPhase $phase

How the touch changed during the current frame.

pressure

public readonly float $pressure

Touch pressure. Current raylib-backed builds report 1.0.

maximumPossiblePressure

public readonly float $maximumPossiblePressure

Maximum pressure value. Current raylib-backed builds report 1.0.

tapCount

public readonly int $tapCount

Tap count for the touch snapshot. Current raylib-backed builds report 1.

Core

TrailRenderer

Namespace:

namespace Lenga\Engine\Core;

final class TrailRenderer extends Component

TrailRenderer draws a ribbon behind a moving GameObject. It samples the owner transform as it moves, keeps those points for time seconds, and renders the newest part with startWidth/startColor fading toward endWidth/endColor.

Constants

public const string ALIGNMENT_VIEW = 'View';
public const string ALIGNMENT_TRANSFORM_Z = 'TransformZ';

Use View for camera-facing 3D trails. Use TransformZ when the ribbon should follow the GameObject orientation.

Properties

time

public float $time { get; set; }

How long trail points remain alive, in seconds.

minVertexDistance

public float $minVertexDistance { get; set; }

Minimum world distance before a new point is added.

emitting

public bool $emitting { get; set; }

When false, the trail stops adding new points while existing points continue to fade out.

autodestruct

public bool $autodestruct { get; set; }

When enabled, the owner GameObject is destroyed after the trail has been stationary for time seconds, or after emission stops and the remaining trail has expired.

startWidth

public float $startWidth { get; set; }

Width at the newest point in the trail.

endWidth

public float $endWidth { get; set; }

Width at the oldest point in the trail.

widthMultiplier

public float $widthMultiplier { get; set; }

Overall multiplier applied to the trail width.

alignment

public string $alignment { get; set; }

Accepts TrailRenderer::ALIGNMENT_VIEW or TrailRenderer::ALIGNMENT_TRANSFORM_Z.

sortingLayer

public string $sortingLayer { get; set; }

Controls 2D sorting.

orderInLayer

public int $orderInLayer { get; set; }

Controls draw order within the selected 2D sorting layer.

maxPoints

public int $maxPoints { get; set; }

Caps the internal point buffer.

positionCount

public int $positionCount { get; }

Returns the number of stored trail points.

Methods

clear

public function clear(): void

Removes all trail points immediately.

addPosition

public function addPosition(Vector3 $position): void

Adds a world-space point manually.

getPosition

public function getPosition(int $index): ?Vector3

Returns a stored point by index, or null if the index is out of range.

getStartColor

public function getStartColor(): array

Returns ['r' => int, 'g' => int, 'b' => int, 'a' => int].

setStartColor

public function setStartColor(int $red, int $green, int $blue, int $alpha = 255): void

getEndColor

public function getEndColor(): array

Returns ['r' => int, 'g' => int, 'b' => int, 'a' => int].

setEndColor

public function setEndColor(int $red, int $green, int $blue, int $alpha = 255): void

getState

public function getState(): array

Returns the current trail state. Prefer named properties and methods for gameplay code.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\TrailRenderer;

final class BallTrail extends Behaviour
{
    private ?TrailRenderer $trail = null;

    public function start(): void
    {
        $this->trail = $this->getComponent(TrailRenderer::class);

        $this->trail->time = 0.45;
        $this->trail->startWidth = 0.5;
        $this->trail->endWidth = 0.0;
        $this->trail->setStartColor(255, 255, 255, 255);
        $this->trail->setEndColor(0, 191, 255, 0);
    }

    public function onRespawn(): void
    {
        $this->trail?->clear();
    }
}

For an editor workflow, see Add Motion Trails.

Core

Transform

Namespace:

namespace Lenga\Engine\Core;

class Transform

Transform stores an object's position, rotation, and scale.

When an object has a parent:

  • localPosition is the offset from that parent
  • localRotation is the rotation relative to that parent
  • localScale is the scale relative to that parent

When you want the final world-space values, use:

  • position
  • rotation
  • lossyScale

Rotation in Lenga

Lenga supports two ways to work with 3D rotation:

  • Euler-angle properties such as $localEulerAngles and $eulerAngles
  • quaternion properties such as $localRotation and $rotation

The Inspector is still mainly Euler-facing in this release, but runtime 3D transform composition uses quaternion-backed rotation math. That means you can author readable Euler values in the editor and still use safer quaternion rotation workflows in code.

Common Properties

Property Type What it means
$localPosition Vector3 Position relative to the parent.
$position Vector3 World-space position.
$localEulerAngles Vector3 Local rotation in degrees.
$eulerAngles Vector3 World rotation in degrees.
$localRotation Quaternion Local rotation relative to the parent.
$rotation Quaternion World rotation.
$localScale Vector3 Scale relative to the parent.
$lossyScale Vector3 Final scale after parent scaling is applied.
$right Vector3 World-space right direction.
$up Vector3 World-space up direction.
$forward Vector3 World-space forward direction.

Methods

getChildren

public function getChildren(): array

Returns the direct child transforms.

childCount

public function childCount(): int

Returns the number of direct children.

getChild

public function getChild(int $index): ?Transform

Returns the child at the given index, or null.

find

public function find(string $path): ?Transform

Finds a child by a slash-separated path such as "Weapon/Muzzle".

isChildOf

public function isChildOf(Transform $parent): bool

detachChildren

public function detachChildren(): void

setParent

public function setParent(?Transform $newParent, bool $worldPositionStays = true): void

Changes the parent transform.

When worldPositionStays is true, Lenga keeps the object's world-space position, rotation, and scale, then recalculates the local values under the new parent.

When worldPositionStays is false, Lenga keeps the current local values and lets the final world transform change relative to the new parent.

translate

public function translate(Vector3 $offset, bool $relativeToSelf = true): void

Moves the transform by the given offset.

  • When relativeToSelf is true, the offset is applied in the object's own rotated space.
  • When relativeToSelf is false, the offset is applied in world space.

translate2D

public function translate2D(Vector2 $offset, bool $relativeToSelf = true): void

rotate

public function rotate(Vector3|float $xOrEuler, ?float $y = null, ?float $z = null, bool $relativeToSelf = true): void

Applies a rotation delta.

  • When relativeToSelf is true, the delta is applied in local space.
  • When relativeToSelf is false, the delta is applied in world space.

For more deliberate 3D rotation control, set $localRotation or $rotation directly with Quaternion.

rotate2D

public function rotate2D(float $degrees, bool $relativeToSelf = true): void

rotateAround

public function rotateAround(Vector3 $point, Vector3 $axis, float $angleDegrees): void

Rotates the object around a world-space point and axis.

Use this for orbit cameras, rotating platforms, satellites, radial projectiles, and objects that need to move around a pivot while their rotation changes by the same amount.

lookAt

public function lookAt(Vector3 $worldPosition): void

Rotates the object so it faces the given world-space point.

transformPoint

public function transformPoint(Vector3 $localPoint): Vector3

Converts a point from this transform's local space to world space.

inverseTransformPoint

public function inverseTransformPoint(Vector3 $worldPoint): Vector3

Converts a point from world space to this transform's local space.

transformDirection

public function transformDirection(Vector3 $localDirection): Vector3

Converts a direction from local space to world space. Position and scale are not applied.

inverseTransformDirection

public function inverseTransformDirection(Vector3 $worldDirection): Vector3

Converts a direction from world space to local space. Position and scale are not applied.

transformVector

public function transformVector(Vector3 $localVector): Vector3

Converts a vector from local space to world space. Scale and rotation are applied; position is not.

inverseTransformVector

public function inverseTransformVector(Vector3 $worldVector): Vector3

Converts a vector from world space to local space. Scale and rotation are removed; position is not applied.

getNativeId

public function getNativeId(): ?int

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Quaternion;
use Lenga\Engine\Core\Time;
use Lenga\Engine\Core\Vector3;

class ObjectController extends Behaviour
{
    public function update(): void
    {
        $transform = $this->transform;

        $worldPosition = $transform->position;
        $worldRotation = $transform->rotation;
        $worldEuler = $transform->eulerAngles;

        $transform->position = new Vector3(
            $worldPosition->x + (1.0 * Time::deltaTime()),
            $worldPosition->y,
            $worldPosition->z,
        );

        $transform->rotate(0.0, 90.0 * Time::deltaTime(), 0.0);

        $transform->rotateAround(
            Vector3::zero(),
            Vector3::up(),
            30.0 * Time::deltaTime()
        );

        $transform->localRotation = Quaternion::fromEulerAngles(
            new Vector3(0.0, 45.0, 0.0)
        );

        $transform->translate(
            new Vector3(0.0, 0.0, -1.0 * Time::deltaTime())
        );

        $forward = $transform->forward;
        $right = $transform->right;
        $muzzleWorldPoint = $transform->transformPoint(new Vector3(0.0, 0.0, -2.0));
    }
}

Usage Notes

  • Use Euler angles when you want readable degree values.
  • Use quaternions when you want safer 3D rotation composition in code.
  • The scene format and Inspector still use Euler-facing values for authoring, but runtime transform composition is quaternion-backed for 3D workflows.
Core

Vector2

2D vector value type mirroring 's .Vector2.

Namespace:

namespace Lenga\Engine\Core;

class Vector2

Methods

down

public static function down()

Unit vector pointing down (0, -1).

up

public static function up()

Unit vector pointing up (0, 1).

left

public static function left()

Unit vector pointing left (-1, 0).

right

public static function right()

Unit vector pointing right (1, 0).

one

public static function one()

Vector with all components set to 1.

zero

public static function zero()

Vector with all components set to 0.

negativeInfinity

public static function negativeInfinity()

Shorthand for Vector2(-INF, -INF).

positiveInfinity

public static function positiveInfinity()

Shorthand for Vector2(INF, INF).

offsetExists

public function offsetExists(mixed $offset)

Checks whether an array offset maps to a valid vector component.

offsetGet

public function offsetGet(mixed $offset)

Gets a vector component by index: 0|'x' => x, 1|'y' => y.

offsetSet

public function offsetSet(mixed $offset, mixed $value)

Sets a vector component by index: 0|'x' => x, 1|'y' => y.

offsetUnset

public function offsetUnset(mixed $offset)

Unsets a vector component by index.

add

public function add(Vector2 $other)

Adds another vector to this vector in place.

subtract

public function subtract(Vector2 $other)

Subtracts another vector from this vector in place.

multiply

public function multiply(Vector2 $other)

Multiplies this vector component-wise by another vector in place.

divide

public function divide(Vector2 $other)

Divides this vector component-wise by another vector in place.

scale

public function scale(float $scalar)

Scales this vector by a scalar in place.

normalize

public function normalize()

Normalizes this vector to have a magnitude of 1 in place.

set

public function set(float $x, float $y)

Sets the x and y components of this vector.

equals

public function equals(Vector2 $other)

Returns true if this vector is exactly equal to the other vector.

__toString

public function __toString()

Returns a formatted string representation of this vector.

toVector3

public function toVector3(float $z = 0.0)

Converts this Vector2 to a Vector3 with an optional z component.

fromVector4

public static function fromVector4(Vector4 $v)

Converts a Vector4 to a Vector2 by dropping the z and w components.

fromVector3

public static function fromVector3(Vector3 $v)

Converts a Vector3 to a Vector2 by dropping the z component.

toVector4

public function toVector4(float $z = 0.0, float $w = 0.0)

Converts this Vector2 to a Vector4 with optional z and w components.

sum

public static function sum(Vector2 $a, Vector2 $b)

Returns the sum of two vectors.

difference

public static function difference(Vector2 $a, Vector2 $b)

Returns the difference of two vectors.

scaleNew

public static function scaleNew(Vector2 $v, float $scalar)

Returns a new vector scaled by a scalar.

product

public static function product(Vector2 $a, Vector2 $b)

Multiplies two vectors component-wise and returns the result.

dot

public static function dot(Vector2 $a, Vector2 $b)

Returns the dot product of two vectors.

angle

public static function angle(Vector2 $from, Vector2 $to)

Returns the unsigned angle in degrees between two vectors.

signedAngle

public static function signedAngle(Vector2 $from, Vector2 $to)

Returns the signed angle in degrees between two vectors.

distance

public static function distance(Vector2 $a, Vector2 $b)

Returns the distance between two points.

lerp

public static function lerp(Vector2 $a, Vector2 $b, float $t)

Linearly interpolates between two vectors by t, clamped to [0, 1].

lerpUnclamped

public static function lerpUnclamped(Vector2 $a, Vector2 $b, float $t)

Linearly interpolates between two vectors by t, without clamping.

max

public static function max(Vector2 $a, Vector2 $b)

Returns a vector made from the largest components of two vectors.

min

public static function min(Vector2 $a, Vector2 $b)

Returns a vector made from the smallest components of two vectors.

clampMagnitude

public static function clampMagnitude(Vector2 $vector, float $maxLength)

Returns a copy of $vector with its magnitude clamped to $maxLength.

moveTowards

public static function moveTowards(Vector2 $current, Vector2 $target, float $maxDelta)

Moves $current towards $target by at most $maxDelta.

perpendicular

public static function perpendicular(Vector2 $inDirection)

Returns the 2D vector perpendicular to the given direction,

reflect

public static function reflect(Vector2 $inDirection, Vector2 $inNormal)

Reflects a vector off the surface defined by a normal.

smoothDamp

public static function smoothDamp(Vector2 $current,
        Vector2 $target,
        Vector2 &$currentVelocity,
        float $smoothTime,
        float $maxSpeed = PHP_FLOAT_MAX,
        float $deltaTime = 0.0,)

Gradually changes a vector towards a desired goal over time using a spring-damper.

fromVector3

public static function fromVector3(Vector3 $v)

Converts a Vector3 to a Vector2 by dropping the z component.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Vector2;
use Lenga\Engine\Core\MathUtil;

class VelocityController extends Behaviour
{
    public function update(): void
    {
        // Create a vector
        $velocity = new Vector2(5.0, 3.0);

        // Get magnitude and normalize
        $speed = $velocity->magnitude();
        $direction = $velocity->normalized();

        // Vector operations
        $acceleration = new Vector2(1.0, 2.0);
        $newVelocity = Vector2::add($velocity, $acceleration);

        // Dot product and angle
        $dot = Vector2::dot($velocity, $acceleration);
        $distance = Vector2::distance($velocity, $acceleration);

        // Lerp between two vectors
        $start = new Vector2(0, 0);
        $end = new Vector2(10, 10);
        $midpoint = Vector2::lerp($start, $end, 0.5);
    }
}
Core

Vector3

3D vector value type with mutating and pure helper operations.

Namespace:

namespace Lenga\Engine\Core;

class Vector3

Methods

zero

public static function zero()

Vector with all components set to 0.

one

public static function one()

Vector with all components set to 1.

up

public static function up()

Unit vector pointing up (0, 1, 0).

down

public static function down()

Unit vector pointing down (0, -1, 0).

left

public static function left()

Unit vector pointing left (-1, 0, 0).

right

public static function right()

Unit vector pointing right (1, 0, 0).

forward

public static function forward()

Unit vector pointing forward (0, 0, 1).

back

public static function back()

Unit vector pointing back (0, 0, -1).

negativeInfinity

public static function negativeInfinity()

Shorthand for Vector3(-INF, -INF, -INF).

positiveInfinity

public static function positiveInfinity()

Shorthand for Vector3(INF, INF, INF).

add

public function add(Vector3 $other)

Adds another vector to this vector in place.

subtract

public function subtract(Vector3 $other)

Subtracts another vector from this vector in place.

multiply

public function multiply(Vector3 $other)

Multiplies this vector component-wise by another vector in place.

divide

public function divide(Vector3 $other)

Divides this vector component-wise by another vector in place.

scale

public function scale(float $scalar)

Scales this vector by a scalar in place.

normalize

public function normalize()

Normalizes the vector to have a magnitude of 1 in place.

set

public function set(float $x, float $y, float $z)

Sets the x, y, and z components of this vector.

clone

public function clone()

Creates and returns a clone of this vector.

equals

public function equals(Vector3 $other)

Checks if this vector is exactly equal to another vector.

__toString

public function __toString()

Returns a formatted string representation of this vector.

toVector4

public function toVector4(float $w = 0.0)

Converts this Vector3 to a Vector4 with an optional w component.

toVector2

public function toVector2()

Converts this Vector3 to a Vector2 by dropping the z component.

toArray

public function toArray()

@return array{x: float, y: float, z: float}

fromArray

public static function fromArray(array $data)

@param array{x?: float, y?: float, z?: float} $data

fromVector4

public static function fromVector4(Vector4 $vector)

Builds a Vector3 from a Vector4 by dropping the w component.

fromVector2

public static function fromVector2(Vector2 $vector, float $z = 0.0)

Builds a Vector3 from a Vector2 with an optional z component.

sum

public static function sum(Vector3 $a, Vector3 $b)

Returns the sum of two vectors.

difference

public static function difference(Vector3 $a, Vector3 $b)

Returns the difference of two vectors.

product

public static function product(Vector3 $a, Vector3 $b)

Multiplies two vectors component-wise and returns the result.

quotient

public static function quotient(Vector3 $a, Vector3 $b)

Returns the quotient of two vectors.

scaleNew

public static function scaleNew(Vector3 $vector, float $scalar)

Returns a new vector scaled by a scalar.

dot

public static function dot(Vector3 $a, Vector3 $b)

Returns the dot product of two vectors.

cross

public static function cross(Vector3 $a, Vector3 $b)

Returns the cross product of two vectors.

angle

public static function angle(Vector3 $from, Vector3 $to)

Returns the unsigned angle in degrees between two vectors.

signedAngle

public static function signedAngle(Vector3 $from, Vector3 $to, Vector3 $axis)

Returns the signed angle in degrees between two vectors around an axis.

distance

public static function distance(Vector3 $a, Vector3 $b)

Returns the distance between two points.

lerp

public static function lerp(Vector3 $a, Vector3 $b, float $t)

Linearly interpolates between two vectors by t, clamped to [0, 1],

lerpUnclamped

public static function lerpUnclamped(Vector3 $a, Vector3 $b, float $t)

@var array{x: float, y: float, z: float}|false $result */

slerp

public static function slerp(Vector3 $a, Vector3 $b, float $t)

Spherically interpolates between two vectors, treating them as directions.

slerpUnclamped

public static function slerpUnclamped(Vector3 $a, Vector3 $b, float $t)

@var array{x: float, y: float, z: float}|false $result */

max

public static function max(Vector3 $a, Vector3 $b)

Returns a vector made from the largest components of two vectors.

min

public static function min(Vector3 $a, Vector3 $b)

Returns a vector made from the smallest components of two vectors.

clampMagnitude

public static function clampMagnitude(Vector3 $vector, float $maxLength)

Returns a copy of vector with its magnitude clamped to maxLength.

moveTowards

public static function moveTowards(Vector3 $current, Vector3 $target, float $maxDelta)

Moves current towards target by at most maxDelta.

project

public static function project(Vector3 $vector, Vector3 $onNormal)

Projects a vector onto another vector.

projectOnPlane

public static function projectOnPlane(Vector3 $vector, Vector3 $planeNormal)

Projects a vector onto a plane defined by a normal.

reflect

public static function reflect(Vector3 $inDirection, Vector3 $inNormal)

Reflects a vector off the plane defined by a normal vector.

rotateTowards

public static function rotateTowards(Vector3 $current,
        Vector3 $target,
        float $maxRadiansDelta,
        float $maxMagnitudeDelta,)

Rotates current towards target by an angular step, while also moving the

smoothDamp

public static function smoothDamp(Vector3 $current,
        Vector3 $target,
        Vector3 &$currentVelocity,
        float $smoothTime,
        float $maxSpeed = PHP_FLOAT_MAX,
        ?float $deltaTime = null,)

Gradually changes a vector towards a target over time using a spring-like damper.

orthoNormalize

public static function orthoNormalize(Vector3 &$normal, Vector3 &$tangent)

@var array{

offsetExists

public function offsetExists(mixed $offset)

Checks whether an array offset maps to a valid vector component.

offsetGet

public function offsetGet(mixed $offset)

Gets a vector component by index: 0|'x' => x, 1|'y' => y, 2|'z' => z.

offsetSet

public function offsetSet(mixed $offset, mixed $value)

Sets a vector component by index: 0|'x' => x, 1|'y' => y, 2|'z' => z.

offsetUnset

public function offsetUnset(mixed $offset)

Unsets a vector component by index.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Vector3;
use Lenga\Engine\Core\Time;

class Movement3D extends Behaviour
{
    public function update(): void
    {
        // Create vectors for positions and directions
        $position = $this->transform->position;
        $target = new Vector3(10, 0, 10);

        // Calculate direction and distance
        $direction = Vector3::subtract($target, $position)->normalized();
        $distance = Vector3::distance($position, $target);

        // Move towards target
        $moveAmount = 5.0 * Time::deltaTime();
        $newPosition = Vector3::add($position, Vector3::scale($direction, $moveAmount));
        $this->transform->position = $newPosition;

        // Perform vector operations
        $velocity = new Vector3(1, 0, 0);
        $gravity = new Vector3(0, -9.8, 0);
        $netForce = Vector3::add($velocity, Vector3::scale($gravity, Time::deltaTime()));

        // Cross product and rotation
        $forward = $this->transform->forward;
        $up = new Vector3(0, 1, 0);
        $right = Vector3::cross($forward, $up);
    }
}
Core

Vector4

4D vector value type with interpolation, projection, clamped length, and movement helpers.

Namespace: Lenga\Engine\Core

Key Members

  • x, y, z, w: scalar components.
  • sqrMagnitude, magnitude, normalized: common length helpers.
  • toVector2(), toVector3(): dimensional down-conversion helpers.
  • fromVector2(...), fromVector3(...): dimensional up-conversion helpers.

Common Methods

  • public static function zero()
  • public static function one()
  • public function add(Vector4 $other)
  • public function subtract(Vector4 $other)
  • public function multiply(Vector4 $other)
  • public function divide(Vector4 $other)
  • public function scale(float $scalar)
  • public function normalize()
  • public function set(float $x, float $y, float $z, float $w)
  • public static function dot(Vector4 $a, Vector4 $b)
  • public static function distance(Vector4 $a, Vector4 $b)
  • public static function lerp(Vector4 $a, Vector4 $b, float $t)
  • public static function lerpUnclamped(Vector4 $a, Vector4 $b, float $t)
  • public static function max(Vector4 $a, Vector4 $b)
  • public static function min(Vector4 $a, Vector4 $b)
  • public static function clampMagnitude(Vector4 $vector, float $maxLength)
  • public static function moveTowards(Vector4 $current, Vector4 $target, float $maxDelta)
  • public static function project(Vector4 $vector, Vector4 $onNormal)

Use Vector4 when you need four-channel math, homogeneous coordinates, packed data, or cross-conversion with Vector2 and Vector3 without losing the familiar scripting API shape.

Core

WaitForFixedUpdate

Namespace:

namespace Lenga\Engine\Core;

class WaitForFixedUpdate

Methods

resumePhase

public function resumePhase()

keepWaiting

public function keepWaiting()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\WaitForFixedUpdate;

class PhysicsSynchronizedAction extends Behaviour
{
    public function start(): void
    {
        $this->startCoroutine($this->physicsSync());
    }

    private function physicsSync(): \Generator
    {
        Debug::log('Waiting for physics frame...');

        // Wait until the next fixed update
        yield new WaitForFixedUpdate();

        Debug::log('One physics frame has completed');
        // Can now safely access physics simulation results
    }
}
Core

WaitForSeconds

Namespace:

namespace Lenga\Engine\Core;

class WaitForSeconds

Methods

getDuration

public function getDuration()

keepWaiting

public function keepWaiting()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\WaitForSeconds;

class DelayedAction extends Behaviour
{
    public function start(): void
    {
        $this->startCoroutine($this->delayedExplosion());
    }

    private function delayedExplosion(): \Generator
    {
        Debug::log('Explosion will happen in 3 seconds...');

        // Wait 3 real-time seconds
        yield new WaitForSeconds(3.0);

        Debug::log('Explosion!');
        $this->explode();
    }

    private function explode(): void
    {
        // Explosion logic here
    }
}
Core

WaitForSecondsRealtime

Namespace:

namespace Lenga\Engine\Core;

class WaitForSecondsRealtime

Methods

getDuration

public function getDuration()

keepWaiting

public function keepWaiting()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\WaitForSecondsRealtime;

class PauseInsensitiveTimer extends Behaviour
{
    public function start(): void
    {
        $this->startCoroutine($this->timedAction());
    }

    private function timedAction(): \Generator
    {
        Debug::log('Action starts now');

        // Wait 2 real seconds, even if game is paused
        yield new WaitForSecondsRealtime(2.0);

        Debug::log('2 real seconds have passed, regardless of pause state');
    }
}
Core

WaitUntil

Namespace:

namespace Lenga\Engine\Core;

class WaitUntil

Methods

keepWaiting

public function keepWaiting()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\WaitUntil;

class ConditionWaiter extends Behaviour
{
    private bool $conditionMet = false;

    public function start(): void
    {
        $this->startCoroutine($this->waitForCondition());
    }

    public function update(): void
    {
        // Some condition check
        if (Input::getKeyDown(KeyCode::SPACE)) {
            $this->conditionMet = true;
        }
    }

    private function waitForCondition(): \Generator
    {
        Debug::log('Waiting for condition...');

        // Wait until condition is true
        yield new WaitUntil(function() {
            return $this->conditionMet;
        });

        Debug::log('Condition was met!');
    }
}
Core

WaitWhile

Namespace:

namespace Lenga\Engine\Core;

class WaitWhile

Methods

keepWaiting

public function keepWaiting()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\WaitWhile;

class WaitForStop extends Behaviour
{
    private bool $isMoving = false;

    public function start(): void
    {
        $this->startCoroutine($this->waitUntilStopped());
    }

    public function update(): void
    {
        // Control movement
        if (Input::getKey(KeyCode::W)) {
            $this->isMoving = true;
        } else {
            $this->isMoving = false;
        }
    }

    private function waitUntilStopped(): \Generator
    {
        Debug::log('Waiting while moving...');

        // Wait while condition is true (wait until moving stops)
        yield new WaitWhile(function() {
            return $this->isMoving;
        });

        Debug::log('Object has stopped moving!');
    }
}
Core

YieldInstruction

Namespace:

namespace Lenga\Engine\Core;

class YieldInstruction

Methods

resumePhase

public function resumePhase()

keepWaiting

public function keepWaiting()

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\YieldInstruction;
use Lenga\Engine\Core\WaitForSeconds;

class CustomYieldExample extends Behaviour
{
    public function start(): void
    {
        $this->startCoroutine($this->myCoroutine());
    }

    private function myCoroutine(): \Generator
    {
        // YieldInstruction is the base class for all yield instructions
        // You typically use concrete subclasses like:

        // Wait for a fixed time
        yield new WaitForSeconds(1.0);

        Debug::log('After waiting 1 second');

        // You can also create custom yield instructions by extending YieldInstruction
    }
}
Enumerations

CanvasRenderMode

Namespace:

namespace Lenga\Engine\Enumerations;

Controls how a Canvas is rendered relative to the screen and camera.

Values

Case Value Description
ScreenSpaceOverlay 'ScreenSpaceOverlay' The canvas is rendered directly on top of the screen, independent of any camera. Always appears on top of all scene content.
ScreenSpaceCamera 'ScreenSpaceCamera' The canvas is rendered in screen space but is associated with a camera, allowing it to participate in the camera's field of view.
WorldSpace 'WorldSpace' The canvas exists as a flat object in world space and can be occluded by other geometry.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Enumerations\CanvasRenderMode;
use Lenga\Engine\UI\Canvas;

class HUDManager extends Behaviour
{
    public function start(): void
    {
        $canvas = $this->getComponent(Canvas::class);

        if ($canvas->getRenderMode() === CanvasRenderMode::ScreenSpaceOverlay) {
            // HUD is always on top — safe to show health/score UI
        }
    }
}
Enumerations

CollisionFlags

Namespace:

namespace Lenga\Engine\Enumerations;

Describes which sides of a CharacterController were touching a collider after a move() call. The values are bit flags and can be combined.

Values

Case Value Description
None 0 No collision occurred.
Sides 1 The character is touching a collider on its sides (horizontal collision).
Above 2 The character's top is touching a collider (e.g. a ceiling).
Below 4 The character's bottom is touching a collider (e.g. the ground).

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\CharacterController;
use Lenga\Engine\Enumerations\CollisionFlags;

class PlatformerCharacter extends Behaviour
{
    private bool $isGrounded = false;

    public function update(): void
    {
        $cc = $this->getComponent(CharacterController::class);
        $flags = $cc->move([0, -9.8 * Time::deltaTime(), 0]);

        // Check if the character landed on the ground
        $this->isGrounded = (bool)($flags->value & CollisionFlags::Below->value);
    }
}
Enumerations

ForceMode

Namespace:

namespace Lenga\Engine\Enumerations;

Defines how a physics force is applied to a 3D Rigidbody3D. Pass a ForceMode case as the second argument to addForce() and related methods.

Values

Case Value Description
Force 0 Add a continuous force over time, scaled by the body's mass. Use for realistic pushes like wind or a rocket engine.
Acceleration 1 Add a continuous acceleration over time, ignoring mass. All objects accelerate at the same rate regardless of weight.
Impulse 2 Apply an instant force in a single frame, scaled by mass. Use for jumps, explosions, and one-shot hits.
VelocityChange 3 Apply an instant velocity change in a single frame, ignoring mass. All objects receive the same velocity delta.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Rigidbody3D;
use Lenga\Engine\Enumerations\ForceMode;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Enumerations\KeyCode;

class Jumper extends Behaviour
{
    public float $jumpForce = 8.0;

    public function update(): void
    {
        if (Input::getKeyDown(KeyCode::SPACE)) {
            $rb = $this->getComponent(Rigidbody3D::class);
            // Apply an instant upward impulse when the player presses Space
            $rb->addForce([0, $this->jumpForce, 0], ForceMode::Impulse);
        }
    }
}
Enumerations

ForceMode2D

Namespace:

namespace Lenga\Engine\Enumerations;

Defines how a physics force is applied to a 2D Rigidbody2D. Pass a ForceMode2D case as the second argument to addForce() and related methods.

Values

Case Value Description
Force 0 Add a continuous force over time, scaled by the body's mass.
Impulse 1 Apply an instant force in a single frame, scaled by mass. Use for jumps and one-shot hits.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Rigidbody2D;
use Lenga\Engine\Enumerations\ForceMode2D;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Enumerations\KeyCode;

class PlatformerJump extends Behaviour
{
    public float $jumpForce = 12.0;

    public function update(): void
    {
        if (Input::getKeyDown(KeyCode::SPACE)) {
            $rb = $this->getComponent(Rigidbody2D::class);
            $rb->addForce([0, $this->jumpForce], ForceMode2D::Impulse);
        }
    }
}
Enumerations

GamepadAxis

Namespace:

namespace Lenga\Engine\Enumerations;

Identifies a physical axis on a gamepad controller. Use with Input::getAxis() to read analogue stick and trigger values.

Values

Case Value Description
LEFT_X 0 Left analogue stick, horizontal axis.
LEFT_Y 1 Left analogue stick, vertical axis.
RIGHT_X 2 Right analogue stick, horizontal axis.
RIGHT_Y 3 Right analogue stick, vertical axis.
LEFT_TRIGGER 4 Left trigger (LT / L2). Range 0–1.
RIGHT_TRIGGER 5 Right trigger (RT / R2). Range 0–1.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Enumerations\GamepadAxis;

class GamepadMovement extends Behaviour
{
    public float $speed = 5.0;

    public function update(): void
    {
        $horizontal = Input::getAxis(GamepadAxis::LEFT_X->value);
        $vertical   = Input::getAxis(GamepadAxis::LEFT_Y->value);

        $this->transform->translate([
            $horizontal * $this->speed * Time::deltaTime(),
            0,
            $vertical   * $this->speed * Time::deltaTime(),
        ]);
    }
}
Enumerations

GamepadButton

Namespace:

namespace Lenga\Engine\Enumerations;

Identifies a physical button on a gamepad controller. Use with Input::getButton() and Input::getButtonDown().

Values

Case Value Description
UNKNOWN 0 Unknown or unmapped button.
LEFT_FACE_UP 1 D-pad up / left face up (e.g. Y on Xbox, △ on PlayStation).
LEFT_FACE_RIGHT 2 D-pad right / left face right (e.g. B, ○).
LEFT_FACE_DOWN 3 D-pad down / left face down (e.g. A, ✕).
LEFT_FACE_LEFT 4 D-pad left / left face left (e.g. X, □).
RIGHT_FACE_UP 5 Right face up button.
RIGHT_FACE_RIGHT 6 Right face right button.
RIGHT_FACE_DOWN 7 Right face down button.
RIGHT_FACE_LEFT 8 Right face left button.
LEFT_TRIGGER_1 9 Left bumper (LB / L1).
LEFT_TRIGGER_2 10 Left trigger (LT / L2).
RIGHT_TRIGGER_1 11 Right bumper (RB / R1).
RIGHT_TRIGGER_2 12 Right trigger (RT / R2).
MIDDLE_LEFT 13 Select / Back / Share button.
MIDDLE 14 Guide / Home / PS button.
MIDDLE_RIGHT 15 Start / Options / Menu button.
LEFT_THUMB 16 Left analogue stick click (L3).
RIGHT_THUMB 17 Right analogue stick click (R3).

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Enumerations\GamepadButton;

class GamepadController extends Behaviour
{
    public float $jumpForce = 8.0;

    public function update(): void
    {
        // Jump on the face-down button (A / ✕)
        if (Input::getButtonDown(GamepadButton::LEFT_FACE_DOWN->name)) {
            $this->jump();
        }
    }

    private function jump(): void { /* ... */ }
}
Enumerations

KeyCode

Namespace:

namespace Lenga\Engine\Enumerations;

Identifies a physical keyboard key. Pass a KeyCode case to Input::getKey(), Input::getKeyDown(), and Input::getKeyUp() for type-safe key checks.

Common values

Case String value Description
BACKSPACE 'Backspace' Backspace key.
TAB 'Tab' Tab key.
ENTER 'Enter' Enter / Return key.
SHIFT 'Shift' Left Shift key.
CONTROL 'Control' Left Control key.
ALT 'Alt' Left Alt key.
ESCAPE 'Escape' Escape key.
SPACE 'Space' Space bar.
LEFT_ARROW 'LeftArrow' Left arrow key.
RIGHT_ARROW 'RightArrow' Right arrow key.
UP_ARROW 'UpArrow' Up arrow key.
DOWN_ARROW 'DownArrow' Down arrow key.
AZ 'A''Z' Letter keys.
ZERONINE '0''9' Number row keys.
F1F12 'F1''F12' Function keys.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Enumerations\KeyCode;

class PlayerController extends Behaviour
{
    public float $speed = 5.0;

    public function update(): void
    {
        $moveX = 0.0;
        $moveZ = 0.0;

        if (Input::getKey(KeyCode::A) || Input::getKey(KeyCode::LEFT_ARROW)) {
            $moveX = -1.0;
        } elseif (Input::getKey(KeyCode::D) || Input::getKey(KeyCode::RIGHT_ARROW)) {
            $moveX = 1.0;
        }

        if (Input::getKey(KeyCode::W) || Input::getKey(KeyCode::UP_ARROW)) {
            $moveZ = 1.0;
        } elseif (Input::getKey(KeyCode::S) || Input::getKey(KeyCode::DOWN_ARROW)) {
            $moveZ = -1.0;
        }

        $this->transform->translate([
            $moveX * $this->speed * Time::deltaTime(),
            0,
            $moveZ * $this->speed * Time::deltaTime(),
        ]);
    }
}

Methods

toRaylibKeyCode

public function toRaylibKeyCode(): int

Returns the numeric key code for this KeyCode case. Most gameplay scripts should pass the enum case directly to Input instead.

resolve

public static function resolve(self|string|int $key): ?int

Converts a KeyCode case, a string key name, or a raw integer to the key code used by Lenga input. Returns null if the key cannot be resolved.

Enumerations

TouchPhase

Namespace:

namespace Lenga\Engine\Enumerations;

Describes how a touch point changed during the current frame.

Values

Case Value Description
BEGAN Began The touch appeared this frame.
MOVED Moved The touch moved since the previous frame.
STATIONARY Stationary The touch stayed active without meaningful movement.
ENDED Ended The touch ended this frame.
CANCELED Canceled The touch was canceled by the backend.
Interfaces

ComponentInterface

Namespace:

namespace Lenga\Engine\Interfaces;

Base marker interface for all Lenga components. RendererInterface and TransformInterface extend this interface. Engine systems that operate on any component can type-hint against ComponentInterface to accept the full range of component types.

You rarely need to implement this interface directly. Behaviour already satisfies it. It is mostly useful when writing reusable gameplay helpers that accept any component reference.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Interfaces\ComponentInterface;

/**
 * Utility that logs information about any component attached to a game object.
 */
function debugComponent(ComponentInterface $component): void
{
    echo get_class($component) . PHP_EOL;
}

class DebugHelper extends Behaviour
{
    public function start(): void
    {
        // Pass any component to the utility
        debugComponent($this->getComponent(Rigidbody3D::class));
    }
}
Interfaces

GameObjectInterface

Namespace:

namespace Lenga\Engine\Interfaces;

Defines the standard contract for a Lenga game object. All scene objects implement this interface, which provides a consistent API for activation, hierarchy traversal, and component management. Type-hinting against GameObjectInterface lets you write code that works with any game object without depending on the concrete GameObject class.

Methods

setActive / isActiveSelf / isActiveInHierarchy

public function setActive(bool $value): void
public function isActiveSelf(): bool
public function isActiveInHierarchy(): bool

Controls and queries the active state of the object. isActiveSelf reflects the local flag; isActiveInHierarchy accounts for parent state.

getScene / getParent / getChildren / childCount

public function getScene(): mixed
public function getParent(): ?static
public function getChildren(): array
public function childCount(): int
public function setParent(?self $parent, bool $worldPositionStays = true): void

Hierarchy traversal and parenting methods.

hasComponent / getComponent / addComponent / destroy

public function hasComponent(string $type): bool
public function getComponent(string $type): mixed
public function addComponent(string $type): mixed
public function destroy(): void

Component management methods, mirroring the GameObject API. Pass concrete component classes such as Rigidbody2D::class to getComponent() and addComponent() when you want static analysis and IDEs to infer the concrete wrapper type.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Interfaces\GameObjectInterface;

/**
 * Recursively activates an object and all of its children.
 */
function activateTree(GameObjectInterface $obj): void
{
    $obj->setActive(true);
    foreach ($obj->getChildren() as $child) {
        activateTree($child);
    }
}

class SceneBootstrapper extends Behaviour
{
    public function start(): void
    {
        activateTree($this->gameObject);
    }
}
Interfaces

RendererInterface

Namespace:

namespace Lenga\Engine\Interfaces;

interface RendererInterface extends ComponentInterface

Marker interface shared by all renderer components (MeshRenderer, SpriteRenderer, ModelRenderer, etc.). Extending ComponentInterface, it allows engine systems and scripts to operate on any renderer without coupling to a specific implementation.

Type-hint against RendererInterface when you need to accept any renderer component generically.

Visual Defaults

For material-backed 3D renderers, setColor changes the renderer's base color. If no material asset is assigned, that base color is what Lenga uses in both the Scene view and Play mode. Assigning a material asset makes the material's base color and textures take over.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Interfaces\RendererInterface;

/**
 * Toggles visibility for any renderer component.
 */
function setVisible(RendererInterface $renderer, bool $visible): void
{
    $renderer->setEnabled($visible);
}

class FlashEffect extends Behaviour
{
    public function flash(): void
    {
        $renderer = $this->getComponent(RendererInterface::class);
        if ($renderer !== null) {
            setVisible($renderer, false);
        }
    }
}
Interfaces

SceneInterface

Namespace:

namespace Lenga\Engine\Interfaces;

Marker interface for Lenga scene objects. All scenes implement this interface, allowing engine systems and scripts to reference scenes generically without coupling to the concrete Scene class.

In most scripts you will work with SceneManager to load and query scenes rather than implementing this interface yourself.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Interfaces\SceneInterface;
use Lenga\Engine\SceneManagement\SceneManager;

class SceneDebugger extends Behaviour
{
    public function start(): void
    {
        $scene = SceneManager::getActiveScene();

        if ($scene instanceof SceneInterface) {
            // work with the scene generically
        }
    }
}
Interfaces

TransformInterface

Namespace:

namespace Lenga\Engine\Interfaces;

interface TransformInterface extends ComponentInterface

Marker interface shared by all transform components. Extending ComponentInterface, it lets engine systems work with any transform implementation without coupling to the concrete Transform class.

In scripts, you typically access the transform through $this->transform rather than type-hinting against this interface directly. Use TransformInterface when writing utilities or editor extensions that must accept any transform type.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Interfaces\TransformInterface;

/**
 * Resets position, rotation, and scale to identity for any transform.
 */
function resetTransform(TransformInterface $t): void
{
    $t->setPosition([0, 0, 0]);
    $t->setRotation([0, 0, 0]);
    $t->setLocalScale([1, 1, 1]);
}

class LevelResetHelper extends Behaviour
{
    public function resetPlayer(): void
    {
        resetTransform($this->transform);
    }
}
Interfaces

Vector3Interface

Namespace:

namespace Lenga\Engine\Interfaces;

Defines the contract for a three-component vector with typed x, y, and z properties. The concrete Vector3 class implements this interface. Use Vector3Interface when writing utilities that operate on any 3D vector without depending on the specific implementation.

Properties

Property Type Description
$x float X component.
$y float Y component.
$z float Z component.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Interfaces\Vector3Interface;

/**
 * Returns the squared magnitude of any Vector3Interface, useful for
 * distance comparisons without the cost of a square root.
 */
function sqrMagnitude(Vector3Interface $v): float
{
    return $v->x ** 2 + $v->y ** 2 + $v->z ** 2;
}

class ProximityTrigger extends Behaviour
{
    public float $triggerRadius = 5.0;

    public function update(): void
    {
        $delta = $this->transform->getPosition();
        if (sqrMagnitude($delta) < $this->triggerRadius ** 2) {
            // within range
        }
    }
}

Welcome to the Lenga Engine Reference!

This section of the documentation provides a comprehensive reference for all the classes, methods, properties, and functions available in the Lenga Engine. Whether you're a beginner or an experienced developer, this API reference will help you understand how to use the various components of the engine effectively.

APIs are organized into namespaces and categories for easy navigation. Each entry includes detailed information about its purpose, parameters, return values, and usage examples.

SceneManagement

BackdropLayer

Namespace:

namespace Lenga\Engine\SceneManagement;

class BackdropLayer

BackdropLayer gives scripts live access to a scene backdrop layer.

Use it when you want to:

  • scroll a repeating background over time
  • slow the backdrop when the player reverses
  • speed it up during boost or warp effects
  • tune repeat, parallax, or offset at runtime

Properties

index

public int $index

The zero-based layer index in the active scene.

imagePath

public string $imagePath

The image assigned to this backdrop layer.

space

public string $space

The layer space, such as Screen or World.

depthPreset

public string $depthPreset

The current depth preset label for the layer.

offset

public Vector2 $offset

Reads or writes the layer offset.

Increasing offset.x makes a screen-space backdrop move left across the screen, which is useful for side-scrolling starfields and endless-runner backgrounds.

repeat

public Vector2 $repeat

Reads or writes the repeat flags as a vector.

In practice, use 1 to enable wrapping on an axis and 0 to disable it.

parallax

public Vector2 $parallax

Reads or writes the layer parallax strength.

Methods

fromNativeData

public static function fromNativeData(array $data): self

translateOffset

public function translateOffset(Vector2 $delta): bool

Adds a delta to the current offset.

This is the easiest way to animate a repeating backdrop each frame.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Time;
use Lenga\Engine\Core\Vector2;
use Lenga\Engine\SceneManagement\SceneManager;

final class StarfieldController extends Behaviour
{
    public float $scrollSpeed = 40.0;

    public function update(): void
    {
        $scene = SceneManager::getActiveScene();
        $layer = $scene?->getBackdropLayer(0);
        if ($layer === null) {
            return;
        }

        $layer->repeat = new Vector2(1.0, 0.0);
        $layer->translateOffset(new Vector2(
            $this->scrollSpeed * Time::deltaTime(),
            0.0,
        ));
    }
}
SceneManagement

Scene

Namespace:

namespace Lenga\Engine\SceneManagement;

class Scene

Methods

fromNativeData

public static function fromNativeData(array $data)

@param array{name?: string} $data

getActive

public static function getActive()

getCanvases

public function getCanvases()

@var array{name?: string}|false $data */

getBackdropLayers

public function getBackdropLayers(): array

Returns the scene backdrop layers as BackdropLayer wrappers.

getBackdropLayer

public function getBackdropLayer(int $index): ?BackdropLayer

Returns one backdrop layer by zero-based index.

getBackdropLayerCount

public function getBackdropLayerCount(): int

findCanvas

public function findCanvas(string $name)

instantiateGameObject

public function instantiateGameObject(
    GameObject $original,
    string|InstantiateOptions|array|null $options = null,
): GameObject

Creates a scene instance from a GameObject or prefab asset reference.

instantiatePrefab

public function instantiatePrefab(
    string $assetPath,
    string|InstantiateOptions|array|null $options = null,
): GameObject

Creates a scene instance from a prefab asset path. Pass InstantiateOptions to spawn at a position, rotation, or parent.

findGameObject

public function findGameObject(string $name)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\SceneManagement\SceneManager;

class SceneInfo extends Behaviour
{
    public function update(): void
    {
        $scene = SceneManager::getActiveScene();
        if ($scene !== null) {
            $layer = $scene->getBackdropLayer(0);
            if ($layer !== null) {
                $layer->translateOffset(new \Lenga\Engine\Core\Vector2(24.0 * \Lenga\Engine\Core\Time::deltaTime(), 0.0));
            }
        }
    }
}

See Also

SceneManagement

SceneManager

`SceneManager` is the PHP API you use when a script needs to work with the current scene or move the player to another scene.

The most common workflow is:

  1. Create the scenes in the editor.
  2. Open Build Settings and add the scenes you want the game to load.
  3. Make sure the scene is enabled in the Build Settings scene list.
  4. Put your PHP code in a Behaviour attached to a normal GameObject.
  5. Call SceneManager::loadScene('SceneName') from that behaviour.

The load request is applied by the engine at a safe point in the frame, after script update work has finished. That keeps the current scene from being destroyed in the middle of a callback.

Namespace:

namespace Lenga\Engine\SceneManagement;

Import:

use Lenga\Engine\SceneManagement\SceneManager;

getActiveScene

Returns the scene that is running, or null if there is no active scene.

public static function getActiveScene(): ?Scene

Use this when you need to find objects, canvases, or other scene-owned content from a script.

$scene = SceneManager::getActiveScene();

if ($scene === null) {
    Debug::warn('There is no active scene yet.');
    return;
}

loadScene

Requests a scene transition.

public static function loadScene(string $sceneNameOrPath): void

Use the scene name when possible:

SceneManager::loadScene('Level1');

For that to work, Level1 should be present and enabled in Build Settings. Lenga can also accept a project-relative scene path when you need to be explicit:

SceneManager::loadScene('Assets/Scenes/Level1.scene.json');

If the scene cannot be found, is disabled, or fails validation, loadScene throws a RuntimeException.

tryLoadScene

Requests a scene transition and returns whether the request was accepted.

public static function tryLoadScene(string $sceneNameOrPath): bool

Use this when a missing scene should be handled gracefully instead of throwing:

if (!SceneManager::tryLoadScene('Level1')) {
    Debug::warn('Could not load Level1. Check Build Settings.');
}

loadSceneByPath

Requests a scene transition by path.

public static function loadSceneByPath(string $scenePath): void

This is a readability wrapper around loadScene. It is useful when you want the code to make it obvious that the value is a path:

SceneManager::loadSceneByPath('Assets/Scenes/MainMenu.scene.json');

instantiatePrefab

Creates an instance of a prefab in the active scene.

public static function instantiatePrefab(
    string $assetPath,
    string|InstantiateOptions|array|null $options = null,
): GameObject
$coin = SceneManager::instantiatePrefab('Assets/Prefabs/Coin.prefab.json', 'Coin Pickup');

assetPath should be a project-relative path. Pass a string to rename the instance, or pass InstantiateOptions to spawn at a specific position, rotation, or parent.

createScene

public static function createScene(string $name): Scene

Runtime scene creation is not supported yet. Use the editor to create scenes, then load them by name or project-relative path.

setActiveScene

public static function setActiveScene(Scene $scene): void

Directly swapping the active scene object is not supported yet. Use loadScene or tryLoadScene for runtime scene transitions.

Example

This example belongs in a Behaviour class attached to a normal GameObject, such as a MainMenuController object in your menu scene.

<?php

declare(strict_types=1);

namespace MyGame\Scripts;

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Debug;
use Lenga\Engine\SceneManagement\SceneManager;

final class MainMenuController extends Behaviour
{
    public function startGame(): void
    {
        SceneManager::loadScene('Level1');
    }

    public function tryOpenBonusLevel(): void
    {
        if (!SceneManager::tryLoadScene('BonusLevel')) {
            Debug::warn('BonusLevel could not be loaded.');
        }
    }
}

You can call these methods from your own button-handling code. The important part is that the script lives on a GameObject in the running scene, because behaviours are how Lenga runs gameplay PHP code.

UI

Button

Namespace:

namespace Lenga\Engine\UI;

final class Button extends UIElement

A clickable UI element. Button extends UIElement and adds text, colour, optional state images, and interaction-state properties. Obtain a reference via Canvas::getElement() or by looking up the element name on a Canvas component.

Interaction state properties

These read-only properties expose the button's current interaction state. Query them inside update().

Property Type Description
$hovered bool true every frame the cursor is over the button.
$pressed bool true every frame the mouse button is held down while the cursor is over the button. Use with care — this fires continuously for every frame the button is held.
$pressedThisFrame bool true only on the first frame the mouse button goes down over the button (edge-triggered).
$releasedThisFrame bool true only on the first frame the mouse button is released over the button (edge-triggered).
$clicked bool true only on the first frame after a full press-and-release cycle completes while the cursor is over the button. Prefer this for menu actions and one-shot triggers.

Tip — Use $clicked for most menu interactions. It fires exactly once per click, equivalent to a traditional button click event. Use $pressed only when you genuinely need continuous feedback (e.g., a hold-to-charge mechanic).

Appearance properties

Property Type Description
$text string The label displayed on the button. Readable and writable.
$fontSize float Font size in points. Readable and writable. Default 24.0.
$fontPath string Path to a custom font file. Readable and writable.
$interactable bool When false, the button is displayed with its disabled colour and ignores all input. Readable and writable.

State image methods

Each button state can use a texture image instead of relying only on colour. Paths are project-relative asset paths such as Assets/Sprites/UI/ButtonIdle.png.

Method Description
setBackgroundImage(path) / getBackgroundImage() Default image used when the button is idle.
setHoverImage(path) / getHoverImage() Image used while the cursor is over the button. If empty, the button falls back to the default image or hover colour.
setPressedImage(path) / getPressedImage() Image used while the mouse button is held down over the button. If empty, the button falls back to the default image or pressed colour.
setDisabledImage(path) / getDisabledImage() Image used when $interactable is false. If empty, the button falls back to the default image or disabled colour.

If a state image is assigned, that image represents the state directly. If a state image is not assigned but the default image is assigned, Lenga uses the default image and applies the state colour as feedback. If no image is available, Lenga draws the button with the state colour.

Colour methods

Each colour is returned as an array{r:int, g:int, b:int, a:int} and changed via the corresponding setter.

Method Description
getTextColor() / setTextColor(r, g, b, a) Foreground (label) colour. Default: white.
getBackgroundColor() / setBackgroundColor(r, g, b, a) Normal state background.
getHoverColor() / setHoverColor(r, g, b, a) Background while the cursor is over the button.
getPressedColor() / setPressedColor(r, g, b, a) Background while the mouse button is held down.
getDisabledColor() / setDisabledColor(r, g, b, a) Background when $interactable is false.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\UI\Button;
use Lenga\Engine\UI\Canvas;
use Lenga\Engine\SceneManagement\SceneManager;

class MainMenu extends Behaviour
{
    private ?Button $playButton = null;
    private ?Button $quitButton = null;

    public function start(): void
    {
        $canvas = $this->getComponent(Canvas::class);

        $this->playButton = $canvas->getElement('PlayButton');
        $this->quitButton = $canvas->getElement('QuitButton');

        // Style the play button
        $this->playButton->setBackgroundColor(34, 139, 34, 255);
        $this->playButton->setHoverColor(50, 180, 50, 255);
        $this->playButton->setBackgroundImage('Assets/Sprites/UI/PlayButton.png');
        $this->playButton->setHoverImage('Assets/Sprites/UI/PlayButtonHover.png');
        $this->playButton->setPressedImage('Assets/Sprites/UI/PlayButtonPressed.png');
    }

    public function update(): void
    {
        // clicked fires exactly once per full press-and-release cycle
        if ($this->playButton?->clicked) {
            SceneManager::loadScene('Game');
        }

        if ($this->quitButton?->clicked) {
            Application::quit();
        }
    }
}
UI

Canvas

Namespace:

namespace Lenga\Engine\UI;

class Canvas

Methods

getId

public function getId()

createText

public function createText(string $name, ?UIElement $parent = null)

getRootElements

public function getRootElements()

@var array{id?: int, name?: string, type?: string, canvasId?: int|null}|false $data */

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\UI\Canvas;

class UISetup extends Behaviour
{
    public function start(): void
    {
        // Get the Canvas component
        $canvas = $this->getComponent(Canvas::class);

        if ($canvas !== null) {
            // Configure canvas properties
            $canvas->renderMode = 'ScreenSpaceOverlay';
            $canvas->scaleMode = 'ScaleWithScreenSize';

            Debug::log('Canvas initialized');
        }
    }
}
UI

Image

Namespace:

namespace Lenga\Engine\UI;

class Image

Methods

getColor

public function getColor()

@return array{r:int, g:int, b:int, a:int}

setColor

public function setColor(int $red, int $green, int $blue, int $alpha = 255)

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\UI\Image;
use Lenga\Engine\Core\Time;

class UIImageController extends Behaviour
{
    public function start(): void
    {
        // Get the Image component
        $image = $this->getComponent(Image::class);

        if ($image !== null) {
            // Set the sprite to display
            $image->sprite = 'assets/ui/icon.png';
            $image->color = [255, 255, 255, 255]; // White, fully opaque
        }
    }

    public function update(): void
    {
        $image = $this->getComponent(Image::class);
        // Fade in/out effect
        $alpha = (int)(255 * (0.5 + 0.5 * MathUtil::sin(Time::time() * 2)));
        $image->color = [255, 255, 255, $alpha];
    }
}
UI

RectTransform

`RectTransform` controls where a UI element sits inside its parent rectangle.

Every UIElement has a rectTransform property. You normally use it to set:

  • where the element is anchored
  • which point inside the element is used as its pivot
  • the element's position relative to its anchor
  • the element's size, scale, and rotation

Namespace:

namespace Lenga\Engine\UI;

final class RectTransform

Basic Usage

This example places a button near the top-right corner of its parent. The code belongs in a Behaviour class attached to a GameObject in the scene.

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Vector2;
use Lenga\Engine\SceneManagement\Scene;

final class HudController extends Behaviour
{
    public function start(): void
    {
        $canvas = Scene::getActive()?->findCanvas('Canvas');
        $button = $canvas?->findButtonByName('Pause Button');

        if ($button === null) {
            return;
        }

        $button->rectTransform->anchorMin = new Vector2(1.0, 0.0);
        $button->rectTransform->anchorMax = new Vector2(1.0, 0.0);
        $button->rectTransform->pivot = new Vector2(1.0, 0.0);
        $button->rectTransform->anchoredPosition = new Vector2(-24.0, 24.0);
        $button->rectTransform->sizeDelta = new Vector2(160.0, 44.0);
    }
}

Coordinate Values

anchorMin, anchorMax, and pivot use normalized coordinates:

  • x = 0.0 means left
  • x = 0.5 means center
  • x = 1.0 means right
  • y = 0.0 means top
  • y = 0.5 means middle
  • y = 1.0 means bottom

For example, (1.0, 0.0) means top right.

Properties

Property Type Description
$anchorMin Vector2 The lower normalized anchor value. When it matches $anchorMax, the element is anchored to a single point.
$anchorMax Vector2 The upper normalized anchor value. Use a different value from $anchorMin when the element should stretch with its parent.
$pivot Vector2 The point inside the element that is positioned, rotated, and scaled.
$anchoredPosition Vector2 The offset from the anchor reference point to the element's pivot.
$sizeDelta Vector2 The element's size when it is anchored to a single point, or the size adjustment when it is stretched.
$scale Vector2 The local UI scale.
$rotation float The element rotation in degrees.

Usage Notes

Use anchoredPosition, not offsetMin or offsetMax, for the standard Lenga UI workflow.

The Inspector includes a 3x3 anchor preset grid for common anchor points such as top-left, middle-center, and bottom-right. Those presets update the anchors and pivot together while preserving the element's current visual position.

For a beginner-friendly walkthrough, see Lay Out UI with Anchors, Buttons, and Parenting.

UI

Text

Namespace:

namespace Lenga\Engine\UI;

final class Text extends UIElement

Text is the UI element you use for labels, score counters, instructions, menu titles, and other screen-space text. It inherits the common layout and hierarchy features from UIElement, including rectTransform, visible, name, and setParent(...).

Where Text Code Lives

Most Text code lives inside a Behaviour class attached to a scene GameObject.

For example, you might create a HUDController object in your scene, attach a HUDController Behaviour to it, then let that Behaviour find a canvas and update the Score Label text element.

The Text element does not need to live on the same GameObject as the Behaviour. The common workflow is:

  1. Put your UI elements under a Canvas.
  2. Attach a controller Behaviour to a normal scene GameObject, such as GameManager or HUDController.
  3. In the Behaviour, ask the active scene for the canvas.
  4. Find the text element by name.
  5. Update the text from start() or update().
use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\SceneManagement\Scene;
use Lenga\Engine\UI\Text;

final class HUDController extends Behaviour
{
    private ?Text $scoreLabel = null;

    public function start(): void
    {
        $canvas = Scene::getActive()?->findCanvas('Canvas');
        $this->scoreLabel = $canvas?->findTextByName('Score Label');

        if ($this->scoreLabel !== null) {
            $this->scoreLabel->text = 'Score: 0';
        }
    }
}

If your code already has a direct Text reference from another API, you can use that reference directly. You only need Scene::getActive()?->findCanvas(...) when you are starting from the scene and need to look up a UI element by name.

Properties

Property Type Description
$text string The string displayed by the element.
$fontSize float Font size in points.
$fontPath string Project-relative path to the font asset. Leave empty to use Lenga's bundled default UI font.
$useSdf bool Enables the SDF rendering path for this text element. Use this with a generated .sdf-font.json font asset.
$sdfOutlineWidth float Width of the SDF outline in pixels. This is separate from the standard outline distance used by raster text rendering.
$sdfSoftness float Edge softness for SDF text in pixels. Higher values make the text edge or outline softer.

Colour Methods

Text colour is read and written through methods rather than a direct property.

public function getColor(): array
public function setColor(int $red, int $green, int $blue, int $alpha = 255): void

getColor() returns:

array{r:int, g:int, b:int, a:int}

Example:

$label->setColor(255, 255, 255, 255);

Outline State for Raster Text

These methods expose the outline settings used when a Text element renders with a normal .ttf or .otf font:

public function getOutlineColor(): array
public function getOutlineDistance(): array

These outline settings are useful when you are using a normal .ttf or .otf font directly. For the crispest scalable outlines, generate an SDF font asset and use $sdfOutlineWidth instead.

SDF Font Workflow

For beginner-friendly editor steps, see Generate SDF Fonts for Crisp UI Text.

The short version is:

  1. Import a .ttf or .otf font into your project.
  2. Right-click the font in the Assets panel.
  3. Choose Generate SDF Font.
  4. Assign the generated .sdf-font.json file to the Font field on a Text element.
  5. Tune SDF Outline and SDF Softness on that Text element.

You can also set those values from PHP:

$title->fontPath = 'Assets/Fonts/MenuTitle.sdf-font.json';
$title->useSdf = true;
$title->sdfOutlineWidth = 3.0;
$title->sdfSoftness = 0.75;

If fontPath points to a normal .ttf or .otf file and useSdf is false, Lenga uses the standard raster font rendering path.

Complete Example

This example belongs inside a Behaviour attached to a normal scene GameObject, such as HUDController.

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\Core\Input;
use Lenga\Engine\Core\KeyCode;
use Lenga\Engine\SceneManagement\Scene;
use Lenga\Engine\UI\Text;

final class ScoreDisplay extends Behaviour
{
    private int $score = 0;
    private ?Text $scoreLabel = null;

    public function start(): void
    {
        $canvas = Scene::getActive()?->findCanvas('Canvas');
        $this->scoreLabel = $canvas?->findTextByName('Score Label');

        if ($this->scoreLabel !== null) {
            $this->scoreLabel->text = 'Score: 0';
            $this->scoreLabel->fontSize = 36.0;
            $this->scoreLabel->fontPath = 'Assets/Fonts/HUD.sdf-font.json';
            $this->scoreLabel->useSdf = true;
            $this->scoreLabel->sdfOutlineWidth = 2.0;
            $this->scoreLabel->sdfSoftness = 0.5;
            $this->scoreLabel->setColor(255, 255, 255, 255);
        }
    }

    public function update(): void
    {
        if (Input::getKeyDown(KeyCode::SPACE)) {
            $this->score += 10;
            $this->scoreLabel?->text = 'Score: ' . $this->score;
        }
    }
}
UI

UIElement

Namespace:

namespace Lenga\Engine\UI;

abstract class UIElement

Base class for all UI elements — Button, Text, and Image all extend UIElement. It provides the shared API for visibility, hierarchy, layout, and child-element creation.

Obtain an element reference via Canvas::getElement(). You do not instantiate UIElement directly; the canvas creates and manages element instances.

Properties

Property Type Access Description
$name string read The name of the element as set in the scene.
$enabled bool read/write Whether the element processes input and updates.
$visible bool read/write Whether the element is drawn on screen.
$activeInHierarchy bool read true when the element and all of its ancestors are active.
$sortOrder int read/write Draw order relative to sibling elements. Higher values render on top.
$rectTransform RectTransform read Layout data (anchors, position, size, pivot).

Methods

getId

public function getId(): int

Returns this element's runtime identifier. Most scripts do not need this; use element references, names, parents, and children for normal UI work.

getCanvas

public function getCanvas(): ?Canvas

Returns the Canvas this element belongs to.

getParent / setParent

public function getParent(): ?UIElement
public function setParent(?UIElement $parent): bool

Gets or re-parents the element in the UI hierarchy.

getChildren / findDescendantByName

public function getChildren(): list<UIElement>
public function findDescendantByName(string $name): ?UIElement

Traverses child elements. findDescendantByName searches the entire subtree recursively.

createText / createImage / createButton

public function createText(string $name): Text
public function createImage(string $name): Image
public function createButton(string $name): Button

Creates a new child element of the given type, parented to this element. Throws RuntimeException if the element is not attached to a canvas.

Example

use Lenga\Engine\Core\Behaviour;
use Lenga\Engine\UI\Canvas;
use Lenga\Engine\UI\Button;

class InventoryPanel extends Behaviour
{
    private ?Canvas $canvas = null;

    public function start(): void
    {
        $this->canvas = $this->getComponent(Canvas::class);
        $panel = $this->canvas->getElement('InventoryPanel');

        // Toggle panel visibility
        $panel->visible = true;

        // Dynamically create a close button as a child of the panel
        $closeBtn = $panel->createButton('CloseButton');
        $closeBtn->text   = 'Close';
        $closeBtn->visible = true;
    }

    public function update(): void
    {
        $closeBtn = $this->canvas?->getElement('CloseButton');
        if ($closeBtn instanceof Button && $closeBtn->clicked) {
            $this->canvas->getElement('InventoryPanel')->visible = false;
        }
    }
}