From 821778036ceb8f83390146085f18e015f4031876 Mon Sep 17 00:00:00 2001 From: Andraxion Date: Mon, 29 Jun 2026 12:59:15 -0400 Subject: [PATCH] first commit --- audio.html | 66 +++ chunks.html | 207 ++++++++ manual.html | 1070 ++++++++++++++++++++++++++++++++++++++ painter.html | 54 ++ sprite editor.html | 62 +++ topology of systems.html | 216 ++++++++ 6 files changed, 1675 insertions(+) create mode 100644 audio.html create mode 100644 chunks.html create mode 100644 manual.html create mode 100644 painter.html create mode 100644 sprite editor.html create mode 100644 topology of systems.html diff --git a/audio.html b/audio.html new file mode 100644 index 0000000..5cb3290 --- /dev/null +++ b/audio.html @@ -0,0 +1,66 @@ + + + + + Plugin System: Audio Example + + + +

Plugin Architecture: Audio System

+

This guide demonstrates how to create a "pop-in" plugin that listens for game events and triggers audio, maintaining strict separation of concerns.

+ +

1. The Design Principle

+

The core engine does not contain a "play sound" function inside the combat logic. Instead, the combat logic emits an event. The Audio Plugin sits in the background, listening for that specific event, and plays the sound when it hears it.

+ +

2. The Plugin Implementation

+
// plugins/AudioPlugin.js
+export const AudioPlugin = {
+    id: 'audio-system',
+    
+    // The engine calls this upon loading the plugin
+    init(eventBus, assetManager) {
+        this.eventBus = eventBus;
+        this.assetManager = assetManager;
+
+        // Listen for global game events
+        this.eventBus.on('ENTITY_DIED', (data) => {
+            this.playSound('explosion_01.mp3');
+        });
+
+        this.eventBus.on('PLAYER_JUMP', () => {
+            this.playSound('jump_sfx.ogg');
+        });
+    },
+
+    playSound(fileName) {
+        // Access pre-loaded audio buffers from the asset manager
+        const sound = this.assetManager.getAudio(fileName);
+        sound.play();
+    }
+};
+ +

3. How it "Pops In"

+

Because the plugin follows a standard interface (the init function), the core engine's loader handles it automatically:

+
// Core Engine Loader
+async function loadPlugins() {
+    const pluginFiles = ['plugins/AudioPlugin.js', 'plugins/RendererPlugin.js'];
+    
+    for (const path of pluginFiles) {
+        const { plugin } = await import(path);
+        // Inject the Event Bus so the plugin can "talk" to the rest of the game
+        plugin.init(globalEventBus, globalAssetManager);
+    }
+}
+ +

4. Key Benefits

+ + + \ No newline at end of file diff --git a/chunks.html b/chunks.html new file mode 100644 index 0000000..8d87961 --- /dev/null +++ b/chunks.html @@ -0,0 +1,207 @@ + + + + + + Infinite Chunk System - Architecture Manual + + + + + + +
+

Infinite Chunk & Integration Architecture

+

A focused guide on coordinating a React-based UI with a PixiJS rendering engine to manage and render an infinite 32x32 grid-based world, alongside a lifecycle-hooked plugin system.

+ +
+

1. The React-Pixi Bridge: Separating UI State from Engine State

+

To keep things modular and performant, PixiJS and React must be treated as two completely separate applications that talk across a bridge.

+
    +
  • The Engine (PixiJS) runs on a requestAnimationFrame loop. It controls the game loop and renders the canvas.
  • +
  • The Editor UI (React) runs on state updates. It should not try to control or re-render Pixi objects directly via React state, as that will tank performance.
  • +
+ +

The Communication Model

+

Use a lightweight Event Bus or a Command Pattern to bridge the gap.

+
    +
  • When a user selects a tile in the React Asset Browser and clicks on the Pixi Canvas, React emits a global event: EDITOR_BRUSH_CHANGED { type: 'grass_tile' }.
  • +
  • The Pixi-based editor controller listens to this event, updates its internal brush state, and handles the actual vertex manipulation on the canvas.
  • +
  • Conversely, when Pixi updates the camera position, it emits a throttled CAMERA_MOVED { x, y } event so React can update the coordinate numbers in the status bar.
  • +
+
+ +
+

2. Managing the Infinite Grid (32x32 Chunks)

+

In an infinite world, you cannot keep every tile in memory. You need a Chunk Manager that dynamically loads and unloads data based on where the camera is looking.

+ +

Data Structure: Hash Map of Coordinates

