LYNX ENGINE

LYNX ENGINE / COMPLETE DOCUMENTATION

Everything inside Lynx.

One documentation hub for the engine's C++ core, internal architecture, public API, JavaScript runtime, editor, UI system, project structure, game modules, assets, build pipeline and runtime.

Start here. You do not need C++ to make a game with Lynx. Use the editor and JavaScript for gameplay. C++ is the extension layer when you need custom Actors, Components, systems or native modules.

1. What is Lynx?

Lynx is a C++ game engine built around Actors, Components, Levels and a lightweight JavaScript layer. Rendering is provided by HRL, while EnTT is used internally for the ECS registry and QuickJS-ng powers scripting.

ActorWorld object with transform, scripts, components and ownership.
LevelOwns active Actors and safely defers spawn/destruction during iteration.
ComponentReusable native gameplay behavior attached to an Actor.
ObjectBase reflected object used by engine and game classes.
EngineOwns global runtime state, the current Level, factory, players and HRL scene.
QuickJSRuns gameplay scripts and JavaScript classes.

2. The project workflow

A normal Lynx project is edited as a project folder. The editor manages the level, Actors, assets, input settings and scripts. The game itself is built as a separate DLL which is loaded by the editor or runtime.

01 / CREATEOpen a project and configure its assets, level and input.
02 / BUILDUse JavaScript and the editor first. Add C++ only when native extension is required.
03 / PLAYEditor Play mode loads the game module and runs the current level.
04 / RELOADReload Game can rebuild/reload the game DLL without restarting the editor.
05 / SHIPLynxRuntime runs the project without the editor.
06 / DISTRIBUTEShip Game prepares a release runtime and project assets.

Typical project content

MyGame/
  assets/
    classes/
    scripts/
    ui/
    voxels.json
    input.json
    *.png / *.wav / ...
  levels/
  CMakeLists.txt
  src/
  build/

3. C++ development

The native API is intended for extending Lynx rather than forcing every game to be written in C++. Public engine headers expose stable concepts such as Engine, Level, Actor, Component, input, scripting and widgets without requiring third-party ECS headers in gameplay code.

Engine lifetime

#include <Lynx.h>

lynx::Engine* engine = lynx::Engine::Create();
engine->CreateScene();

engine->StartGame();
engine->ProgressOneFrame(dt);
engine->EndGame();

lynx::Engine::Destroy();

The host application is responsible for the window and for feeding frames to the engine. Rendering is initialized through HRL before the engine scene is created.

Custom Component

struct Health : lynx::Component
{
    float hp = 100.f;

protected:
    void BeginPlay() override {}
    void Tick(float dt) override
    {
        if (hp <= 0.f)
            GetOwner()->GetController(); // example engine access
    }
    void EndPlay() override {}
};

auto& health = actor->AddComponent<Health>();

Component callbacks are optional. The lifecycle is OnAttach(), BeginPlay(), Update(dt), Tick(dt), EndPlay(), then destruction. Update also runs outside gameplay; Tick is gameplay simulation and respects tick_enabled.

4. Engine internals

The runtime is deliberately split into layers. The editor and host are not the same thing as the engine DLL, and third-party implementation details stay behind the public headers.

LayerResponsibility
HostWindow, platform integration, project selection and gamepad plumbing.
EngineGame state, Level, Actors, Components, players, factory and frame progression.
ECSInternal EnTT registry. Actors expose an opaque entity id instead of EnTT types.
ScriptingQuickJS-ng runtime, script instances, JavaScript classes and bindings.
RenderingHRL scene, viewports, cameras, sprites, lights and rendering resources.
EditorImGui docking UI, viewport, Outliner, Details, Content Browser, graph/editor tools and build controls.

Frame lifecycle

HOST
  ↓ supplies dt
ENGINE
  ↓ advances one frame
LEVEL
  ↓ updates Actors
ACTORS / ECS
  ↓ updates Components
SCRIPTS
  ↓ BeginPlay → Update(dt)
DEFERRED
  ↓ apply queued spawns / destruction
RENDER

Level iteration is protected by deferred queues. Actors spawned or destroyed while the engine is traversing the Actor list are finalized safely after the iteration instead of invalidating the container.

5. Actors, Components and Levels

Actor

lynx::Actor inherits from Object. It contains a transform, script list, possession state, editor folder metadata and an internal ECS entity. Components are owned by the Actor.

actor->transform.position = {0.f, 2.f, 0.f};

actor->AddComponent<Health>(100.f);

if (actor->HasComponent<Health>())
    actor->GetComponent<Health>()->hp = 50.f;

actor->RemoveComponent<Health>();

Level

Level owns active Actors. Native code can spawn by factory class name, construct a concrete Actor, find Actors by id, iterate them, count classes and save the level.

auto* player = level->SpawnActor("Player");
auto* enemy  = level->GetActorFromID("Enemy01");

for (auto* actor : level->GetActors())
{
    // inspect actors
}

level->DestroyActor(enemy);

6. Reflection, properties and functions

Lynx uses explicit reflection. A native member becomes visible to the editor and JavaScript when registered by the class. This keeps the public system predictable and avoids requiring a general-purpose C++ reflection library.

class Pawn : public lynx::Actor
{
public:
    float move_speed_ = 5.f;

    void Jump();

    Pawn()
    {
        HPROPERTY(move_speed_, lynx::Exposed);
        HFUNCTION(Jump);
    }
};

