Giter VIP home page Giter VIP logo

cog's Introduction

A Macro powered B.Y.O.E. (Bring Your Own Entity!) ECS Framework written in Haxe.

Build Status

ECS concepts & implementation inspired by exp-ecs.

Why?

As a big fan of the Macro approach and concepts in exp-ecs, I originally wrote cog as a piece of the ghost framework to provide that same kind of workflow. But as that project evolved and changed it's focus to 2D only, I found I wanted to be able to plug it's dead simple ECS implementation into any kind of project, with no extra dependencies required. So I ripped the ECS out of the ghost framework, and cog was born!

Getting Started

Cog requires Haxe 4 to run.

Install the library from haxelib:

haxelib install cog

Alternatively the dev version of the library can be installed from github:

haxelib git cog https://github.com/AustinEast/cog.git

Then include the library in your project's .hxml:

-lib cog

For OpenFL users, add this into your Project.xml:

<haxelib name="cog" />

For Kha users (who don't use haxelib), clone cog to the Libraries folder in your project root, and then add the following to your khafile.js:

project.addLibrary('cog');

Usage

Concepts

Component

A Component is an object that holds data that define an entity. Generally a component only holds variables, with little-to-no logic. Cog Components are created by implementing the IComponent into any Class, like so:

class Position implements IComponent {
  public var x:Float = 0;
  public var y:Float = 0;
}

Components

A Components object is a container that holds Component instances. This class is meant to be integrated into your own project's base object class (ie Entity, GameObject, Sprite, etc).

System

A System tracks collections of Components (as Nodes) for the purpose of performing logic on them.

Engine

The Engine is the entry point for Cog - it's main purpose is keeping track of Components objects and updating each System.

Node

A Node object keeps reference to a Components object and its relevant Component instances.

Nodes

A Nodes object tracks all the Components objects in the Engine, creating a Node for every Components object that contains all of it's required Component instances. Nodes objects are used by System objects to perform logic on Components.

Integration

A build macro is available to add custom fields to the Components class, such as an Entity class:

in build.hxml:

--macro cog.Macros.add_data("entity", "some.package.Entity")

in Main.hx:

var components = new cog.Components();
components.entity = new some.package.Entity();

This will also add a reference to the custom field into every Node instance:

class TestSystem extends System {
  @:nodes var nodes:Node<Position>;

  override function step(dt:Float) {
    super.step(dt);

    for (node in nodes) {
      // The `Entity` custom field can be accessed through the `Components` object
      trace('${node.components.entity.name}');

      // OR it can be accessed directly from the Node
      trace('${node.entity.name}');
    }
  }
}

Example

import cog.IComponent;
import cog.Components;
import cog.System;
import cog.Engine;
import cog.Node;

// Creating Component classes is as simple as implementing the `IComponent` interface.
// When the interface is implemented, the required Component fields are all added automatically.

@:structInit
class Position implements IComponent {
  public var x:Float = 0;
  public var y:Float = 0;
}

@:structInit
class Velocity implements IComponent {
  public var x:Float = 0;
  public var y:Float = 0;
}

// Plug the `Components` class into your own `Entity` class
class Entity {
  public var components:Components;
  public var name:String = '';

  public function new() {
    components = new Components();

    // Assign the Entity field on the Components instance
    // This is only available by using the integration build macro, detailed here: https://github.com/AustinEast/cog#integration
    components.entity = this;

    // Create the Position & Velocity Components, then add them to the Entity's Components instance
    var position:Position = {};
    var velocity:Velocity = {};
    components.add(position);
    components.add(velocity);
  }
}

// Create a System to randomly move Entities
class MovementSystem extends System {
  // Using the `@:nodes` metadata, create a collection of Nodes.
  // The Nodes class automatically tracks any `Components` object that has the Position and Velocity components,
  // and will create a `Node` object for each one
  @:nodes var movers:Node<Position, Velocity>;

  public function new() {
    super();

    // Two callback methods are automatically generated for each Node variable with the `@:nodes` metadata in this System
    // This callback is invoked every time a new Node is added to the `movers` Node list.
    movers_added = (node) -> {
      // Set a random velocity to each Node as it gets added to the System
      node.velocity.x = Math.random() * 200;
      node.velocity.y = Math.random() * 200;
    }
    // This callback is invoked every time a Node is removed from the `movers` Node list.
    movers_removed = (node) -> {}
  }

  // This method is called every time the Cog Engine is stepped forward by the Game Loop
  override public function step(dt:Float) {
    super.step(dt);
    for (node in movers) {
      // Increment each Node's Position by it's Velocity
      // Each Node holds reference to the `Components` object, along with a reference to each Component defined by the Nodes list
      node.position.x += node.velocity.x * dt;
      node.position.y += node.velocity.y * dt;
    }
  }
}

// Create a System to "Render" the entities
class RenderSystem extends System {
  @:nodes var nodes:Node<Position>;

  override function step(dt:Float) {
    super.step(dt);

    // Log the Entities' Positions
    for (node in nodes) {
      trace('${node.entity.name} is at position (${node.position.x}, ${node.position.y})');
    }
    trace('---------- End Frame ------------');
  }
}

class Main {
  static function main() {
    // Create an Array to hold the Game's Entities
    var entities = [];

    // Create the Cog Engine
    var engine = new Engine();

    // Define a method to remove Entities from the Game
    inline function remove_entity(entity:Entity) {
      // Remove the Entity from the Game's Entity List
      entities.remove(entity);
      // Remove the Entity's `Components` from the Cog Engine
      engine.remove_components(entity.components);
    }

    // Define a method to add Entities to the Game
    inline function add_entity(entity:Entity) {
      // Remove the Entity from the Game first, to make sure we arent adding it twice
      remove_entity(entity);
      // Add the Entity to the Game's Entity List
      entities.push(entity);
      // Add the Entity's `Components` to the Cog Engine
      engine.add_components(entity.components);
    }

    // Add some Entities in random spots
    for (i in 0...10) {
      var entity = new Entity();
      entity.name = 'Entity ${i + 1}';
      var position = entity.components.get(Position);
      if (position != null) {
        position.x = Math.random() * 1000;
        position.y = Math.random() * 1000;
      }
      add_entity(entity);
    }

    // Add the Movement and Render Systems to the Cog Engine
    engine.add_system(new MovementSystem());
    engine.add_system(new RenderSystem());

    // Simulate a Game Loop
    new haxe.Timer(16).run = () -> {
      // Update the Cog Engine.
      engine.step(16 / 1000);
    }
  }
}

Roadmap

  • Source Documentation
  • Improve Disposal

cog's People

Contributors

austineast avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

sangohan

cog's Issues

Uncaught exception - Invalid field access : components

I'm trying to pull a component from an entity, however I'm receiving this error.
Uncaught exception - Invalid field access : components

Iterating over entities in:
@:nodes var nodes:Node;

I'm pulling the component using:
node.entity.components.get(Position).body.x;

Called from ecs/systems/client/TiledEnvironmentSystem.hx line 43
Called from cog/System.hx line 44
Called from cog/Engine.hx line 16
Called from EcsManager.hx line 129
Called from Play.hx line 77
Called from Main.hx line 55
Called from openfl/events/EventDispatcher.hx line 402
Called from a C function
Called from openfl/display/DisplayObject.hx line 1399
Called from openfl/display/Stage.hx line 1159
Invalid field access : components
Called from ? line 1
Called from ApplicationMain.hx line 25
Called from ApplicationMain.hx line 130
Called from lime/app/Application.hx line 150
Called from lime/_internal/backend/native/NativeApplication.hx line 146
Called from a C function
Called from lime/_internal/backend/native/NativeApplication.hx line 370
Called from lime/_internal/macros/EventMacro.hx line 91
Called from openfl/display/Stage.hx line 1950
Called from openfl/display/Stage.hx line 1163
Called from openfl/display/Stage.hx line 1423
Called from openfl/display/Stage.hx line 1159
Called from openfl/display/DisplayObject.hx line 1399
Called from a C function
Called from openfl/events/EventDispatcher.hx line 402
Called from Main.hx line 55
Called from Play.hx line 77
Called from EcsManager.hx line 129
Called from cog/Engine.hx line 16
Called from cog/System.hx line 44
Called from ecs/systems/client/TiledEnvironmentSystem.hx line 43
Uncaught exception - Invalid field access : components

Removing while iterating skips nodes

Due to the Nodes iterator being a regular array iterator, removing individual nodes (component sets) in a loop causes the iterator to skip elements. I don't think you can currently remove back-to-back nodes in a single loop.

I think it needs a custom iterator, since you can't get around this in general without iterating backwards or managing your iteration index in case of removals. One alternative could be a linked list or something as the backing Iterable that inherently avoids the issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.