+

Instead of a giant multi-dimensional array, store your chunks in a standard JavaScript Map using a string coordinate hash as the key:

+
// Key format: "chunkX,chunkY" -> e.g., "0,0", "-1,4"
+const chunkMap = new Map(); 
+
+function getChunkKey(worldX, worldY) {
+    const chunkX = Math.floor(worldX / (32 * 32)); // 32 tiles * 32 pixels
+    const chunkY = Math.floor(worldY / (32 * 32));
+    return `${chunkX},${chunkY}`;
+}
+
+
+ +
+

3. Rendering: PixiJS Container Pools

+

Do not create and destroy Pixi Container or Sprite objects every time a chunk moves out of view. That will trigger the JavaScript Garbage Collector and cause massive frame drops.

+ +

Instead, use an Object Pool:

+
    +
  1. Create a fixed number of Pixi Container objects (enough to cover the screen plus a 1-chunk padding buffer).
  2. +
  3. When a chunk leaves the screen, do not delete it. Instead, clear its children, return the container to the pool, and save its raw tile data (just integers representing tile IDs) back to your JavaScript chunkMap.
  4. +
  5. When a new chunk enters the screen, grab an idle container from the pool, populate it with the correct sprites from your Pixi Texture Atlas, and reposition it.
  6. +
+
+ +
+

4. Designing the "Pop-In" Plugin System for an Infinite Grid

+

To make libraries "pop-in" seamlessly to an infinite chunk system, your plugins need to hook directly into the Chunk Lifecycle. Your plugin interface should expose events that trigger when chunks are created, loaded, simulated, or saved.

+ +

Example: A Procedural Generation Plugin

+

Imagine you want to "pop in" a Perlin Noise terrain generator library. The plugin code would look like this:

+
// BiomeGeneratorPlugin.js
+export const plugin = {
+    id: "procedural-biomes",
+    
+    init(engine) {
+        // Register hooks into the engine's chunk lifecycle
+        engine.chunks.on('chunk_created', (chunk) => {
+            this.generateTerrain(chunk);
+        });
+    },
+
+    generateTerrain(chunk) {
+        // chunk.x and chunk.y tell us where we are in the infinite world
+        for (let x = 0; x < 32; x++) {
+            for (let y = 0; y < 32; y++) {
+                const globalX = (chunk.x * 32) + x;
+                const globalY = (chunk.y * 32) + y;
+                
+                // Determine tile type using your library's math
+                const tileId = noise(globalX * 0.05, globalY * 0.05) > 0.5 ? 1 : 2;
+                chunk.setTile(x, y, tileId);
+            }
+        }
+    }
+};
+
+

Because the Core Engine simply fires a chunk_created event every time the camera moves into ungenerated territory, any plugin can listen to that event, modify the raw tile data array, and let PixiJS handle the rendering automatically. The editor can expose toggle switches in React to enable or disable these loaded generation plugins on the fly.

+
+
+ + + \ No newline at end of file diff --git a/manual.html b/manual.html new file mode 100644 index 0000000..a5c7880 --- /dev/null +++ b/manual.html @@ -0,0 +1,1070 @@ + + + + + + Worldshaper UI System Manual + + + +
+
+
+
UI
+
+

Worldshaper UI System Manual

+

Shared architecture for the engine, runtime, and editor

+
+
+ +
+
+ +
+
+ + +
+
+
+
Design Manual
+

UI as a shared language between tools and play.

+

+ The editor should author UI definitions, the engine should render and manage them, + and the game should supply live data plus behavior bindings. That keeps creation, + execution, and meaning cleanly separated while still letting the same assets move + directly from tooling into runtime. +

+ +
+
+ Engine + Owns runtime behavior: rendering, layout, focus, input, and theme systems. +
+
+ Editor + Owns authoring: visual layout, hierarchy editing, inspectors, and previews. +
+
+ Game + Owns meaning: opening windows, injecting data, and resolving action ids. +
+
+
+
+ +
+

Overview

+

+ The system should support a workflow where the editor creates and modifies UI + visually, saves UI definitions as assets, the game loads those assets at runtime, + and the engine provides the shared UI runtime both sides depend on. +

+ +
+ Core Rule +

+ UI should be stored as data assets, not embedded directly in the editor and not + hardcoded only in game code. The editor authors structure, the runtime executes it, + and the game binds it to real gameplay state. +

+
+ +
+
+

Recommended Split

+
    +
  • Engine owns how UI works.
  • +
  • Editor owns how UI is authored.
  • +
  • Game runtime owns what the UI means.
  • +