HPROPERTY exposes a value to Details, level serialization and JavaScript. HFUNCTION registers a callable function for the same reflection system. Supported property values include int, float, bool, std::string, vec2, vec3, vec4 and transform.

Dynamic properties

Objects can also create reflected properties at runtime. This is used by JavaScript classes for static properties, which then appear in Details and are serialized like native properties.

7. Game DLLs and the Factory

Game projects are separate from the engine. The factory allows native classes to be registered when the game module is loaded and removed again when it is unloaded.

LYNX_LINK_MODULE(
    LYNX_MODULE_REGISTER(MyPlayer);
    LYNX_MODULE_REGISTER(MyEnemy);
);

The exported module functions are FactoryRegisterClasses() and FactoryUnregisterClasses(). The engine removes constructors belonging to an unloaded DLL so the factory never keeps function pointers into a module that no longer exists.

Build contract

A game project can use CMake's Lynx package:

find_package(Lynx CONFIG)
add_library(MyGame SHARED ...)
target_link_libraries(MyGame PRIVATE Lynx::Lynx Lynx::HRL)

The game should be compiled with the same compiler/toolchain and engine version as the editor/runtime.

8. JavaScript runtime

JavaScript is the preferred gameplay iteration layer. A normal attached script is a behavior, not a class. Every attachment receives its own execution environment while the source can be compiled and cached.

function BeginPlay()
{
    parent.position = vec3(5, 0, 0);
}

function Update(dt)
{
    parent.position.x += 3 * dt;
}

JavaScript classes

Assets can also define Actor classes using standard JavaScript inheritance:

class Enemy extends Pawn
{
    static properties = {
        hp: 100,
        speed: 2.5
    };

    BeginPlay()
    {
        this.addTag("enemy");
    }

    Update(dt)
    {
        this.position.x += this.speed * dt;
    }
}

Classes derived from Actor are registered in the same factory concept as C++ classes and can appear in Place Actors, level files and Level.spawn().

Core globals

Levelspawn, find, findWithTag, all, count, destroy.
Inputpressed, held, released, axis.
Enginetime dilation, game state and player access.
UICreate, destroy and inspect UserWidgets.
vec3()Create vector values used by transforms and gameplay.
consolelog, info, warn and error output.

For the complete JavaScript reference, use the dedicated JavaScript guide.

9. Lynx Editor

The editor is a separate application built around Dear ImGui with docking. It is the development environment for levels, assets, scripts, input, widgets and game builds.

ViewportPlay/edit the current world and interact with Actors.
OutlinerBrowse Actors and organize them into folders.
DetailsEdit reflected Actor/component properties.
Content BrowserBrowse project assets and create engine resources.
Script EditorEdit JavaScript with engine-aware workflows.
Input SettingsEdit input.json and apply mappings immediately.
Widget EditorBuild UMG-like interfaces from .widget assets.
Build / ReloadCompile and reload the game DLL without restarting the editor.

Input Settings

Input mappings live in input.json. Actions and axes can use keyboard, mouse and gamepad inputs. Changes made through Input Settings are saved and reloaded immediately.

Project and build tools

The editor contains project/build tooling, output console, profiler views, graph editors, JSON/markdown/text editors and project settings. These are editor facilities and are not required by the standalone runtime.

10. UI and Widget system

Lynx has a UMG-like widget tree. A UserWidget is an asset-backed root containing panels and controls. The same system is available from C++ and JavaScript, while HRL remains an implementation detail of the renderer.

auto* hud = lynx::CreateWidget(player, "ui/Hud.widget");
hud->GetWidget<lynx::ProgressBar>("Health")->percent = 0.75f;
hud->AddToViewport();

Built-in panels include CanvasPanel, VerticalBox, HorizontalBox, Overlay and Border. Controls include TextBlock, Button, Image, Slider, CheckBox, ProgressBar and Spacer.

Widget layouts use slate units and a DPI reference so a design made for 1920×1080 can scale to different viewport sizes. The Widget Editor stores the tree in .widget assets.

11. Assets and project data

Levels

Levels serialize Actors, object ids, reflected properties, scripts and editor metadata such as Outliner folders. Scripts can be attached directly through the Actor's scripts property.

Voxels

assets/voxels.json describes voxel types, including collision and gameplay flags, emissive behavior and destruction rules. The editor and runtime load this data before the level; Reload Game reloads it as well.

Input

input.json contains Action Mappings and Axis Mappings. Named key codes such as KEY_SPACE and KEY_W are supported, while older numeric codes remain accepted.

Widgets and scripts

.widget files describe UI trees. JavaScript files can live anywhere under assets/; a classes/ directory is only a convention.

12. Runtime, host and shipping

Standalone runtime

LynxRuntime.exe
LynxRuntime.exe "C:/path/MyGame"

The runtime plays a project without the editor. The game DLL is searched beside the executable and then in the project's build directory.

Host boundary

The host owns the platform window. GLFW and platform input are kept outside the core engine boundary. The engine receives the relevant opaque handles and frame timing instead of owning the application's window lifecycle.

Release / Ship Game

The release engine build provides LynxRuntime.exe and the required engine DLLs. Ship Game can use a project icon and stores its project-specific settings in .lynx/ship.cfg.

13. Detailed references

ArchitectureEngine, Level, Actor, ECS, Factory, systems and frame lifecycle.
JavaScriptScript model, lifecycle, Actor API, transforms, input and hot reload.
Third-partyLibraries used by Lynx, their roles, layers and upstream licenses.