{"id":18950310,"url":"https://github.com/raphadam/gelly","last_synced_at":"2025-10-25T02:48:00.246Z","repository":{"id":251696913,"uuid":"837036694","full_name":"raphadam/gelly","owner":"raphadam","description":"🕹️  2D game development library on top of Ebiten.","archived":false,"fork":false,"pushed_at":"2024-08-18T06:51:36.000Z","size":326,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-01T23:27:06.425Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raphadam.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-08-02T04:47:29.000Z","updated_at":"2024-08-18T12:38:31.000Z","dependencies_parsed_at":"2024-08-05T06:09:08.782Z","dependency_job_id":"d84d38ff-473b-466d-a47f-67f15bf95552","html_url":"https://github.com/raphadam/gelly","commit_stats":null,"previous_names":["raphadam/gelly"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/raphadam/gelly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphadam%2Fgelly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphadam%2Fgelly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphadam%2Fgelly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphadam%2Fgelly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raphadam","download_url":"https://codeload.github.com/raphadam/gelly/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raphadam%2Fgelly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280897696,"owners_count":26409960,"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","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":[],"created_at":"2024-11-08T13:22:18.969Z","updated_at":"2025-10-25T02:48:00.202Z","avatar_url":"https://github.com/raphadam.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Logo](docs/logo.png)\n\n## Introduction\n\n2D game engine built on top of Ebiten, designed to simplify the development of 2D multiplayer games in Go. Empowers developers to create high-performance games with minimal setup and useful set of features.\n\n## Features\n\n- **Cross-Platform Support:** Compile your game for desktop, web, and consoles like Nintendo Switch.\n- **Physics Engine:** Integrated physics system for smooth collision detection and movement.\n- **Animated Sprite Rendering:** Easy-to-use animated sprite system to bring your characters and environments to life.\n- **Tile Maps:** Support for large, efficient tile maps with multiple layers.\n- **Scene Management:** Manage different game states and scenes with ease.\n- **Entity Management:** Built-in entity management for handling game objects and components.\n- **Multiplayer Support:** Simplified creation of game servers with built-in support for multiple rooms or instances, including automatic message serialization over websocket.\n- **Ease of Use:** Only three interfaces need to be implemented to set up a complete project.\n\n## Architecture\n\n![Architecture Diagram](docs/graph.png)\n\n## Getting Started\n\n### Client template \n```golang\n\n// MainScene defines the main scene of the game.\nvar MainScene = gelly.Scene{\n\tName:   \"MainScene\",\n\tLayers: []gelly.Layer{\u0026GameUI{}, \u0026GameClient{}},\n}\n\nfunc main() {\n\tlog.Fatal(gelly.Run(MainScene))\n}\n\n// GameClient implements the gelly.Layer interface for the game logic.\ntype GameClient struct {\n\tcamera gelly.Camera\n\tplayer gelly.Vector2\n}\n\n// Init is called once at startup to load and set up the game.\nfunc (g *GameClient) Init(c *gelly.Client) {\n\tg.camera = gelly.NewFollowingCamera(1280, 720, 1280, 720)\n\tg.player = gelly.Vector2{X: 100, Y: 100}\n\n\t// Load files...\n\t// Set up game state...\n}\n\n// Message handles custom user messages.\nfunc (g *GameClient) Message(c *gelly.Client, msg gelly.Message) bool {\n\t// Handle user custom messages.\n\t// switch msg.(type) {\n\t// }\n\n\t// Write messages to other layers.\n\t// c.Write(gelly.Local, \u0026CustomUserMessage{})\n\n\t// Return true to capture the message.\n\treturn false\n}\n\n// Update is called periodically to update the game state.\nfunc (g *GameClient) Update(c *gelly.Client, dt time.Duration) {\n\tg.player.X += 1\n\n\tg.camera.Follow(g.player)\n}\n\n// Draw is called to render the game.\nfunc (g *GameClient) Draw(r *ebiten.Image) {\n\tsprite := gelly.Sprite{\n\t\tCentered: true,\n\t\tFlipH:    true,\n\t\tTransform: gelly.Transform{\n\t\t\tPosition: g.player,\n\t\t},\n\t\t// Image: CustomUserImg,\n\t}\n\n\t// Draw on the camera surface.\n\tsprite.Draw(g.camera.Surface)\n\n\t// Draw the camera on the ebiten image.\n\tg.camera.Draw(r)\n}\n\n// Dispose is called once on closing to clean up resources.\nfunc (g *GameClient) Dispose(c *gelly.Client) {\n}\n\n```\n\n### Server template \n```golang\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/raphadam/gelly\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tlog.Fatal(gelly.Serve(\n\t\tctx,                 // your own context\n\t\t\"127.0.0.1:9090\",    // address to serve\n\t\ttime.Millisecond*20, // initial tick rate\n\t\t\u0026GameServer{},       // your game server\n\t))\n}\n\n// GameServer manages different game rooms and dispatches incoming connections.\ntype GameServer struct {\n\tRoom1 *gelly.Room\n\tRoom2 *gelly.Room\n}\n\n// Init is called once on startup to set up state and rooms.\nfunc (g *GameServer) Init(s *gelly.Server) {\n\t// Each room runs in its own goroutine.\n\tg.Room1 = s.CreateRoom(time.Millisecond*30, \u0026GameRoom{})\n\tg.Room2 = s.CreateRoom(time.Millisecond*30, \u0026GameRoom{})\n\n\t// You can create any type of room.\n\t// g.WaitingRoom = s.CreateRoom(time.Second*5, \u0026WaitingRoom{})\n\t// g.AfkRoom = s.CreateRoom(time.Second*5, \u0026AfkRoom{})\n}\n\n// FindRoom is called when a user connects to the server.\nfunc (g *GameServer) FindRoom(s *gelly.Server, conn *gelly.Conn) {\n\t// Here, you can decide to close the incoming connection.\n\t// if reason != nil {\n\t//     conn.Close()\n\t// }\n\n\t// Or you can join a room.\n\tg.Room1.Join(conn)\n}\n\n// Tick is called periodically to update the server state.\nfunc (g *GameServer) Tick(s *gelly.Server, dt time.Duration) {\n\t// You can broadcast a message to any room.\n\t// g.Room1.Broadcast(\u0026gelly.CustomUserMessage{})\n\n\t// You can change the tick rate of a room.\n\t// g.Room1.ChangeTickrate(time.Hour * 3)\n}\n\n// LeftRoom is called when a user leaves a room or is kicked.\nfunc (g *GameServer) LeftRoom(s *gelly.Server, conn *gelly.Conn, room *gelly.Room, reason error) {\n}\n\n// Dispose is called once on server shutdown to dispose of resources.\nfunc (g *GameServer) Dispose(s *gelly.Server) {\n}\n\n// GameRoom is where you write the actual game logic.\ntype GameRoom struct {\n\tusers map[*gelly.Conn]any\n}\n\n// Init is called once to initialize the room.\nfunc (gr *GameRoom) Init(r *gelly.Room) {\n\tgr.users = make(map[*gelly.Conn]any)\n}\n\n// Join is called when a user joins the room.\nfunc (gr *GameRoom) Join(r *gelly.Room, conn *gelly.Conn) {\n\tgr.users[conn] = struct{}{}\n\n\t// You can decide here to kick the connection at any moment.\n\t// r.Kick(conn)\n}\n\n// Message is called when a user sends a message to the room.\nfunc (gr *GameRoom) Message(r *gelly.Room, conn *gelly.Conn, msg gelly.Message) {\n\t// You can type switch to get the underlying message type.\n\t// switch m := msg.(type) {\n\t// case *CustomUserMessage:\n\t// \tm.Something = 32\n\t// }\n\n\t// You can write a message to any connection concurrently.\n\t// conn.Write(\u0026CustomMessageResponse{text: \"hello\"})\n\n\t// You can update your internal game state here.\n}\n\n// Tick is called periodically to update the room state.\nfunc (gr *GameRoom) Tick(r *gelly.Room, dt time.Duration) {\n}\n\n// Left is called when a user leaves the room.\nfunc (gr *GameRoom) Left(r *gelly.Room, conn *gelly.Conn) {\n}\n\n// Dispose is called once to dispose of the room resources.\nfunc (gr *GameRoom) Dispose(r *gelly.Room) {\n}\n\n// MainScene defines the main scene of the game.\nvar MainScene = gelly.Scene{\n\tName:   \"MainScene\",\n\tLayers: []gelly.Layer{\u0026GameUI{}, \u0026GameClient{}},\n}\n\nfunc main() {\n\tlog.Fatal(gelly.Run(MainScene))\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraphadam%2Fgelly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraphadam%2Fgelly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraphadam%2Fgelly/lists"}