noa-engine Documentation

repository·master·Indexed 20 days ago

https://github.com/fenomas/noa

An experimental voxel game engine (v0.33.0) used for multiplayer and single-player games like bloxd.io and Minecraft Classic. It provides core mechanics for world generation, voxel management, and 3D rendering via Babylon.js. The engine features a component-based system for entity behavior, high-precision local coordinate handling to prevent precision loss in large worlds, and a dedicated Engine class for managing the lifecycle of rendering, physics, and entities.

Tokens
13.1K
Snippets
47
Records
62
Agent score
71%

What's inside noa-engine

  1. Understand component renderSystem execution order

    master

    The renderSystem handlers are responsible for updating the visual representation of entities. They run in a specific order to ensure that movement and positioning logic is applied before the final mesh rendering occurs.

    | name | order | render system |
    | ---- | ----- | ------------- |
    | `physics`       | `40` | backtrack entity `renderPosition` towards physics position |
    | `followsEntity` | `50` | moves entity's `renderPosition` to match its follow target |
    | `shadow`         | `80` | update shadow's `x/z` position |
    | `mesh`           | `100`| moves rendering mesh to entity `renderPosition` |
  2. Understand component system execution order

    master

    The system handlers update the internal state of entities. They are executed in a specific order based on their order property. If you define a custom component, you can specify an order in the component definition to ensure it runs at the correct time relative to built-in systems.

    For example, if a component's renderSystem depends on the current camera target position, it should have an order greater than 50 to ensure it runs after followsEntity has moved the camera target.

    | name | order | system |
    | ---- | ----- | ------ |
    | `receivesInputs`  | `20` | update `movement` state based on key/mouse input |
    | `movement`        | `30` | applies physics forces based on `movement` state |
    | `physics`          | `40` | update entity `_localPosition` from physics body |
    | `followsEntity`   | `50` | move own `_localPosition` to match target |
    | `position`         | `60` | update `position` and `extents` properties |
    | `collideEntities` | `70` | runs collision test, fires onCollide events |
    | `shadow`          | `80` | update shadow's `y` position |
    | `fadeOnZoom`      | `99` | checks camera zoom, hides or reveals entity |
    | `smoothCamera`    | `99` | removes itself after time limit |
  3. Understand the internal position component structure

    master

    The position component stores data in local coordinates. Understanding these properties is useful for engine hacking or advanced customization:

    • _localPosition: The single source of truth for the entity's position (used for game logic).
    • _renderPosition: The position used in the 3D scene. This can change every render frame, whereas _localPosition only changes once per tick.
    • _extents: An array representing dimensions: [lox, loy, loz, hix, hiy, hiz].

    The offset between local and global coordinates is defined by noa.worldOriginOffset.

  4. Access the noa-engine API reference

    master
    For detailed information on engine classes, methods, and technical specifications, consult the official API documentation. The documentation is automatically generated from the source code.
  5. Initialize the noa engine using the Engine class

    master

    To start building with noa, the primary entry point is the Engine class. This core class manages the voxel engine lifecycle and serves as the foundation for your game implementation.

    // Reference to the core engine class
    import { Engine } from 'noa';
    
    const engine = new Engine();
  6. Start building with noa-engine using examples

    master

    The recommended way to begin developing a voxel game with noa is to use the noa-examples repository as a foundation. This repository contains a hello-world example that serves as a template for:

    • Instantiating the engine.
    • Defining world geometry.
    • Importing peer dependencies.
    • Testing a world.
    • Building for production.

    Instead of starting from scratch, clone the examples repo and modify the existing source code to suit your game's needs.

    git clone https://github.com/fenomas/noa-examples
  7. Manage the game container and canvas with Container

    master

    The Container class manages the game's HTML container element, the <canvas> element, fullscreen mode, and pointer lock. It wraps micro-game-shell to handle timing, rendering rates, and input states.

    When initializing a Container, you can provide a specific DOM element via opts.domElement. If no element is provided, it creates a default full-screen div with the ID noa-container and a <canvas> with the ID noa-canvas.

    // Example initialization (internal usage pattern)
    const container = new Container(noa, {
      domElement: '#my-game-container',
      tickRate: 60,
      maxRenderRate: 60,
      stickyPointerLock: true,
      stickyFullscreen: true
    });
  8. How block materials are mapped to faces

    master

    When registering a block, the material parameter determines how textures are applied to the 6 faces of a voxel. The engine maps these to the direction indices 0..5 as follows:

    • 0: +x
    • 1: -x
    • 2: +y
    • 3: -y
    • 4: +z
    • 5: -z

    (Note: The source code uses blockMats[id * 6 + i] where i is the face index. The mapping logic for shorthand arrays like [top, bottom, sides] is: [sides, sides, top, bottom, sides, sides] based on the internal mats array construction).

  9. How camera rotation and zoom work

    master

    The Camera manages the player's perspective through two main concepts: Rotation and Zoom.

    Rotation (Heading and Pitch)

    • heading: The yaw angle (rotation around the vertical axis) in the range 0..2π.
    • pitch: The up/down rotation angle in the range -π/2..π/2. The engine clamps this value slightly to prevent the camera from pointing perfectly vertical.
    • Note: While these properties are writable, they are managed by the engine and will be overwritten every frame based on mouse input.

    Zooming

    • zoomDistance: The desired distance the camera should maintain from the target.
    • currentZoom: The actual distance the camera is currently at. This value interpolates towards zoomDistance based on zoomSpeed. It is also clamped by the engine to prevent the camera from clipping into solid terrain behind the player.
  10. Manage entities and components with the Entities class

    master

    The Entities class is an Entity Component System (ECS) that manages game entities and their associated components. It extends ent-comp and provides specialized helpers for querying entity positions, physics bodies, and meshes.

    Built-in components available via this.names include:

    • collideEntities
    • collideTerrain
    • fadeOnZoom
    • followsEntity
    • mesh
    • movement
    • physics
    • position
    • receivesInputs
    • shadow
    • smoothCamera
    import ECS from 'ent-comp'
    // The Entities class is typically accessed via the engine instance, e.g., noa.ents
  11. Handle chunk loading via worldDataNeeded events

    master

    The World class manages chunk lifecycles by requesting data from the client. When a new chunk is needed within the player's range, the engine emits the worldDataNeeded event. The client must listen for this event, generate or fetch the voxel data, and provide it back to the engine using setChunkData.

    // 1. Listen for the request
    world.on('worldDataNeeded', (requestID, dataArr, x, y, z, worldName) => {
      // 2. Generate your voxel data (dataArr is an ndarray)
      const myData = generateVoxelData(x, y, z);
      
      // 3. Send it back to the engine
      world.setChunkData(requestID, myData);
    });
  12. Customize the camera follow target

    master

    The camera follows a special entity called cameraTarget. By default, this entity follows the playerEntity with an offset corresponding to the player's eye height.

    Adjusting Eye Height

    You can change the player's eye height by modifying the followsEntity component on the cameraTarget:

    var followState = noa.ents.getState(noa.camera.cameraTarget, 'followsEntity')
    followState.offset[1] = 0.9 * myPlayerHeight

    Changing the Target

    To make the camera follow a different entity or to control its position manually, remove the followsEntity component from the cameraTarget:

    // make cameraTarget stop following the player
    noa.ents.removeComponent(noa.camera.cameraTarget, 'followsEntity')
    
    // control cameraTarget position directly
    noa.ents.setPosition(noa.camera.cameraTarget, [x, y, z])
    // Adjusting eye height
    var followState = noa.ents.getState(noa.camera.cameraTarget, 'followsEntity')
    followState.offset[1] = 0.9 * myPlayerHeight
    
    // Changing the target
    noa.ents.removeComponent(noa.camera.cameraTarget, 'followsEntity')
    noa.ents.setPosition(noa.camera.cameraTarget, [x, y, z])