Skip to content

v0.12.0

Tilemap Editor v2

A complete tilemap editing system, replacing the previous Tiled-import-only workflow. You can now create, paint, and edit tilemaps entirely within the editor.

Tile Palette & Painting Tools

A new Tile Palette panel provides 5 tools:

  • Brush: paint individual tiles or stamp multi-tile regions
  • Rectangle Fill: drag to fill a rectangular area
  • Bucket Fill: flood-fill connected tiles (BFS, 10k tile limit)
  • Eraser: remove tiles, also available via right-click with any tool
  • Picker: sample a tile from the map to use as the current brush

Select stamp regions by dragging in the palette. Flip tiles horizontally/vertically with toolbar buttons.

Infinite Maps & Chunk Rendering

Tilemaps now support infinite maps (sparse chunk storage). The renderer uses 16×16 chunks with dirty flags for incremental vertex rebuilds — only dirty or animated chunks are rebuilt each frame. Off-screen chunks are culled at the chunk level.

Layer Management

Add, remove, rename, and reorder tilemap layers. Each layer renders independently with its own tile data and visibility toggle.

Native Tilemap Creation

Create tilemaps directly from the Hierarchy context menu without needing an external Tiled editor. Infinite map mode is available in the creation dialog.

Rich Text

The Text component now supports rich text with inline formatting tags:

  • <color=#FF0000>red text</color> — colored text spans
  • <b>bold</b>, <i>italic</i> — font style
  • Inline images via <img src="asset-path"/> tags
  • Enable with the richText toggle on the Text component

The parser is stack-based with per-run font measurement and automatic line breaking.

Text Stroke & Shadow

New properties on the Text component:

  • Stroke: strokeColor and strokeWidth for text outlines
  • Shadow: shadowColor, shadowBlur, shadowOffsetX, shadowOffsetY for drop shadows

Stroke rendering draws all strokes before all fills to prevent visual overlap artifacts.

Data Binding

A new DataBinding component connects component properties to expressions that update automatically each frame.

  • Template strings: "Score: {Score.value}" — reference any resource or component property
  • Math expressions: "{Health.current} / {Health.max} * 100" — recursive descent parser, no eval (WeChat compatible)
  • Auto type coercion: string ↔ number conversions handled automatically
  • Editor UI: custom property editor with dropdown for selecting target properties from the entity’s components
  • LRU expression cache: compiled expressions are cached for performance

Data binding evaluation is automatically skipped in editor edit mode to prevent scene corruption.

Unified Assets Architecture

A new 4-layer asset system replaces the legacy AssetServer:

  • Assets: user-facing API with typed loadTexture(), loadSpine(), loadPrefab() etc.
  • Catalog: address resolution, atlas frame queries, label-based asset groups, dependency graph
  • AssetLoader: pluggable typed loaders (texture, spine, material, font, audio, tilemap, timeline, animclip, prefab)
  • Backend: platform-specific data fetching (HTTP, embedded, WeChat)

Build pipeline generates a manifest catalog for addressable asset loading. Existing AssetServer consumers have been migrated.

CollectionView & Selectable

CollectionView

A virtual scrolling system replacing ListView, with pluggable layout providers:

  • LinearLayout: vertical or horizontal list
  • GridLayout: fixed-column grid with row/column gap
  • FanLayout: radial fan arrangement with configurable angle range

Uses an adapter pattern with item pooling for efficient rendering of large datasets.

Selectable

New Selectable component (C++ builtin) with group-based mutual exclusion — selecting one entity in a group automatically deselects others. Select/deselect events are dispatched via UIEvents.

Spine API Expansion

  • Animation queries: query current animation state, track entries, and mix info at runtime
  • Event callbacks: listen for Spine animation events (start, end, complete, custom events)
  • Constraint APIs: read and modify IK, transform, and path constraints
  • Editor visibility: SpineAnimation respects entity visibility toggling in the editor

Physics API Expansion

  • Shape cast: cast shapes through the physics world with configurable distance
  • AABB overlap: query all bodies overlapping an axis-aligned bounding box
  • Mass data: read and modify body mass, center of mass, and inertia at runtime
  • Joint state: query joint reaction forces, anchor positions, and current limits

Self-Describing Component Metadata

Component metadata (asset fields, color fields, entity references, animatable properties) is now auto-generated from C++ annotations via EHT codegen:

  • defineBuiltin() reads defaults and field metadata from generated COMPONENT_META
  • Editor defineSchema() infers Inspector property types from SDK metadata
  • Animation target enums generated from ES_ANIMATABLE C++ annotations
  • Ptr field layouts generated from C++ struct definitions for zero-copy WASM reads

Declarative System Scheduling

Run Conditions

Systems can declare execution pre-conditions via runIf:

import { playModeOnly } from 'esengine';
app.addSystemToSchedule(Schedule.Update, mySystem, {
runIf: playModeOnly,
});

Skipped systems have zero overhead — the condition is checked before parameter resolution.

System & Plugin Name Constants

Compile-safe constants for all system ordering and plugin dependency references:

import { SystemLabel, PluginName } from 'esengine';
app.addSystemToSchedule(Schedule.PostUpdate, mySystem, {
runAfter: [SystemLabel.UILayout],
});

Disabled Tag

New Disabled tag component for excluding entities from processing:

import { Disabled } from 'esengine';
world.insert(entity, Disabled, {});
const active = Query(Transform).without(Disabled);

Plugin Dependency Sorting

app.addPlugins() performs topological sorting on dependency declarations, allowing plugins in any order.

Engine Internals

  • Unified engine init via corePlugin, centralizing ResourceManager and static API lifecycle
  • Extracted App.runFrame_() to unify tick/mainLoop scheduling logic
  • O(1) entity name index updates via reverse entityToName_ map
  • Refactored AssetRefCounter with generic RefMap (175 → 99 lines)
  • Shared ensureUIRenderer() utility in uiHelpers
  • Editor schemas moved into plugin register() for lazy initialization
  • Split RenderFrame.cpp into mask and submit files for maintainability
  • Deduplicated BatchVertex and packColor into shared renderer header
  • Generated PTR_LAYOUTS from C++ struct definitions, replacing manual offset tables
  • Atomic state swap for scene operations prevents load/unload race conditions
  • WASM error throttling prevents console flood from repeated C++ exceptions

Bug Fixes

  • Fixed entity name not available in spawn callbacks
  • Fixed scene sleep/wake ignoring ShapeRenderer and ParticleEmitter
  • Fixed input event listeners leaking on app destruction
  • Fixed emscripten config not auto-generated on first compile
  • Fixed emsdk cache file check after install
  • Fixed editor visibility toggle for Image/UIRenderer components
  • Fixed text overflow Visible mode clipping to container instead of expanding
  • Fixed rich text stroke rendering order (strokes before fills)
  • Fixed Play Mode script injection order (scripts loaded before scene deserialization)
  • Fixed component schema preservation when script recompilation fails
  • Fixed scene not synced before entering Play Mode
  • Fixed circular schema imports causing editor startup failure