diff --git a/README.md b/README.md index 20c89a6..fc3b75e 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,17 @@ gameLoop() ``` +### get a single entity + +Sometimes you just want a single entity, like for singleton objects (player's character for example). + +```javascript +const h = ECS.getEntities(world, [ 'hero' ])[0] // kinda kludgy + +const h = ECS.getEntity(world, [ 'hero' ]) // same result, less typing +``` + + ### not filter Sometimes it's useful to query by components that are _not_ present: @@ -186,8 +197,6 @@ ECS.getEntities(world, [ 'test_component' ]).length // because we are not defer ``` - - ### devtools chrome extension If you'd like to see a real time view of the data in your ECS powered program, there is a dev tools extension! diff --git a/ecs.js b/ecs.js index f4ca900..08bc7eb 100644 --- a/ecs.js +++ b/ecs.js @@ -330,6 +330,21 @@ export function getEntities (world, componentNames, listenerType, listenerEntiti } +/** + * Get one entity from the world with all provided components. Optionally, + * @param {World} world + * @param {string[]} componentNames A component filter used to match entities. + * Must match all of the components in the filter. + * Can add an exclamation mark at the beginning to query by components that are not present. For example: + * `const e = ECS.getEntity(world, [ 'transform', '!hero' ])` + * + * @returns {Entity|void} one entity that matches the given filters or undefined if none match + */ +export function getEntity (world, componentNames) { + return getEntities(world, componentNames)[0] +} + + /** * returns true if an entity contains all the components that match the filter * all entities having at least one component in the ignore list are excluded. @@ -667,6 +682,7 @@ export default { addComponentToEntity, removeComponentFromEntity, getEntities, + getEntity, removeEntity, addSystem, preFixedUpdate, diff --git a/test/getEntity.js b/test/getEntity.js new file mode 100644 index 0000000..aa964b4 --- /dev/null +++ b/test/getEntity.js @@ -0,0 +1,40 @@ +import ECS from '../ecs.js' +import tap from 'tap' + + +{ + const w = ECS.createWorld() + + const h = ECS.getEntity(w, [ 'hero' ]) + + tap.equal(h, undefined, 'no match returns undefined') +} + + +{ + const w = ECS.createWorld() + + const e = ECS.createEntity(w) + ECS.addComponentToEntity(w, e, 'hero') + + const h = ECS.getEntity(w, [ 'hero' ]) + + tap.equal(h, e, 'finds 1 matching entity') +} + + +{ + const w = ECS.createWorld() + + const e = ECS.createEntity(w) + ECS.addComponentToEntity(w, e, 'hero') + ECS.addComponentToEntity(w, e, 'a') + + const e2 = ECS.createEntity(w) + ECS.addComponentToEntity(w, e2, 'hero') + ECS.addComponentToEntity(w, e2, 'b') + + const h = ECS.getEntity(w, [ 'hero' ]) + + tap.equal(h, e, 'returns the first match even if there are more than one') +} diff --git a/types/ecs.d.ts.map b/types/ecs.d.ts.map index 8d5d131..8d322cf 100644 --- a/types/ecs.d.ts.map +++ b/types/ecs.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"ecs.d.ts","sourceRoot":"","sources":["../ecs.js"],"names":[],"mappings":"AAOA;;GAEG;AAEH;;GAEG;AAEH;;;;GAIG;AAEH;;GAEG;AAEH;;GAEG;AAEH;;;;;;;;GAQG;AAEH;;;;;GAKG;AAEH;;GAEG;AAEH;;GAEG;AAEH;;;;GAIG;AAEH;;GAEG;AAEH;;;;;GAKG;AAEH;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;GAQG;AAEH;;;;;;GAMG;AACH,sCAHW,MAAM,GACJ,KAAK,CA+DjB;AAGD;;;;GAIG;AACH,oCAHW,KAAK,GACH,MAAM,CASlB;AAGD;;;;;;;GAOG;AACH,4CANW,KAAK,UACL,MAAM,iBACN,MAAM,kBACN,SAAS,GACP,IAAI,CA+BhB;AAGD;;;;;;;GAOG;AACF,iDANU,KAAK,UACL,MAAM,iBACN,MAAM,oBACN,OAAO,GACL,IAAI,CAiBhB;AAGD;;;;;;GAMG;AACF,oCALU,KAAK,UACL,MAAM,oBACN,OAAO,GACL,IAAI,CAmBhB;AAGD;;;;;;;;;;;;;GAaG;AACH,mCAZW,KAAK,kBACL,MAAM,EAAE,iBAKR,YAAY,sCAIV,MAAM,EAAE,CAwCpB;AA8BD;;;;GAIG;AACH,iCAHW,KAAK,MACL,cAAc,QA+BxB;AAED;;;;GAIG;AACH,sCAHW,KAAK,MACL,MAAM,QAUhB;AAGD;;;;GAIG;AACH,mCAHW,KAAK,MACL,MAAM,QAUhB;AAED;;;;GAIG;AACH,uCAHW,KAAK,MACL,MAAM,QAUhB;AAGD;;;;GAIG;AACH,iCAHW,KAAK,MACL,MAAM,QAUhB;AAED;;;;GAIG;AACH,8BAHW,KAAK,MACL,MAAM,QAUhB;AAED;;;;GAIG;AACH,kCAHW,KAAK,MACL,MAAM,QAUhB;AA4FD;;;GAGG;AACH,+BAFW,KAAK,QA0Df;;;;;;;;;;;;;;;;;;2BArnBa,OAAO,GAAG,SAAS;wBAInB,GAAG;;;;iCAUH,MAAM,EAAE;wCAIH,MAAM,KAAK,IAAI;;uBAKxB,oBAAoB;oBACpB,oBAAoB;wBACpB,oBAAoB;kBACpB,oBAAoB;eACpB,oBAAoB;mBACpB,oBAAoB;;qCAKjB,KAAK,KAAK,MAAM;;;;;;WAenB,WAAW;aACX,WAAW;;;;;;;;;cASX,MAAM,EAAE;;;;;gBACR,MAAM,EAAE;;;iBAMR,MAAM;;;;;;;;;;;;;;cAIL,MAAM;qBACC,MAAM;;;;;;;;;mBAKd,MAAM;;;;kBAEN,MAAM;;;cAKN,MAAM,EAAE;aACR,SAAS;aACT,MAAM,EAAE;eACR,iBAAiB;sBACjB,kBAAkB;WAClB,UAAU"} \ No newline at end of file +{"version":3,"file":"ecs.d.ts","sourceRoot":"","sources":["../ecs.js"],"names":[],"mappings":"AAOA;;GAEG;AAEH;;GAEG;AAEH;;;;GAIG;AAEH;;GAEG;AAEH;;GAEG;AAEH;;;;;;;;GAQG;AAEH;;;;;GAKG;AAEH;;GAEG;AAEH;;GAEG;AAEH;;;;GAIG;AAEH;;GAEG;AAEH;;;;;GAKG;AAEH;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;GAQG;AAEH;;;;;;GAMG;AACH,sCAHW,MAAM,GACJ,KAAK,CA+DjB;AAGD;;;;GAIG;AACH,oCAHW,KAAK,GACH,MAAM,CASlB;AAGD;;;;;;;GAOG;AACH,4CANW,KAAK,UACL,MAAM,iBACN,MAAM,kBACN,SAAS,GACP,IAAI,CA+BhB;AAGD;;;;;;;GAOG;AACF,iDANU,KAAK,UACL,MAAM,iBACN,MAAM,oBACN,OAAO,GACL,IAAI,CAiBhB;AAGD;;;;;;GAMG;AACF,oCALU,KAAK,UACL,MAAM,oBACN,OAAO,GACL,IAAI,CAmBhB;AAGD;;;;;;;;;;;;;GAaG;AACH,mCAZW,KAAK,kBACL,MAAM,EAAE,iBAKR,YAAY,sCAIV,MAAM,EAAE,CAwCpB;AA8BD;;;;GAIG;AACH,iCAHW,KAAK,MACL,cAAc,QA+BxB;AAED;;;;GAIG;AACH,sCAHW,KAAK,MACL,MAAM,QAUhB;AAGD;;;;GAIG;AACH,mCAHW,KAAK,MACL,MAAM,QAUhB;AAED;;;;GAIG;AACH,uCAHW,KAAK,MACL,MAAM,QAUhB;AAGD;;;;GAIG;AACH,iCAHW,KAAK,MACL,MAAM,QAUhB;AAED;;;;GAIG;AACH,8BAHW,KAAK,MACL,MAAM,QAUhB;AAED;;;;GAIG;AACH,kCAHW,KAAK,MACL,MAAM,QAUhB;AAoHD;;;GAGG;AACH,+BAFW,KAAK,QAyDf;;;;;;;;;;;;;;;;;;2BA5oBa,OAAO,GAAG,SAAS;wBAInB,GAAG;;;;iCAUH,MAAM,EAAE;wCAIH,MAAM,KAAK,IAAI;;uBAKxB,oBAAoB;oBACpB,oBAAoB;wBACpB,oBAAoB;kBACpB,oBAAoB;eACpB,oBAAoB;mBACpB,oBAAoB;;qCAKjB,KAAK,KAAK,MAAM;;;;;;WAenB,WAAW;aACX,WAAW;;;;;;;;;cASX,MAAM,EAAE;;;;;gBACR,MAAM,EAAE;;;iBAMR,MAAM;;;;;;;;;;;;;;cAIL,MAAM;qBACC,MAAM;;;;;;;;;mBAKd,MAAM;;;;kBAEN,MAAM;;;cAKN,MAAM,EAAE;aACR,SAAS;aACT,MAAM,EAAE;eACR,iBAAiB;sBACjB,kBAAkB;WAClB,UAAU"} \ No newline at end of file