{"id":15047905,"url":"https://github.com/wgroeneveld/gba-sprite-engine","last_synced_at":"2025-10-04T07:31:01.132Z","repository":{"id":43148490,"uuid":"143163738","full_name":"wgroeneveld/gba-sprite-engine","owner":"wgroeneveld","description":"An object-oriented Game Boy Advance sprite engine concept","archived":true,"fork":false,"pushed_at":"2022-07-06T09:51:33.000Z","size":1772,"stargazers_count":77,"open_issues_count":0,"forks_count":63,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-09-23T15:48:54.465Z","etag":null,"topics":["cplusplus-11","engine","gameboy-advance","gba","gba-game","gtest","sprites"],"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/wgroeneveld.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":"2018-08-01T14:00:42.000Z","updated_at":"2025-07-29T22:03:49.000Z","dependencies_parsed_at":"2022-09-10T00:02:01.600Z","dependency_job_id":null,"html_url":"https://github.com/wgroeneveld/gba-sprite-engine","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wgroeneveld/gba-sprite-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgroeneveld%2Fgba-sprite-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgroeneveld%2Fgba-sprite-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgroeneveld%2Fgba-sprite-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgroeneveld%2Fgba-sprite-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wgroeneveld","download_url":"https://codeload.github.com/wgroeneveld/gba-sprite-engine/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wgroeneveld%2Fgba-sprite-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278283485,"owners_count":25961309,"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-04T02:00:05.491Z","response_time":63,"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":["cplusplus-11","engine","gameboy-advance","gba","gba-game","gtest","sprites"],"created_at":"2024-09-24T21:05:55.241Z","updated_at":"2025-10-04T07:31:00.488Z","avatar_url":"https://github.com/wgroeneveld.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n⚠️⚠️ **PLEASE READ** ⚠️⚠️: this is an ARCHIVED version. I moved this to my own Git repository. See https://giveupgithub.org/ \n\nThe new URL is at https://git.brainbaking.com/wgroeneveld/gba-sprite-engine \nI will leave this here on Github since it has accumulated quite a few stars.\n\n---\n\n## A high-level object-oriented Gameboy Advance sprite engine library\n\n[![Build Status](https://travis-ci.org/wgroeneveld/gba-sprite-engine.svg?branch=master)](https://travis-ci.org/wgroeneveld/gba-sprite-engine)\n\nThat's a mouthful - let's break that down:\n\n#### High-level object-oriented\n\nThe GBA is an older piece of hardware that's missing an OS. While that is great (`while(1) {}`), it also means there's little software library support. GBA programming boils down to manipulating **memory-mapped IO** pointers. And that's not a lot of fun. \n\nInstead of writing\n\n```C\nvu16* paddle = ((volatile tile_block *)0x06000000)[0][1];\nfor(int i = 0; i \u003c 4 * sizeof(tile_4bpp) / 2; i++) {\n    paddle[i] = 0x2222;\n}\n```\n\nWouldn't it be a bit more readable if one could write\n\n```C\nauto paddle = SpriteBuilder\u003cSprite\u003e()\n    .withData(paddleTile, sizeof(paddleTile))\n    .withLocation(10, 10)\n    .buildPtr();\n```\n\nTo leverage C++11's abilities combined with a clear object-oriented approach using abstraction to hide the hexadecimal addresses? That's the objective of this engine. \n\n**Speed and size are NOT the primary concern**! We value clean readable code above speedy algorithms. This is just a proof-of-concept, intricate games with a lot of data will never work because memory limitations. That said, feel free to fork and/or provide patches.\nThe C++ cross-compiler and the GBA work, but create more metadata than the typical C cross-compiler. Take a look at the file size of the following binaries:\n\n```\n-rwxr-xr-x   1 staff    8320 Jul 25 13:54 main_c.gba\n-rwxr-xr-x   1 staff   23328 Jul 22 20:36 main_cpp.gba\n-rwxr-xr-x   1 staff   24032 Jul 25 13:55 main_cpp_stl.gba\n``` \n\nThe C++ ROM is 280% bigger than the C ROM, and if you include \u003cvector\u003e, even 288% bigger - from 8K to 23K!\nEmulating big files is not a problem, and running them on the actual hardware is still possible using cartridges like the EZ-FLASH Omega. \n\n#### GBA sprite engine\n\nThat means `MODE0`. \n\n#### library\n\nIt's compiled as a static library for your convenience. Simply link with the library and include the header path and you're all set. Take a look at the demo's `CMake` files if you're interested. \n\n## Engine features\n\nA portion of [ToncLib](https://www.coranac.com/man/tonclib/main.htm) has been used as a low-level GBA accessor. If you know what you're doing, you can safely use those, headers are in `\u003clibgba-sprite-engine/gba\u003e`. \n\nBIOS methods and Sin/Cos lookup tables are also compiled in Assembly, as `sin_lut`.\n\nDesign overview:\n\n![design](https://github.com/wgroeneveld/gba-sprite-engine/blob/master/img/design.png?raw=true)\n\nColored blocks are to be implemented in your own game. See below, in section \"implementing your game\".\n\n### Implementing your own GBA Game\n\n#### Scenes\n\nScenes contain sprites and backgrounds. You can transition between scenes with `engine-\u003esetScene(scene)` or `engine-\u003etransitionToScene(scene, effect)`. The main program bootstraps your first scene - from there on it's each scene's responsibility to load another one.\n\nThis is the layout of a main function:\n\n```C\nint main() {\n    std::shared_ptr\u003cGBAEngine\u003e engine(new GBAEngine());\n\n    auto startScene = new SampleStartScene(engine);\n    engine-\u003esetScene(startScene);\n\n    while (true) {\n        engine-\u003eupdate();\n    }\n\n    return 0;\n}\n```\n\nThat's it!\n\nTo create your own scene, subclass `Scene` and implement:\n\n1. `std::vector\u003cSprite *\u003e sprites()`: the sprites to load into VRAM\n2. `std::vector\u003cBackground *\u003e backgrounds()`: your (multilayered) backgrounds\n3. `void load()`: one-time scene loading (create your objects here)\n4. `void tick(u16 keys)`: gets called each engine `update()`\n5. set `foregroundPalette` and `backgroundPalette` in your load.\n\nLoading up a scene usually involves creating some sprites with the builder. Don't forget to set the palettes like this: `std::unique_ptr\u003cForegroundPaletteManager\u003e(new ForegroundPaletteManager(sharedPal, sizeof(sharedPal)));`\n\nThe `sprites()` method gets periodically called to check whether something has been added or deleted and updates the VRAM and OAM accordingly. **You don't need to manage anything yourself!** Take a look at demo 3.\n\nA simple fade out scene effect is implemented in demo 1, that converges the palette colors of both palettes to white. It's easy to **create your own effects** by subclassing `SceneEffect`. \n\n![sample fade out](https://github.com/wgroeneveld/gba-sprite-engine/blob/master/img/fade.gif?raw=true)\n\nSample fade effect, demo 1.\n\n#### Backgrounds\n\nScrollable backgrounds are present:\n\nCall `scroll()` in your scene update.\n\n![scroll bg](https://github.com/wgroeneveld/gba-sprite-engine/blob/master/img/scroll.gif?raw=true)\n\nSample scrolling background demo 1.\n\nCreating a background: \n\n```C\n    bg = std::unique_ptr\u003cBackground\u003e(new Background(1, background_data, sizeof(background_data), map, sizeof(map)));\n    bg-\u003euseMapScreenBlock(16);\n```\n\nBackgrounds work a bit different in VRAM compared to sprites. There are only 4 backgrounds available, and background #4 is taken by the font. Parameter 1 identifies your background used for prioritizing. Remember to use a screen block different than your map data. \n\nIf you want to create bigger maps than 32x32, use `MAPLAYOUT_64x64` or similar in the second constructor.\n\n#### Sprites\n\nConjuring sprites on the screen is a matter of exposing them to the sprites vector in your scene. Create them in your load and set them as a `std::unique_ptr` member variable in your scene so they get cleaned up automatically. \n\nCreating sprites is easy with the `SpriteBuilder`. Specify what kind of sprite you want to make as a template argument (`\u003cSprite\u003e` or `\u003cAffineSprite\u003e`) and specify your data `.with...()`. Done? Call `build()` to get a copy or `buildPtr()` to get a copy wrapped in a unique pointer. \n\n**Affine sprites can transform** using for example `rotate(angle)` - check out demo 1 or 3 for that. \n\n![rotation](https://github.com/wgroeneveld/gba-sprite-engine/blob/master/img/rotate.gif?raw=true)\n\nSample rotation demo 3.\n\n**Sprite animation is built-in**! Just feed your sprite data to the builder and use `.withAnimated(amountOfFrames, frameDelay)`. Remember to position each frame in one column in the image itself (vertically). Like this:\n\n![lama gif example](https://github.com/wgroeneveld/gba-sprite-engine/blob/master/demos/demo1-basicfeatures/img/lama.png?raw=true)\n\nUseful sprite methods:\n\n* `animate()`, `animateToFrame(x)` or `stopAnimating()`\n* `flipVertically(bool)` or `flipHorizontally(bool)`\n* `setVelocity(dx, dy)` (auto-updates) or `moveTo(x, y)`\n* `setWithinBounds(bool)` automatically keeps your sprite within the GBA resolution bounds\n* `collidesWith(otherSprite)` or `isOffScreen()`\n* Various getters like `getWidth()` etc\n\n![stay within bounds](https://github.com/wgroeneveld/gba-sprite-engine/blob/master/img/bounds.gif?raw=true)\n\nThe paddle auto-stays within bounds.\n\nEach sprite has own raw data, so there's no shared sprite image (for the better). The palette of course is shared, so think about that when exporting with a tool like [grit](https://www.coranac.com/man/grit/html/grit.htm) or [png2gba](https://github.com/IanFinlayson/png2gba). Grit has flags to export a shared palette:\n\n\u003e grit pic1.png pic2.png kul.png -ftc -pS -gB8 -O shared.c\n\nConsult the [Grit list of cmdline options](https://www.coranac.com/man/grit/html/grit.htm) for more information.\n\nExtracting a tilemap from a big image to create a splash-like intro screen can be done using these options:\n\n\u003e grit splashimage.png -gt -gB8 -mRtpf -mLs -ftc\n\nIt will export:\n\n1. A tileset. Your data, to be injected into background VRAM char blocks.\n2. A tilemap. Your metadata, to be injected into the next free background VRAM screen block.\n3. A palette. Your one and only background palette.  \n\n#### Sound\n\nThe engine supports **sounds** and **background music** using GBA's Active Sound channel A and B. It's a simple implementation meaning no mixing in either channels (but A and B are mixed).\n\nCall `engine-\u003eenqueueMusic(data, sizeof(data));` as a repeating music or `enqueueSound()` as a one-timer. If some sound is already playing, it will **not be played**. Background music auto-repeats when done. See demo 1.\n\nSound can to be converted from a RAW Signed 8-bit PCM using [raw2gba](https://github.com/IanFinlayson/raw2gba). Record something with for example [Audacity](https://www.audacityteam.org/download/) and choose file -\u003e export -\u003e export audio... to change the format. Like sprite raw data, it'll be converted into a C header that you can feed to the methods above.\n\n#### Text\n\n![default font](https://github.com/wgroeneveld/gba-sprite-engine/blob/master/engine/src/background/text.png?raw=true)\n\nThere's a **default font embedded into the engine** using the `TextStream::instance()` static instance. It takes up background #4 and claims the last background palette bank so watch out with that! \n\nUseful text manipulation:\n\n* `setText(txt, row, col)`\n* `\u003c\u003c text` or `\u003c\u003c int` etc: append to text stream for debugging purposes.\n* `setFontColor(COLOR)`: changes default color palette (white).\n* `setFontStyle(const void* data, int size)` if you prefer your own font face.\n\nChanging the font style assumes a tile width of 32 and the same symbol indexes! It also resets the font color and map so call this before doing anything else. \n\n#### Utilities\n\nThere's a game `Timer` class available using `engine-\u003egetTimer()`. It counts miliseconds, seconds, minutes and hours. \nDo not forget this is an estimate as modulo and divide operations are expensive. \n\nEach VBlank occurs every 280806 cycles (`CYCLES_PER_BLANK`), each cycle is 59.59ns\nSo, each VBlank occurs every 16.73322954 miliseconds or 16733.22954 microseconds.\nThe microseconds after the comma are rounded so irregularities are bound to occur after hours of timing..\n\n\n#### Error logging\n\nThe text stream is also used if something goes wrong, there's a macro `failure_gba(WHOOPS)` that prints file, line, method and \"exception\" message to the text stream background. \n\n### Unit Testing GBA games\n\nThe engine comes with (some) [Google Test](https://github.com/google/googletest) test cases to show you how separate classes can effectively be unit tested. I had to stub out some ARM-specific ToncLib includes, that's why the `add_definitions(-DCODE_COMPILED_AS_PART_OF_TEST)` CMake statement is there. Gtest compiles library source with `g++` and **not with the cross-compiler**! \n\n### Compiling everything\n\n#### Prerequirements\n\n1. cmake 3.12.x or higher: the cmake linker toolchain set contains a bug in .11\n2. A compiled Google Test 1.8.x or higher with `$GTEST_DIR` env. var\n3. The [DevkitPro toolchain](https://devkitpro.org/wiki/Getting_Started) installed in your `$PATH`\n4. The [mGBA emulator](https://mgba.io/downloads.html)\n\n#### Compiling with cmake\n\nThe project has been developed with CLion. The `.idea` dir is there for you to get started. The project can be imported as a cmake project. \n\nAs such, `CMake` was an easy choice. Use the following commands to build everything, including the demos:\n\n1. `mkdir cmake-build-debug \u0026\u0026 cd cmake-build-debug`\n2. `cmake ./../`\n3. `make`\n\nThe demos will be in `cmake-build-debug/demos/demoname.gba`. \n\nThings you might need to change in `CMakeLists.txt` files:\n\n1. gba-sprite-engine assumes your GBA cross-compiler is in your `$PATH`. If it's not, add an absolute path to `SET(CMAKE_C_COMPILER arm-none-eabi-gcc)` etc.\n2. gba-sprite-engine assumes your Google Test Library is compiled and in your `$GTEST_DIR` path. If not, add an absolute path to: `SET(GTEST_LIBRARY \"/Users/jefklak/CLionProjects/googletest-release-1.8.0/googletest\")`. The linker searches for 'ligbtest.a' and 'liggtest_main.a' - if you're on Linux it'll likely be a .so extension. \n3. Some Linux distributions seem to miss the default link to `pthread` that should be added manually in that case. When you see errors like \"undefined reference to 'pthread_setspecific'\" while linking Google Test, change target_link_libraries in the CMakeLists.txt file of the subdir test to: `target_link_libraries(unittest ${GTEST_LIBRARY}/build/libgtest.a ${GTEST_LIBRARY}/build/libgtest_main.a pthread)`\n\n##### Building using Windows\n\nTested and working under Windows 10. Use [MinGW](http://www.mingw.org) or Cygwin, and add the `-G \"Unix Makefiles\"` option to your `cmake ./../` command. \n\nCygwin is also a possibility, but combined with CLion the Unix and Windows path structures will clash. If using an IDE like CLion, resort to using MinGW.\n\n#### Running unit tests\n\nAfter compiling, execute the `unittest` main executable:\n\n`./cmake-build-debug/test/unittest`\n\nAnd hope for exit code 0!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwgroeneveld%2Fgba-sprite-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwgroeneveld%2Fgba-sprite-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwgroeneveld%2Fgba-sprite-engine/lists"}