0.1.19 • Published 8 years ago

fudge v0.1.19

Weekly downloads
3
License
MIT
Repository
github
Last release
8 years ago

fudge

ECS framework for JavaScript

Fudge is a Entity-Component-System framework that adopts some of Flux architecture concepts. Unlike Flux, there is no centralized 'Dispatcher' - Instead the Engine contains signal / action hierarchy which can be used to replace Dispatcher, without creating action object.

Signal

Signal represents single modification / action happens on the engine. Each signal have phases and default handler, and it can be used to cancel/modify original actions, perform additional task, etc.

It has following phases:

  • pre - Runs before any listeners. Can be used to modify / cancel action.
  • (main) - Runs right before executing the handler.
  • (handler) - Actually performs data modification, etc.
  • post - Runs after exeucting the handler.

It also has parents, so signals can go up to the hierarchy.

Methods

  • add(listener, priority, raw, once) - Adds new listener.
  • remove(listener) - Removes the listener.
  • dispatch(...args) - Dispatch the signal.
  • _dispach(args) - Dispatch the signal (Accepts an array as argument)
  • emit(...args) - Dispatch the signal.
  • addRaw(listener, priority) - Same as add(listener, priority, true)
  • on(listener, priority, raw) - Same as add
  • once(listener, priority, raw) - Same as add(listener, priority, raw, true)

Action

An action is a function that performs certain task, like moving an entity to specified position, etc. It may wrap a signal, or it might be a bare function.

System

A system is an object that can provide useful functions, caches, or listen to signals and perform side effects. (Such as moving entities)

It can be a function or an object - If a function is given, Fudge will call it with the engine object: func(engine), and will use return value as the system. Then, it'll call attach(engine) of system object.

It'll automatically register hooks of system object to signals following a schema.

Hook schema

{
  'hello.world': (a, b) => {}, // References hello.world
  'hello.world:pre': (a, b) => [a, b], // References hello.world pre phase
  'hello.world:post': (a, b) => {}, // References post phase
  'hello.world@100': (a, b) => {}, // References hello.world with 100 priority
  'hello.world:pre@100': (a, b) => [a, b], // pre phase with 100 priority
  // References hello.world, get an array as argument (raw). (It's much faster)
  'hello.world!': ([a, b]) => {},
  // References hello.world pre phase, get an array as argument (raw).
  'hello.world:pre!': a => a,
  'hello.*': (a, b) => {}, // References hello.*
  'a.b.c.d': () => {} // References a.b.c.d
}

Component

A component represents single aspect of an entity, such as position or velocity. It comes with default data, and actions to use.

import { signal, signalRaw } from 'fudge';

{
  // The default data to use, can be a function (as constructor)
  component: {
    x: 0,
    y: 0
  },
  // The toJSON function, issued when serialization (if exists)
  toJSON: (component, entity) => {x: component.x, y: component.y, a: entity.id},
  // Actions to register
  actions: {
    // Register a signal. It must be wrapped with signalRaw function
    // signalRaw calls the callback with an argument array.
    set: signalRaw(([entity, data]) => {
      entity.pos = data;
    }),
    // signal calls the callback normally.
    set2: signal((entity, data) => {
      entity.pos = data;
    }),
    // 'this' is the engine object, so we can do this:
    set3: signal(function (entity, data) {
      this.actions.position.set2(entity, data);
    }),
    // signal/signalRaw also control parent's arguments.
    // NOTE It uses an array for args
    set3: signal((entity, data) => {
      entity.pos = data;
    }, cb => ([a, b]) => cb(['set', a, b])),
    x: {
      // It can also have hierarchy.
      set: signalRaw(([entity, x]) => {
        entity.pos.x = x;
      })
    },
    // If bare function is specified, it'll be registered as an action, but
    // not a signal.
    // Also, 'this' is the engine object.
    move: function (entity, data) {
      this.actions.position.set2(entity, data);
    }
    // * is used to raise the signal flow to parent, it won't go upper level
    // if * is not specified.
    // 'position.*' would be still usable, but '*' won't receive 'position.*'.
    // It also controls parent's arguments.
    '*': callback => args => callback(['position'].concat(args)),
  }
}

State

The engine state.

  • global: Object. The global state.
  • entities: Array. The array containing all the entities.

Entity

An entity contains all the components associated with it.

  • id: Number. The entity ID.
  • : Object. The component.

Engine

Engine stores / controls all the actions, signals, systems, entities used in the application.

import { Engine } from 'fudge';

constructor

new Engine(components, systems)

Creates new engine. Components and systems are objects.

import { Engine, signalRaw } from 'fudge';
let engine = new Engine({
  position: {
    component: {
      x: 0, y: 0
    },
    actions: {
      set: signalRaw(([entity, x, y]) => {
        entity.position = { x, y };
      }),
      add: function (entity, x, y) {
        this.actions.position.set(entity,
          entity.position.x + x,
          entity.position.y + y
        );
      }
    }
  }
}, {
  move: function (engine) {
    this.family = engine.systems.family.get('position');
    this.hooks = {
      'external.update!': ([delta]) => {
        this.family.forEach(entity => {
          this.engine.actions.position.add(entity, delta, delta);
        })
      }
    };
  }
});

Properties

  • running: Boolean.
  • actions: Object. An object containing all the actions.
  • signals: Object. An object containing all the signals.
  • systems: Object. An object containing all the systems.
  • components: ComponentStore.
  • state: State. The engine state.

Methods

addComponents(components)

Registers components to the engine. Engine must be not running.

addSystems(systems)

Registers systems to the engine. Engine must be not running.

addComponent(name, component)

Registers component to the engine. Engine must be not running.

addSystem(name, system)

Registers system to the engine. Engine must be not running.

getState()

Returns JSON state.

start()

Starts the engine. (Make it running). Also dispatches external.start.

stop()

Stops the engine. (Stop running). Also dispatches external.stop.

update(delta)

Update the engine. Equals to dispatching external.update(delta).

Built-in Actions

external

  • load(state) - Loads the state to JSON.
  • start() - Start the engine.
  • stop() - Stop the engine.
  • update(delta) - Update the engine.

entity

  • create(data, ignoreMissing) - Creates an entity with the schema.
  • delete(entity) - Deletes the entity.
  • add.(entity, data) - Add a component to the entity.
  • remove.(entity) - Removes the component from the entity.

Built-in Systems

family

FamilySystem manages lists of entities with specified components efficiently. It internally stores BitSets of entities and families, and updates the list when an action has occurred.

  • get(component1, component2, ...): Family - Returns new (or existing) Family object.

Family object has following properties:

  • onAdd: Signal. Issued when an entity is added.
  • onRemove: Signal. Issued when an entity is removed.
  • entities: Array. An array containing the entities.
  • forEach: Function. A delegate function for entities.forEach.

Integrations

0.1.19

8 years ago

0.1.18

8 years ago

0.1.17

8 years ago

0.1.16

8 years ago

0.1.15

8 years ago

0.1.14

8 years ago

0.1.13

8 years ago

0.1.12

8 years ago

0.1.11

8 years ago

0.1.10

8 years ago

0.1.9

8 years ago

0.1.8

8 years ago

0.1.7

8 years ago

0.1.6

8 years ago

0.1.5

8 years ago

0.1.4

8 years ago

0.1.3

8 years ago

0.1.2

8 years ago

0.1.1

8 years ago

0.1.0

8 years ago

0.0.1

9 years ago