npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

game-platform

v3.1.5

Published

Common utility for Canvas games

Downloads

121

Readme

Roadmap

[ ] Provide some basic SCSS that can be imported
[ ] Add a Game Grid to the common utilities
[ ] Add hit detection against rotated rects/images (currently only leveled rects are supported)
[ ] Create real objects for mouseMoveDataInterface to make the interface easier to document
[ ] Add docs for the canvasAPI as a stand alone

#Changelog

19/12/2021

Release version 3.0.0

  • Refactor(CanvasAPI): Rename to Painter (new Painter())
  • Feature(Painter): Added event hooks into CanvasAPI allowing it to receive an initial canvas

02/08/2021

Release version 2.1.1

  • Update all dependencies to latest

31/07/2021

Release version 2.1.0

  • Refactor(build): Remove JARB and rollup dependency
  • Fix(General): Fix all implicit any issues with typescript
  • Breaking(CanvasAPI): Change function name from drawFn to render when adding a generic shape (canvasAPI.addShape).

31/07/2021

Release version 2.0.0

  • Change build to rollup (Starting migration away from jarb)

14/11/2019

Release version 0.4.0:

  • Added click detection against all layers
  • Clicking on the Canvas will now return an array of objects with ID and layerName
  • Added color param for addCircle()
  • Fixed bugs related to click-hit not working

###29/10/2019 Release version 0.3.1:

  • Moved the RAF in Engine, now RAF will be queued first before an system runs, this will enable systems to cancel the frame ###29/10/2019 Release version 0.3.0:
  • Moved all layerName arguments into the objects for all functions (for functions that accept Objects)
  • Added a drawTextBubble() text function
  • Added click detection(for click and select box) against rect shapes (it was only circles)\
  • Added tests for click detection against rect\
  • onViewMapMove/click and onMinimapMove/click are now optional callbacks\
  • Add hit detection against Images\
  • Add configuration to disable selection box (options.enableSelectBox = false)\
  • Added documentation to how writeBubble works\
  • Updated docs that hit detection only works on 'initial' layer\

###24/10/2019 Release version 0.2.1

  • Added the addArc() method which enables drawing arcs.

###22/10/2019 Release version 0.2.0

  • Added an Engine class to encapsulate rAF loop (start, stop, addSystem).

###17/10/2019 Release version 0.1.2

  • Added the ability to remove a layer (canvasAPI.removeLayer(name))

###03/10/2019 Release version 0.1.1

  • Added onMiniMapClick and onMiniMapView

###03/10/2019 Release version 0.1.0

  • Added support for Layers.
  • To use layers:
    • the canvas element must be positioned in a container element.
    • The container element must be positioned absolute with a set height and width
    • The canvas elements must be positioned absolute (To enable layering)

###02/10/2019 Release version 0.0.9

  • Updated JARB to 3.0.6
  • Updated Canvas to 2.6.0 and removed canvas prebuilt.
  • These changes now enable support for node 12.x

###15/02/2019###
Release version 0.0.8

  • Updated JARB to 2.0.0-beta.2

###09/01/2019 Release version 0.0.7

  • Fix ObjectPool.generate(amount) to generate UP TO $amount of free objects

###06/01/2019

  • Updated jarb to 1.0.8

###27/12/2018 Release version 0.0.6

  • Ensure React is not bundled in the dist file
  • Document all exported properties
  • Remove ALL TODOs and REFACTORS

#Features

  • Engine
  • CanvasAPI that can be initialized with 2d ctx
  • (React) Game Canvas (Both Main Map and Mini Map) with click detection on what shape was clicked
  • ECS Entity
  • Utility to loop over entities
  • Object Pool utility to pre-create and manage instances of objects

Usage - Engine


import {Engine} from 'game-platform/dist';
let engine = new Engine();

engine.addSystem((systemArguments) => {
   // run any system you want, order specified by order of insertion  
});

engine.addSystem((systemArguments) => {
   // run any system you want, order specified by order of insertion  
});


let systemArguments = {
  // object that is passed to all systems
  // alternatively it can also be a function that returns the object
  // in case of a function, it's evaluated every frame, so keep it light!
}

engine.run(systemArguments); // runs in a loop
  
engine.stop(); // stops the loop completely

Usage - Game Canvas


import {render} from 'react-dom';
import 'index.scss';

import GameCanvas from 'lib/GameCanvas/GameCanvas';


let gameCanvas = new GameCanvas({
  // The mapHeight/width determine the size of the map,
  // while viewHeight/width determine the size visible on the screen.
  // these are not CSS attributes, but rather canvas html properties
  // The library uses viewHeight for the main view, and mapWidth for the minimap
  mapHeight: 4000, // in pixels
  mapWidth : 4000, // in pixels
  viewHeight : 400, // this is the what you actually see in the canvas
  viewWidth : 400, // this is the what you actually see in the canvas
  selectedBoxColor: 'blue', // the color for the current selection box of the user
  enableSelectBox: true, // defaults to true, can be set to false to disable the click->drag select box
  onViewMapClick : (...args) => {
    let clickDataInterface = {
      // The library only detects hits against circles, rects and images, it ignores all other shapes
      // rects and images are calculated without orientation (rotation)
      // you can implement your own click detection system
      hits: [], // array of shape IDs that were clicked on
      dbClick: true, // BOOL, true or false
      isMouseDown: true, // BOOL, true or false
      // Information about the currently selected area (when you select an area with your mouse)
      // if mouse is not held down(no selection), these numbers will be set to 0
      selectedBox: {
        start: {
          x:10,
          y:0
        },
        end: {
          x:110,
          y:75
        },
        height: 75,
        width: 100
      }
    };
  },
  onViewMapMove : (mouseMoveData) => {
    let mouseMoveDataInterface = {
      dbClick: true, // BOOL, true or false
      isMouseDown: true, // BOOL, true or false
      // Information about the currently selected area (when you select an area with your mouse)
      // if mouse is not held down(no selection), these numbers will be set to 0
      selectedBox: {
        start: {
          x:10,
          y:0
        },
        end: {
          x:110,
          y:75
        },
        height: 75,
        width: 100
      }
    };
  }
});

