GithubHelp home page GithubHelp logo

scent's People

Contributors

danielkcz avatar fredyc avatar greenkeeper[bot] avatar martikaljuve avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

scent's Issues

Here's a simple TypeScript definition file for Scent.

https://github.com/martikaljuve/typed-scent

I've been using VSCode for a few months now, which has pretty nice IntelliSense for ES6 projects by using TypeScript definitions. I thought it would be nice to have auto-complete for Scent as well.

scent-intellisense

You can get it using typings (npm install typings -g) with:

typings install github:martikaljuve/typed-scent --ambient --save

This definition file was done pretty quickly and I have never used TypeScript, so expect a few issues. I don't know if I can get auto-complete to work for the dependency injections inside function-based systems and for dynamically created component properties and nodes, but at least there's completion for the engine and entities. :P

Chained setting of data to component

In the light of upcoming PR #15, this is another change that would simplify things. With component registry it's much easier to add component into entity, but since it has no data, it might be less useful. Let's improve it like this.

entity.add('personComponent')
    .set('firstName', 'Daniel')
    .set('lastName', 'K.');

This would obviously require to introduce small breaking change since entity.add returns entity instance itself. In my opinion it's not that useful as having component returned.

Adding the set method on component instance might be also seen as a step toward the future and perhaps implement some more sophisticated ways of setting component data.

What is purpose of the System?

The implementation of the System is very weak attempt to give ability to organize game code. However in the overall it creates only a confusion and doesn't help at all. There is no possibility to remove (or disable) a system once it has been added to the Engine. We cannot organize Systems in any way except controlling order they are added in. System code will be just executed once when engine.start is invoked. There is nothing else behind this.

Basically we need to call the Engine methods like onUpdate, but these can be registered anywhere as it mostly depends when we call the actual engine.update. Also async nature of the System was kinda blind shot. So far we had no need to use that and we kinda ended up creating separate modules for any async code anyway.

Do we need to include the System at all? Wouldn't be better to remove it completely and just leave this open for people to implement however they want instead of confusing them?

There is another derived issue around this - lack of cleanup methods. We can add handler to onUpdate or signup for changes in the Node (with onAdded & onRemoved), but there is no way to actually remove these handlers once they are not needed. This is kinda essential to implementing state machine which usually need to disable code that shouldn't be run anymore.

Lately I am working with ReactJS and I absolutely love the way how the components are organized, nested and updated. Of course the overall API isn't that useful for Scent needs, but I can imagine something like this (forgive me ES6 syntax):

class AnimationSystem extends Scent.System {
    systemWillStart() {
        // first time setup before first update is run
    }
    systemDidStart() {
        // first time setup after first update has run
    }
    shouldSystemUpdate() {
        // enable/disable system completely based on some state
    }
    systemWillUpdate() {
        // code before actual update 
    }
    update(timestamp) {
        // essentially just engine.onUpdate
    }
    execute() {
        return [
            SparkleSystem,
            ShadowSystem,
        ]
    }
    systemDidUpdate() {
        // code after the update
    }
    systemWillEnd() {
        // cleanup before system is going to be removed
    }
    systemDidEnd() {
        // cleanup after the system has been removed
    }
}

All these "lifecycle" methods would be completely optional, but giving great power when needed. However there is kinda a lot of work behind to implement this. Perhaps we can use ReactJS under it since version 0.14 is no longer dependent on DOM anyway. Also possibility to use the props and perhaps even the state might be useful in configuring these Systems dynamically.

Well it's all just idea and definitely need more thinking and possibly some opinions (@martikaljuve ?).

Register available component types within Engine

This is one of the biggest blind spots of the Scent which kinda makes harder to start with it. All component types has to be created and maintained outside. This makes all the interaction with Scent much more annoying and usually means something along these lines:

cMyComponent = grabComponentTypeSomewhere('myComponent');
eMyEntity.add(new cMyComponent);

