Hex Engine

website·Indexed 31 days ago

https://hex-engine.dev/

A modern 2D game engine for the browser. It features a component-based architecture with first-class support for Aseprite and includes packages such as @hex-engine/core for entity and component logic, @hex-engine/2d for 2D models like Circle, and @hex-engine/inspector for frame execution controls and overlays.

Tokens
15.9K
Snippets
89
Records
107
Agent score
57%

What's inside Hex Engine

  1. Overview of Hex Engine features and architecture

    Hex Engine is a TypeScript-based 2D game engine for the browser. It implements a variant of the Entity-Component-System (ECS) model where Components are functions that use Hook functions to define behavior, making them radically composable (similar to React).

    Key built-in capabilities include:

    • 2D rendering using HTML5 Canvas
    • Sprite sheet and animation support
    • Sound playback and synthesis helpers
    • Physics integration via matter.js
    • Input handling for Mouse, Keyboard, and Gamepad
    • Native support for industry tools: Aseprite, Tiled, and BMFont
    • Integrated development inspector and frontend compilation pipeline.
  2. Implement ECS Systems in Hex Engine

    Hex Engine does not have explicit 'System' objects. Instead, systems are implemented as Components that communicate with other Components. Typically, a system is implemented as a single instance of a Component (acting as a Singleton) that manages the logic for other entities in the game.
  3. Handle mouse input with HexMouseEvent

    The HexMouseEvent class provides data about mouse interactions. Note that LowLevelMouse components reuse a single instance of this class for all events to avoid garbage collection; developers must copy required values immediately rather than storing the event object.
    // Properties available on HexMouseEvent:
    // event.pos: Vector (cursor position relative to Entity origin)
    // event.type: "move" | "down" | "up" | "canvasEnter" | "canvasLeave"
    // event.delta: Vector (movement since last event of same type)
    // event.buttons: { left: boolean, right: boolean, middle: boolean, mouse4: boolean, mouse5: boolean }
  4. Understand the Hex Engine component execution lifecycle

    Unlike React, where component functions re-run on every state change to produce a new virtual DOM for diffing, Hex Engine component functions run exactly once per instance at creation time.

    Because Hex Engine is designed for games, it does not use a persistent DOM-like structure for layout. Instead, it redraws the entire scene every frame. To handle per-frame logic and rendering, components use hooks like useDraw and useUpdate. While the outer component function runs once, the callbacks passed to these hooks are executed every frame.

    import { useType, useDraw } from "@hex-engine/2d";
    
    function MyComponent() {
      // This log runs ONLY ONCE when the component is created
      console.log("In MyComponent");
    
      useDraw((context) => {
        // This log runs EVERY FRAME
        console.log("In useDraw callback");
      });
    }
  5. Initialize Web Audio API with AudioContext

    The AudioContext component should be placed on the root Entity. It handles the browser requirement for user interaction before audio playback by creating the Web Audio API AudioContext upon the first click or keypress.
    import { AudioContext } from "@hex-engine/2d";
    
    // Add to root entity
    const audioCtxComp = AudioContext();
    
    // Access the native context if it has been initialized
    console.log(audioCtxComp.audioContext);
  6. Define entity spatial properties with Geometry

    The Geometry component provides essential information regarding an Entity's shape, position, rotation, origin, and scale. It is a requirement for useDraw and Physics.Body. Each Entity should have exactly one Geometry component.
    import { Geometry } from "@hex-engine/2d";
  7. Initialize a Hex Engine game root

    To start a game, use the createRoot function from @hex-engine/2d. This creates the 'root Entity' and assigns it a behavior defined by a Component function.
    import { createRoot } from "@hex-engine/2d";
    import Root from "./Root";
    
    createRoot(Root);
  8. Make a game entity draggable in Hex Engine 2D

    To make an entity draggable, import the Draggable component and attach it to the entity's geometry using the useNewComponent hook. This allows the entity to be moved via mouse interaction in the browser.
    import { useNewComponent, Geometry, Vector, Polygon } from "@hex-engine/2d";
    import Draggable from "./Draggable";
    
    export default function Floor(position: Vector) {
      // 1. Define the geometry for the entity
      const geometry = useNewComponent(() =>
        Geometry({
          shape: Polygon.rectangle(new Vector(200, 25)),
          position: position.clone(),
        })
      );
    
      // 2. Add the Draggable component linked to that geometry
      useNewComponent(() => Draggable(geometry));
    }
  9. Manage global state using Storage components

    In Hex Engine, 'Storage' components (often named starting with StorageFor) are used to store state or references to entities on the root entity, allowing state to be shared and coordinated across the game. When a component function returns an object, the getters and setters for that object's properties are copied onto the component instance. This allows you to retrieve and modify the state using useRootEntity().getComponent(ComponentName).
    function QuestListState() {
      useType(QuestListState);
      const quests: Array<Quest> = [];
      return { quests };
    }
    
    // Accessing the state in another component
    function QuestListUI() {
      useType(QuestListUI);
      useDraw((context) => {
        const questListState = useRootEntity().getComponent(QuestListState);
        if (questListState) {
          const quests = questListState.quests;
          // Render quests...
        }
      });
    }
  10. Scaffold a new Hex Engine project

    Use the create-hex-engine-game npm package to quickly generate a template game project. Replace my-game with your preferred directory name.
    npx create-hex-engine-game@latest my-game
  11. Compose components using useNewComponent

    Unlike React, Hex Engine components do not return markup (JSX) to compose child components. Because game components handle diverse concerns like input, sound, and physics beyond just visuals, composition is handled via the useNewComponent hook. To instantiate another component within a component, call useNewComponent passing the component function or a factory function.
    import { useType, useNewComponent } from "@hex-engine/core";
    import SomeOtherComponent from "./SomeOtherComponent";
    import YetAnotherComponent from "./YetAnotherComponent";
    
    function MyComponent() {
      useType(MyComponent);
    
      // Compose a component without arguments
      useNewComponent(SomeOtherComponent);
      
      // Compose a component with arguments using a factory function
      useNewComponent(() => YetAnotherComponent(42));
    }