Experimental macro-powered Entity-Component-System game engine
Branch: develop
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.vscode Lixify and add tests Sep 22, 2018
haxe_libraries Fix EngineStateMachine and use it in asteroid Nov 7, 2018
sample/asteroid Fix EngineStateMachine and use it in asteroid Nov 7, 2018
src/ecs Expose engine life cycle events Feb 7, 2019
tests Make FSM less complex Nov 7, 2018
.gitignore
.haxerc Lixify and add tests Sep 22, 2018
.travis.yml Only test hl on latest haxe Sep 24, 2018
README.md Update readme [ci skip] Sep 24, 2018
haxelib.json Fix EngineStateMachine and use it in asteroid Nov 7, 2018
playgound.hxml Add a simple read me Sep 22, 2018
tests.hxml Lixify and add tests Sep 22, 2018

README.md

Macro-powered Entity-Component-System framework

Build Status

This is a Entity-Component-System framework, inspired by the Ash Framework
Thanks to the Haxe macro system we are able to reduce boilerplate code and allow some optimizations.

This is platform-agnostic, you can use it on any targets supported by Haxe (e.g. cpp, js, lua, etc)
This is also game-framework-agnostic, you can use it with any game frameworks such as Kha, OpenFL, Heaps, etc.

Elements of the framework

Engine
The "core" that manages everything

Entity
A container holding various components

Component
Building blocks of an entity, contains attributes/properties but not any logics

System
Logics that operate on Components

Node
A container holding an Entity and the Components of interest. This is mostly for optimization, pre-fetching components from the entity so that we don't need to do so on every iteration.

NodeList
A list of Nodes. The specialized TrackingNodeList will keep track of entities together with their components and add/remove them from the list when appropriate.

Example

import ecs.*;
import ecs.node.*;
import ecs.entity.*;
import ecs.system.*;
import component.*;
import haxe.Timer;

class Playground {
	static function main() {
		// create an engine
		var engine = new Engine();
		
		// create an entity and adds 2 components to it
		var entity = new Entity();
		entity.add(new Velocity(1, 0));
		entity.add(new Position(0, 0));
		
		// add entity to engine
		engine.entities.add(entity);
		
		// create and add 2 systems to engine
		engine.systems.add(new MovementSystem());
		engine.systems.add(new RenderSystem());
		engine.systems.add(new CustomSystem());
		
		// run the engine
		new Timer(16).run = function() engine.update(16 / 1000);
	}
}

// Use metadata `@:nodes` to create a TrackingNodeList that contains nodes of entities that contains the specified components
// NodeList created this way will be cached in the engine, i.e. multiple systems will share the same NodeList instance if their Node type is the same
class MovementSystem extends System {
	// prepares a NodeList that contains entities having both the Position and Velocity components
	@:nodes var nodes:Node<Position, Velocity>;
	
	override function update(dt:Float) {
		// on each update we iterate all the nodes and update their Position components
		for(node in nodes) {
			node.position.x += node.velocity.x * dt;
			node.position.y += node.velocity.y * dt;
		}
	}
}

class RenderSystem extends System {
	// prepares a NodeList that contains entities having the Position component
	@:nodes var nodes:Node<Position>;
	
	override function update(dt:Float) {
		// on each update we iterate all the nodes and print out their positions on screen
		for(node in nodes) {
			trace('${node.entity} @ ${node.position.x}, ${node.position.y}');
		}
	}
}

// Besides using `@:nodes`, you can also create a NodeList manually
class CustomSystem extends System {
	var nodes:NodeList<CustomNode>;
	
	override function update(dt:Float) {
		for(node in nodes) {
			$type(node); // CustomNode
		}
	}
	
	override function onAdded(engine) {
		super.onAdded(engine);
		nodes = new TrackingNodeList(engine, CustomNode.new, entity -> entity.has(Position));
	}
	
	override function onRemoved(engine) {
		super.onRemoved(engine);
		nodes = null;
	}
}

// Manually declare a Node type
class CustomNode implements NodeBase {
	public var entity(default, null):Entity;
	public function new(entity) this.entity = entity;
}