let apis = {};
// these functions return React Element instances
// their callback provides a way to access the internal Canvas API
let mainMap = gameCanvas.generateMapCanvas((API) => {
  apis.main = API;
});

let miniMap = gameCanvas.generateMiniMapCanvas((API) => {
  apis.mini = API;
});

// Unfortunate, as we can only expose the APIs once the canvas context exists
// but we also can't have the canvas context until we render


// We render our new canvas react elements with React
render(<div>
  {mainMap}
  {miniMap}
</div>, document.getElementById('app'), () => {
  apis.main.addRect({
    id: 'SomeRect', // Must be Unique, as a Map is used internally
    x: 10,
    y: 15,
    width: 20,
    height: 35,
    strokeStyle: 'blue'
  });

  apis.main.addCircle({
    id: 'ExampleID',
    x:50,
    y: 50,
    radius: 15,
    strokeStyle: 'red'
  });

  let img = new Image;
  img.src = 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png';
  img.onload = () => {
    apis.main.drawImage({
      id : 'my-image',
      image:img, // the image to display
      x: 100, y: 100, // pos for x,y..
      height: 100, width: 100,
      cropStartX:0, cropStartY:0, cropSizeX:img.width, cropSizeY:img.height,
      rotation : 0.2 // in radians
    });
  };
  
  
  apis.main.drawTextBubble({
    id: 'bubbleTextExampleID',
    text: 'It is dangerous to go alone! \ntake this!',
    backgroundColor: 'green',
    borderColor:'orange',
    borderWidth: 3,
    fontColor: 'purple',
    x: 200,
    y: 200,
    height:0, // the minimum value is the text value within
    width:0, // the minimum value is the text value within
    fontSize: 16
  });

  // in a game, you usually render in a loop, the API supports it by having deterministic draws
  setInterval(() => {
    // Once we have all our shapes in place, we use the internal Painter to draw them
    // Internally this deletes the entire canvas, and re-renders all the shapes.
    apis.main.drawAllShapesInLayer();
    apis.mini.drawAllShapesInLayer();
  }, 16);
});

Usage - ECS

  import Entity from 'lib/ECS/Entity';
  it('Creates a new entity', () => {
    let e = new Entity();
    expect(e.id).not.toBeUndefined();
    expect(e.components).toEqual({});
  });

  it('Adds and removes components', () => {
    let e = new Entity();
    let comp = {name:'test', foo:'bar'};
    e.addComponent(comp);
    expect(e.components.test).toBe(comp);
    e.removeComponent(comp);
    expect(e.components.test).toBeUndefined();
  });

  it('Tests the hasComponent method', () => {
    let e = new Entity();
    let comp1 = {name:'test1', foo:'bar'};
    e.addComponent(comp1);
    let comp2 = {name:'test2', foo:'bar'};
    e.addComponent(comp2);

    expect(e.hasComponents('test1')).toBe(true);
    expect(e.hasComponents(['test1'])).toBe(true);
    expect(e.hasComponents(['test1', 'test2'])).toBe(true);
    expect(e.hasComponents(['anotherComp'])).toBe(false);
    expect(e.hasComponents('anotherComp')).toBe(false);
    expect(e.hasComponents()).toBe(true);
  });

  it('Entity can destroy itself', () => {
    let e = new Entity();
    let comp1 = {name:'test1', foo:'bar'};

    e.addComponent(comp1);
    // always returns a collection
    expect(Entity.getByComps(['test1'])[0]).toBe(e);

    e.destroy();
    expect(Entity.entities[e.id]).toBeUndefined();

    let resp = Entity.getByComps(['test1']);
    expect(resp.length).toBe(0);
  });

Usage - The EntityLoop

  import entityLoop from 'lib/ECS/util/entityLoop';
  import Entity from 'lib/ECS/Entity';

  it ('Loops on Entity arrays', () => {
    let foo = new Entity();

    foo.addComponent({
      name : 'lol'
    });

    let runs = 0;
    let ents = entityLoop(Entity.getByComps('lol'), (ent) => {
      runs++;
      expect(ent).not.toBeUndefined();
      return true;
    });

    expect(ents.length).toBe(1);
    expect(runs).toBe(1);
  });

Usage - The Object Pool

  import ObjectPool from 'lib/ObjectPool/ObjectPool';

  // Define what type of pool you'd like to make
  beforeEach(() => {
    class Foo {
      constructor() {
        this.isAlive = true;
      }
    }
    // if you try to acquire an object when the pool is empty, this will create more instances on the fly
    let incrementWhenEmpty = 100;
    pool = new ObjectPool(Foo, incrementWhenEmpty);
  });

  it('Acquires an object instance', () => {
    pool.generate(100);
    let obj = pool.acquire();
    expect(obj.isAlive).toBe(true);
    expect(pool.stats.free).toBe(99);
  });

  // the pool exposes the following stats
  pool.stats = {
      free: 0,
      used: 0,
      total: 0
    };