Skip to content

ECS & Components

jEngine uses hecs, a minimal Entity-Component-System (ECS) framework. Every game object is an entity — a lightweight ID. Behavior and data are added through components. Systems are functions that process entities with specific component combinations.

An entity is just a number (ID). By itself, it has no behavior or data. You give it meaning by attaching components.

Components are plain data structs. jEngine registers over a hundred built-in components, and packages add more. A few of the core ones, with the key a scene file uses:

Scene keyRust typePurpose
transformTransformPosition, rotation, scale
spriteSpriteComponentTexture or atlas region, color, z-order, size, anchor, y_sort
scriptScriptComponentLua or JS script attachment
textTextComponentWorld text: font, size, alignment, wrapping, rich text, outline
cameraCamera2DComponentZoom, viewport, active state, render target, pixel size
camera_followCameraFollowFollows the entity it is on, with dead zone and look-ahead
rigid_bodyRigidBody2DPhysics body (dynamic, static, kinematic)
colliderCollider2DCollision shape (box, circle, capsule, polygon, …)
jointJoint2DPhysics joints (revolute, fixed, prismatic, rope, spring)
character_controllerCharacterController2DA kinematic body that walks a level
audio_sourceAudioSourceSound clip attached to entity, optionally spatial
tilemapTilemapTile-based map: orthogonal, isometric or hexagonal
particle_emitterParticleEmitterCPU particle emitter
materialMaterialComponentMaterial: blend mode, shader graph, parameters
point_light, spot_light, directional_lightLights
light_occluderLightOccluderCasts shadows
velocityVelocity2DSimple kinematic velocity (no physics engine)
lifetimeLifetimeDespawn after a time

The Components Reference is generated from the source and lists them all.

Systems are Rust functions that query the ECS world for entities with specific components:

pub fn render_sprites(world: &World, renderer: &mut Renderer, camera: &Camera) {
for (_, (transform, sprite)) in world.query::<(&Transform, &SpriteComponent)>().iter() {
renderer.draw_sprite(transform, sprite, camera);
}
}

Entities and their components are stored as JSON .scene files:

{
"name": "Level 1",
"entities": [
{
"name": "Player",
"components": {
"transform": { "position": [0, 0], "rotation": 0, "scale": [1, 1] },
"sprite": { "texture_id": "player.png", "color": [1,1,1,1], "z_order": 0 },
"script": { "language": "Lua", "source_path": "scripts/player.lua" }
}
}
]
}

jEngine has two ways to add custom components.

For lightweight per-entity data that scripts read/write, use the CustomComponents component defined in Project Settings → Component Definitions:

{
"component_definitions": [
{
"name": "Health",
"fields": [
{ "name": "current", "field_type": "Float", "default_value": 100.0 },
{ "name": "max", "field_type": "Float", "default_value": 100.0 }
]
}
]
}

Access the fields from Lua or JS scripts:

local hp = Entity.get_custom(entity, "Health", "current")
Entity.set_custom(entity, "Health", "current", hp - 10)

This approach requires no compilation — good for rapid iteration and simple data.

For performance-critical components, shared data consumed by ECS systems, or components that integrate with third-party Rust crates, create a native Rust package.

A native component is a plain Rust struct with serde derives:

use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Health {
pub current: f32,
pub max: f32,
pub regeneration: f32,
}

It is registered through the JEnginePackage trait and appears in the editor just like built-in components. ECS systems can query it at full native speed:

for (_, health) in world.query_mut::<&mut Health>() {
health.current = (health.current + health.regeneration * dt).min(health.max);
}

See the Package System guide for a full walkthrough: scaffolding the crate, implementing JEnginePackage, registering components and systems, adding script commands every runtime can call, and getting the package into a build. A scene that uses a package component must be a v2 scene ("version": 2).

  1. Select an entity in the Scene Tree
  2. In the Inspector, click Add Component
  3. Choose from categorized menu: Core, Visuals, Physics, Audio, Camera, UI, Custom
  4. Configure the component’s properties in the Inspector

See the Components Reference for detailed field documentation.