+
+
+

Design Direction

+
    +
  • Use a retained-mode UI system.
  • +
  • Store screens and windows as declarative assets.
  • +
  • Render them through shared HTML, CSS, and JavaScript runtime code.
  • +
+
+
+
+ +
+

Shared Asset Model

+

+ The editor should write structured UI data, and the game should read that same data + through the engine runtime. This avoids arbitrary code generation and keeps assets + portable, testable, and safe to evolve. +

+ +
+
+

The Editor Should Save

+
    +
  • layout
  • +
  • widget hierarchy
  • +
  • theme and style references
  • +
  • data-binding keys
  • +
  • symbolic action identifiers
  • +
+
+
+

The Editor Should Not Save

+
    +
  • raw executable game logic
  • +
  • hardcoded runtime object instances
  • +
  • engine-internal transient state
  • +
  • function bodies embedded in assets
  • +
+
+
+
+ +
+

Package Layout

+

+ A package split like this keeps schema, runtime, editor, and game-facing code + independent while still sharing a common vocabulary. +

+
/packages
+  /ui-schema
+  /ui-runtime
+  /ui-editor
+  /game-runtime
+ +
+
+

ui-schema

+
    +
  • UI node definitions
  • +
  • validation
  • +
  • schema versioning
  • +
  • migrations
  • +
+
+
+

ui-runtime

+
    +
  • renderer
  • +
  • layout engine
  • +
  • window manager
  • +
  • widget registry
  • +
  • theme system
  • +
+
+
+

ui-editor

+
    +
  • scene or workspace
  • +
  • selection tools
  • +
  • property inspector
  • +
  • asset browser
  • +
  • save and load flow
  • +
+
+
+

game-runtime

+
    +
  • game-specific data providers
  • +
  • action handlers
  • +
  • UI asset loading
  • +
  • window orchestration
  • +
+
+
+
+ +
+

Engine

+

+ The engine should be the single owner of the UI runtime so the editor and game do + not drift apart in behavior over time. +

+ +
+
+

Responsibilities

+
    +
  • window lifecycle
  • +
  • widget rendering
  • +
  • layout calculation
  • +
  • input routing
  • +
  • focus management
  • +
  • dragging and resizing behavior
  • +
  • modal behavior
  • +
  • z-order and activation
  • +
  • theming and skinning
  • +
  • serialization and asset loading hooks
  • +
+
+
+

Why It Belongs Here

+
    +
  • accurate editor preview
  • +
  • fewer runtime and editor mismatches
  • +
  • one place to fix layout and interaction bugs
  • +
  • reusable widgets and themes
  • +
+
+
+ +
+

Core Runtime Systems

+
+
+

UI Manager

+
    +
  • open windows
  • +
  • focus order
  • +
  • active modal
  • +
  • hovered and pressed widgets
  • +
  • drag and resize operations
  • +
+
+
+

Window Manager

+
    +
  • create
  • +
  • close
  • +
  • minimize
  • +
  • maximize
  • +
  • bring to front
  • +
  • move
  • +
  • resize
  • +
+
+
+

Widget Registry

+
    +
  • window
  • +
  • panel
  • +
  • label
  • +
  • button
  • +
  • input
  • +
  • list
  • +
  • tabs
  • +
+
+
+

Theme System

+
    +
  • fonts
  • +
  • spacing
  • +
  • borders
  • +
  • colors
  • +
  • states
  • +
  • animation references
  • +
+
+
+
+
+ +
+

Runtime API

+

+ The engine should expose stable, high-level APIs that both the editor and the game + can consume without caring about rendering internals. +

+
ui.loadAsset("quest-log");
+ui.createWindow(windowDefinition, bindings);
+ui.closeWindow("quest-log");
+ui.updateBindings("quest-log", bindings);
+
ui.createWindow({
+  definition: windowDefinition,
+  bindings: {
+    data: gameViewModel,
+    actions: actionRegistry
+  }
+});
+
+ +
+

Editor

+

+ The editor should be an authoring tool for UI assets, not a place where gameplay code + gets embedded. +

+ +
+
+

Responsibilities

+
    +
  • create windows and widgets visually
  • +
  • arrange hierarchy
  • +
  • move and resize elements
  • +
  • edit properties
  • +
  • assign themes or style references
  • +
  • set data-binding keys
  • +
  • assign symbolic action identifiers
  • +
  • preview final layout using the shared runtime
  • +
+
+
+

Recommended Early Features

