Dice-Box 3D Dice Roller

repository·main·Indexed 42 days ago

https://github.com/3d-dice/dice-box

A high-performance 3D environment for rolling game dice built with BabylonJS and AmmoJS. It utilizes web workers and OffscreenCanvas for optimized rendering and is designed for easy integration into JavaScript applications. Version 1.1.4 supports simple dice notation (e.g., 2d20) and can be extended for advanced notation via companion modules. It includes a WorldFacade for managing the simulation, theme configuration for visual customization, and optional UI components via @3d-dice/dice-ui.

Tokens
4.4K
Snippets
16
Records
21
Agent score
54%

What's inside Dice-Box

  1. Dice notation support and advanced rolling

    main

    By default, Dice-Box only accepts simple dice notations and modifiers, such as 2d20 or 2d6+4.

    To support advanced dice notation (e.g., Roll20 Dice Specification like 4d6dl1 or 4d6!r<2), you should integrate the following companion modules:

    • dice-roller-parser: Parses complex strings into component parts.
    • dice-parser-interface: Provides the bridge between the parser and Dice-Box, handling rerolls, exploding dice, and result generation.
  2. Install @3d-dice/dice-box via npm

    main

    Install the library using npm. During installation, the terminal will prompt you to specify a destination for static assets (defaulting to /public/assets). You can manually move these files later if needed. The assets are located in the @3d-dice/dice-box/src/assets folder within the package.

    npm install @3d-dice/dice-box
  3. Integrate Dice-Box with @3d-dice/dice-ui

    main

    The @3d-dice/dice-ui package provides vanilla UI modules to enhance the user experience. Common components include:

    • AdvancedRoller: A form input for advanced roll notations.
    • DisplayResults: A modal popup to show roll results.
    • BoxControls: A dat.gui based interface for configuring dice-box options (themes, colors, etc.).
    • Dice Picker: Clickable icons for mobile-friendly dice adding.
  4. Initialize DiceBox in your project

    main

    Dice-Box is an ES module designed for build systems. To use it, import the DiceBox class, instantiate it with a DOM selector and a configuration object, and then call the asynchronous init() method.

    Important: You must provide the assetPath in the configuration object, pointing to the location where you copied the static assets.

    import DiceBox from "@3d-dice/dice-box";
    
    // 1. Create instance with a selector and config
    const diceBox = new DiceBox("#dice-box", {
      assetPath: "/assets/dice-box", // required: path to your copied assets
    });
    
    // 2. Initialize the engine
    diceBox.init().then(() => {
      // 3. Roll dice using simple notation
      diceBox.roll("2d20");
    });
  5. Use WorldOffScreen for OffscreenCanvas rendering

    main

    WorldOffScreen is an internal implementation used to manage a 3D BabylonJS world within a Web Worker via OffscreenCanvas. This allows for high-performance dice rendering that doesn't block the main UI thread.

    When initializing WorldOffScreen, you must provide a canvas that has its control transferred to offscreen using canvas.transferControlToOffscreen(). The constructor accepts an options object which includes an onInitComplete callback.

    Note: This class is part of the internal world implementation and is typically managed by the main @3d-dice/dice-box package rather than being instantiated directly by end-users.

    // Example of the expected initialization pattern
    const world = new WorldOffScreen({
      canvas: myCanvas.transferControlToOffscreen(),
      options: myDiceOptions,
      onInitComplete: (success) => {
        console.log('World initialized:', success);
      }
    });
  6. Initialize WorldOnscreen with configuration and callbacks

    main

    To use the on-screen canvas rendering, instantiate WorldOnscreen by passing a configuration object. The constructor accepts several lifecycle callbacks to handle initialization, theme loading, roll results, and die removal.

    Key configuration requirements:

    • canvas: The HTML canvas element to render into.
    • width and height: Dimensions for the canvas.
    • options: A sub-object containing dice-specific settings (e.g., assetPath, enableShadows, scale).

    Available callbacks in the options object:

    • onInitComplete: Called when the BabylonJS scene is ready.
    • onThemeLoaded: Called when a theme has been successfully loaded.
    • onRollResult: Called when a die stops moving and its value is determined.
    • onRollComplete: Called when all dice in a roll have come to rest.
    • onDieRemoved: Called when a die is removed from the scene.
    const world = new WorldOnscreen({
      canvas: myCanvasElement,
      width: 800,
      height: 600,
      onInitComplete: () => console.log('Ready!'),
      onRollResult: ({ rollId, value }) => console.log(`Die ${rollId} rolled: ${value}`),
      options: {
        assetPath: '/assets/dice/',
        enableShadows: true,
        scale: 1
      }
    });
  7. Initialize Dice-Box with WorldFacade

    main

    To use Dice-Box, instantiate the WorldFacade class with a configuration object. The constructor accepts an options object to define the canvas container, themes, and callbacks. If you provide a string as the first argument and an object as the second, it will trigger a deprecation warning; always use a single configuration object.

    After instantiation, you must call .init() to asynchronously load the physics engine, the 3D world (BabylonJS), and the selected theme before performing any rolls.

    import WorldFacade from '@3d-dice/dice-box';
    
    const diceBox = new WorldFacade({
      container: '#dice-container', // CSS selector for the container
      theme: 'purpleRock',
      onRollComplete: (results) => console.log('Roll results:', results)
    });
    
    await diceBox.init();
  8. Configure Dice-Box options

    main

    The WorldFacade constructor accepts a configuration object. Below are the primary available keys:

    KeyTypeDefaultDescription
    idstringdice-canvas-${Date.now()}The HTML ID for the generated canvas
    containerstringnullCSS selector for the element to host the canvas
    enableShadowsbooleantrueWhether dice cast shadows onto the mesh
    shadowTransparencynumber0.8Transparency of shadows
    lightIntensitynumber1Intensity of the scene lighting
    delaynumber10Delay (ms) between dice generation to prevent physics popping
    scalenumber5Scale of the dice
    themestring'default'Hex color or a pre-defined theme name
    preloadThemesArray[]List of themes to load in advance
    externalThemesObject{}Map of theme names to CDN paths
    themeColorstring'#2e8555'Color used for dice or named theme variants
    offscreenbooleantrueUse OffscreenCanvas for performance (falls back to false if unsupported)
    assetPathstring'/assets/dice-box/'Path to 'ammo', 'themes', and web workers
    originstringwindow.location.originThe base origin for asset fetching
    suspendSimulationbooleanfalseIf true, uses random number fallback instead of 3D physics
  9. Handle roll results with callbacks

    main

    You can monitor the lifecycle of dice rolls using callback functions passed in the configuration object:

    • onBeforeRoll(parsedNotation): Triggered before dice are generated.
    • onDieComplete(die): Triggered when an individual die finishes its roll. Returns the die object (excluding internal IDs).
    • onRollComplete(results): Triggered when an entire roll collection is finished. Returns an array of roll group objects.
    • onCollision(body0Id, body1Id, force): Triggered when two dice bodies collide.
    • onThemeLoaded(themeConfig): Triggered when a theme is successfully loaded.
    • onThemeConfigLoaded(themeConfig): Triggered when the theme configuration JSON is fetched.
    const diceBox = new WorldFacade({
      onDieComplete: (die) => console.log(`Die ${die.id} rolled a ${die.value}`),
      onRollComplete: (results) => console.log('All rolls finished:', results),
      onCollision: (id1, id2, force) => console.log(`Collision between ${id1} and ${id2} with force ${force}`)
    });
  10. Roll dice using .roll() and .add()

    main

    Dice-Box provides two primary ways to trigger dice rolls:

    1. roll(notation, options): Clears the current dice box and performs a new roll. This is typically used for a fresh roll sequence.
    2. add(notation, options): Adds new dice to the existing scene without clearing previous dice.

    Notation Formats:

    • String: '4d6', '2d10+3', 'd100', '1d20f' (fudge/fate).
    • Object: { sides: 6, qty: 4, modifier: 2 }.
    • Array: ['4d6', '2d10'].

    Both methods return a Promise that resolves when the roll collection is complete (i.e., all dice have finished rolling).

    // A fresh roll that clears the box
    await diceBox.roll('2d20+5');
    
    // Adding more dice to the current scene
    await diceBox.add({ sides: 6, qty: 2, themeColor: '#ff0000' });
    
    // Reroll specific dice (removes them and adds new ones)
    await diceBox.reroll([{ sides: 6, qty: 1 }], { remove: true });
  11. Update world configuration dynamically

    main

    The updateConfig(options) method allows you to change visual settings without re-instantiating the world. Supported dynamic updates include:

    • enableShadows: Toggles shadow rendering (regenerates lights if changed).
    • shadowTransparency: Adjusts the darkness of shadows.
    • lightIntensity: Adjusts the brightness of directional and hemispheric lights.
    • scale: Resizes all existing dice meshes in the scene.
    world.updateConfig({
      enableShadows: true,
      shadowTransparency: 0.5,
      lightIntensity: 1.2,
      scale: 1.5
    });
  12. Manage the dice box visibility and state

    main

    Use the following methods to control the UI and internal state of the WorldFacade:

    • clear(): Resets all internal indexes and clears all rendered dice bodies and physics bodies from the scene.
    • hide(className?): Hides the canvas. If className is provided, it adds that class to the canvas; otherwise, it sets display: none.
    • show(): Shows the canvas and triggers a resize to ensure the world fits the container.
    • updateConfig(options): Merges new options into the existing configuration and updates both the visual world and the physics engine.
    diceBox.hide('my-custom-fade-class');
    
    // Later...
    diceBox.show();
    
    diceBox.updateConfig({ theme: 'blueRock', scale: 10 });
    
    diceBox.clear();