{"id":13500417,"url":"https://github.com/andygeiss/ecs","last_synced_at":"2025-04-13T08:41:15.101Z","repository":{"id":51490982,"uuid":"164319238","full_name":"andygeiss/ecs","owner":"andygeiss","description":"Build your own Game-Engine based on the Entity Component System concept in Golang.","archived":false,"fork":false,"pushed_at":"2024-09-01T11:05:58.000Z","size":406,"stargazers_count":140,"open_issues_count":0,"forks_count":11,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-04T13:15:49.117Z","etag":null,"topics":["benchmark","bitmask","bitset","entity-component-system","game-development","game-engine","game-engine-2d","go","golang","goroutine","raylib","scalability"],"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/andygeiss.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":"2019-01-06T15:18:42.000Z","updated_at":"2025-03-25T16:38:22.000Z","dependencies_parsed_at":"2024-02-10T19:29:39.508Z","dependency_job_id":"1e9d3858-b181-43f9-98ad-1243f9d7d2a3","html_url":"https://github.com/andygeiss/ecs","commit_stats":{"total_commits":155,"total_committers":6,"mean_commits":"25.833333333333332","dds":0.09677419354838712,"last_synced_commit":"0f190e9bbbeaab82437c6d7428b7e618268005ee"},"previous_names":[],"tags_count":92,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andygeiss%2Fecs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andygeiss%2Fecs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andygeiss%2Fecs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andygeiss%2Fecs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andygeiss","download_url":"https://codeload.github.com/andygeiss/ecs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248686470,"owners_count":21145496,"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":["benchmark","bitmask","bitset","entity-component-system","game-development","game-engine","game-engine-2d","go","golang","goroutine","raylib","scalability"],"created_at":"2024-07-31T22:00:59.953Z","updated_at":"2025-04-13T08:41:15.079Z","avatar_url":"https://github.com/andygeiss.png","language":"Go","readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/andygeiss/ecs/blob/master/logo.png?raw=true\" /\u003e\n\u003c/p\u003e\n\n# ECS - Entity Component System\n\n[![License](https://img.shields.io/github/license/andygeiss/ecs)](https://github.com/andygeiss/ecs/blob/master/LICENSE)\n[![Releases](https://img.shields.io/github/v/release/andygeiss/ecs)](https://github.com/andygeiss/ecs/releases)\n[![Go Report Card](https://goreportcard.com/badge/github.com/andygeiss/ecs)](https://goreportcard.com/report/github.com/andygeiss/ecs)\n[![Codacy Grade Badge](https://app.codacy.com/project/badge/Grade/b4f4c9b35f4b46d8bf19f73379864b45)](https://app.codacy.com/gh/andygeiss/ecs/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_grade)\n[![Codacy Coverage Badge](https://app.codacy.com/project/badge/Coverage/b4f4c9b35f4b46d8bf19f73379864b45)](https://app.codacy.com/gh/andygeiss/ecs/dashboard?utm_source=gh\u0026utm_medium=referral\u0026utm_content=\u0026utm_campaign=Badge_coverage)\n[![Maintainability](https://api.codeclimate.com/v1/badges/5a2fd230f2eae6f244f2/maintainability)](https://codeclimate.com/github/andygeiss/ecs/maintainability)\n\nBuild your own Game-Engine based on the Entity Component System concept in Golang.\n\n## Features\n\n- [x] Provide an **easy-to-use** framework to build a game engine from scratch.\n- [x] **No dependencies** to other modules or specific game libraries - Feel free to use what fits your needs.\n- [x] **Minimum overhead** - use only what is really needed.\n\n### Example engine\n\nSee [engine-example](https://github.com/andygeiss/engine-example) for a basic\nimplementation using [raylib](https://www.raylib.com).\n\n## Walkthrough\n\n### Project layout\n\nAt first we create a basic project layout:\n\n```bash\nmkdir ecs-example\ncd ecs-example\ngo mod init example\nmkdir components systems\n```\n\nNext we create a `main.go` with the following content:\n\n```go\npackage main\n\nimport (\n    \"github.com/andygeiss/ecs\"\n)\n\nfunc main() {\n    em := ecs.NewEntityManager()\n    sm := ecs.NewSystemManager()\n    de := ecs.NewDefaultEngine(em, sm)\n    de.Setup()\n    defer de.Teardown()\n    de.Run()\n}\n```\n\nThe execution of the program leads to an endless loop, as our engine is not yet\nable to react to user input.\n\n### The movement system\n\nA system needs to implement the methods defined by the interface\n[System](https://github.com/andygeiss/ecs/blob/master/core/system.go).\nSo we create a new file locally at `systems/movement.go`:\n\n```go\npackage systems\n\nimport (\n    \"github.com/andygeiss/ecs\"\n)\n\ntype movementSystem struct{}\n\nfunc (a *movementSystem) Process(em ecs.EntityManager) (state int) {\n    // This state simply tells the engine to stop after the first call.\n    return ecs.StateEngineStop\n}\n\nfunc (a *movementSystem) Setup() {}\n\nfunc (a *movementSystem) Teardown() {}\n\nfunc NewMovementSystem() ecs.System {\n    return \u0026movementSystem{}\n}\n```\n\nNow we can add the following lines to `main.go`:\n\n```go\nsm := ecs.NewSystemManager()\nsm.Add(systems.NewMovementSystem()) // \u003c--\nde := ecs.NewDefaultEngine(em, sm)\n```\n\nIf we start our program now, it returns immediately without looping forever.\n\n### The player entity\n\nA game engine usually processes different types of components that represent\ninformation about the game world itself. A component only represents the data,\nand the systems are there to implement the behavior or game logic and change\nthese components. Entities are simply a composition of components that provide\na scalable data-oriented architecture.\n\nA component needs to implement the methods defined by the interface\n[Component](https://github.com/andygeiss/ecs/blob/master/core/component.go).\nLet's define our `Player` components by first creating a mask at\n`components/components.go`:\n\n```go\npackage components\n\nconst (\n    MaskPosition = uint64(1 \u003c\u003c 0)\n    MaskVelocity = uint64(1 \u003c\u003c 1)\n)\n```\n\nThen create a component for `Position` and `Velocity` by creating\ncorresponding files such as `components/position.go`:\n\n```go\npackage components\n\ntype Position struct {\n    X  float32 `json:\"x\"`\n    Y  float32 `json:\"y\"`\n}\n\nfunc (a *Position) Mask() uint64 {\n    return MaskPosition\n}\n\nfunc (a *Position) WithX(x float32) *Position {\n    a.X = x\n    return a\n}\n\nfunc (a *Position) WithY(y float32) *Position {\n    a.Y = y\n    return a\n}\n\nfunc NewPosition() *Position {\n    return \u0026Position{}\n}\n```\n\nNow we can add the following lines to `main.go`:\n\n```go\nem := ecs.NewEntityManager()\nem.Add(ecs.NewEntity(\"player\", []ecs.Component{ // \u003c--\ncomponents.NewPosition().\n    WithX(10).\n    WithY(10),\ncomponents.NewVelocity().\n    WithX(100).\n    WithY(100),\n})) // --\u003e\n```\n\n### Extend the movement system\n\nOur final step is to add behavior to our movement system:\n\n```go\nfunc (a *movementSystem) Process(em ecs.EntityManager) (state int) {\n    for _, e := range em.FilterByMask(components.MaskPosition | components.MaskVelocity) {\n        position := e.Get(components.MaskPosition).(*components.Position)\n        velocity := e.Get(components.MaskVelocity).(*components.Velocity)\n        position.X += velocity.X * rl.GetFrameTime()\n        position.Y += velocity.Y * rl.GetFrameTime()\n    }\n    return ecs.StateEngineStop\n}\n```\n\nThe movement system now moves every entity which has a position and velocity component.\n\nWe can replace `ecs.StateEngineStop` with `ecs.StateEngineContinue` later if we add\nanother system to handle user input.\n\nA rendering system is also essential for a game, so you can use game libraries\nsuch as [raylib](https://www.raylib.com) or\n[SDL](https://github.com/libsdl-org/SDL).\nThis system could look like this with raylib:\n\n```go\n// ...\nfunc (a *renderingSystem) Setup() {\n    rl.InitWindow(a.width, a.height, a.title)\n}\n\nfunc (a *renderingSystem) Process(em core.EntityManager) (state int) {\n    // First check if app should stop.\n    if rl.WindowShouldClose() {\n        return core.StateEngineStop\n    }\n    // Clear the screen\n    if rl.IsWindowReady() {\n        rl.BeginDrawing()\n        rl.ClearBackground(rl.Black)\n        rl.DrawFPS(10, 10)\n        rl.EndDrawing()\n    }\n    return core.StateEngineContinue\n}\n\nfunc (a *renderingSystem) Teardown() {\n    rl.CloseWindow()\n}\n```\n","funding_links":[],"categories":["Game Development","Go","游戏开发"],"sub_categories":["Search and Analytic Databases","检索及分析资料库"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandygeiss%2Fecs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandygeiss%2Fecs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandygeiss%2Fecs/lists"}