216 lines
No EOL
9.1 KiB
HTML
216 lines
No EOL
9.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Infinite Web Game Engine - Architecture Manual</title>
|
|
<style>
|
|
:root {
|
|
--bg-color: #f4f4f9;
|
|
--text-color: #333;
|
|
--sidebar-bg: #2c3e50;
|
|
--sidebar-text: #ecf0f1;
|
|
--link-color: #3498db;
|
|
--code-bg: #e2e2e8;
|
|
}
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
background-color: var(--bg-color);
|
|
color: var(--text-color);
|
|
line-height: 1.6;
|
|
}
|
|
/* Sidebar Styles */
|
|
nav {
|
|
width: 280px;
|
|
background-color: var(--sidebar-bg);
|
|
color: var(--sidebar-text);
|
|
height: 100vh;
|
|
position: fixed;
|
|
overflow-y: auto;
|
|
padding: 20px;
|
|
box-sizing: border-box;
|
|
}
|
|
nav h2 {
|
|
margin-top: 0;
|
|
font-size: 1.2rem;
|
|
border-bottom: 1px solid #455a64;
|
|
padding-bottom: 10px;
|
|
}
|
|
nav ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
nav ul li {
|
|
margin-bottom: 10px;
|
|
}
|
|
nav ul li a {
|
|
color: var(--sidebar-text);
|
|
text-decoration: none;
|
|
font-size: 0.95rem;
|
|
display: block;
|
|
padding: 5px;
|
|
border-radius: 4px;
|
|
transition: background 0.2s;
|
|
}
|
|
nav ul li a:hover {
|
|
background-color: #34495e;
|
|
}
|
|
/* Main Content Styles */
|
|
main {
|
|
margin-left: 280px;
|
|
padding: 40px;
|
|
max-width: 900px;
|
|
box-sizing: border-box;
|
|
}
|
|
section {
|
|
margin-bottom: 50px;
|
|
background: #fff;
|
|
padding: 30px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
|
}
|
|
h1 {
|
|
border-bottom: 2px solid var(--link-color);
|
|
padding-bottom: 10px;
|
|
margin-top: 0;
|
|
}
|
|
h2 {
|
|
color: #2c3e50;
|
|
margin-top: 0;
|
|
}
|
|
h3 {
|
|
color: #34495e;
|
|
}
|
|
code {
|
|
background-color: var(--code-bg);
|
|
padding: 2px 5px;
|
|
border-radius: 4px;
|
|
font-family: "Courier New", Courier, monospace;
|
|
font-size: 0.9em;
|
|
}
|
|
pre {
|
|
background-color: #2d2d2d;
|
|
color: #ccc;
|
|
padding: 15px;
|
|
border-radius: 6px;
|
|
overflow-x: auto;
|
|
}
|
|
pre code {
|
|
background-color: transparent;
|
|
color: inherit;
|
|
padding: 0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<nav>
|
|
<h2>Engine Manual</h2>
|
|
<ul>
|
|
<li><a href="#overview">1. Overview & Separation of Concerns</a></li>
|
|
<li><a href="#tech-stack">2. Tech Stack</a></li>
|
|
<li><a href="#ecs">3. Entity-Component-System (ECS)</a></li>
|
|
<li><a href="#world-chunks">4. Infinite World & Chunk Management</a></li>
|
|
<li><a href="#plugins">5. The "Pop-In" Plugin System</a></li>
|
|
<li><a href="#react-pixi-bridge">6. React & PixiJS Bridge</a></li>
|
|
</ul>
|
|
</nav>
|
|
|
|
<main>
|
|
<h1>Infinite Web Game Engine Architecture</h1>
|
|
<p>A comprehensive guide to building a modular, data-driven 2D game engine designed for infinite, chunk-based worlds using HTML5, JavaScript, React, and PixiJS.</p>
|
|
|
|
<section id="overview">
|
|
<h2>1. Overview & Separation of Concerns</h2>
|
|
<p>To keep the engine scalable and highly modular, the architecture relies on strict separation between three core pillars:</p>
|
|
<ul>
|
|
<li><strong>The Core Engine (PixiJS):</strong> The bedrock. Handles rendering, math, and the Entity-Component-System (ECS). It is entirely agnostic to game logic and only knows about raw data.</li>
|
|
<li><strong>The Editor (React):</strong> A specialized UI layer that sits on top of the engine. Its primary job is <em>Serialization</em>—allowing visual arrangement of resources and saving them as JSON data.</li>
|
|
<li><strong>The Runtime (Game):</strong> The lightweight entry point. It boots the Engine, parses the JSON data exported by the Editor, and executes the game loop.</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section id="tech-stack">
|
|
<h2>2. Tech Stack</h2>
|
|
<p>The chosen technologies leverage the speed of WebGL and the dynamic nature of JavaScript:</p>
|
|
<ul>
|
|
<li><strong>Rendering & Viewport:</strong> <code>PixiJS</code> (Handles the core canvas game loop and batching)</li>
|
|
<li><strong>Editor UI:</strong> <code>React</code> (Manages the complex state of the editor interface, scene graphs, and asset browsers)</li>
|
|
<li><strong>Modularity:</strong> Native ES Modules (<code>import()</code>)</li>
|
|
<li><strong>Data & Serialization:</strong> standard JSON</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section id="ecs">
|
|
<h2>3. Entity-Component-System (ECS) & Memory</h2>
|
|
<p>To avoid JavaScript Garbage Collection (GC) stutters, the engine uses a <strong>Data-Oriented Design</strong>.</p>
|
|
<ul>
|
|
<li><strong>Entities:</strong> Plain integer IDs (e.g., <code>Entity 45</code>).</li>
|
|
<li><strong>Components:</strong> Stored in flat TypedArrays (like <code>Float32Array</code>) rather than standard JS objects. This keeps memory contiguous and blazing fast for the CPU.</li>
|
|
<li><strong>Systems:</strong> Functions that iterate over these flat arrays to update game state.</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section id="world-chunks">
|
|
<h2>4. Infinite World & Chunk Management</h2>
|
|
<p>The world is based on a 32x32 infinite grid. Because you cannot hold an infinite world in memory, data is dynamically loaded and unloaded.</p>
|
|
|
|
<h3>Chunk Storage Map</h3>
|
|
<p>World data is stored in a JavaScript <code>Map</code> using a string coordinate hash as the key. This allows the world to expand infinitely in any direction, including negative coordinates.</p>
|
|
<pre><code>// 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}`;
|
|
}
|
|
</code></pre>
|
|
|
|
<h3>Rendering Object Pool</h3>
|
|
<p>To maintain performance, PixiJS objects are never created or destroyed on the fly. The engine uses an <strong>Object Pool</strong>.</p>
|
|
<ol>
|
|
<li>Pre-allocate enough Pixi <code>Container</code> objects to cover the screen plus a buffer.</li>
|
|
<li>When a chunk leaves the screen, clear its children, return the container to the pool, and save its raw tile data to the <code>chunkMap</code>.</li>
|
|
<li>When a new chunk enters, grab an idle container from the pool and populate it with sprites.</li>
|
|
</ol>
|
|
</section>
|
|
|
|
<section id="plugins">
|
|
<h2>5. The "Pop-In" Plugin System</h2>
|
|
<p>Libraries (like procedural generation or specialized AI) can simply be dropped into a folder and instantly recognized.</p>
|
|
<p>This is achieved using native ES Dynamic Imports. Plugins hook into the engine's <strong>Event Bus</strong> and chunk lifecycles.</p>
|
|
|
|
<h3>Example: Procedural Generation Hook</h3>
|
|
<pre><code>// 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
|
|
}
|
|
};
|
|
</code></pre>
|
|
</section>
|
|
|
|
<section id="react-pixi-bridge">
|
|
<h2>6. React & PixiJS Communication Bridge</h2>
|
|
<p>React (State) and PixiJS (Render Loop) must remain strictly decoupled to protect performance.</p>
|
|
<p>They communicate entirely via a lightweight <strong>Event Bus</strong> or <strong>Command Pattern</strong>:</p>
|
|
<ul>
|
|
<li><strong>React to PixiJS:</strong> React emits events based on user input. For example, selecting a tile in the editor emits <code>EDITOR_BRUSH_CHANGED</code>. The Pixi loop listens and updates its internal placement state.</li>
|
|
<li><strong>PixiJS to React:</strong> Pixi emits throttled state events. For example, as the camera pans, it emits <code>CAMERA_MOVED</code>. React listens to this and updates the coordinate UI in the sidebar without re-rendering the game canvas.</li>
|
|
</ul>
|
|
</section>
|
|
</main>
|
|
|
|
</body>
|
|
</html> |