+
    +
  • hierarchy tree
  • +
  • canvas or workspace
  • +
  • property inspector
  • +
  • theme picker
  • +
  • save and load
  • +
  • live preview
  • +
+
+
+ +
+

Editor Workflow

+
    +
  1. Create or open a UI asset.
  2. +
  3. Add a root node such as a window or screen.
  4. +
  5. Add child widgets like panel, label, list, or button.
  6. +
  7. Configure properties in an inspector.
  8. +
  9. Save the asset as JSON.
  10. +
  11. Preview the result using the same UI runtime the game uses.
  12. +
+
+ +
+

Symbolic Actions

+

+ Instead of embedding code, a button should store a stable action id and let the + game resolve it later. +

+
{
+  "action": "inventory.useSelectedItem"
+}
+
+
+ +
+

Game Runtime

+

+ The game runtime consumes UI assets and supplies them with live data and behavior. + It decides when a screen opens, what it displays, and how action ids map into actual + game systems. +

+ +
+
+

Responsibilities

+
    +
  • deciding when UI opens or closes
  • +
  • selecting which asset to load
  • +
  • supplying live data
  • +
  • resolving symbolic actions into real functions
  • +
  • updating bindings as game state changes
  • +
+
+
+

Runtime Mutability

+
    +
  • visibility
  • +
  • enabled state
  • +
  • bound text
  • +
  • bound lists
  • +
  • dynamic styling flags
  • +
  • open and closed state of windows
  • +
+
+
+ +
+

Consumption Flow

+
    +
  1. Load a UI asset.
  2. +
  3. Validate it through the shared schema layer.
  4. +
  5. Create a runtime instance through the engine UI manager.
  6. +
  7. Inject data bindings and action handlers.
  8. +
  9. Let the engine handle rendering and interaction.
  10. +
+
+ +
+

Example Usage

+
const definition = await ui.loadAsset("quest-log");
+
+ui.createWindow({
+  definition,
+  bindings: {
+    data: gameViewModel,
+    actions: {
+      "ui.closeWindow": ({ windowId }) => ui.closeWindow(windowId),
+      "inventory.useSelectedItem": () => inventory.useSelectedItem()
+    }
+  }
+});
+
+
+ +
+

UI Assets

+

+ UI assets should start as serializable JSON files. The format should be stable, + versioned, and expressive enough to define hierarchy, layout, binding, and symbolic + actions without containing gameplay logic. +

+ +
+
+

Schema Goals

+
    +
  • schema versioning
  • +
  • stable ids
  • +
  • nested hierarchy
  • +
  • typed widgets
  • +
  • layout properties
  • +
  • style references
  • +
  • data bindings
  • +
  • symbolic actions
  • +
+
+
+

Recommended Fields

+
    +
  • schemaVersion
  • +
  • id
  • +
  • type
  • +
  • children
  • +
  • title, x, y, width, height
  • +
  • bind, visibleWhen, enabledWhen, action
  • +
  • theme, variant, className
  • +
+
+
+
+ +
+

Asset Example

+

+ A typical authored window should look like structured data the engine can interpret + consistently in both the editor and the game. +

+
{
+  "schemaVersion": 1,
+  "id": "quest-log",
+  "type": "window",
+  "title": "Quest Log",
+  "x": 120,
+  "y": 80,
+  "width": 420,
+  "height": 300,
+  "flags": {
+    "draggable": true,
+    "resizable": true,
+    "closable": true
+  },
+  "children": [
+    {
+      "id": "quest-list",
+      "type": "list",
+      "bind": "quests.active"
+    },
+    {
+      "id": "close-button",
+      "type": "button",
+      "text": "Close",
+      "action": "ui.closeWindow"
+    }
+  ]
+}
+
+ +
+

Versioning

+

+ Every asset should declare a schema version so the format can evolve without breaking + older editor output. +

+ +
+ Migration Policy +

+ When the asset format changes, add migration functions in the shared schema layer. + The editor can write the newest version, while the game and engine can continue to + load older assets by upgrading them during validation. +

+
+ +
{
+  "schemaVersion": 1
+}
+ +
+

Long-Term Extensions

+
    +
  • prefabs and templates
  • +
  • inherited style variants
  • +
  • animation descriptors
  • +
  • localization keys
  • +
  • responsive layout rules
  • +
  • asset references to icons and images
  • +
+
+
+ + +
+
+
+ + + + diff --git a/painter.html b/painter.html new file mode 100644 index 0000000..c659f12 --- /dev/null +++ b/painter.html @@ -0,0 +1,54 @@ + + + + + Plugin System: TypeScript Export + + + +

