blackdice / scent Goto Github PK
View Code? Open in Web Editor NEWNOT MAINTAINED anymore, let me know if you are interested.
License: MIT License
NOT MAINTAINED anymore, let me know if you are interested.
License: MIT License
Sometimes it might useful for some dynamic checks to actually see what is node type made of.
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.
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
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.
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 ?).
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.
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 πͺ
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.
There is a collection of frequently asked questions and of course you may always ask my humans.
Your Greenkeeper Bot π΄
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.
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 πͺ
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.
There is a collection of frequently asked questions and of course you may always ask my humans.
Your Greenkeeper Bot π΄
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.
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
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.
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.
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.
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.
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.
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.
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:
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
.
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.
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?
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, ...]);
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.