{"id":19133212,"url":"https://github.com/tversteeg/chuot","last_synced_at":"2025-05-06T19:12:32.554Z","repository":{"id":197290834,"uuid":"698363868","full_name":"tversteeg/chuot","owner":"tversteeg","description":"🐭 AGPL licensed Rust game engine for 2D pixel-art games","archived":false,"fork":false,"pushed_at":"2025-04-10T23:52:29.000Z","size":6890,"stargazers_count":34,"open_issues_count":12,"forks_count":3,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-04T07:03:19.769Z","etag":null,"topics":["2d","game-engine","gamedev","rust"],"latest_commit_sha":null,"homepage":"http://tversteeg.nl/chuot/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tversteeg.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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,"zenodo":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":"tversteeg","issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"custom":null}},"created_at":"2023-09-29T18:43:06.000Z","updated_at":"2025-04-16T16:07:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"e47bc3c7-6bec-4f12-9365-d46c3c6a183e","html_url":"https://github.com/tversteeg/chuot","commit_stats":null,"previous_names":["tversteeg/pixel-game-lib","tversteeg/chuot"],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tversteeg%2Fchuot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tversteeg%2Fchuot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tversteeg%2Fchuot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tversteeg%2Fchuot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tversteeg","download_url":"https://codeload.github.com/tversteeg/chuot/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252299554,"owners_count":21725722,"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":["2d","game-engine","gamedev","rust"],"created_at":"2024-11-09T06:21:31.962Z","updated_at":"2025-05-06T19:12:32.531Z","avatar_url":"https://github.com/tversteeg.png","language":"Rust","funding_links":["https://liberapay.com/tversteeg"],"categories":[],"sub_categories":[],"readme":"# 🐭 Chuột\n\n[![Build Status](https://img.shields.io/github/actions/workflow/status/tversteeg/chuot/rust.yml?branch=main)](https://github.com/tversteeg/chuot/actions?workflow=CI)\n[![Crates.io](https://img.shields.io/crates/v/chuot.svg)](https://crates.io/crates/chuot)\n[![Documentation](https://docs.rs/chuot/badge.svg)](https://docs.rs/chuot)\n[![License: AGPL-3.0](https://img.shields.io/crates/l/chuot.svg)](#license)\n[![Dependency Status](https://deps.rs/repo/github/tversteeg/chuot/status.svg)](https://deps.rs/repo/github/tversteeg/chuot)\n[![Downloads](https://img.shields.io/crates/d/chuot.svg)](#downloads)\n[![Matrix](https://img.shields.io/badge/chat-matrix-green.svg)](https://matrix.to/#/#chuot:matrix.org)\n\n### [Website](https://tversteeg.nl/chuot/)\n\n\u003c!-- cargo-rdme start --\u003e\n\nAGPL licensed and opinionated game engine for 2D pixel-art games.\n\n#### Features\n\n- Pixel-perfect pixel art rendering with built-in rotsprite rotation shader.\n- Window creation with independent update and render game loop.\n- Hot-reloadable assets, seeing your assets update live in the game when you save them is a great boost in productivity for quickly iterating on ideas.\n- Single-binary, all non-texture assets will be embedded directly, and textures will be diced into a single atlas map embedded in the binary when deploying.\n- Simple bitmap font drawing.\n- OGG audio playback.\n- First-class gamepad support.\n- Separate managed camera systems for UI and game elements.\n\n#### Goals\n\n- Ergonomic API with a focus on quickly creating small games, especially for game jams.\n- Reasonable performance, drawing thousands of animated sprites at the same time shouldn't be a problem.\n- Proper web support, it should be very easy to bundle as WASM for the web.\n\n#### Non-Goals\n\n- An ECS (Entity component system), although an ECS architecture is great for cache locality and thus performance, I feel that it's overkill for most small games. Nothing is stopping you to add your own on top of this engine if that's what you want though!\n- 3D, this engine is only for 2D pixel art.\n- Vector graphics, similar to the above, this engine is focused specifically on pixel art with lower resolutions.\n- Reinventing the wheel for everything, when there's a proper crate with good support I prefer to use that instead of creating additional maintainer burden.\n- Support all possible file formats, this bloats the engine.\n\n#### Usage\n\nUsing this crate is quite simple, there is a single trait [`Game`] with two required functions, [`Game::update`] and [`Game::render`], that need to be implemented for a game state object.\n\n```rust\nuse chuot::{Config, Context, Game};\n\nstruct MyGame;\n\nimpl Game for MyGame {\n    fn update(\u0026mut self, ctx: Context) {\n        // ..\n    }\n\n    fn render(\u0026mut self, ctx: Context) {\n        // ..\n    }\n}\n\n// In main\n\nlet game = MyGame;\n\ngame.run(chuot::load_assets!(), Config::default());\n```\n\n#### Features\n\n##### `embed-assets`\n\nEmbed all assets into the binary when building.\n\n_Must_ be enabled when building for the web.\nIf disabled all assets will be loaded from disk.\n\nThis will dice all PNG assets into a single tiny optimized PNG atlas.\nOn startup this diced atlas will be efficiently uploaded to the GPU as a single bigger atlas, which will be used for all static sprites.\n\n##### `read-texture` (default)\n\nExpose read operations on images, if disabled sprites will be uploaded to the GPU and their data will be removed from memory.\n\n#### Install Requirements\n\nOn Linux you need to install `asound2-dev` for audio and `udev-dev` for gamepads:\n\n```sh\nsudo apt install libasound2-dev libudev-dev\n```\n\n#### Example\n\nThis example will show a window with a counter that's incremented when pressing the left mouse button[^left-mouse].\nThe counter is rendered as text[^text] loaded from a font in the top-left corner.\nWhen the 'Escape' key is pressed[^escape-key] the game will exit and the window will close.\n\n```rust\nuse chuot::{Game, Context, Config, MouseButton, KeyCode};\n\n/// Object holding all game state.\nstruct MyGame {\n  /// A simple counter we increment by clicking on the screen.\n  counter: u32,\n}\n\nimpl Game for MyGame {\n  fn update(\u0026mut self, ctx: Context) {\n    // ^1\n    // Increment the counter when we press the left mouse button\n    if ctx.mouse_pressed(MouseButton::Left) {\n      self.counter += 1;\n    }\n\n    // ^3\n    // Exit the game if 'Escape' is pressed\n    if ctx.key_pressed(KeyCode::Escape) {\n      ctx.exit();\n    }\n  }\n\n  fn render(\u0026mut self, ctx: Context) {\n    // ^2\n    // Display the counter with a font called 'font' automatically loaded from the `assets/` directory\n    // It will be shown in the top-left corner\n    ctx.text(\"font\", \u0026format!(\"Counter: {}\", self.counter)).draw();\n  }\n}\n\n// In main\n\n// Initialize the game state\nlet game = MyGame { counter: 0 };\n\n// Run the game until exit is requested\ngame.run(chuot::load_assets!(), Config::default().with_title(\"My Game\"));\n```\n\n[^left-mouse]: [`Context::mouse_pressed`]\n[^text]: [`Context::text`]\n[^escape-key]: [`Context::key_pressed`]\n\n\u003c!-- cargo-rdme end --\u003e\n\n#### Rotation Algorithms\n\nIn the library it's possible to choose between multiple upscale implementations for the single-pass RotSprite algorithm, see the Rust documentation for more information:\n\n##### Nearest Neighbor\n\nThis doesn't apply any extra rotation effects.\n\n![Nearest Neighbor](./img/nearestneighbor.png)\n\n##### cleanEdge\n\n![cleanEdge](./img/cleanedge.png)\n\n##### Scale3x (default)\n\n![Scale3x](./img/scale3x.png)\n\n##### Diag2x\n\n![Diag2x](./img/diag2x.png)\n\n##### Scale2x\n\n![Scale2x](./img/scale2x.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftversteeg%2Fchuot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftversteeg%2Fchuot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftversteeg%2Fchuot/lists"}