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

moonoom

v0.0.7

Published

elastic entity component system

Downloads

6

Readme

Usage: see demo.js file (const {Engine, Entity, Query} = require('moonoom'))

CDN: https://unpkg.com/moonoom (const {Engine, Entity, Query} = moonoom)

Design

data / entities
  • shorthand component access: entity.component.property, player.health.value
  • can have many of one component: entity.component$[2].property, player.debuff$[2].type
  • components can have components: entity.component.component2.property, player.arm.equipment.armour
systems / engine
  • systems are added once: engine.add('systems', ...yourSystems.map(s=> (...args)=>s.update(args))
  • systems are categorized by name, so systems can be run at differing intervals, for example logic or render loops being separate: engine.update('render', renderer)
  • systems added function should expect the first parameter to be the engine and subsequent to be parameters sent in at the update call: (engine, renderer){} for the above call for example
query
  • search with AND and/or OR
  • even depth arrays (starting at 0) are AND queries, odd are OR
  • example: ['hat', ['pants', 'shorts'], 'shirt'] = get anyone wearing hat, shirt, and (pants or shorts)
  • can exclude found entities by the same logic
  • can use wildcard * for component of any name, example if you want all entities that have armour on a body part: body.*.armour
  • can use wildcard ** for zero or more components of any name, example if you want all entities that have anything of steel: **.steel

example, system has entity.arm.armour.steel

engine.get('arm.armour') //gets all entities that have armored arm
engine.get('*.armour') //gets all entities that have something armoured
engine.get('**.steel') //gets all entities that have anything of steel

engine.search(['*.armour'], ['*.armour.steel'])
//gets all entities that have something armoured, but the armour isn't steel

API

Component

Any class instanced object. Will be named/referenced as its constructor name, in camelCase. Your original instance will be wrapped in a proxy, so === won't work unless you access the component off an entity first.

class Position{
	constructor(x, y){
		this.x = x
		this.y = y
	}
}

Entity

Bag of components and some convenience methods.

create(...componentsOrArray)

  • ...componentsOrArray any variable number of instanced Component, or arrays of instanced Components. Use arrays if you want the first component in the array to have all the subsequent components.
  • returns Entity instance
const entity = Entity.create(
	new Position(0, 0),
	[new Body(), new Torso(), new Arm(), new Arm(), new Leg(), new Leg(), new Head()]
	[new Bag(),
		[new Sword(), new Steel()]
	]
)

wrap(base, ...components)

  • base object will have all the Component attached to it. Equivalent to the arrays in create, but only makes one entity. Used for making one component have a set of other components.
  • returns Entity instance
collideds.forEach(collided=> entity.add(Entity.wrap(new Collided(), collided)))
//searchable by engine.get(collided.entity.point), if collided object had a point component

attach(engine)

  • attaches entity to the given Engine, must be done for an entity to "exist". Cannot attach to multiple engines.
  • returns the Entity instance

dettach()

  • dettaches entity from engine, if it had one
  • returns true or false if it had an engine to dettach from

add(...components)

  • adds components to this entity & its engine (if exists)
  • ...components any variable number of instanced Component
  • returns Entity instance wrapped Components, with a proxy around them. Can use this for ===.

remove(...componentsOrNames)

  • removes components from this entity & its engine (if it exists)
  • ...componentsOrNames any variable number of instanced Component or their camelCase names
    • if instance is given, removes strict equal match
    • if name is given, removes first component with the name

has(componentQuery)

  • componentQuery Component camelCase names, but also accepts . for separator, * for one component of any name, and ** for zero or more components. Example: body.arm.*.steel
  • return boolean true or false if exists on this entity

shorthand usage/proxy

const entity = Entity.create(
	new Position(0, 0),
	new Velocity(1, 1),
	new Velocity(1, 2),
	new Velocity(2, 2)
)

entity.position // {x: 0, y: 0}
entity.velocity // {x: 1, y: 1} // gets first velocity
entity.velocity$ // [{x: 1, y: 1}, {x: 1, y: 2}, {x: 2, y: 2}]
entity.position$ // [{x: 0, y: 0}]

delete entity.velocity // deletes first velocity
entity.velocity$ // [{x: 1, y: 2}, {x: 2, y: 2}]

delete entity.velocity$ // deletes all velocity
entity.velocity$ // []
entity.velocity // undefined

//you can also shorthand wrap components
entity.add(new Head())
entity.head.add(new Helmet()) //components after being added are essentially entities
//or entity.add(new Head())[0].add(new Helmet())

Engine

Bag of searchable entities & categorized systems.

add(category, ...systems)

  • category name of systems. Can only be added once.
  • ...systems any variable number of functions that want to be called every update of this engine, receiving engine, ...args

update(category, ...args)

  • category name of systems to call
  • ...args anything you want to propagate to systems

get(...componentQueries)

  • ...componentQueries any variable number of Component camelCase names, but also accepts . for separator, * for one component of any name, and ** for zero or more components. Example: engine.get('body', 'velocity', 'body.legs', '*.jets')
  • search with AND and/or OR
  • even depth arrays (starting at 0) are AND queries, odd are OR
  • example: ['hat', ['pants', 'shorts'], 'shirt']: get anyone wearing hat, shirt, and (pants or shorts)
  • returns array of Entity

search(include, exclude)

  • like query but one use

query(query)

  • query a Query instance to run on this engine
  • returns array of Entity that match query

create(...componentsOrArrays)

  • shorthand for Entity.create(...componentsOrArrays).attach(engine)

Query

constructor([include], [exclude])

  • include optional, if omitted will get all entites. Component queries: any variable number of Component camelCase names, but also accepts . for separator, * for one component of any name, and ** for zero or more components. Example: new Query(['body', 'velocity', 'body.legs', '*.jets'])
  • search with AND and/or OR
  • even depth arrays (starting at 0) are AND queries, odd are OR
  • example: ['hat', ['pants', 'shorts'], 'shirt']: get anyone wearing hat, shirt, and (pants or shorts)
  • exclude optional. Same as include but excludes the matches

search(engine)