Exporting to TypeScript

+

Instead of relying on JSON.parse() at runtime, this plugin converts your 16x16 sprite data into static TypeScript files. This allows your IDE to provide autocomplete for your assets and ensures your data structure is validated at compile time.

+ +

1. The TypeScript Exporter Plugin

+
// plugins/TypeScriptExporter.js
+export const TypeScriptExporter = {
+    id: 'ts-exporter',
+
+    // Transforms internal pixel grid to a TypeScript source string
+    generateExport(spriteName, pixelData) {
+        const typeDefinition = `export interface SpriteData { name: string; pixels: number[]; }`;
+        const dataConstant = `export const ${spriteName}: SpriteData = { 
+            name: "${spriteName}", 
+            pixels: ${JSON.stringify(pixelData)} 
+        };`;
+        
+        return `${typeDefinition}\n\n${dataConstant}`;
+    }
+};
+ +

2. The Workflow: From Canvas to Code

+
    +
  1. Tooling: The user draws on the 16x16 canvas in the React Editor.
  2. +
  3. Serialization: The Editor calls TypeScriptExporter.generateExport('hero_walk', pixels).
  4. +
  5. Write to Disk: The Editor (via Electron/Tauri/Node) saves the resulting string as src/assets/hero_walk.ts.
  6. +
  7. Runtime Usage: In your game code, you simply import the asset: +
    import { hero_walk } from './assets/hero_walk';
    +console.log(hero_walk.pixels); // Type-safe and immediately available
    +
  8. +
+ +

3. Why Export to TS?

+ + +

4. Architecture Tip: Dual-Mode

+

You might want to keep the raw JSON for the editor to load/save while working, and use the TypeScript exporter only for the final build pipeline. This keeps your editor fast and iterative while making your runtime lean and performant.

+ + \ No newline at end of file diff --git a/sprite editor.html b/sprite editor.html new file mode 100644 index 0000000..7cfa416 --- /dev/null +++ b/sprite editor.html @@ -0,0 +1,62 @@ + + + + + Plugin System: Sprite Editor + + + +

Plugin Architecture: Sprite Editor

+

This plugin acts as a feature-set provider. It does two things: it injects a toolset into the React Editor UI and provides the serialization logic to convert 16x16 pixel arrays into your project's JSON format.

+ +

1. The Plugin Structure

+

To make this "pop in," the plugin exports a UI component for React and a logic object for the Engine.

+ +
// plugins/SpriteEditorPlugin.js
+export const SpriteEditorPlugin = {
+    id: 'sprite-editor',
+    
+    // 1. Injected UI for React
+    renderTool(editorProps) {
+        return <SpriteToolUI palette={PICO8_PALETTE} {...editorProps} />;
+    },
+
+    // 2. Logic for serialization
+    serialize(pixelData) {
+        // Converts 16x16 array to compact JSON
+        return {
+            type: 'sprite',
+            size: 16,
+            data: btoa(pixelData.join('')) // Base64 encoding for size
+        };
+    }
+};
+ +

2. Dynamic Injection

+

Your main Editor shell uses an "Extension Registry." On boot, it iterates through loaded plugins and asks if they have a renderTool method. If they do, it mounts them into the Sidebar.

+ +
// EditorSidebar.jsx
+function EditorSidebar({ plugins }) {
+    return (
+        <div>
+            {plugins.map(p => p.renderTool && p.renderTool())}
+        </div>
+    );
+}
+ +

3. The "Tooling" Workflow

+
    +
  1. Discovery: The Editor scans the /plugins directory.
  2. +
  3. Registration: It adds SpriteEditorPlugin to the central PluginRegistry.
  4. +
  5. UI Mounting: React sees the new tool in the Registry and dynamically renders the 16x16 canvas component in the sidebar.
  6. +
  7. Serialization: When the user hits "Save," the Editor loops through all registered plugins, calling their serialize() method to bundle the file.
  8. +
+ +

4. Why this keeps the Engine clean

+

The Core Engine doesn't know "Sprite Editor" exists. It only knows that when it loads a map file, it might contain a sprite data block. The plugin handles the complexity of the 16x16 grid math and color palette, while the Engine just treats the result as another JSON asset to be rendered by PixiJS.

+ + \ No newline at end of file diff --git a/topology of systems.html b/topology of systems.html new file mode 100644 index 0000000..7e03385 --- /dev/null +++ b/topology of systems.html @@ -0,0 +1,216 @@ + + + + + + Infinite Web Game Engine - Architecture Manual + + + + + + +
+

