{"id":13732285,"url":"https://github.com/redxdev/ECS","last_synced_at":"2025-05-08T06:31:50.635Z","repository":{"id":41562704,"uuid":"69604895","full_name":"redxdev/ECS","owner":"redxdev","description":"C++ single-header entity component system library","archived":true,"fork":false,"pushed_at":"2020-08-19T21:14:02.000Z","size":247,"stargazers_count":460,"open_issues_count":0,"forks_count":56,"subscribers_count":23,"default_branch":"master","last_synced_at":"2024-08-04T02:10:58.563Z","etag":null,"topics":["cpp","cpp11","entity-component-system","single-header-lib"],"latest_commit_sha":null,"homepage":"","language":"C++","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/redxdev.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}},"created_at":"2016-09-29T20:24:04.000Z","updated_at":"2024-07-31T15:45:40.000Z","dependencies_parsed_at":"2022-09-21T13:20:55.442Z","dependency_job_id":null,"html_url":"https://github.com/redxdev/ECS","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redxdev%2FECS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redxdev%2FECS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redxdev%2FECS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redxdev%2FECS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/redxdev","download_url":"https://codeload.github.com/redxdev/ECS/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224708077,"owners_count":17356480,"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":["cpp","cpp11","entity-component-system","single-header-lib"],"created_at":"2024-08-03T02:01:51.888Z","updated_at":"2024-11-14T23:31:51.976Z","avatar_url":"https://github.com/redxdev.png","language":"C++","funding_links":[],"categories":["[ECS Libraries](#contents)","ECS Libraries","GameProgramming","Libraries"],"sub_categories":["Entity Component System"],"readme":"# ECS\n\nThis is a simple C++ header-only type-safe entity component system library. It makes heavy use of C++11\nconstructs, so make sure you have an up to date compiler. It isn't meant to do absolutely everything,\nso please feel free to modify it when using. There's a VS2015 solution provided, but it should\ncompile on any standard compiler with C++11 support (C++14 is more ideal, however, as it lets you use `auto`\nparameters in lambdas).\n\nAgain, this is meant for quick prototyping or as a starting point for a more advanced ECS toolkit. It works and it works well, but it isn't optimized for speed or anything.\n\nThis has been tested on the following compilers:\n\n* Visual Studio 2015 on Windows 10 (x64)\n* G++ 5.4.1 (using -std=c++11 and -std=c++14) on Ubuntu 14.04 (x64)\n\nContributions are welcome! Submit a pull request, or if you find a problem (or have a feature request) make a new issue!\n\n## Tutorial\n\nThis ECS library is based on the [Evolve Your Hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/) article. If you haven't read it, please do or else things won't make much sense. This is a data-driven entity component system library, and to know how to work with it you need to know what that entails (this is _not_ the same as Unity's components so don't expect it to be).\n\n### Your first components\n\nComponents in ECS can be any data type, but generally they'll be a struct containing some plain old data.\nFor now, let's define two components:\n\n    struct Position\n    {\n        Position(float x, float y) : x(x), y(y) {}\n        Position() : x(0.f), y(0.f) {}\n    \n        float x;\n        float y;\n    }\n    \n    struct Rotation\n    {\n        Rotation(float angle) : angle(angle) {}\n        Rotation() : angle(0) {}\n        float angle;\n    }\n\nThis isn't the most realistic example - normally you'd just have a single transform component for a game but this should\nhelp illustrate some functionality later. Also note that we don't have to do anything special for these structs to\nact as components, though there is the requirement for at least a default constructor.\n\n### Create a system\n\nNow we need some logic to act on that data. Let's make a simple gravity system:\n\n    class GravitySystem : public EntitySystem\n    {\n    public:\n        GravitySystem(float amount)\n        {\n            gravityAmount = amount;\n        }\n        \n        virtual ~GravitySystem() {}\n        \n        virtual void tick(World* world, float deltaTime) override\n        {\n            world-\u003eeach\u003cPosition\u003e([\u0026](Entity* ent, ComponentHandle\u003cPosition\u003e position) {\n                position-\u003ey += gravityAmount * deltaTime;\n            });\n        }\n        \n    private:\n        float gravityAmount;\n    }\n\nThis is a pretty standard class definition. We subclass `EntitySystem` and implement the `tick()` method. The world\nprovides the `each` method, which takes a list of component types and runs a given function (in this case a\nlambda) on every entity that has those components. Note that the lambda is passed a `ComponentHandle`, and not the\ncomponent itself.\n\n#### Alternate iteration methods\n\nIn addition to the lambda-based each, there's also an iterator-based each, made to be used with the range based for loop.\nLambda-based each isn't a true loop, and as such you can't break from it. Instead, you can use a range based for loop. The\ndownside is that it will not directly expose components as arguments, but you can combine it with `Entity::with` for a\nsimilar result:\n\n    for (Entity* ent : world-\u003eeach\u003cPosition\u003e())\n\t{\n\t    ent-\u003ewith\u003cPosition\u003e([\u0026](ComponentHandle\u003cPosition\u003e position) {\n\t\t    position-\u003ey += gravityAmount * deltaTime;\n\t\t});\n\t}\n\nAlternatively, you may retrieve a single component at a time with `Entity::get`, though this will return an invalid component\nhandle (see `ComponentHandle\u003cT\u003e::isValid` and `ComponentHandle\u003cT\u003e::operator bool()`) if there isn't a component of that type attached:\n\n    ComponentHandle\u003cPosition\u003e position = ent-\u003eget\u003cPosition\u003e();\n\tposition-\u003ey += gravityAmount * deltaTime; // this will crash if there is no position component on the entity\n\t\n`with\u003cT\u003e()` only runs the given function if the entity has the listed components. It also returns true if all components were\nfound, or false if not all components were on the entity.\n\nFinally, if you want to run a function on all entities, regardless of components, then use the `all` function in the same way\nas `each`:\n\n    world-\u003eall([](Entity* ent) {\n\t\t// do something with ent\n\t});\n\nYou may also use `all` in a range based for loop in a similar fashion to `each`.\n\n### Create the world\n\nNext, inside a `main()` function somewhere, you can add the following code to create the world, setup the system, and\ncreate an entity:\n\n    World* world = World::createWorld();\n    world-\u003eregisterSystem(new GravitySystem(-9.8f));\n    \n    Entity* ent = world-\u003ecreate();\n    ent-\u003eassign\u003cPosition\u003e(0.f, 0.f); // assign() takes arguments and passes them to the constructor\n    ent-\u003eassign\u003cRotation\u003e(35.f);\n\nNow you can call the tick function on the world in order to tick all systems that have been registered with the world:\n\n    world-\u003etick(deltaTime);\n\t\nOnce you are done with the world, make sure to destroy it (this will also deallocate the world).\n\n    world-\u003edestroyWorld();\n\t\n#### Custom Allocators\n\nYou may use any standards-compliant custom allocator. The world handles all allocations and deallocations for entities and components.\n\nIn order to use a custom allocator, define `ECS_ALLOCATOR_TYPE` before including `ECS.h`:\n\n    #define ECS_ALLOCATOR_TYPE MyAllocator\u003cEntity\u003e\n\t#include \"ECS.h\"\n\nAllocators must have a default constructor. When creating the world with `World::createWorld`, you may pass in your custom\nallocator if you need to initialize it first. Additionally, custom allocators must be rebindable via `std::allocator_traits`.\n\nThe default implementation uses `std::allocator\u003cEntity\u003e`. Note that the world will rebind allocators for different types.\n\n### Working with components\n\nYou may retrieve a component handle (for example, to print out the position of your entity) with `get`:\n\n    ComponentHandle\u003cPosition\u003e pos = ent-\u003eget\u003cPosition\u003e();\n    std::cout \u003c\u003c \"My position is \" \u003c\u003c pos-\u003ex \u003c\u003c \", \" \u003c\u003c pos-\u003ey \u003c\u003c std::endl;\n\nIf an entity doesn't have a component and you try to retrieve that type from it, `get` will return an invalid\ncomponent handle:\n\n    ComponentHandle\u003cPosition\u003e pos = otherEnt-\u003eget\u003cPosition\u003e(); // assume otherEnt doesn't have a Position component\n    pos.isValid(); // returns false, note the . instead of the -\u003e\n\nAlternatively, you may use a handle's bool conversion operator instead of `isValid`:\n\n    if (pos)\n\t{\n\t    // pos is valid\n\t}\n\telse\n\t{\n\t    // pos is not valid\n\t}\n\n### Events\n\nFor communication between systems (and with other objects outside of ECS) there is an event system. Events can be any\ntype of object, and you can subscribe to specific types of events by subclassing `EventSubscriber` and calling\n`subscribe` on the world:\n\n    struct MyEvent\n    {\n        int foo;\n        float bar;\n    }\n    \n    class MyEventSubscriber : public EventSubscriber\u003cMyEvent\u003e\n    {\n    public:\n        virtual ~MyEventSubscriber() {}\n        \n        virtual void receive(World* world, const MyEvent\u0026 event) override\n        {\n            std::cout \u003c\u003c \"MyEvent was emitted!\" \u003c\u003c std::endl;\n        }\n    }\n    \n    // ...\n    \n    MyEventSubscriber* mySubscriber = new MyEventSubscriber();\n    world-\u003esubscribe\u003cMyEvent\u003e(mySubscriber);\n    \nThen, to emit an event:\n\n    world-\u003eemit\u003cMyEvent\u003e({ 123, 45.67f }); // you can use initializer syntax if you want, this sets foo = 123 and bar = 45.67f\n\nMake sure you call `unsubscribe` or `unsubscribeAll` on your subscriber before deleting it, or else emitting the event\nmay cause a crash or other undesired behavior.\n\n### Systems and events\n\nOften, your event subscribers will also be systems. Systems have `configure` and `unconfigure` functions that are called\nwhen they are added to/removed from the world and which you may use to subscribe and unsubscribe from events:\n\n    class MySystem : public EntitySystem, public EventSubscriber\u003cMyEvent\u003e\n    {\n        // ...\n        \n        virtual void configure(World* world) override\n        {\n            world-\u003esubscribe\u003cMyEvent\u003e(this);\n        }\n        \n        virtual void unconfigure(World* world) override\n        {\n            world-\u003eunsubscribeAll(this);\n            // You may also unsubscribe from specific events with world-\u003eunsubscribe\u003cMyEvent\u003e(this), but\n            // when unconfigure is called you usually want to unsubscribe from all events.\n        }\n        \n        // ...\n    }\n\n### Built-in events\n\nThere are a handful of built-in events. Here is the list:\n\n  * `OnEntityCreated` - called when an entity has been created.\n  * `OnEntityDestroyed` - called when an entity is being destroyed (including when a world is beind deleted).\n  * `OnComponentAssigned` - called when a component is assigned to an entity. This might mean the component is new to the entity, or there's just a new assignment of the component to that entity overwriting an old one.\n  * `OnComponentRemoved` - called when a component is removed from an entity. This happens upon manual removal (via `Entity::remove()` and `Entity::removeAll()`) or upon entity destruction (which can also happen as a result of the world being destroyed).\n\n## Avoiding RTTI\n\nIf you wish to avoid using RTTI for any reason, you may define the ECS_NO_RTTI macro before including\nECS.h. When doing so, you must also add a couple of macros to your component and event types:\n\n    // in a header somewhere\n\tstruct MyComponent\n\t{\n\t\tECS_DECLARE_TYPE; // add this at the top of the structure, make sure to include the semicolon!\n\n\t\t// ...\n\t};\n\n\t// in a cpp somewhere\n\tECS_DEFINE_TYPE(MyComponent);\n\nAgain, make sure you do this with events as well.\n\nAdditionally, you will have to put the following in a cpp file:\n\n    #include \"ECS.h\"\n\tECS_TYPE_IMPLEMENTATION;\n\nIf you have any templated events, you may do the following:\n\n\ttemplate\u003ctypename T\u003e\n    struct MyEvent\n\t{\n\t\tECS_DECLARE_TYPE;\n\n\t\tT someField;\n\n\t\t// ...\n\t}\n\n\ttemplate\u003ctypename T\u003e\n\tECS_DEFINE_TYPE(MyEvent\u003cT\u003e);\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredxdev%2FECS","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredxdev%2FECS","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredxdev%2FECS/lists"}