Skip to content

Instantly share code, notes, and snippets.

@kenwebb
Last active April 29, 2017 09:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenwebb/05ec8432a050ad82483a65cc55a92929 to your computer and use it in GitHub Desktop.
Save kenwebb/05ec8432a050ad82483a65cc55a92929 to your computer and use it in GitHub Desktop.
Entity Component System (ECS)
<?xml version="1.0" encoding="UTF-8"?>
<!--Xholon Workbook http://www.primordion.com/Xholon/gwt/ MIT License, Copyright (C) Ken Webb, Sat Apr 29 2017 05:10:37 GMT-0400 (EDT)-->
<XholonWorkbook>
<Notes><![CDATA[
Xholon
------
Title: Entity Component System (ECS)
Description:
Url: http://www.primordion.com/Xholon/gwt/
InternalName: 05ec8432a050ad82483a65cc55a92929
Keywords:
My Notes
--------
April 26, 2017
I already use some of the ECS ideas, for example in Bestiary where cats can have from 0 to 3 Cat behaviors. Cat itself is just a container for behaviors.
I want to find out more about ECS, and compare it with Xholon.
There's a consensus that Components contain data for that aspect.
So probably System should contain an array of Components instead of an array of Entities.
TODO Do this in a separate XholonWorkbook.
Graphviz
--------
$wnd.xh.xport("Graphviz", $wnd.xh.root().parent().xpath("Chameleon/EntityComponentSystem"), '{"gvFileExt":".gv","gvGraph":"digraph","layout":"dot","edgeOp":"->","gvCluster":"","shouldShowStateMachineEntities":false,"filter":"--Behavior,Script","nameTemplateNodeId":"R^^_i^","nameTemplateNodeLabel":"R^^^^^","shouldQuoteLabels":true,"shouldShowLinks":true,"shouldShowLinkLabels":true,"shouldSpecifyLayout":false,"maxLabelLen":-1,"shouldColor":true,"defaultColor":"#f0f8ff","shouldSpecifyShape":true,"shape":"ellipse","shouldSpecifySize":true,"size":"20","shouldSpecifyFontname":true,"fontname":"\"Courier New\"","shouldSpecifyArrowhead":true,"arrowhead":"vee","shouldSpecifyStylesheet":true,"stylesheet":"Xholon.css","shouldSpecifyRankdir":true,"rankdir":"LR","shouldDisplayGraph":true,"outputFormat":"svg"}');
References
----------
(1) https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system
Entity-component system (ECS) is an architectural pattern that is mostly used in game development. An ECS follows the Composition over inheritance principle that allows greater flexibility in defining entities where every object in a game's scene is an entity (e.g. enemies, bullets, vehicles, etc.). Every Entity consists of one or more components which add additional behavior or functionality. Therefore, the behavior of an entity can be changed at runtime by adding or removing components. This eliminates the ambiguity problems of deep and wide inheritance hierarchies that are difficult to understand, maintain and extend. Common ECS approaches are highly compatible and often combined with data oriented design techniques.
(2) http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/
(3) http://t-machine.org/index.php/2007/11/11/entity-systems-are-the-future-of-mmog-development-part-2/
part 2 is an especially good intro
(4) http://t-machine.org/index.php/category/entity-systems/
(5) http://entity-systems.wikidot.com/
Component/Entity Systems (CES)
Entity System (ES)
(6) https://github.com/adngdb/entity-system-js
]]></Notes>
<_-.XholonClass>
<EntityComponentSystem/>
<!-- see [3] "What’s an Entity?"; entities just provide an identity -->
<EntityECS>
<Animal xhType="XhtypePureActiveObject">
<Cat/>
<Dog/>
<Kangaroo/>
<Fish/>
</Animal>
</EntityECS>
<!-- see [3] "What’s a Component?"; components are simple markers -->
<ComponentECS>
<MoveC>
<WalkC/>
<HopC/>
<SwimC/>
</MoveC>
<InteractC/>
</ComponentECS>
<!-- see [3] "What’s a System? (or subsystem)" -->
<SystemECS>
<InitS/> <!-- the system that initializes everything else -->
<MoveS>
<WalkS/>
<HopS/>
<SwimS/>
</MoveS>
<InteractS/>
</SystemECS>
<Entities/>
<Systems/>
<!-- places -->
<Universe/>
<Place/>
</_-.XholonClass>
<xholonClassDetails>
<Place><Color>green</Color></Place>
<Avatar><Color>orange</Color></Avatar>
<Animal implName="org.primordion.xholon.base.Avatar"><Color>rgba(255,0,0,0.8)</Color></Animal>
<Dog><Color>rgba(255,128,128,0.8)</Color></Dog>
<Kangaroo><Color>rgba(255,128,0,0.8)</Color></Kangaroo>
<MoveS implName="org.primordion.xholon.base.Behavior_gwtjs"/>
<InteractS implName="org.primordion.xholon.base.Behavior_gwtjs"/>
<WalkS><DefaultContent><![CDATA[
var me, beh = {
postConfigure: function() {
me = this.cnode;
me.ents = [];
},
act: function() {
for (var ix = 0; ix < me.ents.length; ix++) {
var ent = me.ents[ix];
var rnum = $wnd.Math.floor($wnd.Math.random() * 3);
switch(rnum) {
case 0: ent.action("go next"); break;
case 1: ent.action("go prev"); break;
case 2:
default:
break;
}
}
}
}
//# sourceURL=WalkS.js
]]></DefaultContent></WalkS>
<HopS><DefaultContent><![CDATA[
var me, beh = {
postConfigure: function() {
me = this.cnode;
me.ents = [];
},
act: function() {
for (var ix = 0; ix < me.ents.length; ix++) {
var ent = me.ents[ix];
ent.action("go prev;go prev;");
//ent.action("anim this hop"); // anim does not work when other nodes are moving; OK when app is paused
}
}
}
//# sourceURL=HopS.js
]]></DefaultContent></HopS>
<SwimS><DefaultContent><![CDATA[
var me, beh = {
postConfigure: function() {
me = this.cnode;
me.ents = [];
},
act: function() {
}
}
//# sourceURL=SwimS.js
]]></DefaultContent></SwimS>
<InteractS><DefaultContent><![CDATA[
var me, beh = {
postConfigure: function() {
me = this.cnode;
me.ents = [];
},
act: function() {
for (var ix = 0; ix < me.ents.length; ix++) {
var ent = me.ents[ix];
var other = ent.prev();
if (!other) {
other = ent.next();
}
if (other) {
me.println(ent.name() + ' says "Good day ' + other.name() + '".');
}
}
}
}
//# sourceURL=InteractS.js
]]></DefaultContent></InteractS>
</xholonClassDetails>
<EntityComponentSystem>
<Entities>
<Cat multiplicity="5">
<WalkC/>
</Cat>
<Cat roleName="Licorice">
<Color>rgba(0,128,0,0.8)</Color>
<WalkC/>
</Cat>
<Dog>
<WalkC/>
<SwimC/>
<InteractC/>
</Dog>
<Kangaroo>
<HopC/>
</Kangaroo>
</Entities>
<Systems>
<WalkS/>
<HopS/>
<SwimS/>
<InteractS/>
</Systems>
<!-- a simple linear space where entities reside -->
<Universe>
<Place multiplicity="30"/>
</Universe>
<InitS/>
<Animate duration="2" selection="#xhgraph" tweenScript="xhSvgTween_exp" xpath="EntityComponentSystem" cssStyle=".d3cpnode circle {stroke-width: 0px;} .d3cpnode[id^=&quot;place_&quot;]&gt;text {opacity: 0;}" efParams="{&quot;selection&quot;:&quot;#xhgraph&quot;,&quot;shouldIncludeDecorations&quot;:true,&quot;useIcons&quot;:false,&quot;shape&quot;:&quot;circle&quot;, &quot;iconPos&quot;:&quot;outside&quot;,&quot;sort&quot;:&quot;disable&quot;,&quot;width&quot;:600,&quot;height&quot;:600,&quot;mode&quot;:&quot;tween&quot;,&quot;labelContainers&quot;:false,&quot;includeId&quot;:true,&quot;supportTouch&quot;:true,&quot;maxChars&quot;:3}"/>
</EntityComponentSystem>
<InitSbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var me, entities, systems, universe, numPlaces, beh = {
postConfigure: function() {
me = this.cnode.parent();
entities = me.xpath("../Entities");
systems = me.xpath("../Systems");
universe = me.xpath("../Universe");
// calculate the number of places in the universe
numPlaces = 0;
var place = universe.first();
while (place) {
numPlaces++;
place = place.next();
}
//me.println("number of places in the universe: " + numPlaces);
var ent = entities.first();
while (ent) {
var nextEnt = ent.next();
ent.action("param transcript false");
this.moveComponentsToSystems(ent);
this.moveEntityToUniverse(ent);
ent = nextEnt;
}
},
/**
* Move each component to its corresponding system.
* To do this, the system will retain a reference to the component's entity.
*/
moveComponentsToSystems: function(ent) {
var comp = ent.first();
while (comp) {
var nextComp = comp.next();
var compName = comp.xhc().name();
var sysName = compName.substring(0, compName.length - 1) + "S";
var sys = systems.xpath(sysName);
sys.ents.push(ent);
comp.remove();
comp = nextComp;
}
},
/**
* Move an entity to a random place in the universe.
*/
moveEntityToUniverse: function(ent) {
var rnum = $wnd.Math.floor($wnd.Math.random() * numPlaces) + 1;
//me.println("rnum: " + rnum);
var place = universe.xpath("Place[" + rnum + "]");
//me.println(place);
place.append(ent.remove());
}
}
]]></InitSbehavior>
<SvgClient><Attribute_String roleName="svgUri"><![CDATA[data:image/svg+xml,
<svg width="100" height="50" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Universe</title>
<rect id="EntityComponentSystem/Universe" fill="#98FB98" height="50" width="50" x="25" y="0"/>
<g>
<title>Systems</title>
<rect id="EntityComponentSystem/Systems" fill="#6AB06A" height="50" width="10" x="80" y="0"/>
</g>
</g>
</svg>
]]></Attribute_String><Attribute_String roleName="setup">${MODELNAME_DEFAULT},${SVGURI_DEFAULT}</Attribute_String></SvgClient>
</XholonWorkbook>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment