{"id":13571133,"url":"https://github.com/jesstelford/pecs","last_synced_at":"2025-07-25T20:10:57.103Z","repository":{"id":41446192,"uuid":"346019747","full_name":"jesstelford/pecs","owner":"jesstelford","description":"Entity Component System (ECS) for PICO-8 \u0026 Picotron","archived":false,"fork":false,"pushed_at":"2024-07-21T18:15:16.000Z","size":86,"stargazers_count":61,"open_issues_count":0,"forks_count":4,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-01-02T17:15:30.099Z","etag":null,"topics":["entity-component-system","game-development","game-library","lua","pico-8","picotron"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jesstelford.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","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":"2021-03-09T13:42:06.000Z","updated_at":"2024-12-10T19:30:56.000Z","dependencies_parsed_at":"2024-11-05T03:32:25.948Z","dependency_job_id":null,"html_url":"https://github.com/jesstelford/pecs","commit_stats":{"total_commits":16,"total_committers":2,"mean_commits":8.0,"dds":0.0625,"last_synced_commit":"bf1b6851a2460e61d74a9b60a9dd87166891ef73"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jesstelford%2Fpecs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jesstelford%2Fpecs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jesstelford%2Fpecs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jesstelford%2Fpecs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jesstelford","download_url":"https://codeload.github.com/jesstelford/pecs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233347536,"owners_count":18662683,"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":["entity-component-system","game-development","game-library","lua","pico-8","picotron"],"created_at":"2024-08-01T14:00:58.993Z","updated_at":"2025-01-10T12:43:36.523Z","avatar_url":"https://github.com/jesstelford.png","language":"Lua","funding_links":[],"categories":["Lua"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cbr\u003e\n  \u003ch1\u003ePECS\u003c/h1\u003e\n  \u003cp\u003e\n    \u003cb\u003e\u003ca href=\"https://en.wikipedia.org/wiki/Entity_component_system\"\u003eEntity Component System (ECS)\u003c/a\u003e for \u003ca href=\"https://www.lexaloffle.com/pico-8.php\"\u003ePICO-8\u003c/a\u003e \u0026 \u003ca href=\"https://www.lexaloffle.com/picotron.php\"\u003ePicotron\u003c/a\u003e in 567 tokens.\u003c/b\u003e\u003cbr /\u003e\n  \u003csup\u003e(Based on \u003ca href=\"https://www.lexaloffle.com/bbs/?uid=45947\"\u003eKatrinaKitten\u003c/a\u003e's excellent \u003ca href=\"https://www.lexaloffle.com/bbs/?tid=39021\"\u003eTiny ECS Framework v1.1\u003c/a\u003e)\u003c/sup\u003e\n  \u003c/p\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n\u003c/div\u003e\n\n## Usage\n\nEverything is part of a _World_. Create one with `pecs()`:\n\n```lua\nlocal world = pecs()\n```\n\n_Components_ describe data containers that can be instantiated:\n\n```lua\nlocal Position = world.component()\nlocal Velocity = world.component()\n```\n\nAn _Entity_ is a collection of instantiated _Components_.\n\n```lua\nlocal player = world.entity()\nplayer += Position({ x=10, y=0 })\nplayer += Velocity({ x=0, y=1 })\n```\n\nAll data within an _Entity_ can be accessed as long as you know the _Component_\nit belongs to:\n\n```lua\nprint(player[Position].x, 10, 10, 7)\n```\n\n_Systems_ allow specifying game logic (as a function) which applies to\n_Entities_ that have a certain set of _Components_ (ie; a _filter_).\n\nThe game logic function of a _System_ is executed once per matched _Entity_,\nensuring performance is maintained when there are many entities.\nThe function receives any arguments passed when calling the method. Useful for\npassing in elapsed time, etc.\n\n```lua\nlocal move = world.system({ Position, Velocity }, function(entity, ticks)\n  entity[Position].x += entity[Velocity].x * ticks\n  entity[Position].y += entity[Velocity].y * ticks\nend)\n\n-- Run the system method against all matched entities\n-- Any args passed will be available in the system callback function\nlocal ticks = 1\nmove(ticks)\n```\n\n## Example\n\n```lua\nlocal world = pecs()\n\nlocal Position = world.component()\nlocal Velocity = world.component()\n\nlocal player = world.entity({ name=\"Jess\" })\n\nplayer += Position({ x=10, y=0 })\nplayer += Velocity({ x=0, y=1 })\n\nlocal move = world.system({ Position, Velocity }, function(entity, ticks)\n  entity[Position].x += entity[Velocity].x * ticks\n  entity[Position].y += entity[Velocity].y * ticks\nend)\n\nlocal lastTime = time()\nfunction _update()\n  move(time() - lastTime)\n  lastTime = time()\nend\n\nfunction _draw()\n  cls()\n  print(player[Position].x..\" \"..player[Position].y, 10, 10, 7)\nend\n```\n\nFor more complete \u0026 practical examples, see the `example/` folder:\n\n- **[`example/particles.p8`](./example/particles.p8)**: A Particle Emitter\n  showing how to spawn entities and adding/removing components (the type of\n  Emitter) on a button press.\n- **[`example/camera-follow.p8`](./example/camera-follow.p8)**: A camera\n  follow/window technique built using Components \u0026 Systems. This example has a\n  visual representation of the \"camera\" to see the effect.\n\n## API\n\n### `pecs() =\u003e World`\n\nEverything in PECS happens within a world.\n\nCan be called multiple times to create multiple worlds:\n\n```lua\nlocal world1 = pecs()\nlocal world2 = pecs()\n```\n\nEach world has its own _Components_ and _Entities_.\n\n#### `World#update()`\n\nMust be called at the start of each `_update()` before anything else.\n\n#### `World#entity([attr[, Component, ...]]) =\u003e Entity`\n\n```lua\nlocal player = world.entity()\n\nlocal trap = world.entity({ type=\"spikes\" })\n\nlocal enemy = world.entity({}, Position({ x=10, y=10 }), Rotation({ angle=45 })\n```\n\n##### Adding a Component to an Entity\n\n```lua\nplayer += Position({ x=100, y=20 })\n```\n\n##### Removing a Component from an Entity\n\n```lua\nplayer -= Position\n```\n\n#### `World#component([defaults]) =\u003e Component`\n\n```lua\nlocal Position = world.component()\n\nlocal Drawable = world.component({ color: 8 })\n```\n\n#### `World#system(filter, callback) =\u003e Function`\n\nWhere `filter` is a table of Components, and `callback` is a function that's\npassed the entity to operate on.\n\nReturns a function that when called will execute the `callback` once per Entity\nthat contains all the specified Components.\n\nWhen executing the function, any parameters are passed through to the\n`callback`.\n\n```lua\nlocal move = world.system({ Position, Velocity }, function(entity, ticks)\n  entity[Position].x += entity[Velocity].x * ticks\n  entity[Position].y += entity[Velocity].y * ticks\nend)\n\n-- Run the system method against all matched entities\n-- Any args passed will be available in the system callback function\nlocal ticks = 1\nmove(ticks)\n```\n\nSystems efficiently maintain a list of filtered entities that is only updated\nwhen needed. It is safe to create many systems that operate over large lists of\nEntities (within PICO-8's limits).\n\n#### `World#query(filter) =\u003e Table\u003cEntity\u003e`\n\n⚠️ _It is recommended to use `World#system` (which calls `query` internally)._\n\nWhere `filter` is a table of Components, eg; `{ Position, Velocity }`.\n\nReturn a reference to a filtered table of `Entity`s. The table is automatically\nupdated when new entities match or old entities no longer match. Modifying this\ntable can cause Systems to misbehave; treat it as read-only.\n\n```lua\nlocal entities = world.query({ Position })\nfor _, ent in pairs(entities) do\n  printh(ent[Position].x .. \",\" ..ent[Position].y)\nend\n```\n\nQueries efficiently maintain a list of filtered entities that is only updated\nwhen needed. It is safe to create many queries that operate over large lists of\nEntities (within PICO-8's limits).\n\n#### `World#remove(Entity)`\n\nRemove the given entity from the world.\n\nAny Systems or Queries which previously matched this entity will no longer\noperate on it.\n\n#### `World#queue(Function)`\n\nUseful for delaying actions until the next turn of the update loop.\nParticularly when the action would modify a list that's currently being iterated\non such as removing an item due to collision, or spawning new items.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjesstelford%2Fpecs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjesstelford%2Fpecs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjesstelford%2Fpecs/lists"}