What if we could do instead just this ?

eMyEntity.add('myComponent');
engine.getNodeType(['myComponent']);

To achieve this simplicity, the Engine should be used to store component types and use them whenever the actual name of component is used.

An in-range update of debug is breaking the build 🚨

Version 2.4.0 of debug just got published.

Branch Build failing 🚨
Dependency debug
Current Version 2.3.3
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

As debug is a direct dependency of this project this is very likely breaking your project right now. If other packages depend on you it’s very likely also breaking them.
I recommend you give this issue a very high priority. I’m sure you can resolve this πŸ’ͺ


Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build failed Details
Commits

The new version differs by 7 commits .

  • b82d4e6 release 2.4.0
  • 41002f1 Update bower.json (#342)
  • e58d54b Node: configurable util.inspect() options (#327)
  • 00f3046 Node: %O (big O) pretty-prints the object (#322)
  • bd9faa1 allow colours in workers (#335)
  • 501521f Use same color for same namespace. (#338)
  • e2a1955 Revert "handle regex special characters"

See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Make engine iterable over entities

Currently full entity list is exposed only as the Lill attached object. In essence this isn't very friendly to use. Let's use now standardized Iterator interface and provide that by default on engine instance allowing to use code like for (const entity of engine) or use other utilities like wu.js. This also corresponds well with exposed size property having count of entities.

An in-range update of debug is breaking the build 🚨

Version 2.5.0 of debug just got published.

Branch Build failing 🚨
Dependency debug
Current Version 2.4.5
Type dependency

This version is covered by your current version range and after updating it in your project the build failed.

As debug is a direct dependency of this project this is very likely breaking your project right now. If other packages depend on you it’s very likely also breaking them.
I recommend you give this issue a very high priority. I’m sure you can resolve this πŸ’ͺ


Status Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build failed Details
Commits

The new version differs by 26 commits .

  • 355e327 release 2.5.0
  • d85c0c6 build perf, fix for bowerfile, fix for webworker
  • dc043cf Merge pull request #378 from yamikuronue/replace-babel-with-browserify
  • f512cf2 Merge remote-tracking branch 'upstream/master' into replace-babel-with-browserify
  • f098331 Merge pull request #388 from ForsakenHarmony/patch-1
  • a16cf42 Merge pull request #387 from kribblo/patch-1
  • 78e31a8 Update browser.js
  • 408ae94 Use typeof window.process !== 'undefined'
  • f484cfe fix merge errors
  • 23af483 Merge remote-tracking branch 'upstream/master' into replace-babel-with-browserify
  • 3e1a15d fix: merged again, because it didn't take. Guess I did it wrong?
  • 67182fd fix: Whoops, didn't merge the tests properly. Resolved.
  • 2a01c6c Merged from upstream/master. I left the dist/debug in place because my hook now updates it automatically, but I removed the babelrc file since we don't need it anymore.
  • 6e934e9 Fix for #381. Moved .babelrc into test folder, where it is used and which is not built with the module. (#383)
  • 6a8d525 chore: add coveralls

There are 26 commits in total. See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

Only first Node.onAdded callback is firing?

E.g. with this:

nStructure.onAdded(function(node) {
   // first callback
});

nStructure.onAdded(function(node) {
   // second callback
});

Only the first one gets called.

Looking at src/node.coffee#L156 and lib/node.js#L154, the onAdded callback list gets cleared after the first one is called. This is probably not as intended? Moving Lill.clear added outside the for-loop made it work.

A possibly useful tip to get component properties on entities.

Just thought about this, maybe this is useful for someone. It allows you to use eDoor.door in addition to eDoor.get('door').

require('babel-runtime/core-js');
const Scent = require('scent');

const { Engine, Entity, Component, Symbols } = Scent;

// Add component properties to entity

Entity.componentAdded.notify(function(component) {
	Object.defineProperty(this, component[Symbols.bName], {
		configurable: true,
		get() { return this.get(component[Symbols.bType]); }
	});
});

Entity.componentRemoved.notify(function(componentType) {
	delete this[componentType.typeName];
});

// ----

var engine = new Engine();

var cDoor = new Component('door', 'open');
var cOnFire = new Component('onFire');

engine.registerComponent(cOnFire);

var door = new cDoor([true]);

var eDoor = engine.buildEntity([door]);

console.log('' + eDoor.door); // "Component `door` #2 [open]"
eDoor.remove(cDoor);
console.log('' + eDoor.door); // "undefined"

console.log('' + eDoor.onFire); // "undefined"
eDoor.add('onFire');
console.log('' + eDoor.onFire); // "Component `onFire` #3 []"

RunKit link: https://runkit.com/58ca44e957cfa40015693bdb/58ca44e957cfa40015693bdc/branches/master

Browser build with dependencies

Current prebuilt file in a lib folder is not that useful as it doesn't include any dependencies. Ideally we should have a separate bundle with Scent along with bundle of dependencies that can be included if they are not supplied from anywhere else.

Consider using Webpack instead of Browserify as it's much easier to configure.

Release 0.8.3

There is couple changes which should be released before #16 is done as it will introduce small breaking change. That way it would be possible to enjoy new component register without needed to make a change.

Engine update invoked before engine start

The start method is used to initialize added systems. In case the update method is called before this one, systems will not run which might be hard to understand why. Engine should guard against this and fail if trying to update when not started yet.

Allow triggering actions with no handlers?

docs/engine.md states:

Note that trying to trigger action without single registered handler will basically throw away such actions. This is to prevent stacking up in memory without anyone interested.

I thought this would mean that I can use engine.triggerAction(...) without having any engine.onAction(...) handlers, but apparently that's not allowed?

var Scent = require('scent');
var engine = new Scent.Engine();

engine.start();

var cDoor = new Scent.Component('door', 'open');
var door = new cDoor();

// no .onAction handler!

engine.triggerAction('doorOpen', door);

engine.update();

Results in:
Uncaught TypeError: Cannot read property 'length' of undefined node_modules\scent\lib\scent-browser.js:670

Using release 0.7.1 from the repository.

Make better tutorial and setup small demo

Current door closing example in README can help to understand basics, but it doesn't highlight well separate parts of framework and how they can be helpful. Also without actually seeing it in action it's somewhat hard to imagine whole functionality.

Tutorial should go from fundamentals and adding more complexity over time. Demo can be hosted on JSFiddle or something similar, although it would need to include whole Scent inline in there as we don't have a way to host it.

Triggering actions of the same type within single update

The engine.processActions method contains small attempt to catch case when action A triggers action B and that triggers action A again - within single update run. However it checks only action types, not the actual data so essentially it's not possible to trigger same action type with different data. What's even worst, it can become recursive loop itself because of the check for number of triggered action equal to 0 while in case of recursive detection it can go to negative numbers.

This definitely needs more investigation, but it would either need to compare data which (might be kinda expensive) or perhaps add some threshold after which it will be considered recursive triggering.

Any recommendations for tracking changes?

I've been trying out your framework for a few weeks now, it does indeed smell good!

I'm having trouble with finding a good solution to keep the changing values of Components in sync with external libraries, more specifically, with THREE.js.

For example, I might have a Scent.Component('position3D', 'x y z') and whenever a value in it changes, I would like to update the x/y/z values in a THREE.Vector3.

Here's the stuff I have tried:

Observing changes in the component with Object.observe (EcmaScript 7)

var Components = {
    Position3D: new Scent.Component('position3D', 'position')
};

var position = new Components.Position3D('position3D', 'x y z');
var threePosition = new THREE.Vector3();

Object.observe(position, function(changes) {
    threePosition.set(position.x, position.y, position.z);
});

this results in a TypeError: Object.observe cannot observe non-object.

Storing the Vector3 in the component

var Components = {
    Position3D: new Scent.Component('position3D', 'position')
};

var position = new Components.Position3D([
    new THREE.Vector3(10, 20, 30)
]);

engine.addEntity([position]);

engine.getNodeType([Components.Position3D]).each(function(node) {
    var object = new THREE.Object3D();

    object.position.copy(node.position3D.position); // THREE.Object3D.position doesn't have a setter, so copy values instead.
    node.position3D.position = object.position; // Update to the real reference.
});

It works, but doesn't look good.

Tracking @@Changed in a System

var bChanged = Scent.Symbols.bChanged;

var Components = {
    IsMovable: new Scent.Component('isMovable'),
    Object3D: new Scent.Component('object3D', 'object'),
    Position3D: new Scent.Component('position3D', 'x y z')
}

var nMovables = engine.getNodeType([
    Components.IsMovable,
    Components.Object3D,
    Components.Position3D
])

var movableChanged = {}; // keys are THREE.Object3D uuid-s, values are @@changed timestamps. 

nMovables.onAdded(function(node) {
    movableChanged[node.object3D.object.uuid] = node.position3D[bChanged];
});

engine.onUpdate(function() {
    nMovables.each(function (node){
        var object = node.object3D.object;
        var changed = node.position3D[bChanged];
        if (movableChanged[object.uuid] < changed) {
            movableChanged[object.uuid] = changed;

            var pos = node.position3D;
            node.object3D.object.position.set(pos.x, pos.y, pos.z);
        }
    });
});

This works, but would like to see something simpler.

Is there a good way to keep values in sync that I'm totally overlooking?

Adding an entity instead of array of components to engine?

It would be nice to have engine.addEntity(entity) in addition to engine.addEntity(components). This would be useful for creating parent-child relationships without having to add the parents to the engine first, e.g.

// ./components.js
import { Component } from 'scent';

export const cParent = new Component('parent', 'entityRef');
// ./prefabs/some_child_entity.js
import { Entity } from 'scent';
import { cParent } from '../components';

export default function SomeChildEntity(parent) {
    var entity = new Entity();

    entity.add(new cParent([parent]));
    /../

    return entity;
}
// ./index.js
import { Engine, Entity } from 'scent';
import SomeChildEntity from './prefabs/some_child_entity';

var eParent = new Entity([...]);
var eChild = new SomeChildEntity(eParent);

var engine = new Engine();

engine.addEntity(eParent);
engine.addEntity(eChild);

Right now I could achieve the same using this:

// ./index.js

/../

var engine = new Engine();

var eParent = engine.addEntity([...]);
var parent = new cParent([eParent]);
var eChild = engine.addEntity([parent, ...]);

Replacing component doesn't trigger node change events

The entity.replace is the most controversial method which isn't even included in original Ash. Initial idea was to have semantic separation to clearly see in the game code when you actually expect component being replaced. On it's own, it's not that bad idea.

Node types onAdded and onRemoved methods are not triggered if component of same type is removed and again added during same update cycle. Even methods remove && add called directly don't trigger that. In general I think it's good approach since nothing has been actually added or removed from entity.

Another point of view is that these change handlers can be used for like one time setup / cleanup of the component within entity. Node types helps to filter out group of components we are interested in and once this group is complete, it's possible to do some setup based on that. This is fine in essence, but component data should be set before being added to entity instead of relying on some node type hidden somewhere.

However there is also case we need to do setup/teardown outside of actual Engine and components, for example to create game objects to be drawn or remove them when entity no longer fulfill node group. This is completely valid point. What I don't agree with is that replacing component (meaning it got different data) should cause another setup. If we need to update game objects based on component data, this should be done exclusively during update loop.

My conclusion here is actually to not change anything as would cause problems and bad use case scenarios of Scent.

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.