Kontra.js

repository·main·Indexed 21 days ago

https://github.com/straker/kontra

A lightweight JavaScript micro-library for HTML5 game development, specifically optimized for the size constraints of the js13kGames competition. It provides essential game development tools including animation systems, accessible UI buttons, a spatial layout Grid class, and a comprehensive Gamepad API.

Tokens
13.7K
Snippets
53
Records
67
Agent score
76%

What's inside kontra

  1. Customize child alignment with alignSelf and justifySelf

    main

    While the Grid defines global alignment for its rows and columns, individual child objects can override this behavior using two properties:

    1. alignSelf: Overrides the grid's vertical (align) alignment for that specific child.
    2. justifySelf: Overrides the grid's horizontal (justify) alignment for that specific child.

    Valid values for both are 'start', 'center', and 'end'.

  2. Initialize the Gamepad API

    main

    Before using any gamepad functions, you must call initGamepad(). This sets up the necessary event listeners for gamepad connections and updates the gamepad state every frame using the internal tick event.

    Note: Gamepad support requires a secure context (HTTPS). Additionally, because gamepad state must be checked every frame, you should use the GameLoop provided by Kontra.js to ensure the state is updated automatically.

    import { initGamepad, GameLoop } from 'kontra';
    
    // Must be called first
    initGamepad();
    
    // Using GameLoop ensures gamepad state is updated automatically every frame
    let loop = GameLoop({
      update: function() {
        // your game logic
      }
    });
    loop.start();
  3. Create a Scene with Scene()

    main

    Use the Scene factory function to organize a group of objects that will update and render together. A scene manages a collection of objects, a camera for viewport control, and an accessible DOM node for screen readers.

    Configuration Options

    When creating a scene, you can pass an options object with the following properties:

    • id (String): The unique identifier for the scene.
    • name (String, optional): A human-friendly name used for screen reader accessibility. Defaults to id.
    • objects (Object[], optional): An initial array of objects to add to the scene.
    • context (CanvasRenderingContext2D, optional): The canvas context to draw to. Defaults to the result of core.getContext().
    • cullObjects (Boolean, optional): If true, objects outside the camera bounds will not be rendered. Defaults to true.
    • cullFunction (Function, optional): The function used to filter objects for rendering. Defaults to helpers.collides.
    • sortFunction (Function, optional): The function used to sort objects before rendering (e.g., for depth sorting).
    • onShow (Function, optional): Callback triggered when the scene is shown.
    • onHide (Function, optional): Callback triggered when the scene is hidden.
    • props (Object, optional): Any additional properties to be assigned to the scene instance.
    import { Scene, Sprite } from 'kontra';
    
    const sprite = Sprite({
      x: 100,
      y: 200,
      width: 20,
      height: 40,
      color: 'red'
    });
    
    const scene = Scene({
      id: 'game',
      objects: [sprite]
    });
    
    scene.render();
  4. Animate a Sprite using SpriteSheet animations

    main

    To use animations, pass an animations object to the Sprite constructor. This object should contain Animation instances (usually obtained from a SpriteSheet).

    When animations are provided:

    • The sprite automatically clones each animation so that multiple sprites can use the same sheet without interfering with each other's playback state.
    • The currentAnimation is set to the first animation in the list by default.
    • You can switch between animations using playAnimation(name).
    • The sprite's width and height will default to the dimensions of the animation frames if not explicitly set.
    import { Sprite, SpriteSheet } from 'kontra';
    
    let spriteSheet = SpriteSheet({
      // ... configuration for spriteSheet
      animations: {
        idle: {
          frames: 1,
          loop: false,
        },
        walk: {
          frames: [1, 2, 3]
        }
      }
    });
    
    let sprite = Sprite({
      x: 100,
      y: 200,
      animations: spriteSheet.animations
    });
    
    // Play a specific animation by name
    sprite.playAnimation('idle');
  5. Initialize a Quadtree

    main

    A Quadtree is a 2D spatial partitioning data structure used to group objects by their position to optimize collision detection and spatial queries. You can initialize it using the default factory function or the QuadtreeClass constructor.

    To prevent excessive garbage collection, the quadtree acts like an object pool: it creates subnodes as needed but does not destroy them when the tree collapses. Therefore, you should call .clear() every frame instead of re-instantiating the tree.

    import Quadtree from 'kontra';
    
    // Using the default factory
    let quadtree = Quadtree({
      maxDepth: 3,
      maxObjects: 25,
      bounds: { x: 0, y: 0, width: 800, height: 600 }
    });
  6. Initialize the TileEngine

    main

    The TileEngine is used to manage and render tile-based maps. It uses an off-screen canvas to pre-render tiles for performance. You can initialize it by passing a properties object containing map dimensions, tile dimensions, tilesets, and layers.

    Properties:

    • width (Number): Width of the tile map in number of tiles.
    • height (Number): Height of the tile map in number of tiles.
    • tilewidth (Number): Width of a single tile in pixels.
    • tileheight (Number): Height of a single tile in pixels.
    • context (CanvasRenderingContext2D, optional): The context to draw to. Defaults to core.getContext().
    • tilesets (Object[], required): Array of tileset objects.
    • layers (Object[], required): Array of layer objects.

    Tileset Object Properties:

    • firstgid (Number): First tile index of the tileset (the first tileset should have firstgid: 1).
    • image (String|HTMLImageElement): Path to the image or the element itself. If a path is used, it must be pre-loaded via assets.load.
    • spacing (Number, default: 0): Whitespace between tiles in pixels.
    • margin (Number, default: 0): Whitespace border around the tileset image.
    • tilewidth (Number, optional): Width of the tileset tile. Defaults to engine tilewidth.
    • tileheight (Number, optional): Height of the tileset tile. Defaults to engine tileheight.
    • columns (Number, optional): Number of columns in the tileset image.
    • source (String, optional): Path to a Tiled JSON source file (must be pre-loaded).

    Layer Object Properties:

    • name (String): Unique name of the layer.
    • data (Number[]): 1D array of tile indices.
    • visible (Boolean, default: true): Whether the layer is drawn.
    • opacity (Number, default: 1): Layer opacity (0 to 1).
    import { TileEngine } from 'kontra';
    
    let tileEngine = TileEngine({
      tilewidth: 32,
      tileheight: 32,
      width: 4,
      height: 4,
      tilesets: [{
        firstgid: 1,
        image: myLoadedImage
      }],
      layers: [{
        name: 'background',
        data: [ 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
      }]
    });
  7. Initialize keyboard input with initKeys()

    main

    Before using any keyboard-related functions in Kontra, you must call initKeys(). This function sets up the necessary global event listeners for keydown, keyup, and blur, and populates the default keyMap with alpha (a-z) and numeric (0-9) keys.

    import { initKeys } from 'kontra';
    
    initKeys();
  8. Use the Grid class for spatial layouts

    main

    The Grid class allows you to organize UI elements into structured layouts (columns, rows, or grids) similar to CSS Grid Layout. It automatically calculates the positions of child objects based on properties like flow, alignment, gaps, and breakpoints.

    To use it, instantiate it via the Grid factory function and pass a configuration object. You can then add child objects using addChild().

    import Grid from './grid.js';
    
    const grid = Grid({
      flow: 'grid',
      numCols: 3,
      colGap: 10,
      rowGap: 10,
      align: 'center',
      justify: 'start'
    });
    
    grid.addChild(someGameObject);
    // The grid will automatically position 'someGameObject' based on the settings above.
  9. Configure Grid properties

    main

    When initializing a Grid, you can provide the following properties to control its layout behavior:

    PropertyTypeDefaultDescription
    flowString'column'How to organize objects: 'column' (single column), 'row' (single row), or 'grid' (uses numCols).
    alignString | String[]'start'Vertical alignment of the grid. Values: 'start', 'center', 'end'. An array allows alternating alignment per row (e.g., ['end', 'start']).
    justifyString | String[]'start'Horizontal alignment of the grid. Values: 'start', 'center', 'end'. An array allows alternating alignment per column.
    colGapNumber | Number[]0Horizontal gap between columns. An array allows alternating gaps per column.
    rowGapNumber | Number[]0Vertical gap between rows. An array allows alternating gaps per row.
    numColsNumber1Number of columns. Only applies if flow is set to 'grid'.
    dirString''Direction of the grid. Set to 'rtl' for right-to-left organization.
    breakpointsArray<{metric: Function, callback: Function}>[]Responsive settings that trigger a callback when a metric function returns true.
  10. Extend the gamepad button map

    main

    By default, gamepadMap maps standard hardware indices to human-readable names (e.g., 0: 'south'). You can extend or modify this map to support custom buttons or non-standard controllers by assigning a name to a new index.

    Note: This must be done before checking button states.

    import { gamepadMap, gamepadPressed } from 'kontra';
    
    // Map index 2 to a custom name
    gamepadMap[2] = 'buttonWest';
    
    if (gamepadPressed('buttonWest')) {
      // handle custom button
    }