{"id":16298984,"url":"https://github.com/unitoftime/ecs","last_synced_at":"2025-04-05T17:03:42.257Z","repository":{"id":57631384,"uuid":"409979765","full_name":"unitoftime/ecs","owner":"unitoftime","description":"A simple and generic ECS implementation in Go","archived":false,"fork":false,"pushed_at":"2025-03-06T13:33:56.000Z","size":361,"stargazers_count":108,"open_issues_count":1,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-29T16:03:41.231Z","etag":null,"topics":["ecs","go","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","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/unitoftime.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":"2021-09-24T13:47:35.000Z","updated_at":"2025-03-23T19:58:51.000Z","dependencies_parsed_at":"2024-02-08T20:23:53.625Z","dependency_job_id":"994651c9-087f-4060-8c4f-1c60f278deaf","html_url":"https://github.com/unitoftime/ecs","commit_stats":{"total_commits":118,"total_committers":4,"mean_commits":29.5,"dds":"0.10169491525423724","last_synced_commit":"ec18695a8c24259e372d72a31a907bedd26ef60f"},"previous_names":["jstewart7/ecs"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unitoftime%2Fecs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unitoftime%2Fecs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unitoftime%2Fecs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unitoftime%2Fecs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unitoftime","download_url":"https://codeload.github.com/unitoftime/ecs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247369953,"owners_count":20927928,"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":["ecs","go","golang"],"created_at":"2024-10-10T20:46:11.795Z","updated_at":"2025-04-05T17:03:42.226Z","avatar_url":"https://github.com/unitoftime.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Go Reference](https://pkg.go.dev/badge/github.com/unitoftime/ecs.svg)](https://pkg.go.dev/github.com/unitoftime/ecs)\n[![Build](https://github.com/unitoftime/ecs/actions/workflows/build.yml/badge.svg)](https://github.com/unitoftime/ecs/actions/workflows/build.yml)\n[![Go Coverage](https://github.com/unitoftime/ecs/wiki/coverage.svg)](https://raw.githack.com/wiki/unitoftime/ecs/coverage.html)\n\n\nThis is an ecs library I wrote for doing game development in Go. I'm actively using it and its pretty stable, but I do find bugs every once in a while. I might vary the APIs in the future if native iterators are added.\n\n### Overview\nConceptually you can imagine an ECS as one big table, where an `Id` column associates an *Entity Id* with various other component columns. Kind of like this:\n\n| Id | Position | Rotation | Size |\n|:--:|:--------:|:--------:|:----:|\n| 0  | {1, 1}   | 3.14     | 11   |\n| 1  | {2, 2}   | 6.28     | 22   |\n\nWe use an archetype-based storage mechanism. Which simply means we have a specific table for a specific component layout. This means that if you add or remove components it can be somewhat expensive, because we have to copy the entire entity to the new table.\n\n## Basic Full Example\nYou can find a fairly comprehensive example here:\n- [Basic Example](https://github.com/unitoftime/ecs/tree/master/examples/basic)\n\n\n### Basic Usage\nImport the library: `import \"github.com/unitoftime/ecs\"`\n\nCreate Components like you normally would:\n```\ntype Position struct {\n    X, Y float64\n}\n\ntype Rotation float64\n```\n\nCreate a `World` to store all of your data\n```\nworld := ecs.NewWorld()\n```\n\nCreate an entity and add components to it\n```\nid := world.NewId()\necs.Write(world, id,\n    ecs.C(Position{1, 1}),\n    ecs.C(Rotation(3.14)),\n    // Note: Try to reduce the number of write calls by packing as many components as you can in\n)\n\n// Side-Note: I'm trying to get rid of the `ecs.C(...)` boxing, but I couldn't figure out how when\n//            I first wrote the ECS. I'll try to get back to fixing that because ideally you\n//            shouldn't have to worry about it. For now though, you have to box your components\n//            to the `ecs.Component` interface type before passing them in, so `ecs.C(...)`\n//            does that for you.\n```\n\nCreate a View, by calling `QueryN`:\n```\nquery := ecs.Query2[Position, Rotation](world)\n```\n\nIterate on the query. You basically pass in a lambda, and internally the library calls it for every entity in the world which has all of the components specified. Notably your lambda takes pointer values which represent a pointer to the internally stored component. So modifying these pointers will modify the entity's data.\n```\nquery.MapId(func(id ecs.Id, pos *Position, rot *Rotation) {\n    pos.X += 1\n    pos.Y += 1\n\n    rot += 0.01\n})\n```\n\nThere are several map functions you can use, each with varying numbers of parameters. I support up to `Map12`. They all look like this:\n```\necs.MapN(world, func(id ecs.Id, a *ComponentA, /*... */, n *ComponentN) {\n    // Do your work\n})\n```\n\n### Advanced queries\nYou can also filter your queries for more advanced usage:\n```\n// Returns a view of Position and Velocity, but only if the entity also has the `Rotation` component.\nquery := ecs.Query2[Position, Velocity](world, ecs.With(Rotation))\n\n// Returns a view of Position and Velocity, but if velocity is missing on the entity, will just return nil during the `MapId(...)`. You must do nil checks for all components included in the `Optional()`!\nquery := ecs.Query2[Position, Velocity](world, ecs.Optional(Velocity))\n```\n\n### Commands\n\nCommands will eventually replace `ecs.Write(...)` once I figure out how their usage will work. Commands essentially buffer some work on the ECS so that the work can be executed later on. You can use them in loop safe ways by calling `Execute()` after your loop has completed. Right now they work like this:\n```\nworld := NewWorld()\ncmd := NewCommandQueue(world)\n\ncmd.SpawnEmpty().\n    Insert(ecs.C(Position{1, 2, 3})).\n    Insert(ecs.C(Velocity{1, 2, 3}))\n\ncmd.Execute()\n```\n\n### Still In Progress\n- [ ] Improving iterator performance: See: https://github.com/golang/go/discussions/54245\n\n### Videos\nHopefully, eventually I can have some automated test-bench that runs and measures performance, but for now you'll just have to refer to my second video and hopefully trust me. Of course, you can run the benchmark in the `bench` folder to measure how long frames take on your computer.\n\n1. How it works: https://youtu.be/71RSWVyOMEY\n2. Simulation Performance: https://youtu.be/i2gWDOgg50k\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funitoftime%2Fecs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funitoftime%2Fecs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funitoftime%2Fecs/lists"}