Infinite Web Game Engine Architecture

+

A comprehensive guide to building a modular, data-driven 2D game engine designed for infinite, chunk-based worlds using HTML5, JavaScript, React, and PixiJS.

+ +
+

1. Overview & Separation of Concerns

+

To keep the engine scalable and highly modular, the architecture relies on strict separation between three core pillars:

+
    +
  • The Core Engine (PixiJS): The bedrock. Handles rendering, math, and the Entity-Component-System (ECS). It is entirely agnostic to game logic and only knows about raw data.
  • +
  • The Editor (React): A specialized UI layer that sits on top of the engine. Its primary job is Serialization—allowing visual arrangement of resources and saving them as JSON data.
  • +
  • The Runtime (Game): The lightweight entry point. It boots the Engine, parses the JSON data exported by the Editor, and executes the game loop.
  • +
+
+ +
+

2. Tech Stack

+

The chosen technologies leverage the speed of WebGL and the dynamic nature of JavaScript:

+
    +
  • Rendering & Viewport: PixiJS (Handles the core canvas game loop and batching)
  • +
  • Editor UI: React (Manages the complex state of the editor interface, scene graphs, and asset browsers)
  • +
  • Modularity: Native ES Modules (import())
  • +
  • Data & Serialization: standard JSON
  • +
+
+ +
+

3. Entity-Component-System (ECS) & Memory

+

To avoid JavaScript Garbage Collection (GC) stutters, the engine uses a Data-Oriented Design.

+
    +
  • Entities: Plain integer IDs (e.g., Entity 45).
  • +
  • Components: Stored in flat TypedArrays (like Float32Array) rather than standard JS objects. This keeps memory contiguous and blazing fast for the CPU.
  • +
  • Systems: Functions that iterate over these flat arrays to update game state.
  • +
+
+ +
+

4. Infinite World & Chunk Management

+

The world is based on a 32x32 infinite grid. Because you cannot hold an infinite world in memory, data is dynamically loaded and unloaded.

+ +

Chunk Storage Map

+

World data is stored in a JavaScript Map using a string coordinate hash as the key. This allows the world to expand infinitely in any direction, including negative coordinates.

+
// Example hash key format: "chunkX,chunkY" -> "-1,4"
+const chunkMap = new Map();
+
+function getChunkKey(worldX, worldY) {
+    const chunkX = Math.floor(worldX / (32 * 32));
+    const chunkY = Math.floor(worldY / (32 * 32));
+    return `${chunkX},${chunkY}`;
+}
+
+ +

Rendering Object Pool

+

To maintain performance, PixiJS objects are never created or destroyed on the fly. The engine uses an Object Pool.

+
    +
  1. Pre-allocate enough Pixi Container objects to cover the screen plus a buffer.
  2. +
  3. When a chunk leaves the screen, clear its children, return the container to the pool, and save its raw tile data to the chunkMap.
  4. +
  5. When a new chunk enters, grab an idle container from the pool and populate it with sprites.
  6. +
+
+ +
+

5. The "Pop-In" Plugin System

+

Libraries (like procedural generation or specialized AI) can simply be dropped into a folder and instantly recognized.

+

This is achieved using native ES Dynamic Imports. Plugins hook into the engine's Event Bus and chunk lifecycles.

+ +

Example: Procedural Generation Hook

+
// plugins/BiomeGenerator.js
+export const plugin = {
+    id: "procedural-biomes",
+    init(engine) {
+        // Hook into the core engine's chunk creation event
+        engine.chunks.on('chunk_created', (chunk) => {
+            this.generateTerrain(chunk);
+        });
+    },
+    generateTerrain(chunk) {
+        // Populate the 32x32 grid with noise data
+    }
+};
+
+
+ +
+

6. React & PixiJS Communication Bridge

+

React (State) and PixiJS (Render Loop) must remain strictly decoupled to protect performance.

+

They communicate entirely via a lightweight Event Bus or Command Pattern:

+
    +
  • React to PixiJS: React emits events based on user input. For example, selecting a tile in the editor emits EDITOR_BRUSH_CHANGED. The Pixi loop listens and updates its internal placement state.
  • +
  • PixiJS to React: Pixi emits throttled state events. For example, as the camera pans, it emits CAMERA_MOVED. React listens to this and updates the coordinate UI in the sidebar without re-rendering the game canvas.
  • +
+
+
+ + + \ No newline at end of file