{"id":18652481,"url":"https://github.com/fforw/entity","last_synced_at":"2026-04-28T15:37:58.236Z","repository":{"id":152270140,"uuid":"625641701","full_name":"fforw/entity","owner":"fforw","description":"Experimental entity component system for JavaScript with supporting babel macro","archived":false,"fork":false,"pushed_at":"2023-05-26T18:23:29.000Z","size":118,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-17T22:35:30.235Z","etag":null,"topics":["babel","babel-","babel-macro","ecs","entity-component-system","javascript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fforw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-09T18:33:06.000Z","updated_at":"2023-04-11T01:45:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"e914454a-3938-4166-88fb-460320f1955d","html_url":"https://github.com/fforw/entity","commit_stats":{"total_commits":25,"total_committers":1,"mean_commits":25.0,"dds":0.0,"last_synced_commit":"d124a0afd94a3dd9d10e69ce096fc1539d81b3ba"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/fforw/entity","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fforw%2Fentity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fforw%2Fentity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fforw%2Fentity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fforw%2Fentity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fforw","download_url":"https://codeload.github.com/fforw/entity/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fforw%2Fentity/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260648419,"owners_count":23041743,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["babel","babel-","babel-macro","ecs","entity-component-system","javascript"],"created_at":"2024-11-07T07:06:54.946Z","updated_at":"2026-04-28T15:37:53.207Z","avatar_url":"https://github.com/fforw.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @fforw/entity \n\nThe @fforw/entity package implements an experimental entity component system for JavaScript.\nIt stores its entities and components in typed arrays for quick access. \n\n## Requirements\n                                                                                   \n * Requires a babel build chain (i.e. Babel standalone, Webpack and others)\n * Requires [babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros) as dependecy and a Babel configuration that activates macros (Can work without macro \n   though that is not recommended)\n * Browser with BigInt support\n\n## Entity Macro\n\nThe entity system comes with a babel macro that provides access to the typed arrays in\na user-friendly manner. The already quick access to the typed-array is made slightly\nfaster by moving most of the access logic into compile time.\n\n### Example\n\n```javascript\n    import EntitySystem from \"@fforw/entity\"\n    import $entity from \"@fforw/entity/entity.macro\"\n\n    // ...\n\n    $entity(a =\u003e {\n        a.y = 10\n        a.health--\n    })\n\n```\n\nAlso, the variable *a* only contains a numerical entity id, the macro can provide normal \nmember access to the component props of the entity. \n\n$entity doesn't mean anything and does nothing beyond being a marker in the AST marking the limit of the macro transformation. \nThe whole thing including import gets removed from the final code.\n\nThe parameters of the arrow function are just present to mark for what variables we want the macro's member access magic \nto do it's thing. A variable \"a\" outside of $entity is exactly the same as inside, but that fact that the arrow function\nmakes it seem like it's different is actually a good thing because we can pretend that the new function comes with \ndifferently typed entity variables and our IDEs stop complaining about us trying to do member access on a number. \n                                                  \n#### Technical details\n\nThe macro will transform the above to the code below. The newly introduced *_a_T0_* contains\nthe row offset of the entity *a* within table *_array* (table 0)\n```javascript\n// ...\nlet _a_T0_ = entitySystem.e[a * 2 + 1],\n    _array = entitySystem.c0;\n_array[_a_T0_ + 2] = 10;\n_array[_a_T0_ + 4]--;\n```\n\n### Macro Config\n\nThe macro does not need to be configured and will work with the default config name.\nYou need to enable the \"babel-plugin-macros\" plugin in your babel configuration.\n\nThe macro can be configured by two configuration options in the .babelrc ( or any\nother config location suported by the cosmicconfig used by babel macros e.g. *.babel-plugin-macrosrc.json*)\n                  \nHere's an example of using the .babelrc to configure the macro:\n\n```json\n{\n    \"presets\": [\n        \"@babel/preset-react\",\n        \"@babel/preset-env\"\n    ],\n    \"plugins\": [\n        [\n            \"macros\",\n            {\n                \"entityMacro\": {\n                    \"config\": \"test/test-macro-config.json\",\n                    \"entitySystemName\" : \"system\",\n                    \"debug\": false\n                }\n            }\n        ]\n    ]\n}\n```\n                                \n#### config option\n\nThe *config* option can be used to configure an alternate config location. \n\n#### entitySystemName option\n\nThe entitySystemName changes the variable name the macro expects the entity system to be available as. \n(Default is \"entitySystem\")\n\n#### debug option\nThe *debug* option will make the macro log the generated code.\n             \n## EntitySystem Configuration\n\nThe entity system is defined by a static JSON configuration that defines all possible components\nand how to lay out the memory tables for them.\n\n### Example\n```json\n{\n    \"Components\" : {\n        \"Appearance\" : [ \"x\", \"y\", \"z\"],\n        \"Health\" : [ \"health\" ],\n        \"Marked\" : []\n    },\n\n    \"Layout\" : [\n        {\n            \"components\": [\"Appearance\", \"Health\", \"Marked\"],\n            \"size\" : 1024\n        }\n    ],\n    \n    \"entityCount\" : 1024\n}\n```\n\nThe *Components* map defines the components of the system and the unique props for\neach component. \n\nThe *Layout* array defines the memory layout of the components. Each entry defines a\ntable shared by the configured components. The size defines the initial array size in \nrows. The array will grow if that size is overstepped. In general it is recommended to\nconfigure your system to sizes that never or only rarely require growing.\n\nThe *entityCount* setting defines the initial number of entity slots. It too will grow \nand the same caveats apply.\n                                              \n*\"Marked\"* is a tag component that has no props associated with it. It needs to be \nadded to a table nevertheless. \n\n# API\n\nThe API revolves around the EntitySystem class which is created with the JSON\nconfiguration\n```javascript\nimport EntitySystem from \"@fforw/entity\"\nimport config from \"../../entity-config.json\"\nconst entitySystem = new EntitySystem(config)\n``` \n\n## newEntity()\n                                       \nCreates a new entity, optionally from a template object.\n\n```javascript\n// just the entity\nconst entity = entitySystem.newEntity()\n\n// .. or from a convenient template\nconst another = entitySystem.newEntity({\n    x: 0,\n    y: 0,\n    z: 100,\n    health: 100\n})\n``` \n                                                        \nThe properties of the optional template object must match a component definition. The components\ncorresponding to the given props will be automatically added.\n\n\n## forEach(tableIndex, mask, callback)\n\nAllows iteration over entities matching the given table index and mask.\n\n```javascript\nconst mask = entitySystem.mask([\"Appearance\", \"Health\"])\n    \n    // ...\n\nconst entity = entitySystem.forEach(0, mask, entity =\u003e {\n    // ...\n})\n``` \n\n## has(entity, components)\n\nReturns true if the given entity has the given components. Components can be given as component names \n(from any table) or as an array of numeric mask values, one for each table.\n\n\n## exists(entity)\n\nReturns true if the given entity exists currently. Note that entity ids are recycled, so if you need permanent ids, \nyou need to make that happen yourself. The entity id is only constant and unique over the lifetime of the entity.\n\n## removeEntity(entity)\n\nRemoves the given entity from the system.\n\n\n## addComponent(entity, component)\n\nAdds the given component to the given entity. \n\n\n## removeComponent(entity, component)\n\nRemoves the given component from the given entity.\n\n\n## addComponents(entity, template)\n           \nAdds the implied components to given entity and sets the properties of the given template as \ncomponent props for that entity. \n\n## getValue(entity, name) / setValue(entity, name, value)\n\nA pair of methods to read or write a single component value without using the macro.\nNote that using the macro will be slightly faster as it moves things to compile time and\ninlines the access. It also is cheaper on repeated accesses.\n                                                            \n\n## onEnter(mask, callback)\n\nDefines a callback function to be called whenever an entity enters the combination of\ncomponents expressed by the mask. It is only triggered when an entity did not have\nall the components and then gains all of them (including creation).\n                                                            \n\n## onExit(mask, callback)\n\nDefines a callback function to be called whenever an entity exits the combination of\ncomponents expressed by the mask. It is only triggered when a component had all the components\ngiven and then loses one of them.\n\n\n## mask(components)\n\nReturns a bitmask for the given component names. The bitmask functionality requires\nthat the components given are all stored in the same table. This is the general\nrule for all mask accepting methods.\n\n```javascript\nconst mask = entitySystem.mask([\"Appearance\", \"Health\"])\n``` \nThe masks are needed for some functions are meant to be reused.\n\n\n## getArrayIndex(component)\n\nReturns the property name that contains the table for the given component.\n\n```javascript\nconst property = entitySystem.getTableName(\"Appearance\")\nconst array = entitySystem[property]\n``` \n\n                                   \n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffforw%2Fentity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffforw%2Fentity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffforw%2Fentity/lists"}