├── .gitattributes ├── LICENSE ├── README.md ├── builds ├── Engine │ └── JBlankedCustom │ │ ├── micropython_game_engine_pico.uf2 │ │ ├── micropython_game_engine_pico2.uf2 │ │ ├── micropython_game_engine_pico2_w.uf2 │ │ └── micropython_game_engine_pico_w.uf2 ├── FlipperZero │ └── vgm_game_remote.fap ├── Games │ └── VideoGameModule │ │ ├── AirLabyrinth-VGM.uf2 │ │ ├── Arkanoid-VGM.uf2 │ │ ├── Doom-VGM.uf2 │ │ ├── Doom_8bit-VGM.uf2 │ │ ├── FlappyBird-VGM.uf2 │ │ ├── FlightAssault-VGM.uf2 │ │ ├── FlipWorld-VGM.uf2 │ │ ├── FuriousBirds-VGM.uf2 │ │ ├── Hawaii-VGM.uf2 │ │ ├── Pong-VGM.uf2 │ │ ├── T-Rex-Runner-VGM.uf2 │ │ ├── Tetris-VGM.uf2 │ │ └── example_8bit-VGM.uf2 └── Screensavers │ └── VideoGameModule │ ├── aquarium-VGM.uf2 │ ├── boing_ball-VGM.uf2 │ ├── bouncing_balls-VGM.uf2 │ ├── dvi_logo_bounce-VGM.uf2 │ ├── flying_toasters-VGM.uf2 │ └── trippy_tvhost-VGM.uf2 ├── guides ├── Boards.md ├── Engine.md ├── Games.md ├── JBlankedCustom.md ├── PicoCalcKit.md ├── Screensavers.md └── VideoGameModule.md └── src ├── ArduinoIDE └── pico_game_engine │ ├── examples │ ├── AirLabyrinth │ │ ├── AirLabyrinth.ino │ │ ├── assets.hpp │ │ ├── player.cpp │ │ └── player.hpp │ ├── Arkanoid │ │ ├── Arkanoid.ino │ │ ├── flipper.cpp │ │ ├── flipper.hpp │ │ ├── player.cpp │ │ └── player.hpp │ ├── Doom │ │ ├── Doom.ino │ │ ├── assets.cpp │ │ ├── assets.hpp │ │ ├── constants.hpp │ │ ├── display.cpp │ │ ├── display.hpp │ │ ├── entities.cpp │ │ ├── entities.hpp │ │ ├── flipper.cpp │ │ ├── flipper.hpp │ │ ├── icon.cpp │ │ ├── icon.hpp │ │ ├── level.hpp │ │ ├── player.cpp │ │ ├── player.hpp │ │ ├── types.cpp │ │ └── types.hpp │ ├── Doom_8bit │ │ ├── Doom_8bit.ino │ │ ├── assets.cpp │ │ ├── assets.hpp │ │ ├── constants.hpp │ │ ├── display.cpp │ │ ├── display.hpp │ │ ├── entities.cpp │ │ ├── entities.hpp │ │ ├── flipper.cpp │ │ ├── flipper.hpp │ │ ├── icon.cpp │ │ ├── icon.hpp │ │ ├── level.hpp │ │ ├── player.cpp │ │ ├── player.hpp │ │ ├── types.cpp │ │ └── types.hpp │ ├── FlappyBird │ │ ├── FlappyBird.ino │ │ ├── assets.hpp │ │ ├── player.cpp │ │ └── player.hpp │ ├── FlightAssault │ │ ├── FlightAssault.ino │ │ ├── player.cpp │ │ └── player.hpp │ ├── FlipWorld-PROGMEM │ │ ├── FlipWorld-PROGMEM.ino │ │ ├── assets.hpp │ │ ├── icon.cpp │ │ ├── icon.hpp │ │ ├── player.cpp │ │ ├── player.hpp │ │ └── sprites.hpp │ ├── FlipWorld │ │ ├── FlipWorld.ino │ │ ├── assets.cpp │ │ ├── assets.hpp │ │ ├── icon.cpp │ │ ├── icon.hpp │ │ ├── player.cpp │ │ ├── player.hpp │ │ ├── sprites.cpp │ │ └── sprites.hpp │ ├── FuriousBirds │ │ ├── FuriousBirds.ino │ │ ├── assets.hpp │ │ ├── flipper.cpp │ │ ├── flipper.hpp │ │ ├── player.cpp │ │ └── player.hpp │ ├── Hawaii │ │ ├── Hawaii.ino │ │ └── frames.hpp │ ├── Pong │ │ ├── Pong.ino │ │ ├── player.cpp │ │ └── player.hpp │ ├── T-Rex-Runner │ │ ├── T-Rex-Runner.ino │ │ ├── assets.hpp │ │ ├── player.cpp │ │ └── player.hpp │ ├── Tetris │ │ ├── Tetris.ino │ │ ├── flipper.cpp │ │ ├── flipper.hpp │ │ ├── player.cpp │ │ └── player.hpp │ ├── example │ │ └── example.ino │ └── example_8bit │ │ └── example_8bit.ino │ ├── library.properties │ └── src │ ├── PicoGameEngine.h │ └── internal │ ├── boards.hpp │ ├── draw.cpp │ ├── draw.hpp │ ├── engine.cpp │ ├── engine.hpp │ ├── entity.cpp │ ├── entity.hpp │ ├── game.cpp │ ├── game.hpp │ ├── image.cpp │ ├── image.hpp │ ├── input.cpp │ ├── input.hpp │ ├── level.cpp │ ├── level.hpp │ ├── vector.cpp │ └── vector.hpp ├── CircuitPython ├── examples │ ├── air_labyrinth │ │ ├── air_labyrinth.py │ │ └── player.bmp │ └── example │ │ ├── Dino.bmp │ │ └── example.py └── src │ ├── adafruit_display_text │ ├── __init__.mpy │ ├── bitmap_label.mpy │ ├── label.mpy │ ├── outlined_label.mpy │ ├── scrolling_label.mpy │ └── text_box.mpy │ ├── adafruit_framebuf.mpy │ ├── adafruit_imageload │ ├── __init__.mpy │ ├── bmp │ │ ├── __init__.mpy │ │ ├── indexed.mpy │ │ ├── negative_height_check.mpy │ │ └── truecolor.mpy │ ├── displayio_types.mpy │ ├── gif.mpy │ ├── jpg.mpy │ ├── png.mpy │ ├── pnm │ │ ├── __init__.mpy │ │ ├── pbm_ascii.mpy │ │ ├── pbm_binary.mpy │ │ ├── pgm │ │ │ ├── __init__.mpy │ │ │ ├── ascii.mpy │ │ │ └── binary.mpy │ │ ├── ppm_ascii.mpy │ │ └── ppm_binary.mpy │ └── tilegrid_inflator.mpy │ ├── pico_game_engine │ ├── __init__.py │ ├── engine.py │ ├── entity.py │ ├── game.py │ ├── input.py │ ├── input_manager.py │ └── level.py │ ├── picogui │ ├── __init__.py │ ├── boards.py │ ├── desktop.py │ ├── draw.py │ ├── image.py │ ├── list.py │ ├── loading.py │ ├── menu.py │ ├── scrollbar.py │ ├── textbox.py │ └── vector.py │ └── uart.py └── FlipperZeroApp ├── app.c ├── app.png ├── application.fam ├── assets └── main.png ├── easy_flipper ├── easy_flipper.c └── easy_flipper.h └── flipper_http ├── flipper_http.c └── flipper_http.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | * linguist-vendored 4 | 5 | # Arduino IDE files as C++ 6 | src/ArduinoIDE/**/*.cpp linguist-language=cpp linguist-vendored=false 7 | src/ArduinoIDE/**/*.h linguist-language=cpp linguist-vendored=false 8 | src/ArduinoIDE/**/*.hpp linguist-language=cpp linguist-vendored=false 9 | src/ArduinoIDE/**/*.ino linguist-language=cpp linguist-vendored=false 10 | 11 | # CircuitPython files as Python 12 | src/CircuitPython/**/*.py linguist-language=python linguist-vendored=false 13 | 14 | # FlipperZero files as C 15 | src/FlipperZeroApp/**/*.c linguist-language=c linguist-vendored=false 16 | src/FlipperZeroApp/**/*.h linguist-language=c linguist-vendored=false -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## pico-game-engine 2 | 3 | Create games and animations for Raspberry Pi Pico devices. 4 | 5 | ### About 6 | 7 | This game engine leverages the [PicoDVI](https://github.com/Wren6991/PicoDVI) and [TFT\_eSPI](https://github.com/Bodmer/TFT_eSPI/tree/master) libraries. It processes user input, compresses and renders sprites (8-bit/16‑bit BMP, bytearray, and string formats), manages collisions between entities, and more. The included screensavers are compiled examples from the [PicoDVI‑Adafruit Fork](https://learn.adafruit.com/picodvi-arduino-library-video-out-for-rp2040-boards/screensavers). 8 | 9 | **Supported boards:** 10 | 11 | * [Flipper Zero Video Game Module](https://shop.flipperzero.one/products/video-game-module-for-flipper-zero) 12 | * [PicoCalc Kit](https://www.clockworkpi.com/product-page/picocalc) 13 | 14 | 15 | ### Related Videos 16 | 17 | * Derek Jamison: [https://www.youtube.com/watch?v=eJDsqlTX6Gk](https://www.youtube.com/watch?v=eJDsqlTX6Gk) 18 | * Sn0ren: [https://www.youtube.com/watch?v=gHgeCLeA5sM](https://www.youtube.com/watch?v=gHgeCLeA5sM) 19 | * JBlanked: [https://www.youtube.com/watch?v=NHhpUs5HSmQ](https://www.youtube.com/watch?v=NHhpUs5HSmQ) 20 | -------------------------------------------------------------------------------- /builds/Engine/JBlankedCustom/micropython_game_engine_pico.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Engine/JBlankedCustom/micropython_game_engine_pico.uf2 -------------------------------------------------------------------------------- /builds/Engine/JBlankedCustom/micropython_game_engine_pico2.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Engine/JBlankedCustom/micropython_game_engine_pico2.uf2 -------------------------------------------------------------------------------- /builds/Engine/JBlankedCustom/micropython_game_engine_pico2_w.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Engine/JBlankedCustom/micropython_game_engine_pico2_w.uf2 -------------------------------------------------------------------------------- /builds/Engine/JBlankedCustom/micropython_game_engine_pico_w.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Engine/JBlankedCustom/micropython_game_engine_pico_w.uf2 -------------------------------------------------------------------------------- /builds/FlipperZero/vgm_game_remote.fap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/FlipperZero/vgm_game_remote.fap -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/AirLabyrinth-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/AirLabyrinth-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/Arkanoid-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/Arkanoid-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/Doom-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/Doom-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/Doom_8bit-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/Doom_8bit-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/FlappyBird-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/FlappyBird-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/FlightAssault-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/FlightAssault-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/FlipWorld-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/FlipWorld-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/FuriousBirds-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/FuriousBirds-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/Hawaii-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/Hawaii-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/Pong-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/Pong-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/T-Rex-Runner-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/T-Rex-Runner-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/Tetris-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/Tetris-VGM.uf2 -------------------------------------------------------------------------------- /builds/Games/VideoGameModule/example_8bit-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Games/VideoGameModule/example_8bit-VGM.uf2 -------------------------------------------------------------------------------- /builds/Screensavers/VideoGameModule/aquarium-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Screensavers/VideoGameModule/aquarium-VGM.uf2 -------------------------------------------------------------------------------- /builds/Screensavers/VideoGameModule/boing_ball-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Screensavers/VideoGameModule/boing_ball-VGM.uf2 -------------------------------------------------------------------------------- /builds/Screensavers/VideoGameModule/bouncing_balls-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Screensavers/VideoGameModule/bouncing_balls-VGM.uf2 -------------------------------------------------------------------------------- /builds/Screensavers/VideoGameModule/dvi_logo_bounce-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Screensavers/VideoGameModule/dvi_logo_bounce-VGM.uf2 -------------------------------------------------------------------------------- /builds/Screensavers/VideoGameModule/flying_toasters-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Screensavers/VideoGameModule/flying_toasters-VGM.uf2 -------------------------------------------------------------------------------- /builds/Screensavers/VideoGameModule/trippy_tvhost-VGM.uf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/builds/Screensavers/VideoGameModule/trippy_tvhost-VGM.uf2 -------------------------------------------------------------------------------- /guides/Boards.md: -------------------------------------------------------------------------------- 1 | # Boards 2 | This project supports several Raspberry Pi Pico (RP2040/RP2350) based products, such as Flipper Device's Video Game Module. It also supports DIY projects that make use of similar drivers. 3 | 4 | ## Video Game Module 5 | This device by Flipper Devices has a DVI port and uses the PicoDVI library. More details [here](https://github.com/jblanked/pico-game-engine/tree/main/guides/VideoGameModule.md). 6 | 7 | ## PicoCalc Kit 8 | This device by ClockworkPi has a SPI interface and uses the ILI9488 driver. More details [here](https://github.com/jblanked/pico-game-engine/tree/main/guides/PicoCalcKit.md). 9 | 10 | ## JBlanked Custom 11 | A custom Raspberry Pi Pico setup with a SPI interface that uses the ILI9341 driver. More details [here](https://github.com/jblanked/pico-game-engine/tree/main/guides/JBlankedCustom.md). -------------------------------------------------------------------------------- /guides/Engine.md: -------------------------------------------------------------------------------- 1 | # Engine 2 | 3 | The engine is available for the Arduino IDE (C++), CircuitPython, and C/C++ (using the official SDK). Each environment offers similar configurations, methods, and functionalities to ensure a smooth development experience. 4 | 5 | ### Arduino IDE 6 | 7 | Use the Arduino IDE library to create and manage games. Copy the `pico_game_engine` folder from the `src/ArduinoIDE` directory into your Arduino `libraries` folder (usually `~/Documents/Arduino/libraries`). Then, add this at the top of your `.ino` file: 8 | 9 | ```cpp 10 | #include "PicoGameEngine.h" 11 | ``` 12 | 13 | Review the included examples to learn how to use the library. 14 | 15 | ### CircuitPython 16 | 17 | Developers can also use CircuitPython to create and manage games, although due to limited memory, they must be more conservative with memory usage. Perform all heap allocations before the engine starts. 18 | 19 | 1. Download and install Thonny IDE (https://thonny.org). 20 | 2. Download this repository as a .zip file (available here: https://github.com/jblanked/pico-game-engine/archive/refs/heads/main.zip). 21 | 3. Press and hold the `BOOT` button on your device for 2 seconds. 22 | 4. While holding the `BOOT` button, connect the device to your computer using a USB cable. 23 | 5. Open Thonny, and in the bottom-right corner, select `Install CircuitPython`. 24 | 6. Change the settings to your specific Raspberry Pi Pico type and proceed with the installation. 25 | 7. Once finished, close the window and press the red `Stop/Restart` button. 26 | 8. Afterwards, open the .zip file downloaded earlier and navigate to the `src/CircuitPython/src` folder. Copy and paste all contents of that folder into the `lib` folder in your `CIRCUITPY` drive that appeared after CircuitPython finished installing. 27 | 9. Inside the .zip file (in the `src/CircuitPython/examples` folder), there are examples that you can copy and paste into your `CIRCUITPY` drive. These should be in your root directory (or optionally the `/sd` directory). The `.bmp` assets should be placed in the `/sd` directory. 28 | 10. In Thonny, press the `Stop/Restart` button again, then double-click the example you added to your `CIRCUITPY` drive and click the green `Start/Run` button. 29 | 30 | ### C/C++ SDK 31 | 32 | This integration is not yet available. -------------------------------------------------------------------------------- /guides/Games.md: -------------------------------------------------------------------------------- 1 | # Games 2 | Developers can create their own games and animations using the Pico Game Engine. The compiled examples use default input and board configurations. 3 | 4 | ### Installing Compiled Examples 5 | There are two installation methods; choose the one that is most convenient for you: 6 | 7 | **Universal Method** 8 | 1. Download one of the examples from the `builds/Games` directory. 9 | 2. Press and hold the `BOOT` button on your device for 2 seconds. 10 | 3. While holding the `BOOT` button, connect the device to your computer using a USB cable. 11 | 4. Drag and drop the downloaded file onto the device. It will automatically reboot and begin running the example. 12 | 13 | **Video Game Module (Only)** 14 | 1. Install the Video Game Module Tool app on your Flipper Zero from the Apps catalog: [Video Game Module Tool](https://lab.flipper.net/apps/video_game_module_tool). 15 | 2. Download one of the examples from the `builds/Games` directory. 16 | 3. Disconnect your Video Game Module from your Flipper and connect your Flipper to your computer. 17 | 4. Open qFlipper. 18 | 5. Navigate to the `SD Card/apps_data/vgm/` directory. If the folder doesn’t exist, create it. Inside that folder, create a folder named `Engine`. 19 | 6. Drag the file you downloaded earlier into the `Engine` directory. 20 | 7. Disconnect your Flipper from your computer, then turn it off. 21 | 8. Connect your Video Game Module to your Flipper, then turn your Flipper on. 22 | 9. Open the Video Game Module Tool app on your Flipper. It should be located in the `Apps -> Tools` folder from the main menu. 23 | 10. In the Video Game Module Tool app, select `Install Firmware from File`, then choose `apps_data`. 24 | 11. Scroll down and click on the `vgm` folder, then the `Engine` folder, and finally select the `.uf2` file. 25 | 12. The app will begin flashing the firmware to your Video Game Module. Wait until the process is complete. -------------------------------------------------------------------------------- /guides/JBlankedCustom.md: -------------------------------------------------------------------------------- 1 | ## About 2 | A custom Raspberry Pi Pico setup. 3 | 4 | ## Materials 5 | - Raspberry Pi Pico (any variant) 6 | - 2.4-inch SPI TFT LCD Display ILI9341 Touch Panel 240x320 7 | - HW504 joystick (optional) 8 | - Push button switches 9 | 10 | ## TFT to Pico Wiring 11 | - VCC -> VSYS (Pin 39) 12 | - GND -> GND 13 | - CS -> GP5 (Pin 7) 14 | - RESET -> GP10 (Pin 14) 15 | - DC -> GP11 (Pin 15) 16 | - SDI/MOSI -> GP7 (Pin 10) 17 | - SCK -> GP6 (Pin 9) 18 | - LED -> VSYS (Pin 39) 19 | - SDO/MISO -> GP4 (Pin 6) 20 | - SD_SCK -> GP14 (Pin 19) 21 | - SD_MISO -> GP12 (Pin 16) 22 | - SD_MOSI -> GP15 (Pin 20) 23 | - SD_CS -> GP13 (Pin 17) 24 | 25 | ## HW504 to Pico Wiring 26 | - SW -> GP17 (Pin 22 - TX) 27 | - VRx -> GP27 (Pin 32 - ADC1) 28 | - VRy -> GP26 (Pin 31 - ADC0) 29 | - GND -> GND 30 | - 5V -> VSYS (Pin 39) 31 | 32 | ## Button Mapping (Pico) Wiring 33 | - GP16 (Pin 21) -> UP 34 | - GP17 (Pin 22) -> RIGHT 35 | - GP18 (Pin 24) -> DOWN 36 | - GP19 (Pin 25) -> LEFT -------------------------------------------------------------------------------- /guides/PicoCalcKit.md: -------------------------------------------------------------------------------- 1 | ## About 2 | A "retro" handle-held device powered by Raspberry Pi Pico Devices created by ClockworkPi: https://www.clockworkpi.com/picocalc 3 | 4 | -------------------------------------------------------------------------------- /guides/Screensavers.md: -------------------------------------------------------------------------------- 1 | # Screensavers 2 | The current screensavers are compiled examples from the [PicoDVI-Adafruit Fork](https://learn.adafruit.com/picodvi-arduino-library-video-out-for-rp2040-boards/screensavers). 3 | 4 | ### Installation 5 | Here are the installation methods; choose the one that is most convenient for you: 6 | 7 | **Universal Method** 8 | 1. Download your preferred screensaver from the `builds/Screensavers` directory. 9 | 2. Press and hold the `BOOT` button on your device for 2 seconds. 10 | 3. While holding the `BOOT` button, connect the device to your computer via USB. A new device named `RPI-RP2` should appear in your device list. 11 | 4. Drag and drop the downloaded file onto the device. The device will automatically disconnect after the file transfer completes. 12 | 13 | **Video Game Module (Only)** 14 | 1. Install the Video Game Module Tool app on your Flipper Zero from the Apps catalog: [Video Game Module Tool](https://lab.flipper.net/apps/video_game_module_tool). 15 | 2. Download all screensavers from the `builds/Screensavers` directory. 16 | 3. Disconnect your Video Game Module from your Flipper and connect your Flipper to your computer. 17 | 4. Open qFlipper. 18 | 5. Navigate to the `SD Card/apps_data/vgm/` directory. If the folder doesn’t exist, create it. 19 | 6. Drag all screensavers you downloaded earlier into the `vgm` directory. 20 | 7. Once completed, disconnect your Flipper from your computer, then turn it off. 21 | 8. Connect your Video Game Module to your Flipper, then turn your Flipper on. 22 | 9. Open the Video Game Module Tool app on your Flipper. It should be located in the `Apps -> Tools` folder from the main menu. 23 | 10. In the Video Game Module Tool app, select `Install Firmware from File`, then choose `apps_data`. 24 | 11. Scroll down and click on the `vgm` folder and then select the `.uf2` file. 25 | 12. The app will begin flashing the firmware to your Video Game Module. Wait until the process is complete. Afterwards, connect your HDMI display and power on your Video Game Module to view the screensaver. 26 | 27 | -------------------------------------------------------------------------------- /guides/VideoGameModule.md: -------------------------------------------------------------------------------- 1 | ## About 2 | The Video Game Module is a device by Flipper Devices, released in 2024. Initially designed for screen mirroring the Flipper Zero’s display to a TV or monitor, it includes a built-in gyroscope and accelerometer, a USB-C port, a 14-pin GPIO header, and a DVI port. Learn more: https://shop.flipperzero.one/products/video-game-module-for-flipper-zero 3 | 4 | ## Game Usage 5 | To get started, flash your Video Game Module with an example or your own custom script, then install the companion app on your Flipper Zero. The module handles video output and the game engine, while the Flipper Zero serves as a controller—reversing their traditional roles. Developers can create new games or port existing Flipper titles, taking advantage of increased memory, full-color graphics, and output to an external display. 6 | 7 | ### Flipper Zero App 8 | The Flipper Zero app lets you use the D-pad as input for games built with the engine. The source code resides in `src/FlipperZeroApp`. Use [ufbt](https://github.com/flipperdevices/flipperzero-ufbt) to compile it into a `.fap` file, or install it directly on your Flipper Zero from https://lab.flipper.net/apps/vgm_game_remote 9 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/AirLabyrinth/AirLabyrinth.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "assets.hpp" 4 | #include "player.hpp" 5 | // Translated from https://github.com/jamisonderek/flipper-zero-tutorials/tree/main/vgm/apps/air_labyrinth 6 | // All credits to Derek Jamison 7 | /* 8 | Board Manager: Raspberry Pi Pico 9 | Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 10 | CPU Speed: 200MHz 11 | */ 12 | auto board = VGMConfig; // Video Game Module Configuration 13 | void setup() 14 | { 15 | // Setup file system (must be called in setup) 16 | setup_fs(); 17 | 18 | // Create the game instance with its name, start/stop callbacks, and colors. 19 | Game *game = new Game( 20 | "VGM Game Engine", // Game name 21 | Vector(board.width, board.height), // Game size 22 | NULL, // start callback 23 | NULL, // stop callback 24 | TFT_RED, // Foreground color 25 | TFT_WHITE, // Background color 26 | true, // Use 8-bit graphics? 27 | board, // Board configuration 28 | true // Use double buffering for TFT 29 | ); 30 | 31 | // set world size 32 | game->world_size = Vector(board.width, board.height); 33 | 34 | // UART buttons 35 | ButtonUART *uart = new ButtonUART(); 36 | 37 | // Add input buttons 38 | game->input_add(new Input(uart)); 39 | 40 | // Create and add a level to the game. 41 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 42 | game->level_add(level); 43 | 44 | // set game position to center of player 45 | game->pos = Vector(board.width / 2, (board.height / 2) + 10); 46 | game->old_pos = game->pos; 47 | 48 | // Add walls to the level 49 | wall_spawn(level); 50 | 51 | // Add the player entity to the level 52 | player_spawn(level); 53 | 54 | // Create the game engine (with 30 frames per second target). 55 | GameEngine *engine = new GameEngine("VGM Game Engine", 30, game); 56 | 57 | // Run the game engine's main loop. 58 | engine->run(); 59 | } 60 | 61 | void loop() 62 | { 63 | // nothing to do here 64 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/AirLabyrinth/assets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | // Translated with: https://www.jblanked.com/flipper/png-to-fb/ 5 | 6 | typedef struct 7 | { 8 | bool horizontal; 9 | int x; 10 | int y; 11 | int length; 12 | } Wall; 13 | 14 | #define WALL(h, y, x, l) \ 15 | (Wall) \ 16 | { \ 17 | h, (x * 320) / 128, (y * 240) / 64, l \ 18 | } 19 | 20 | // https://github.com/jamisonderek/flipper-zero-tutorials/blob/main/vgm/apps/air_labyrinth/sprites/player.png 21 | const PROGMEM uint8_t player_10x10[100] = { 22 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 23 | ; -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/AirLabyrinth/player.cpp: -------------------------------------------------------------------------------- 1 | #include "player.hpp" 2 | #include "assets.hpp" 3 | 4 | Wall walls[32] = { 5 | WALL(true, 12, 0, 3), 6 | WALL(false, 3, 3, 17), 7 | WALL(false, 23, 3, 6), 8 | WALL(true, 3, 4, 57), 9 | WALL(true, 28, 4, 56), 10 | WALL(false, 4, 7, 5), 11 | WALL(false, 12, 7, 13), 12 | WALL(true, 8, 8, 34), 13 | WALL(true, 12, 8, 42), 14 | WALL(true, 24, 8, 8), 15 | WALL(true, 16, 11, 8), 16 | WALL(false, 17, 11, 4), 17 | WALL(true, 20, 12, 22), 18 | WALL(false, 6, 17, 2), 19 | WALL(true, 24, 19, 15), 20 | WALL(true, 16, 22, 16), 21 | WALL(false, 4, 24, 1), 22 | WALL(false, 21, 28, 2), 23 | WALL(false, 6, 33, 2), 24 | WALL(false, 13, 34, 3), 25 | WALL(false, 17, 37, 11), 26 | WALL(true, 16, 41, 14), 27 | WALL(false, 20, 41, 5), 28 | WALL(true, 20, 45, 12), 29 | WALL(true, 24, 45, 12), 30 | WALL(false, 4, 46, 2), 31 | WALL(false, 9, 46, 3), 32 | WALL(false, 6, 50, 3), 33 | WALL(true, 12, 53, 7), 34 | WALL(true, 8, 54, 6), 35 | WALL(false, 4, 60, 19), 36 | WALL(false, 26, 60, 6), 37 | }; 38 | 39 | static void player_update(Entity *self, Game *game) 40 | { 41 | Vector oldPos = self->position; 42 | Vector newPos = oldPos; 43 | 44 | // Move according to input 45 | if (game->input == BUTTON_UP) 46 | { 47 | newPos.y -= 5; 48 | self->direction = ENTITY_UP; 49 | } 50 | else if (game->input == BUTTON_DOWN) 51 | { 52 | newPos.y += 5; 53 | self->direction = ENTITY_DOWN; 54 | } 55 | else if (game->input == BUTTON_LEFT) 56 | { 57 | newPos.x -= 5; 58 | self->direction = ENTITY_LEFT; 59 | } 60 | else if (game->input == BUTTON_RIGHT) 61 | { 62 | newPos.x += 5; 63 | self->direction = ENTITY_RIGHT; 64 | } 65 | 66 | // reset input 67 | game->input = -1; 68 | 69 | // Tentatively set new position 70 | self->position_set(newPos); 71 | 72 | // check if new position is within the level boundaries 73 | if (newPos.x < 0 || newPos.x + self->size.x > game->current_level->size.x || 74 | newPos.y < 0 || newPos.y + self->size.y > game->current_level->size.y) 75 | { 76 | // restore old position 77 | self->position_set(oldPos); 78 | } 79 | 80 | // Store the current camera position before updating 81 | game->old_pos = game->pos; 82 | 83 | // Update camera position to center the player 84 | float camera_x = self->position.x - (game->size.x / 2); 85 | float camera_y = self->position.y - (game->size.y / 2); 86 | 87 | // Clamp camera position to the world boundaries 88 | camera_x = constrain(camera_x, 0, game->current_level->size.x - game->size.x); 89 | camera_y = constrain(camera_y, 0, game->current_level->size.y - game->size.y); 90 | 91 | // Set the new camera position 92 | game->pos = Vector(camera_x, camera_y); 93 | } 94 | 95 | static void player_render(Entity *self, Draw *draw, Game *game) 96 | { 97 | draw->text(Vector(100, 6), "@codeallnight - ver 0.1", TFT_RED); 98 | draw->text(Vector(100, 224), "vgm game engine demo", TFT_RED); 99 | } 100 | 101 | void player_spawn(Level *level) 102 | { 103 | // Create the player entity 104 | Entity *player = new Entity("Player", ENTITY_PLAYER, Vector(160, 130), Vector(10, 10), player_10x10, NULL, NULL, NULL, NULL, player_update, player_render, NULL, true); 105 | level->entity_add(player); 106 | } 107 | 108 | static void wall_render(Entity *self, Draw *draw, Game *game) 109 | { 110 | Vector pos = self->position; 111 | Vector size = self->size; 112 | 113 | draw->display->fillRect(pos.x, pos.y, size.x, size.y, TFT_BLACK); 114 | } 115 | 116 | // Wall collision function: when this is called, the wall has collided with the player 117 | static void wall_collision(Entity *self, Entity *other, Game *game) 118 | { 119 | if (strcmp(other->name, "Player") == 0) 120 | { 121 | other->position_set(other->old_position); 122 | game->draw->clear(Vector(0, 0), Vector(game->size.x, game->size.y), TFT_WHITE); // clear the screen 123 | } 124 | } 125 | 126 | static void wall_start(Level *level, Vector position, Vector size) 127 | { 128 | // Create the wall entity 129 | // the real position is Vector(position.x + size.x / 2, position.y + size.y / 2) 130 | Vector real_position = Vector(position.x - size.x / 2, position.y - size.y / 2); 131 | Entity *wall = new Entity("Wall", ENTITY_ICON, real_position, size, NULL, NULL, NULL, NULL, NULL, NULL, wall_render, wall_collision, true); 132 | level->entity_add(wall); 133 | } 134 | 135 | void wall_spawn(Level *level) 136 | { 137 | for (int i = 0; i < 32; i++) 138 | { 139 | const int scale = 2; 140 | 141 | Wall wall = walls[i]; 142 | Vector size; 143 | if (wall.horizontal) 144 | { 145 | size.x = (wall.length * 320) / 128; // scale length in x 146 | size.y = (1 * 240) / 64; // thickness in y 147 | } 148 | else 149 | { 150 | size.x = (1 * 320) / 128; // thickness in x 151 | size.y = (wall.length * 240) / 64; // scale length in y 152 | } 153 | 154 | size.x *= scale; 155 | size.y *= scale; 156 | 157 | Vector position = Vector(wall.x * scale + size.x / 2, 158 | wall.y * scale + size.y / 2); 159 | 160 | wall_start(level, position, size); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/AirLabyrinth/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PicoGameEngine.h" 4 | void player_spawn(Level *level); 5 | void wall_spawn(Level *level); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Arkanoid/Arkanoid.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | // Translated from https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/arkanoid/arkanoid_game.c 5 | // All credits to @xMasterX @gotnull 6 | 7 | /* 8 | - Board Manager: Raspberry Pi Pico 9 | - Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 10 | - CPU Speed: 200MHz 11 | */ 12 | auto board = VGMConfig; // Video Game Module Configuration 13 | void setup() 14 | { 15 | // Setup file system (must be called in setup) 16 | setup_fs(); 17 | 18 | // Create the game instance with its name, start/stop callbacks, and colors. 19 | Game *game = new Game( 20 | "VGM Game Engine", // Game name 21 | Vector(board.width, board.height), // Game size 22 | NULL, // start callback 23 | NULL, // stop callback 24 | TFT_BLACK, // Foreground color 25 | TFT_WHITE, // Background color 26 | true, // Use 8-bit graphics? 27 | board, // Board configuration 28 | true // Use double buffering for TFT? 29 | ); 30 | 31 | // set world size 32 | game->world_size = Vector(board.width, board.height); 33 | 34 | // UART buttons 35 | ButtonUART *uart = new ButtonUART(); 36 | 37 | // Add input buttons 38 | game->input_add(new Input(uart)); 39 | 40 | // Create and add a level to the game. 41 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 42 | game->level_add(level); 43 | 44 | // Add the player entity to the level 45 | player_spawn(level, game); 46 | 47 | // Create the game engine (with 120 frames per second target). 48 | GameEngine *engine = new GameEngine("VGM Game Engine", 120, game); 49 | 50 | // Run the game engine's main loop. 51 | engine->run(); 52 | } 53 | 54 | void loop() 55 | { 56 | // nothing to do here 57 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Arkanoid/flipper.cpp: -------------------------------------------------------------------------------- 1 | #include "flipper.hpp" 2 | #include 3 | 4 | void canvas_draw_dot(Draw *canvas, int x, int y, uint16_t color) 5 | { 6 | #ifdef PicoEngine 7 | canvas->tft.drawPixel(x, y, color); 8 | #else 9 | canvas->display->drawPixel(x, y, color); 10 | #endif 11 | } 12 | 13 | void canvas_draw_frame(Draw *canvas, int x, int y, int w, int h, uint16_t color) 14 | { 15 | #ifdef PicoEngine 16 | canvas->tft.drawRect(x, y, w, h, color); 17 | #else 18 | canvas->display->drawRect(x, y, w, h, color); 19 | #endif 20 | } 21 | 22 | void canvas_draw_str(Draw *canvas, int x, int y, const char *str, uint16_t color) 23 | { 24 | #ifdef PicoEngine 25 | canvas->text(Vector(x, y), str, 1, color); 26 | #else 27 | canvas->text(Vector(x, y), str, color); 28 | #endif 29 | } 30 | 31 | void canvas_draw_str_aligned(Draw *canvas, int x, int y, int align_x, int align_y, const char *str, uint16_t color) 32 | { 33 | #ifdef PicoEngine 34 | canvas->text(Vector(x, y), str, 1, color); 35 | #else 36 | canvas->text(Vector(x, y), str, color); 37 | #endif 38 | } 39 | 40 | void furi_hal_random_fill_buf(void *buffer, size_t len) 41 | { 42 | uint8_t *buf = (uint8_t *)buffer; 43 | for (size_t i = 0; i < len; i++) 44 | { 45 | buf[i] = (uint8_t)random(256); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Arkanoid/flipper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define furi_get_tick() (millis() / 10) 7 | #define furi_kernel_get_tick_frequency() 200 8 | 9 | #ifndef AlignBottom 10 | #define AlignBottom 0 11 | #endif 12 | #ifndef AlignTop 13 | #define AlignTop 1 14 | #endif 15 | #ifndef AlignLeft 16 | #define AlignLeft 0 17 | #endif 18 | #ifndef AlignRight 19 | #define AlignRight 1 20 | #endif 21 | 22 | #ifndef TFT_DARKCYAN 23 | #define TFT_DARKCYAN 0x03EF 24 | #endif 25 | #ifndef TFT_DARKGREEN 26 | #define TFT_DARKGREEN 0x03E0 27 | #endif 28 | #ifndef TFT_BLACK 29 | #define TFT_BLACK 0x0000 30 | #endif 31 | #ifndef TFT_WHITE 32 | #define TFT_WHITE 0xFFFF 33 | #endif 34 | #ifndef TFT_BLUE 35 | #define TFT_BLUE 0x001F 36 | #endif 37 | #ifndef TFT_RED 38 | #define TFT_RED 0xF800 39 | #endif 40 | #ifndef TFT_SKYBLUE 41 | #define TFT_SKYBLUE 0x867D 42 | #endif 43 | #ifndef TFT_VIOLET 44 | #define TFT_VIOLET 0x915C 45 | #endif 46 | 47 | #ifndef FLIPPER_SCREEN_WIDTH 48 | #define FLIPPER_SCREEN_WIDTH 128 49 | #endif 50 | #ifndef FLIPPER_SCREEN_HEIGHT 51 | #define FLIPPER_SCREEN_HEIGHT 64 52 | #endif 53 | 54 | #ifndef FLIPPER_SCREEN_SIZE 55 | #define FLIPPER_SCREEN_SIZE Vector(FLIPPER_SCREEN_WIDTH, FLIPPER_SCREEN_HEIGHT) 56 | #endif 57 | 58 | #define InputKeyRight BUTTON_RIGHT 59 | #define InputKeyLeft BUTTON_LEFT 60 | #define InputKeyUp BUTTON_UP 61 | #define InputKeyDown BUTTON_DOWN 62 | #define InputKeyOk BUTTON_CENTER 63 | 64 | void canvas_draw_dot(Draw *canvas, int x, int y, uint16_t color = TFT_BLACK); 65 | void canvas_draw_frame(Draw *canvas, int x, int y, int w, int h, uint16_t color = TFT_BLACK); 66 | void canvas_draw_str(Draw *canvas, int x, int y, const char *str, uint16_t color = TFT_BLACK); 67 | void canvas_draw_str_aligned(Draw *canvas, int x, int y, int align_x, int align_y, const char *str, uint16_t color = TFT_BLACK); 68 | void furi_hal_random_fill_buf(void *buffer, size_t len); 69 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Arkanoid/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PicoGameEngine.h" 4 | void player_spawn(Level *level, Game *game); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/Doom.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | // Note: Use the Doom_8bit for no screen flickering/tearing 5 | // Translated from https://github.com/xMasterX/all-the-plugins/tree/dev/base_pack/doom 6 | // All credits to @xMasterX @Svarich @hedger (original code by @p4nic4ttack) 7 | 8 | /* 9 | - Board Manager: Raspberry Pi Pico 10 | - Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 11 | - CPU Speed: 200MHz 12 | */ 13 | auto board = VGMConfig; // Video Game Module Configuration 14 | void setup() 15 | { 16 | // Setup file system (must be called in setup) 17 | setup_fs(); 18 | 19 | // Create the game instance with its name, start/stop callbacks, and colors. 20 | Game *game = new Game( 21 | "Doom", // Game name 22 | Vector(board.width, board.height), // Game size 23 | NULL, // start callback 24 | NULL, // stop callback 25 | TFT_WHITE, // Foreground color 26 | TFT_BLACK, // Background color 27 | false, // Use 8-bit graphics? 28 | board, // Board configuration 29 | false // Use double buffering for TFT 30 | ); 31 | 32 | // set world size 33 | game->world_size = Vector(board.width, board.height); 34 | 35 | // UART buttons 36 | ButtonUART *uart = new ButtonUART(); 37 | 38 | // Add input buttons 39 | game->input_add(new Input(uart)); 40 | 41 | // Create and add a level to the game. 42 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 43 | game->level_add(level); 44 | 45 | // Add the player entity to the level 46 | player_spawn(level, game); 47 | 48 | // Create the game engine (with 120 frames per second target). 49 | GameEngine *engine = new GameEngine("VGM Game Engine", 120, game); 50 | 51 | // Run the game engine's main loop. 52 | engine->run(); 53 | } 54 | 55 | void loop() 56 | { 57 | // nothing to do here 58 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/assets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | 4 | #ifndef _sprites_h 5 | #define _sprites_h 6 | 7 | #define bmp_font_width 24 // in bytes 8 | #define bmp_font_height 6 9 | #define bmp_font_width_pxs 192 10 | #define bmp_font_height_pxs 48 11 | #define CHAR_MAP " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.,-_(){}[]#" 12 | #define UICHAR_WIDTH 4 13 | #define CHAR_HEIGHT 6 14 | 15 | #define BMP_GUN_WIDTH 32 16 | #define BMP_GUN_HEIGHT 32 17 | 18 | #define BMP_FIRE_WIDTH 24 19 | #define BMP_FIRE_HEIGHT 20 20 | 21 | #define BMP_IMP_WIDTH 32 22 | #define BMP_IMP_HEIGHT 32 23 | #define BMP_IMP_COUNT 5 24 | 25 | #define BMP_FIREBALL_WIDTH 16 26 | #define BMP_FIREBALL_HEIGHT 16 27 | 28 | #define BMP_DOOR_WIDTH 100 29 | #define BMP_DOOR_HEIGHT 100 30 | 31 | #define BMP_ITEMS_WIDTH 16 32 | #define BMP_ITEMS_HEIGHT 16 33 | #define BMP_ITEMS_COUNT 2 34 | 35 | #define BMP_LOGO_WIDTH 128 36 | #define BMP_LOGO_HEIGHT 64 37 | 38 | #define GRADIENT_WIDTH 2 39 | #define GRADIENT_HEIGHT 8 40 | #define GRADIENT_COUNT 8 41 | #define GRADIENT_WHITE 7 42 | #define GRADIENT_BLACK 0 43 | 44 | // Fonts 45 | extern const PROGMEM uint8_t zero[]; 46 | extern const PROGMEM uint8_t one[]; 47 | extern const PROGMEM uint8_t two[]; 48 | extern const PROGMEM uint8_t three[]; 49 | extern const PROGMEM uint8_t four[]; 50 | extern const PROGMEM uint8_t five[]; 51 | extern const PROGMEM uint8_t six[]; 52 | extern const PROGMEM uint8_t seven[]; 53 | extern const PROGMEM uint8_t eight[]; 54 | extern const PROGMEM uint8_t nine[]; 55 | extern const PROGMEM uint8_t A[]; 56 | extern const PROGMEM uint8_t B[]; 57 | extern const PROGMEM uint8_t C[]; 58 | extern const PROGMEM uint8_t D[]; 59 | extern const PROGMEM uint8_t E[]; 60 | extern const PROGMEM uint8_t F[]; 61 | extern const PROGMEM uint8_t G[]; 62 | extern const PROGMEM uint8_t H[]; 63 | extern const PROGMEM uint8_t I[]; 64 | extern const PROGMEM uint8_t J[]; 65 | extern const PROGMEM uint8_t K[]; 66 | extern const PROGMEM uint8_t L[]; 67 | extern const PROGMEM uint8_t M[]; 68 | extern const PROGMEM uint8_t N[]; 69 | extern const PROGMEM uint8_t O[]; 70 | extern const PROGMEM uint8_t P[]; 71 | extern const PROGMEM uint8_t Q[]; 72 | extern const PROGMEM uint8_t R[]; 73 | extern const PROGMEM uint8_t S[]; 74 | extern const PROGMEM uint8_t T[]; 75 | extern const PROGMEM uint8_t U[]; 76 | extern const PROGMEM uint8_t V[]; 77 | extern const PROGMEM uint8_t W[]; 78 | extern const PROGMEM uint8_t X[]; 79 | extern const PROGMEM uint8_t Y[]; 80 | extern const PROGMEM uint8_t Z[]; 81 | extern const PROGMEM uint8_t dot[]; 82 | extern const PROGMEM uint8_t comma[]; 83 | extern const PROGMEM uint8_t dash[]; 84 | extern const PROGMEM uint8_t underscore[]; 85 | extern const PROGMEM uint8_t bracket_open[]; 86 | extern const PROGMEM uint8_t bracket_close[]; 87 | extern const PROGMEM uint8_t cross_left[]; 88 | extern const PROGMEM uint8_t cross_right[]; 89 | extern const PROGMEM uint8_t pacman_left[]; 90 | extern const PROGMEM uint8_t pacman_right[]; 91 | extern const PROGMEM uint8_t box[]; 92 | extern const PROGMEM uint8_t *char_arr[48]; 93 | extern const PROGMEM uint8_t gradient[]; 94 | extern const PROGMEM uint8_t gun[]; 95 | extern const PROGMEM uint8_t gun_mask[]; 96 | 97 | extern const PROGMEM uint8_t imp_inv[]; 98 | extern const PROGMEM uint8_t imp_mask_inv[]; 99 | extern const PROGMEM uint8_t fireball[]; 100 | extern const PROGMEM uint8_t fireball_mask[]; 101 | extern const PROGMEM uint8_t item[]; 102 | extern const PROGMEM uint8_t item_mask[]; 103 | 104 | extern const PROGMEM uint8_t door[]; 105 | 106 | #endif -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/constants.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _constants_h 2 | #define _constants_h 3 | #include 4 | #define PB_CONSTEXPR constexpr 5 | 6 | #ifndef PI 7 | #define PI 3.14159265358979323846 8 | #endif 9 | 10 | // GFX settings 11 | #define OPTIMIZE_SSD1306 // Optimizations for SSD1366 displays 12 | 13 | // Desired time per frame in ms (~120 fps) 14 | #define FRAME_TIME 8 15 | #define RES_DIVIDER 2 16 | 17 | /* Higher values will result in lower horizontal resolution when rasterize and lower process and memory usage 18 | Lower will require more process and memory, but looks nicer 19 | */ 20 | #define Z_RES_DIVIDER 2 // Zbuffer resolution divider. We sacrifice resolution to save memory 21 | #define DISTANCE_MULTIPLIER 20 22 | 23 | /* Distances are stored as uint8_t, multiplying the distance we can obtain more precision taking care 24 | of keep numbers inside the type range. Max is 256 / MAX_RENDER_DEPTH 25 | */ 26 | 27 | #define MAX_RENDER_DEPTH 12 28 | #define MAX_SPRITE_DEPTH 8 29 | 30 | #define ZBUFFER_SIZE SCREEN_WIDTH / Z_RES_DIVIDER 31 | 32 | // Level 33 | #define LEVEL_WIDTH_BASE 6 34 | #define LEVEL_WIDTH (2 << LEVEL_WIDTH_BASE) 35 | #define LEVEL_HEIGHT 225 36 | #define LEVEL_SIZE LEVEL_WIDTH / 2 * LEVEL_HEIGHT 37 | 38 | // scenes 39 | #define INTRO 0 40 | #define GAME_PLAY 1 41 | 42 | // Game 43 | #define GUN_TARGET_POS 30 44 | #define GUN_SHOT_POS GUN_TARGET_POS + 4 45 | 46 | #define ROT_SPEED .1 47 | #define MOV_SPEED .2 48 | #define MOV_SPEED_INV 3 // 1 / MOV_SPEED 49 | 50 | #define JOGGING_SPEED .005 51 | #define ENEMY_SPEED .02 52 | #define FIREBALL_SPEED .3 53 | #define FIREBALL_ANGLES 45 // Num of angles per PI 54 | 55 | #define _MAX_ENTITIES 5 // Max num of active entities 56 | 57 | #define MAX_ENTITY_DISTANCE 200 // * DISTANCE_MULTIPLIER 58 | #define MAX_ENEMY_VIEW 80 // * DISTANCE_MULTIPLIER 59 | #define ITEM_COLLIDER_DIST 6 // * DISTANCE_MULTIPLIER 60 | #define ENEMY_COLLIDER_DIST 4 // * DISTANCE_MULTIPLIER 61 | #define FIREBALL_COLLIDER_DIST 2 // * DISTANCE_MULTIPLIER 62 | #define ENEMY_MELEE_DIST 6 // * DISTANCE_MULTIPLIER 63 | #define WALL_COLLIDER_DIST .2 64 | 65 | #define ENEMY_MELEE_DAMAGE 8 66 | #define ENEMY_FIREBALL_DAMAGE 20 67 | #define GUN_MAX_DAMAGE 20 68 | 69 | // display 70 | const uint16_t SCREEN_WIDTH = 320; 71 | const uint8_t SCREEN_HEIGHT = 240; 72 | const uint8_t HALF_WIDTH = SCREEN_WIDTH / 2; 73 | const uint8_t RENDER_HEIGHT = 220; // raycaster working height (the rest is for the hud) 74 | const uint8_t HALF_HEIGHT = SCREEN_HEIGHT / 2; 75 | 76 | #endif -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "constants.hpp" 3 | #include "flipper.hpp" 4 | #include "assets.hpp" 5 | 6 | #define CHECK_BIT(var, pos) ((var) & (1 << (pos))) 7 | 8 | static const uint8_t bit_mask[8] = {128, 64, 32, 16, 8, 4, 2, 1}; 9 | 10 | #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) 11 | #define read_bit(b, n) ((b) & pgm_read_byte(bit_mask + n) ? 1 : 0) 12 | 13 | void drawVLine(int16_t x, int16_t start_y, int16_t end_y, uint16_t intensity, Draw *const canvas); 14 | void drawPixel(int16_t x, int16_t y, bool color, bool raycasterViewport, Draw *const canvas); 15 | void drawBitmap( 16 | int16_t x, 17 | int16_t y, 18 | const uint8_t *i, 19 | int16_t w, 20 | int16_t h, 21 | uint16_t color, 22 | Draw *const canvas); 23 | void drawSprite( 24 | int16_t x, 25 | int16_t y, 26 | const uint8_t *bitmap, 27 | const uint8_t *bitmap_mask, 28 | int16_t w, 29 | int16_t h, 30 | uint8_t sprite, 31 | double distance, 32 | Draw *const canvas); 33 | void drawTextSpace(int16_t x, int16_t y, const char *txt, uint16_t space, Draw *const canvas); 34 | void drawChar(int16_t x, int16_t y, char ch, Draw *const canvas); 35 | void clearRect(int16_t x, int16_t y, int16_t w, int16_t h, Draw *const canvas); 36 | void drawGun( 37 | int16_t x, 38 | int16_t y, 39 | const uint8_t *bitmap, 40 | int16_t w, 41 | int16_t h, 42 | uint16_t color, 43 | Draw *const canvas); 44 | void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, Draw *const canvas); 45 | void drawText(int16_t x, int16_t y, uint16_t num, Draw *const canvas); 46 | void fadeScreen(uint16_t intensity, bool color, Draw *const canvas); 47 | bool getGradientPixel(int16_t x, int16_t y, uint8_t i); 48 | double getActualFps(); 49 | void fps(); 50 | uint8_t reverse_bits(uint16_t num); 51 | 52 | // FPS control 53 | extern double delta; 54 | extern uint32_t lastFrameTime; 55 | extern uint8_t zbuffer[320]; 56 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/entities.cpp: -------------------------------------------------------------------------------- 1 | #include "entities.hpp" 2 | 3 | // extern "C" 4 | /*Player create_player(double x, double y){ 5 | return {create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100, 0}; 6 | }*/ 7 | 8 | Player create_player(float x, float y) 9 | { 10 | Player p; 11 | // p.pos = create_coords((double)x + (double)0.5, (double)y + (double)0.5); 12 | // p.dir = create_coords(1, 0); 13 | // p.plane = create_coords(0, -0.66); 14 | p.pos = Vector(x + 0.5, y + 0.5); 15 | p.dir = Vector(1, 0); 16 | p.plane = Vector(0, -0.66); 17 | p.velocity = 0; 18 | p.health = 100; 19 | p.keys = 0; 20 | return p; //{create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100, 0}; 21 | } 22 | 23 | // extern "C" 24 | _Entity create_entity(uint8_t type, uint8_t x, uint8_t y, uint8_t initialState, uint8_t initialHealth, Vector size) 25 | { 26 | UID uid = create_uid(type, x, y); 27 | // Coords pos = create_coords((double)x + (double).5, (double)y + (double).5); 28 | Vector pos = Vector((float)x + (float).5, (float)y + (float).5); 29 | _Entity new_entity; // = { uid, pos, initialState, initialHealth, 0, 0 }; 30 | new_entity.uid = uid; 31 | new_entity.pos = pos; 32 | new_entity.state = initialState; 33 | new_entity.health = initialHealth; 34 | new_entity.distance = 0; 35 | new_entity.timer = 0; 36 | // 37 | new_entity.size = size; 38 | return new_entity; 39 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/entities.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _entities_h 2 | #define _entities_h 3 | #include 4 | #include 5 | #include "types.hpp" 6 | #include "PicoGameEngine.h" 7 | 8 | #define create_enemy(x, y) create_entity(E_ENEMY, x, y, S_STAND, 50, Vector(BMP_IMP_WIDTH, BMP_IMP_HEIGHT)) 9 | #define create_medikit(x, y) create_entity(E_MEDIKIT, x, y, S_STAND, 0, Vector(BMP_ITEMS_WIDTH, BMP_ITEMS_HEIGHT)) 10 | #define create_key(x, y) create_entity(E_KEY, x, y, S_STAND, 0, Vector(BMP_ITEMS_WIDTH, BMP_ITEMS_HEIGHT)) 11 | #define create_fireball(x, y, dir) create_entity(E_FIREBALL, x, y, S_STAND, dir, Vector(BMP_FIREBALL_WIDTH, BMP_FIREBALL_HEIGHT)) 12 | #define create_door(x, y) create_entity(E_DOOR, x, y, S_STAND, 0, Vector(BMP_DOOR_WIDTH, BMP_DOOR_HEIGHT)) 13 | 14 | // entity statuses 15 | #define S_STAND 0 16 | #define S_ALERT 1 17 | #define S_FIRING 2 18 | #define S_MELEE 3 19 | #define S_HIT 4 20 | #define S_DEAD 5 21 | #define S_HIDDEN 6 22 | #define S_OPEN 7 23 | #define S_CLOSE 8 24 | 25 | typedef struct 26 | { 27 | Vector pos; 28 | Vector dir; 29 | Vector plane; 30 | double velocity; 31 | uint8_t health; 32 | uint8_t keys; 33 | } Player; 34 | 35 | typedef struct 36 | { 37 | UID uid; 38 | Vector pos; // world coordinates 39 | Vector prevScreenPos; // last drawn screen position 40 | int prevWidth; // last drawn width 41 | int prevHeight; // last drawn height 42 | Vector prevPlayerPos; 43 | Vector prevPlayerDir; 44 | Vector prevPlayerPlane; 45 | uint8_t state; 46 | uint8_t health; 47 | uint8_t distance; 48 | uint8_t timer; 49 | // 50 | Vector size; 51 | } _Entity; 52 | 53 | _Entity create_entity(uint8_t type, uint8_t x, uint8_t y, uint8_t initialState, uint8_t initialHealth, Vector size); 54 | Player create_player(float x, float y); 55 | #endif -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/flipper.cpp: -------------------------------------------------------------------------------- 1 | #include "flipper.hpp" 2 | #include 3 | 4 | void canvas_draw_dot(Draw *canvas, int x, int y, uint16_t color) 5 | { 6 | #ifdef PicoEngine 7 | canvas->tft.drawPixel(x, y, color); 8 | #else 9 | canvas->display->drawPixel(x, y, color); 10 | #endif 11 | } 12 | 13 | void canvas_draw_frame(Draw *canvas, int x, int y, int w, int h, uint16_t color) 14 | { 15 | #ifdef PicoEngine 16 | canvas->tft.drawRect(x, y, w, h, color); 17 | #else 18 | canvas->display->drawRect(x, y, w, h, color); 19 | #endif 20 | } 21 | 22 | void canvas_draw_icon(Draw *canvas, int x, int y, const uint8_t *icon, int w, int h, uint16_t color) 23 | { 24 | #ifdef PicoEngine 25 | canvas->tft.drawBitmap(x, y, icon, w, h, color); 26 | #else 27 | canvas->display->drawBitmap(x, y, icon, w, h, color); 28 | #endif 29 | } 30 | 31 | void canvas_draw_str(Draw *canvas, int x, int y, const char *str, uint16_t color) 32 | { 33 | #ifdef PicoEngine 34 | canvas->text(Vector(x, y), str, 1, color); 35 | #else 36 | canvas->text(Vector(x, y), str, color); 37 | #endif 38 | } 39 | 40 | void canvas_draw_str_aligned(Draw *canvas, int x, int y, int align_x, int align_y, const char *str, uint16_t color) 41 | { 42 | #ifdef PicoEngine 43 | canvas->text(Vector(x, y), str, 1, color); 44 | #else 45 | canvas->text(Vector(x, y), str, color); 46 | #endif 47 | } 48 | 49 | void furi_hal_random_fill_buf(void *buffer, size_t len) 50 | { 51 | uint8_t *buf = (uint8_t *)buffer; 52 | for (size_t i = 0; i < len; i++) 53 | { 54 | buf[i] = (uint8_t)random(256); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/flipper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #define furi_get_tick() (millis() / 10) 6 | #define furi_kernel_get_tick_frequency() 200 7 | 8 | #ifndef AlignBottom 9 | #define AlignBottom 0 10 | #endif 11 | #ifndef AlignTop 12 | #define AlignTop 1 13 | #endif 14 | #ifndef AlignLeft 15 | #define AlignLeft 0 16 | #endif 17 | #ifndef AlignRight 18 | #define AlignRight 1 19 | #endif 20 | // https://doc-tft-espi.readthedocs.io/tft_espi/colors/ 21 | #ifndef TFT_DARKCYAN 22 | #define TFT_DARKCYAN 0x03EF 23 | #endif 24 | #ifndef TFT_DARKGREEN 25 | #define TFT_DARKGREEN 0x03E0 26 | #endif 27 | #ifndef TFT_BLACK 28 | #define TFT_BLACK 0x0000 29 | #endif 30 | #ifndef TFT_WHITE 31 | #define TFT_WHITE 0xFFFF 32 | #endif 33 | #ifndef TFT_BLUE 34 | #define TFT_BLUE 0x001F 35 | #endif 36 | #ifndef TFT_RED 37 | #define TFT_RED 0xF800 38 | #endif 39 | #ifndef TFT_SKYBLUE 40 | #define TFT_SKYBLUE 0x867D 41 | #endif 42 | #ifndef TFT_VIOLET 43 | #define TFT_VIOLET 0x915C 44 | #endif 45 | #ifndef TFT_BROWN 46 | #define TFT_BROWN 0x9A60 47 | #endif 48 | #ifndef TFT_TRANSPARENT 49 | #define TFT_TRANSPARENT 0x0120 50 | #endif 51 | #ifndef TFT_YELLOW 52 | #define TFT_YELLOW 0xFFE0 53 | #endif 54 | #ifndef TFT_ORANGE 55 | #define TFT_ORANGE 0xFDA0 56 | #endif 57 | 58 | #ifndef FLIPPER_SCREEN_WIDTH 59 | #define FLIPPER_SCREEN_WIDTH 128 60 | #endif 61 | #ifndef FLIPPER_SCREEN_HEIGHT 62 | #define FLIPPER_SCREEN_HEIGHT 64 63 | #endif 64 | 65 | #ifndef FLIPPER_SCREEN_SIZE 66 | #define FLIPPER_SCREEN_SIZE Vector(FLIPPER_SCREEN_WIDTH, FLIPPER_SCREEN_HEIGHT) 67 | #endif 68 | 69 | #define InputKeyRight BUTTON_RIGHT 70 | #define InputKeyLeft BUTTON_LEFT 71 | #define InputKeyUp BUTTON_UP 72 | #define InputKeyDown BUTTON_DOWN 73 | #define InputKeyOk BUTTON_CENTER 74 | 75 | void canvas_draw_dot(Draw *canvas, int x, int y, uint16_t color = TFT_BLACK); 76 | void canvas_draw_frame(Draw *canvas, int x, int y, int w, int h, uint16_t color = TFT_BLACK); 77 | void canvas_draw_icon(Draw *canvas, int x, int y, const uint8_t *icon, int w, int h, uint16_t color = TFT_BLACK); 78 | void canvas_draw_str(Draw *canvas, int x, int y, const char *str, uint16_t color = TFT_BLACK); 79 | void canvas_draw_str_aligned(Draw *canvas, int x, int y, int align_x, int align_y, const char *str, uint16_t color = TFT_BLACK); 80 | void furi_hal_random_fill_buf(void *buffer, size_t len); 81 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/icon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | 4 | // translated with https://www.flipsocial.net/png-to-fb/ 5 | // all assets from https://github.com/xMasterX/all-the-plugins/tree/dev/base_pack/doom/assets 6 | 7 | extern const PROGMEM uint8_t I_door_inv_32x32[]; // 32x32 8 | extern const PROGMEM uint8_t I_fire_inv_24x20[]; // 24x20 -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PicoGameEngine.h" 3 | void player_spawn(Level *level, Game *game); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/types.cpp: -------------------------------------------------------------------------------- 1 | #include "types.hpp" 2 | 3 | // extern "C" 4 | uint8_t vector_distance(Vector a, Vector b) 5 | { 6 | return sqrt(sq(a.x - b.x) + sq(a.y - b.y)) * 20; 7 | } 8 | 9 | // extern "C" 10 | UID create_uid(uint8_t type, uint8_t x, uint8_t y) 11 | { 12 | return ((y << 6) | x) << 4 | type; 13 | } 14 | 15 | // extern "C" 16 | uint8_t uid_get_type(UID uid) 17 | { 18 | return uid & 0x0F; 19 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom/types.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _types_h 2 | #define _types_h 3 | 4 | #include 5 | #include 6 | #include "PicoGameEngine.h" 7 | // #include "constants.h" 8 | 9 | #define UID_null 0 10 | 11 | // Entity types (legend applies to level.h) 12 | #define E_FLOOR 0x0 // . (also null) 13 | #define E_WALL 0xF // # 14 | #define E_PLAYER 0x1 // P 15 | #define E_ENEMY 0x2 // E 16 | #define E_DOOR 0x4 // D 17 | #define E_LOCKEDDOOR 0x5 // L 18 | #define E_EXIT 0x7 // X 19 | // collectable entities >= 0x8 20 | #define E_MEDIKIT 0x8 // M 21 | #define E_KEY 0x9 // K 22 | #define E_FIREBALL 0xA // not in map 23 | 24 | typedef uint16_t UID; 25 | typedef uint8_t EType; 26 | 27 | UID create_uid(uint8_t type, uint8_t x, uint8_t y); 28 | EType uid_get_type(UID uid); 29 | uint8_t vector_distance(Vector a, Vector b); 30 | 31 | #endif -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/Doom_8bit.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | // Translated from https://github.com/xMasterX/all-the-plugins/tree/dev/base_pack/doom 5 | // All credits to @xMasterX @Svarich @hedger (original code by @p4nic4ttack) 6 | 7 | /* 8 | - Board Manager: Raspberry Pi Pico 9 | - Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 10 | - CPU Speed: 200MHz 11 | */ 12 | auto board = VGMConfig; // Video Game Module Configuration 13 | void setup() 14 | { 15 | // Setup file system (must be called in setup) 16 | setup_fs(); 17 | 18 | // Create the game instance with its name, start/stop callbacks, and colors. 19 | Game *game = new Game( 20 | "Doom", // Game name 21 | Vector(board.width, board.height), // Game size 22 | NULL, // start callback 23 | NULL, // stop callback 24 | TFT_WHITE, // Foreground color 25 | TFT_BLACK, // Background color 26 | true, // Use 8-bit graphics? 27 | board, // Board configuration 28 | true // Use double buffering for TFT 29 | ); 30 | 31 | // set world size 32 | game->world_size = Vector(board.width, board.height); 33 | 34 | // UART buttons 35 | ButtonUART *uart = new ButtonUART(); 36 | 37 | // Add input buttons 38 | game->input_add(new Input(uart)); 39 | 40 | // Create and add a level to the game. 41 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 42 | game->level_add(level); 43 | 44 | // Add the player entity to the level 45 | player_spawn(level, game); 46 | 47 | // Create the game engine (with 120 frames per second target). 48 | GameEngine *engine = new GameEngine("VGM Game Engine", 120, game); 49 | 50 | // Run the game engine's main loop. 51 | engine->run(); 52 | } 53 | 54 | void loop() 55 | { 56 | // nothing to do here 57 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/assets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | 4 | #ifndef _sprites_h 5 | #define _sprites_h 6 | 7 | #define bmp_font_width 24 // in bytes 8 | #define bmp_font_height 6 9 | #define bmp_font_width_pxs 192 10 | #define bmp_font_height_pxs 48 11 | #define CHAR_MAP " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.,-_(){}[]#" 12 | #define UICHAR_WIDTH 4 13 | #define CHAR_HEIGHT 6 14 | 15 | #define BMP_GUN_WIDTH 32 16 | #define BMP_GUN_HEIGHT 32 17 | 18 | #define BMP_FIRE_WIDTH 24 19 | #define BMP_FIRE_HEIGHT 20 20 | 21 | #define BMP_IMP_WIDTH 32 22 | #define BMP_IMP_HEIGHT 32 23 | #define BMP_IMP_COUNT 5 24 | 25 | #define BMP_FIREBALL_WIDTH 16 26 | #define BMP_FIREBALL_HEIGHT 16 27 | 28 | #define BMP_DOOR_WIDTH 100 29 | #define BMP_DOOR_HEIGHT 100 30 | 31 | #define BMP_ITEMS_WIDTH 16 32 | #define BMP_ITEMS_HEIGHT 16 33 | #define BMP_ITEMS_COUNT 2 34 | 35 | #define BMP_LOGO_WIDTH 128 36 | #define BMP_LOGO_HEIGHT 64 37 | 38 | #define GRADIENT_WIDTH 2 39 | #define GRADIENT_HEIGHT 8 40 | #define GRADIENT_COUNT 8 41 | #define GRADIENT_WHITE 7 42 | #define GRADIENT_BLACK 0 43 | 44 | // Fonts 45 | extern const PROGMEM uint8_t zero[]; 46 | extern const PROGMEM uint8_t one[]; 47 | extern const PROGMEM uint8_t two[]; 48 | extern const PROGMEM uint8_t three[]; 49 | extern const PROGMEM uint8_t four[]; 50 | extern const PROGMEM uint8_t five[]; 51 | extern const PROGMEM uint8_t six[]; 52 | extern const PROGMEM uint8_t seven[]; 53 | extern const PROGMEM uint8_t eight[]; 54 | extern const PROGMEM uint8_t nine[]; 55 | extern const PROGMEM uint8_t A[]; 56 | extern const PROGMEM uint8_t B[]; 57 | extern const PROGMEM uint8_t C[]; 58 | extern const PROGMEM uint8_t D[]; 59 | extern const PROGMEM uint8_t E[]; 60 | extern const PROGMEM uint8_t F[]; 61 | extern const PROGMEM uint8_t G[]; 62 | extern const PROGMEM uint8_t H[]; 63 | extern const PROGMEM uint8_t I[]; 64 | extern const PROGMEM uint8_t J[]; 65 | extern const PROGMEM uint8_t K[]; 66 | extern const PROGMEM uint8_t L[]; 67 | extern const PROGMEM uint8_t M[]; 68 | extern const PROGMEM uint8_t N[]; 69 | extern const PROGMEM uint8_t O[]; 70 | extern const PROGMEM uint8_t P[]; 71 | extern const PROGMEM uint8_t Q[]; 72 | extern const PROGMEM uint8_t R[]; 73 | extern const PROGMEM uint8_t S[]; 74 | extern const PROGMEM uint8_t T[]; 75 | extern const PROGMEM uint8_t U[]; 76 | extern const PROGMEM uint8_t V[]; 77 | extern const PROGMEM uint8_t W[]; 78 | extern const PROGMEM uint8_t X[]; 79 | extern const PROGMEM uint8_t Y[]; 80 | extern const PROGMEM uint8_t Z[]; 81 | extern const PROGMEM uint8_t dot[]; 82 | extern const PROGMEM uint8_t comma[]; 83 | extern const PROGMEM uint8_t dash[]; 84 | extern const PROGMEM uint8_t underscore[]; 85 | extern const PROGMEM uint8_t bracket_open[]; 86 | extern const PROGMEM uint8_t bracket_close[]; 87 | extern const PROGMEM uint8_t cross_left[]; 88 | extern const PROGMEM uint8_t cross_right[]; 89 | extern const PROGMEM uint8_t pacman_left[]; 90 | extern const PROGMEM uint8_t pacman_right[]; 91 | extern const PROGMEM uint8_t box[]; 92 | extern const PROGMEM uint8_t *char_arr[48]; 93 | extern const PROGMEM uint8_t gradient[]; 94 | extern const PROGMEM uint8_t gun[]; 95 | extern const PROGMEM uint8_t gun_mask[]; 96 | 97 | extern const PROGMEM uint8_t imp_inv[]; 98 | extern const PROGMEM uint8_t imp_mask_inv[]; 99 | extern const PROGMEM uint8_t fireball[]; 100 | extern const PROGMEM uint8_t fireball_mask[]; 101 | extern const PROGMEM uint8_t item[]; 102 | extern const PROGMEM uint8_t item_mask[]; 103 | 104 | extern const PROGMEM uint8_t door[]; 105 | 106 | #endif -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/constants.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _constants_h 2 | #define _constants_h 3 | #include 4 | #define PB_CONSTEXPR constexpr 5 | 6 | #ifndef PI 7 | #define PI 3.14159265358979323846 8 | #endif 9 | 10 | // GFX settings 11 | #define OPTIMIZE_SSD1306 // Optimizations for SSD1366 displays 12 | 13 | // Desired time per frame in ms (~120 fps) 14 | #define FRAME_TIME 8 15 | #define RES_DIVIDER 2 16 | 17 | /* Higher values will result in lower horizontal resolution when rasterize and lower process and memory usage 18 | Lower will require more process and memory, but looks nicer 19 | */ 20 | #define Z_RES_DIVIDER 2 // Zbuffer resolution divider. We sacrifice resolution to save memory 21 | #define DISTANCE_MULTIPLIER 20 22 | 23 | /* Distances are stored as uint8_t, multiplying the distance we can obtain more precision taking care 24 | of keep numbers inside the type range. Max is 256 / MAX_RENDER_DEPTH 25 | */ 26 | 27 | #define MAX_RENDER_DEPTH 12 28 | #define MAX_SPRITE_DEPTH 8 29 | 30 | #define ZBUFFER_SIZE SCREEN_WIDTH / Z_RES_DIVIDER 31 | 32 | // Level 33 | #define LEVEL_WIDTH_BASE 6 34 | #define LEVEL_WIDTH (2 << LEVEL_WIDTH_BASE) 35 | #define LEVEL_HEIGHT 225 36 | #define LEVEL_SIZE LEVEL_WIDTH / 2 * LEVEL_HEIGHT 37 | 38 | // scenes 39 | #define INTRO 0 40 | #define GAME_PLAY 1 41 | 42 | // Game 43 | #define GUN_TARGET_POS 30 44 | #define GUN_SHOT_POS GUN_TARGET_POS + 4 45 | 46 | #define ROT_SPEED .1 47 | #define MOV_SPEED .2 48 | #define MOV_SPEED_INV 3 // 1 / MOV_SPEED 49 | 50 | #define JOGGING_SPEED .005 51 | #define ENEMY_SPEED .02 52 | #define FIREBALL_SPEED .3 53 | #define FIREBALL_ANGLES 45 // Num of angles per PI 54 | 55 | #define _MAX_ENTITIES 5 // Max num of active entities 56 | 57 | #define MAX_ENTITY_DISTANCE 200 // * DISTANCE_MULTIPLIER 58 | #define MAX_ENEMY_VIEW 80 // * DISTANCE_MULTIPLIER 59 | #define ITEM_COLLIDER_DIST 6 // * DISTANCE_MULTIPLIER 60 | #define ENEMY_COLLIDER_DIST 4 // * DISTANCE_MULTIPLIER 61 | #define FIREBALL_COLLIDER_DIST 2 // * DISTANCE_MULTIPLIER 62 | #define ENEMY_MELEE_DIST 6 // * DISTANCE_MULTIPLIER 63 | #define WALL_COLLIDER_DIST .2 64 | 65 | #define ENEMY_MELEE_DAMAGE 8 66 | #define ENEMY_FIREBALL_DAMAGE 20 67 | #define GUN_MAX_DAMAGE 20 68 | 69 | // display 70 | const uint16_t SCREEN_WIDTH = 320; 71 | const uint8_t SCREEN_HEIGHT = 240; 72 | const uint8_t HALF_WIDTH = SCREEN_WIDTH / 2; 73 | const uint8_t RENDER_HEIGHT = 220; // raycaster working height (the rest is for the hud) 74 | const uint8_t HALF_HEIGHT = SCREEN_HEIGHT / 2; 75 | 76 | #endif -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/display.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "constants.hpp" 3 | #include "flipper.hpp" 4 | #include "assets.hpp" 5 | 6 | #define CHECK_BIT(var, pos) ((var) & (1 << (pos))) 7 | 8 | static const uint8_t bit_mask[8] = {128, 64, 32, 16, 8, 4, 2, 1}; 9 | 10 | #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) 11 | #define read_bit(b, n) ((b) & pgm_read_byte(bit_mask + n) ? 1 : 0) 12 | 13 | void drawVLine(int16_t x, int16_t start_y, int16_t end_y, uint16_t intensity, Draw *const canvas, uint16_t color = TFT_BROWN); 14 | void drawPixel(int16_t x, int16_t y, uint16_t color, bool raycasterViewport, Draw *const canvas); 15 | void drawBitmap( 16 | int16_t x, 17 | int16_t y, 18 | const uint8_t *i, 19 | int16_t w, 20 | int16_t h, 21 | uint16_t color, 22 | Draw *const canvas, 23 | bool is_8bit = false); 24 | void drawSprite( 25 | int16_t x, 26 | int16_t y, 27 | const uint8_t *bitmap, 28 | const uint8_t *bitmap_mask, 29 | int16_t w, 30 | int16_t h, 31 | uint8_t sprite, 32 | double distance, 33 | Draw *const canvas, 34 | uint16_t color = TFT_RED); 35 | void drawTextSpace(int16_t x, int16_t y, const char *txt, uint16_t space, Draw *const canvas); 36 | void drawChar(int16_t x, int16_t y, char ch, Draw *const canvas, int16_t color = TFT_RED); 37 | void clearRect(int16_t x, int16_t y, int16_t w, int16_t h, Draw *const canvas); 38 | void drawGun( 39 | int16_t x, 40 | int16_t y, 41 | const uint8_t *bitmap, 42 | int16_t w, 43 | int16_t h, 44 | uint16_t color, 45 | Draw *const canvas); 46 | void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, Draw *const canvas); 47 | void drawText(int16_t x, int16_t y, uint16_t num, Draw *const canvas); 48 | void fadeScreen(uint16_t intensity, uint16_t color, Draw *const canvas); 49 | bool getGradientPixel(int16_t x, int16_t y, uint8_t i); 50 | double getActualFps(); 51 | void fps(); 52 | uint8_t reverse_bits(uint16_t num); 53 | 54 | // FPS control 55 | extern double delta; 56 | extern uint32_t lastFrameTime; 57 | extern uint8_t zbuffer[320]; 58 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/entities.cpp: -------------------------------------------------------------------------------- 1 | #include "entities.hpp" 2 | 3 | // extern "C" 4 | /*Player create_player(double x, double y){ 5 | return {create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100, 0}; 6 | }*/ 7 | 8 | Player create_player(float x, float y) 9 | { 10 | Player p; 11 | // p.pos = create_coords((double)x + (double)0.5, (double)y + (double)0.5); 12 | // p.dir = create_coords(1, 0); 13 | // p.plane = create_coords(0, -0.66); 14 | p.pos = Vector(x + 0.5, y + 0.5); 15 | p.dir = Vector(1, 0); 16 | p.plane = Vector(0, -0.66); 17 | p.velocity = 0; 18 | p.health = 100; 19 | p.keys = 0; 20 | return p; //{create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100, 0}; 21 | } 22 | 23 | // extern "C" 24 | _Entity create_entity(uint8_t type, uint8_t x, uint8_t y, uint8_t initialState, uint8_t initialHealth, Vector size) 25 | { 26 | UID uid = create_uid(type, x, y); 27 | // Coords pos = create_coords((double)x + (double).5, (double)y + (double).5); 28 | Vector pos = Vector((float)x + (float).5, (float)y + (float).5); 29 | _Entity new_entity; // = { uid, pos, initialState, initialHealth, 0, 0 }; 30 | new_entity.uid = uid; 31 | new_entity.pos = pos; 32 | new_entity.state = initialState; 33 | new_entity.health = initialHealth; 34 | new_entity.distance = 0; 35 | new_entity.timer = 0; 36 | // 37 | new_entity.size = size; 38 | return new_entity; 39 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/entities.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _entities_h 2 | #define _entities_h 3 | #include 4 | #include 5 | #include "types.hpp" 6 | #include "PicoGameEngine.h" 7 | 8 | #define create_enemy(x, y) create_entity(E_ENEMY, x, y, S_STAND, 50, Vector(BMP_IMP_WIDTH, BMP_IMP_HEIGHT)) 9 | #define create_medikit(x, y) create_entity(E_MEDIKIT, x, y, S_STAND, 0, Vector(BMP_ITEMS_WIDTH, BMP_ITEMS_HEIGHT)) 10 | #define create_key(x, y) create_entity(E_KEY, x, y, S_STAND, 0, Vector(BMP_ITEMS_WIDTH, BMP_ITEMS_HEIGHT)) 11 | #define create_fireball(x, y, dir) create_entity(E_FIREBALL, x, y, S_STAND, dir, Vector(BMP_FIREBALL_WIDTH, BMP_FIREBALL_HEIGHT)) 12 | #define create_door(x, y) create_entity(E_DOOR, x, y, S_STAND, 0, Vector(BMP_DOOR_WIDTH, BMP_DOOR_HEIGHT)) 13 | 14 | // entity statuses 15 | #define S_STAND 0 16 | #define S_ALERT 1 17 | #define S_FIRING 2 18 | #define S_MELEE 3 19 | #define S_HIT 4 20 | #define S_DEAD 5 21 | #define S_HIDDEN 6 22 | #define S_OPEN 7 23 | #define S_CLOSE 8 24 | 25 | typedef struct 26 | { 27 | Vector pos; 28 | Vector dir; 29 | Vector plane; 30 | double velocity; 31 | uint8_t health; 32 | uint8_t keys; 33 | } Player; 34 | 35 | typedef struct 36 | { 37 | UID uid; 38 | Vector pos; // world coordinates 39 | Vector prevScreenPos; // last drawn screen position 40 | int prevWidth; // last drawn width 41 | int prevHeight; // last drawn height 42 | Vector prevPlayerPos; 43 | Vector prevPlayerDir; 44 | Vector prevPlayerPlane; 45 | uint8_t state; 46 | uint8_t health; 47 | uint8_t distance; 48 | uint8_t timer; 49 | // 50 | Vector size; 51 | } _Entity; 52 | 53 | _Entity create_entity(uint8_t type, uint8_t x, uint8_t y, uint8_t initialState, uint8_t initialHealth, Vector size); 54 | Player create_player(float x, float y); 55 | #endif -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/flipper.cpp: -------------------------------------------------------------------------------- 1 | #include "flipper.hpp" 2 | #include 3 | 4 | void canvas_draw_dot(Draw *canvas, int x, int y, uint16_t color) 5 | { 6 | #ifdef PicoEngine 7 | canvas->tft.drawPixel(x, y, color); 8 | #else 9 | canvas->display->drawPixel(x, y, color); 10 | #endif 11 | } 12 | 13 | void canvas_draw_frame(Draw *canvas, int x, int y, int w, int h, uint16_t color) 14 | { 15 | #ifdef PicoEngine 16 | canvas->tft.drawRect(x, y, w, h, color); 17 | #else 18 | canvas->display->drawRect(x, y, w, h, color); 19 | #endif 20 | } 21 | 22 | void canvas_draw_icon(Draw *canvas, int x, int y, const uint8_t *icon, int w, int h, uint16_t color) 23 | { 24 | #ifdef PicoEngine 25 | canvas->tft.drawBitmap(x, y, icon, w, h, color); 26 | #else 27 | canvas->display->drawBitmap(x, y, icon, w, h, color); 28 | #endif 29 | } 30 | 31 | void canvas_draw_str(Draw *canvas, int x, int y, const char *str, uint16_t color) 32 | { 33 | #ifdef PicoEngine 34 | canvas->text(Vector(x, y), str, 1, color); 35 | #else 36 | canvas->text(Vector(x, y), str, color); 37 | #endif 38 | } 39 | 40 | void canvas_draw_str_aligned(Draw *canvas, int x, int y, int align_x, int align_y, const char *str, uint16_t color) 41 | { 42 | #ifdef PicoEngine 43 | canvas->text(Vector(x, y), str, 1, color); 44 | #else 45 | canvas->text(Vector(x, y), str, color); 46 | #endif 47 | } 48 | 49 | void furi_hal_random_fill_buf(void *buffer, size_t len) 50 | { 51 | uint8_t *buf = (uint8_t *)buffer; 52 | for (size_t i = 0; i < len; i++) 53 | { 54 | buf[i] = (uint8_t)random(256); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/flipper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define furi_get_tick() (millis() / 10) 8 | #define furi_kernel_get_tick_frequency() 200 9 | 10 | #ifndef AlignBottom 11 | #define AlignBottom 0 12 | #endif 13 | #ifndef AlignTop 14 | #define AlignTop 1 15 | #endif 16 | #ifndef AlignLeft 17 | #define AlignLeft 0 18 | #endif 19 | #ifndef AlignRight 20 | #define AlignRight 1 21 | #endif 22 | // https://doc-tft-espi.readthedocs.io/tft_espi/colors/ 23 | #ifndef TFT_DARKCYAN 24 | #define TFT_DARKCYAN 0x03EF 25 | #endif 26 | #ifndef TFT_DARKGREEN 27 | #define TFT_DARKGREEN 0x03E0 28 | #endif 29 | #ifndef TFT_BLACK 30 | #define TFT_BLACK 0x0000 31 | #endif 32 | #ifndef TFT_WHITE 33 | #define TFT_WHITE 0xFFFF 34 | #endif 35 | #ifndef TFT_BLUE 36 | #define TFT_BLUE 0x001F 37 | #endif 38 | #ifndef TFT_RED 39 | #define TFT_RED 0xF800 40 | #endif 41 | #ifndef TFT_SKYBLUE 42 | #define TFT_SKYBLUE 0x867D 43 | #endif 44 | #ifndef TFT_VIOLET 45 | #define TFT_VIOLET 0x915C 46 | #endif 47 | #ifndef TFT_BROWN 48 | #define TFT_BROWN 0x9A60 49 | #endif 50 | #ifndef TFT_TRANSPARENT 51 | #define TFT_TRANSPARENT 0x0120 52 | #endif 53 | #ifndef TFT_YELLOW 54 | #define TFT_YELLOW 0xFFE0 55 | #endif 56 | #ifndef TFT_ORANGE 57 | #define TFT_ORANGE 0xFDA0 58 | #endif 59 | 60 | #ifndef FLIPPER_SCREEN_WIDTH 61 | #define FLIPPER_SCREEN_WIDTH 128 62 | #endif 63 | #ifndef FLIPPER_SCREEN_HEIGHT 64 | #define FLIPPER_SCREEN_HEIGHT 64 65 | #endif 66 | 67 | #ifndef FLIPPER_SCREEN_SIZE 68 | #define FLIPPER_SCREEN_SIZE Vector(FLIPPER_SCREEN_WIDTH, FLIPPER_SCREEN_HEIGHT) 69 | #endif 70 | 71 | #define InputKeyRight BUTTON_RIGHT 72 | #define InputKeyLeft BUTTON_LEFT 73 | #define InputKeyUp BUTTON_UP 74 | #define InputKeyDown BUTTON_DOWN 75 | #define InputKeyOk BUTTON_CENTER 76 | 77 | void canvas_draw_dot(Draw *canvas, int x, int y, uint16_t color = TFT_BLACK); 78 | void canvas_draw_frame(Draw *canvas, int x, int y, int w, int h, uint16_t color = TFT_BLACK); 79 | void canvas_draw_icon(Draw *canvas, int x, int y, const uint8_t *icon, int w, int h, uint16_t color = TFT_BLACK); 80 | void canvas_draw_str(Draw *canvas, int x, int y, const char *str, uint16_t color = TFT_BLACK); 81 | void canvas_draw_str_aligned(Draw *canvas, int x, int y, int align_x, int align_y, const char *str, uint16_t color = TFT_BLACK); 82 | void furi_hal_random_fill_buf(void *buffer, size_t len); 83 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/icon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | 4 | // translated with https://www.flipsocial.net/png-to-fb/ 5 | // all assets from https://github.com/xMasterX/all-the-plugins/tree/dev/base_pack/doom/assets 6 | 7 | extern const PROGMEM uint8_t I_door_inv_32x32[]; // 32x32 8 | extern const PROGMEM uint8_t I_fire_inv_24x20[]; // 24x20 -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PicoGameEngine.h" 3 | void player_spawn(Level *level, Game *game); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/types.cpp: -------------------------------------------------------------------------------- 1 | #include "types.hpp" 2 | 3 | // extern "C" 4 | uint8_t vector_distance(Vector a, Vector b) 5 | { 6 | return sqrt(sq(a.x - b.x) + sq(a.y - b.y)) * 20; 7 | } 8 | 9 | // extern "C" 10 | UID create_uid(uint8_t type, uint8_t x, uint8_t y) 11 | { 12 | return ((y << 6) | x) << 4 | type; 13 | } 14 | 15 | // extern "C" 16 | uint8_t uid_get_type(UID uid) 17 | { 18 | return uid & 0x0F; 19 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Doom_8bit/types.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _types_h 2 | #define _types_h 3 | 4 | #include 5 | #include 6 | #include "PicoGameEngine.h" 7 | // #include "constants.h" 8 | 9 | #define UID_null 0 10 | 11 | // Entity types (legend applies to level.hpp) 12 | #define E_FLOOR 0x0 // . (also null) 13 | #define E_WALL 0xF // # 14 | #define E_PLAYER 0x1 // P 15 | #define E_ENEMY 0x2 // E 16 | #define E_DOOR 0x4 // D 17 | #define E_LOCKEDDOOR 0x5 // L 18 | #define E_EXIT 0x7 // X 19 | // collectable entities >= 0x8 20 | #define E_MEDIKIT 0x8 // M 21 | #define E_KEY 0x9 // K 22 | #define E_FIREBALL 0xA // not in map 23 | 24 | typedef uint16_t UID; 25 | typedef uint8_t EType; 26 | 27 | UID create_uid(uint8_t type, uint8_t x, uint8_t y); 28 | EType uid_get_type(UID uid); 29 | uint8_t vector_distance(Vector a, Vector b); 30 | 31 | #endif -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlappyBird/FlappyBird.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "assets.hpp" 4 | #include "player.hpp" 5 | // Translated from https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/flappy_bird 6 | // All credits to @DroomOne & @xMasterX 7 | /* 8 | Board Manager: Raspberry Pi Pico 9 | Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 10 | CPU Speed: 200MHz 11 | */ 12 | auto board = VGMConfig; // Video Game Module Configuration 13 | void setup() 14 | { 15 | // Setup file system (must be called in setup) 16 | setup_fs(); 17 | 18 | // Create the game instance with its name, start/stop callbacks, and colors. 19 | Game *game = new Game( 20 | "FlappyBird", // Game name 21 | Vector(board.width, board.height), // Game size 22 | NULL, // start callback 23 | NULL, // stop callback 24 | TFT_BLACK, // Foreground color 25 | TFT_WHITE, // Background color 26 | true, // Use 8-bit graphics? 27 | board, // Board configuration 28 | true // Use double buffering for TFT? 29 | ); 30 | 31 | // set world size 32 | game->world_size = Vector(board.width, board.height); 33 | 34 | // UART buttons 35 | ButtonUART *uart = new ButtonUART(); 36 | 37 | // Add input buttons 38 | game->input_add(new Input(uart)); 39 | 40 | // Create and add a level to the game. 41 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 42 | game->level_add(level); 43 | 44 | // Add the player entity to the level 45 | player_spawn(level, game); 46 | 47 | // Create the game engine (with 30 frames per second target). 48 | GameEngine *engine = new GameEngine("VGM Game Engine", 30, game); 49 | 50 | // Run the game engine's main loop. 51 | engine->run(); 52 | } 53 | 54 | void loop() 55 | { 56 | // nothing to do here 57 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlappyBird/assets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | // Translated with: https://www.flipsocial.net/png-to-fb/ 5 | 6 | // from https://prohama.com/bird-1-size-13x12/ 7 | const PROGMEM uint8_t bird[] = { 8 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xED, 0xED, 0xF2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xE9, 0xC0, 0xC0, 0xE4, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC4, 0xFB, 0xFF, 0xFF, 0xFF, 0xFB, 0xC4, 0xE5, 0xE9, 0xEA, 0xE9, 0xC4, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xC4, 0xF6, 0xFF, 0xF6, 0xC4, 0xE5, 0xE9, 0xF2, 0xFF, 0xFB, 0xFB, 0xE9, 0xF2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xE5, 0xE9, 0xF6, 0xF6, 0xC0, 0xE5, 0xF2, 0xFF, 0xDB, 0x6D, 0xFB, 0xC9, 0xF2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xE5, 0xE5, 0xE9, 0xE9, 0xC0, 0xE9, 0xFB, 0xFF, 0xD6, 0x92, 0xFF, 0xF1, 0xF1, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xE5, 0xEA, 0xE5, 0xC0, 0xC0, 0xE9, 0xFB, 0xFB, 0xFB, 0xFF, 0xF6, 0xF5, 0xF9, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xE5, 0xEA, 0xE5, 0xE5, 0xC0, 0xE9, 0xF7, 0xFF, 0xFB, 0xF6, 0xC9, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE9, 0xEA, 0xE9, 0xC4, 0xC9, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xE4, 0xC0, 0xE5, 0xEA, 0xE9, 0xED, 0xF0, 0xEC, 0xEC, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xC4, 0xE0, 0xE9, 0xED, 0xF4, 0xF4, 0xF4, 0xF4, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF6, 0xF6, 0xF6, 0xC4, 0xE0, 0xED, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xE9, 0xF4, 0xF4, 0xF4, 0xF4, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xC0, 0xE5, 0xE5, 0xE5, 0xE5, 0xE8, 0xF4, 0xF4, 0xF4, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xC0, 0xC0, 0xC0, 0xC0, 0xED, 0xFB, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xED, 0xED, 0xED, 0xED, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlappyBird/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PicoGameEngine.h" 4 | #ifndef TFT_DARKGREEN 5 | #define TFT_DARKGREEN 0x07E0 6 | #endif 7 | #ifndef TFT_BLACK 8 | #define TFT_BLACK 0x0000 9 | #endif 10 | #ifndef TFT_WHITE 11 | #define TFT_WHITE 0xFFFF 12 | #endif 13 | void player_spawn(Level *level, Game *game); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlightAssault/FlightAssault.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | // Translated from https://github.com/evillero/flight_assault/tree/main 5 | // All credits to @evillero 6 | 7 | /* 8 | - Board Manager: Raspberry Pi Pico 9 | - Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 10 | - CPU Speed: 200MHz 11 | */ 12 | auto board = VGMConfig; // Video Game Module Configuration 13 | void setup() 14 | { 15 | // Setup file system (must be called in setup) 16 | setup_fs(); 17 | 18 | // Create the game instance with its name, start/stop callbacks, and colors. 19 | Game *game = new Game( 20 | "Flight Assault", // Game name 21 | Vector(board.width, board.height), // Game size 22 | NULL, // start callback 23 | NULL, // stop callback 24 | TFT_BLACK, // Foreground color 25 | TFT_WHITE, // Background color 26 | true, // Use 8-bit graphics? 27 | board, // Board configuration 28 | true // Use double buffering for TFT? 29 | ); 30 | 31 | // set world size 32 | game->world_size = Vector(board.width, board.height); 33 | 34 | // UART buttons 35 | ButtonUART *uart = new ButtonUART(); 36 | 37 | // Add input buttons 38 | game->input_add(new Input(uart)); 39 | 40 | // Create and add a level to the game. 41 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 42 | game->level_add(level); 43 | 44 | // Add the player entity to the level 45 | player_spawn(level, game); 46 | 47 | // Create the game engine (with 120 frames per second target). 48 | GameEngine *engine = new GameEngine("VGM Game Engine", 120, game); 49 | 50 | // Run the game engine's main loop. 51 | engine->run(); 52 | } 53 | 54 | void loop() 55 | { 56 | // nothing to do here 57 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlightAssault/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PicoGameEngine.h" 4 | void player_spawn(Level *level, Game *game); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlipWorld-PROGMEM/FlipWorld-PROGMEM.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | #include "icon.hpp" 5 | #include "assets.hpp" 6 | /* 7 | Board Manager: Raspberry Pi Pico 8 | Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 9 | CPU Speed: 200MHz 10 | */ 11 | auto board = VGMConfig; // Video Game Module Configuration 12 | void setup() 13 | { 14 | // Setup file system (must be called in setup) 15 | setup_fs(); 16 | 17 | // Create the game instance with its name, start/stop callbacks, and colors. 18 | Game *game = new Game( 19 | "FlipWorld", // Game name 20 | Vector(board.width, board.height), // Game size 21 | NULL, // start callback 22 | NULL, // stop callback 23 | TFT_BLACK, // Foreground color 24 | TFT_WHITE, // Background color 25 | true, // Use 8-bit graphics? 26 | board, // Board configuration 27 | true // Use double buffering for TFT? 28 | ); 29 | 30 | // set world size 31 | game->world_size = Vector(768, 384); 32 | 33 | // UART buttons 34 | ButtonUART *uart = new ButtonUART(); 35 | 36 | // Add input buttons 37 | game->input_add(new Input(uart)); 38 | 39 | // Create and add a level to the game. 40 | Level *level = new Level("Level 1", Vector(768, 384), game, NULL, NULL); 41 | game->level_add(level); 42 | 43 | // set game position to center of player 44 | game->pos = Vector(384, 192); 45 | game->old_pos = game->pos; 46 | 47 | // spawn icons from json 48 | icon_spawn_json(level, shadow_woods_v4); 49 | 50 | // spawn enemys from json 51 | enemy_spawn_json(level, shadow_woods_v4); 52 | 53 | // Add the player entity to the level 54 | player_spawn(level, "sword", Vector(384, 192)); 55 | 56 | // Create the game engine (with 30 frames per second target). 57 | GameEngine *engine = new GameEngine("VGM Game Engine", 30, game); 58 | 59 | // Run the game engine's main loop. 60 | engine->run(); 61 | } 62 | 63 | void loop() 64 | { 65 | // nothing to do here 66 | } 67 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlipWorld-PROGMEM/icon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PicoGameEngine.h" 3 | void icon_spawn_json(Level *level, const char *json); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlipWorld-PROGMEM/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PicoGameEngine.h" 3 | void enemy_spawn_json(Level *level, const char *json); 4 | void player_spawn(Level *level, const char *name, Vector position); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlipWorld/FlipWorld.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | #include "icon.hpp" 5 | #include "assets.hpp" 6 | /* 7 | Board Manager: Raspberry Pi Pico 8 | Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 9 | CPU Speed: 200MHz 10 | */ 11 | auto board = VGMConfig; // Video Game Module Configuration 12 | void setup() 13 | { 14 | // Setup file system (must be called in setup) 15 | setup_fs(); 16 | 17 | // Create the game instance with its name, start/stop callbacks, and colors. 18 | Game *game = new Game( 19 | "FlipWorld", // Game name 20 | Vector(board.width, board.height), // Game size 21 | NULL, // start callback 22 | NULL, // stop callback 23 | TFT_BLACK, // Foreground color 24 | TFT_WHITE, // Background color 25 | true, // Use 8-bit graphics? 26 | board, // Board configuration 27 | true // Use double buffering for TFT? 28 | ); 29 | 30 | // set world size 31 | game->world_size = Vector(768, 384); 32 | 33 | // UART buttons 34 | ButtonUART *uart = new ButtonUART(); 35 | 36 | // Add input buttons 37 | game->input_add(new Input(uart)); 38 | 39 | // Create and add a level to the game. 40 | Level *level = new Level("Level 1", Vector(768, 384), game, NULL, NULL); 41 | game->level_add(level); 42 | 43 | // set game position to center of player 44 | game->pos = Vector(384, 192); 45 | game->old_pos = game->pos; 46 | 47 | // spawn icons from json 48 | icon_spawn_json(level, shadow_woods_v4); 49 | 50 | // spawn enemys from json 51 | enemy_spawn_json(level, shadow_woods_v4); 52 | 53 | // Add the player entity to the level 54 | player_spawn(level, "sword", Vector(384, 192)); 55 | 56 | // Create the game engine (with 30 frames per second target). 57 | GameEngine *engine = new GameEngine("Pico Game Engine", 30, game); 58 | 59 | // Run the game engine's main loop. 60 | engine->run(); 61 | } 62 | 63 | void loop() 64 | { 65 | // nothing to do here 66 | } 67 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlipWorld/assets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | // All assets from FlipWorld game: https://github.com/jblanked/FlipWorld 5 | // Translated with: https://www.jblanked.com/flipper/png-to-fb/ 6 | 7 | extern const PROGMEM char shadow_woods_v4[2210]; 8 | 9 | /* 10 | Name: icon_tree_16x16.png 11 | Link: /assets/icon_tree_16x16.png 12 | Author: https://github.com/the1anonlypr3 13 | */ 14 | extern uint8_t icon_tree_16x16[256]; 15 | 16 | /* 17 | Name: icon_fence_16x8px.png 18 | Link: /assets/icon_fence_16x8px.png 19 | Author: https://github.com/the1anonlypr3 20 | */ 21 | extern uint8_t icon_fence_16x8px[128]; 22 | 23 | /* 24 | Name: icon_fence_end_16x8px.png 25 | Link: /assets/icon_fence_end_16x8px.png 26 | Author: https://github.com/the1anonlypr3 27 | */ 28 | extern uint8_t icon_fence_end_16x8px[128]; 29 | 30 | /* 31 | Name: icon_fence_vertical_end_6x8px.png 32 | Link: /assets/icon_fence_vertical_end_6x8px.png 33 | Author: https://github.com/the1anonlypr3 34 | */ 35 | extern uint8_t icon_fence_vertical_end_6x8px[48]; 36 | 37 | /* 38 | Name: icon_fence_vertical_start_6x15px.png 39 | Link: /assets/icon_fence_vertical_start_6x15px.png 40 | Author: https://github.com/the1anonlypr3 41 | */ 42 | extern uint8_t icon_fence_vertical_start_6x15px[90]; 43 | 44 | /* 45 | Name: icon_rock_small_10x8px.png 46 | Link: /assets/icon_rock_small_10x8px.png 47 | Author: https://github.com/the1anonlypr3 48 | */ 49 | extern uint8_t icon_rock_small_10x8px[80]; 50 | 51 | /* 52 | Name: icon_rock_medium_16x14px.png 53 | Link: /assets/icon_rock_medium_16x14px.png 54 | Author: https://github.com/the1anonlypr3 55 | */ 56 | extern uint8_t icon_rock_medium_16x14px[224]; 57 | 58 | /* 59 | Name: icon_rock_large_18x19px.png 60 | Link: /assets/icon_rock_large_18x19px.png 61 | Author: https://github.com/the1anonlypr3 62 | */ 63 | extern uint8_t icon_rock_large_18x19px[342]; 64 | 65 | /* 66 | Name: icon_flower_16x16.png 67 | Link: /assets/icon_flower_16x16.png 68 | Author: https://github.com/the1anonlypr3 69 | */ 70 | extern uint8_t icon_flower_16x16[256]; 71 | 72 | /* 73 | Name: icon_plant_16x16.png 74 | Link: /assets/icon_plant_16x16.png 75 | Author: https://github.com/the1anonlypr3 76 | */ 77 | extern uint8_t icon_plant_16x16[256]; 78 | 79 | /* 80 | Name: icon_man_7x16.png 81 | Link: /assets/icon_man_7x16.png 82 | Author: https://github.com/the1anonlypr3 83 | */ 84 | extern uint8_t icon_man_7x16[112]; 85 | 86 | /* 87 | Name: icon_woman_9x16.png 88 | Link: /assets/icon_woman_9x16.png 89 | Author: https://github.com/the1anonlypr3 90 | */ 91 | extern uint8_t icon_woman_9x16[144]; 92 | 93 | /* 94 | Name: icon_lake_bottom_31x12px.png 95 | Link: /assets/icon_lake_bottom_31x12px.png 96 | Author: https://github.com/the1anonlypr3 97 | */ 98 | extern uint8_t icon_lake_bottom_31x12px[372]; 99 | 100 | /* 101 | Name: icon_lake_bottom_left_24x22px.png 102 | Link: /assets/icon_lake_bottom_left_24x22px.png 103 | Author: https://github.com/the1anonlypr3 104 | */ 105 | extern uint8_t icon_lake_bottom_left_24x22px[528]; 106 | 107 | /* 108 | Name: icon_lake_bottom_right_24x22px.png 109 | Link: /assets/icon_lake_bottom_right_24x22px.png 110 | Author: https://github.com/the1anonlypr3 111 | */ 112 | extern uint8_t icon_lake_bottom_right_24x22px[528]; 113 | 114 | /* 115 | Name: icon_lake_left_11x31px.png 116 | Link: /assets/icon_lake_left_11x31px.png 117 | Author: https://github.com/the1anonlypr3 118 | */ 119 | extern uint8_t icon_lake_left_11x31px[341]; 120 | 121 | /* 122 | Name: icon_lake_right_11x31.png 123 | Link: /assets/icon_lake_right_11x31.png 124 | Author: https://github.com/the1anonlypr3 125 | */ 126 | extern uint8_t icon_lake_right_11x31[341]; 127 | 128 | /* 129 | Name: icon_lake_top_31x12px.png 130 | Link: /assets/icon_lake_top_31x12px.png 131 | Author: https://github.com/the1anonlypr3 132 | */ 133 | extern uint8_t icon_lake_top_31x12px[372]; 134 | 135 | /* 136 | Name: icon_lake_top_left_24x22px.png 137 | Link: /assets/icon_lake_top_left_24x22px.png 138 | Author: https://github.com/the1anonlypr3 139 | */ 140 | extern uint8_t icon_lake_top_left_24x22px[528]; 141 | 142 | /* 143 | Name: icon_lake_top_right_24x22px.png 144 | Link: /assets/icon_lake_top_right_24x22px.png 145 | Author: https://github.com/the1anonlypr3 146 | */ 147 | extern uint8_t icon_lake_top_right_24x22px[528]; 148 | 149 | /* 150 | Name: icon_house_48x32px.png 151 | Link: /assets/icon_house_48x32px.png 152 | Author: https://github.com/the1anonlypr3 153 | */ 154 | extern uint8_t icon_house_48x32px[1536]; -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlipWorld/icon.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PicoGameEngine.h" 3 | void icon_spawn_json(Level *level, const char *json); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlipWorld/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "PicoGameEngine.h" 3 | void enemy_spawn_json(Level *level, const char *json); 4 | void player_spawn(Level *level, const char *name, Vector position); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FlipWorld/sprites.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | // All sprites from FlipWorld game: https://github.com/jblanked/FlipWorld 5 | // Translated with: https://www.jblanked.com/flipper/png-to-fb/ 6 | 7 | /* 8 | Name: player_left_naked_10x10px.png 9 | Link: /sprites/player_left_naked_10x10px.png 10 | Author: https://github.com/jblanked 11 | */ 12 | extern uint8_t player_left_naked_10x10px[100]; 13 | 14 | /* 15 | Name: player_right_naked_10x10px.png 16 | Link: /sprites/player_right_naked_10x10px.png 17 | Author: https://github.com/jblanked 18 | */ 19 | extern uint8_t player_right_naked_10x10px[100]; 20 | 21 | /* 22 | Name: player_left_sword_15x11px.png 23 | Link: /sprites/player_left_sword_15x11px.png 24 | Author: https://github.com/the1anonlypr3 25 | */ 26 | extern uint8_t player_left_sword_15x11px[165]; 27 | 28 | /* 29 | Name: player_right_sword_15x11px.png 30 | Link: /sprites/player_right_sword_15x11px.png 31 | Author: https://github.com/the1anonlypr3 32 | */ 33 | extern uint8_t player_right_sword_15x11px[165]; 34 | 35 | /* 36 | Name: player_left_axe_15x11px.png 37 | Link: /sprites/player_left_axe_15x11px.png 38 | Author: https://github.com/the1anonlypr3 39 | */ 40 | extern uint8_t player_left_axe_15x11px[165]; 41 | 42 | /* 43 | Name: player_right_axe_15x11px.png 44 | Link: /sprites/player_right_axe_15x11px.png 45 | Author: https://github.com/the1anonlypr3 46 | */ 47 | extern uint8_t player_right_axe_15x11px[165]; 48 | 49 | /* 50 | Name: player_left_bow_13x11px.png 51 | Link: /sprites/player_left_bow_13x11px.png 52 | Author: https://github.com/the1anonlypr3 53 | */ 54 | extern uint8_t player_left_bow_13x11px[143]; 55 | 56 | /* 57 | Name: player_right_bow_13x11px.png 58 | Link: /sprites/player_right_bow_13x11px.png 59 | Author: https://github.com/the1anonlypr3 60 | */ 61 | extern uint8_t player_right_bow_13x11px[143]; 62 | 63 | /* 64 | Name: enemy_left_cyclops_10x11px.png 65 | Link: /sprites/enemy_left_cyclops_10x11px.png 66 | Author: https://github.com/the1anonlypr3 67 | */ 68 | extern uint8_t enemy_left_cyclops_10x11px[110]; 69 | 70 | /* 71 | Name: enemy_right_cyclops_10x11px.png 72 | Link: /sprites/enemy_right_cyclops_10x11px.png 73 | Author: https://github.com/the1anonlypr3 74 | */ 75 | extern uint8_t enemy_right_cyclops_10x11px[110]; 76 | 77 | /* 78 | Name: enemy_left_ghost_15x15px.png 79 | Link: /sprites/enemy_left_ghost_15x15px.png 80 | Author: https://github.com/the1anonlypr3 81 | */ 82 | extern uint8_t enemy_left_ghost_15x15px[225]; 83 | 84 | /* 85 | Name: enemy_right_ghost_15x15px.png 86 | Link: /sprites/enemy_right_ghost_15x15px.png 87 | Author: https://github.com/the1anonlypr3 88 | */ 89 | extern uint8_t enemy_right_ghost_15x15px[225]; 90 | 91 | /* 92 | Name: enemy_left_ogre_10x13px.png 93 | Link: /sprites/enemy_left_ogre_10x13px.png 94 | Author: https://github.com/the1anonlypr3 95 | */ 96 | extern uint8_t enemy_left_ogre_10x13px[130]; 97 | 98 | /* 99 | Name: enemy_right_ogre_10x13px.png 100 | Link: /sprites/enemy_right_ogre_10x13px.png 101 | Author: https://github.com/the1anonlypr3 102 | */ 103 | extern uint8_t enemy_right_ogre_10x13px[130]; 104 | 105 | /* 106 | Name: npc_left_funny_15x21px.png 107 | Link: /sprites/npc_left_funny_15x21px.png 108 | Author: https://github.com/the1anonlypr3 109 | */ 110 | extern uint8_t npc_left_funny_15x21px[315]; 111 | 112 | /* 113 | Name: npc_right_funny_15x21px.png 114 | Link: /sprites/npc_right_funny_15x21px.png 115 | Author: https://github.com/the1anonlypr3 116 | */ 117 | extern uint8_t npc_right_funny_15x21px[315]; -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FuriousBirds/FuriousBirds.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | // Translated from https://github.com/bmstr-ru/furious-birds/blob/main/furious_birds.c 5 | // All credits to @bmstr-ru (Dmitriy Ermashov) 6 | 7 | /* 8 | - Board Manager: Raspberry Pi Pico 9 | - Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 10 | - CPU Speed: 200MHz 11 | */ 12 | auto board = VGMConfig; // Video Game Module Configuration 13 | void setup() 14 | { 15 | // Setup file system (must be called in setup) 16 | setup_fs(); 17 | 18 | // Create the game instance with its name, start/stop callbacks, and colors. 19 | Game *game = new Game( 20 | "Furious Birds", // Game name 21 | Vector(board.width, board.height), // Game size 22 | NULL, // start callback 23 | NULL, // stop callback 24 | TFT_BLACK, // Foreground color 25 | TFT_WHITE, // Background color 26 | true, // Use 8-bit graphics? 27 | board, // Board configuration 28 | true // Use double buffering for TFT? 29 | ); 30 | 31 | // set world size 32 | game->world_size = Vector(board.width, board.height); 33 | 34 | // UART buttons 35 | ButtonUART *uart = new ButtonUART(); 36 | 37 | // Add input buttons 38 | game->input_add(new Input(uart)); 39 | 40 | // Create and add a level to the game. 41 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 42 | game->level_add(level); 43 | 44 | // Add the player entity to the level 45 | player_spawn(level, game); 46 | 47 | // Create the game engine (with 120 frames per second target). 48 | GameEngine *engine = new GameEngine("VGM Game Engine", 120, game); 49 | 50 | // Run the game engine's main loop. 51 | engine->run(); 52 | } 53 | 54 | void loop() 55 | { 56 | // nothing to do here 57 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FuriousBirds/assets.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | // Translated with: https://www.jblanked.com/flipper/png-to-fb/ 5 | 6 | // All assets from https://github.com/bmstr-ru/furious-birds/tree/main/images 7 | static const PROGMEM uint8_t I_Pig[] = { 8 | 0xDB, 0x24, 0x00, 0x00, 0xB6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB6, 0x00, 0x00, 0x49, 0xDB, 0x24, 0xB2, 0xD6, 0xD6, 0x49, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x49, 0xD6, 0xD6, 0xB2, 0x24, 0x00, 0xD6, 0xFB, 0x69, 0x00, 0x8D, 0xD6, 0xD6, 0xD6, 0xD6, 0x8D, 0x00, 0x69, 0xFB, 0xD6, 0x00, 0xB6, 0x49, 0x24, 0x8D, 0xB2, 0xF7, 0xFB, 0xFB, 0xFB, 0xFB, 0xF7, 0xB2, 0x8D, 0x24, 0x49, 0xB6, 0xFF, 0x24, 0x8D, 0x69, 0x49, 0xD6, 0xFB, 0xFB, 0xFB, 0xFB, 0xB2, 0x49, 0x69, 0x8D, 0x24, 0xFF, 0x6D, 0xB2, 0xF7, 0x24, 0x00, 0xB2, 0xFB, 0xFB, 0xFB, 0xFB, 0xB2, 0x00, 0x24, 0xF7, 0xB2, 0x6D, 0x00, 0xD6, 0xFB, 0xB2, 0x8D, 0x6D, 0x69, 0x69, 0x69, 0x49, 0x6D, 0x8D, 0xB2, 0xFB, 0xD6, 0x00, 0x00, 0xD6, 0xFB, 0xFB, 0xB2, 0x69, 0x89, 0x89, 0x89, 0x89, 0x69, 0xB2, 0xFB, 0xFB, 0xD6, 0x00, 0x00, 0xD6, 0xFB, 0xFB, 0x44, 0x8D, 0xAD, 0xAD, 0xAD, 0xAD, 0x8D, 0x49, 0xFB, 0xFB, 0xD6, 0x00, 0x00, 0xD6, 0xFB, 0xFB, 0x44, 0x8D, 0xAD, 0xAD, 0xAD, 0xAD, 0x8D, 0x49, 0xFB, 0xFB, 0xD6, 0x00, 0x6D, 0x8D, 0xF7, 0xFB, 0x8D, 0x69, 0x89, 0x89, 0x89, 0x89, 0x69, 0x8D, 0xFB, 0xF7, 0x8D, 0x6D, 0xFF, 0x24, 0xD6, 0xFB, 0xFB, 0x8D, 0x49, 0x49, 0x49, 0x49, 0x8D, 0xFB, 0xFB, 0xD6, 0x24, 0xFF, 0xFF, 0x6D, 0xB2, 0xF7, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xF7, 0xB2, 0x6D, 0xFF, 0xFF, 0xFF, 0x49, 0xB2, 0xD6, 0xF7, 0xFB, 0xFB, 0xFB, 0xFB, 0xF7, 0xD6, 0xB2, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, 0x49, 0xB2, 0x8D, 0x8D, 0xD6, 0xD6, 0xD6, 0xD6, 0x8D, 0x8D, 0xB2, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, 0x49, 0x8D, 0xD6, 0x44, 0x24, 0x24, 0x24, 0x24, 0x44, 0xD6, 0x8D, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, 0x24, 0x00, 0x00, 0x00, 0x92, 0xFF, 0xFF, 0x92, 0x00, 0x00, 0x00, 0x24, 0xFF, 0xFF}; 9 | 10 | static const PROGMEM uint8_t I_Red[] = { 11 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xED, 0xED, 0xF2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xE9, 0xC0, 0xC0, 0xE4, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC4, 0xFB, 0xFF, 0xFF, 0xFF, 0xFB, 0xC4, 0xE5, 0xE9, 0xEA, 0xE9, 0xC4, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xC4, 0xF6, 0xFF, 0xF6, 0xC4, 0xE5, 0xE9, 0xF2, 0xFF, 0xFB, 0xFB, 0xE9, 0xF2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xE5, 0xE9, 0xF6, 0xF6, 0xC0, 0xE5, 0xF2, 0xFF, 0xDB, 0x6D, 0xFB, 0xC9, 0xF2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xE5, 0xE5, 0xE9, 0xE9, 0xC0, 0xE9, 0xFB, 0xFF, 0xD6, 0x92, 0xFF, 0xF1, 0xF1, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xE5, 0xEA, 0xE5, 0xC0, 0xC0, 0xE9, 0xFB, 0xFB, 0xFB, 0xFF, 0xF6, 0xF5, 0xF9, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC0, 0xE5, 0xEA, 0xE5, 0xE5, 0xC0, 0xE9, 0xF7, 0xFF, 0xFB, 0xF6, 0xC9, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xC4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE9, 0xEA, 0xE9, 0xC4, 0xC9, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xE4, 0xC0, 0xE5, 0xEA, 0xE9, 0xED, 0xF0, 0xEC, 0xEC, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xC4, 0xE0, 0xE9, 0xED, 0xF4, 0xF4, 0xF4, 0xF4, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF6, 0xF6, 0xF6, 0xC4, 0xE0, 0xED, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xE9, 0xF4, 0xF4, 0xF4, 0xF4, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xC0, 0xE5, 0xE5, 0xE5, 0xE5, 0xE8, 0xF4, 0xF4, 0xF4, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0xC0, 0xC0, 0xC0, 0xC0, 0xED, 0xFB, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xED, 0xED, 0xED, 0xED, 0xF6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 12 | 13 | static const PROGMEM uint8_t I_Slingshot[] = { 14 | 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FuriousBirds/flipper.cpp: -------------------------------------------------------------------------------- 1 | #include "flipper.hpp" 2 | 3 | void furi_hal_random_fill_buf(void *buffer, size_t len) 4 | { 5 | uint8_t *buf = (uint8_t *)buffer; 6 | for (size_t i = 0; i < len; i++) 7 | { 8 | buf[i] = (uint8_t)random(256); 9 | } 10 | } 11 | 12 | void canvas_clear(Canvas *canvas, uint16_t color) 13 | { 14 | canvas->clear(Vector(0, 0), canvas->getSize(), color); 15 | } 16 | 17 | size_t canvas_current_font_height(const Canvas *canvas) 18 | { 19 | return 8; 20 | } 21 | 22 | void canvas_draw_box(Canvas *canvas, int32_t x, int32_t y, size_t width, size_t height, uint16_t color) 23 | { 24 | canvas->display->drawRect(x, y, width, height, color); 25 | } 26 | 27 | void canvas_draw_dot(Canvas *canvas, int32_t x, int32_t y, uint16_t color) 28 | { 29 | canvas->display->drawPixel(x, y, color); 30 | } 31 | 32 | void canvas_draw_frame(Canvas *canvas, int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color) 33 | { 34 | #ifdef PicoEngine 35 | canvas->tft.drawRect(x, y, w, h, color); 36 | #else 37 | canvas->display->drawRect(x, y, w, h, color); 38 | #endif 39 | } 40 | 41 | void canvas_draw_icon(Canvas *canvas, int32_t x, int32_t y, const uint8_t *icon, int32_t w, int32_t h, uint16_t color) 42 | { 43 | #ifdef PicoEngine 44 | canvas->tft.drawBitmap(x, y, icon, w, h, color); 45 | #else 46 | canvas->display->drawBitmap(x, y, icon, w, h, color); 47 | #endif 48 | } 49 | 50 | void canvas_draw_line(Canvas *canvas, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color) 51 | { 52 | canvas->display->drawLine(x1, y1, x2, y2, color); 53 | } 54 | 55 | void canvas_draw_rframe(Canvas *canvas, int32_t x, int32_t y, size_t width, size_t height, size_t radius, uint16_t color) 56 | { 57 | canvas->display->drawRoundRect(x, y, width, height, radius, color); 58 | } 59 | 60 | void canvas_draw_str(Canvas *canvas, int32_t x, int32_t y, const char *str, uint16_t color) 61 | { 62 | canvas->text(Vector(x, y), str, color); 63 | } 64 | 65 | void canvas_draw_str_aligned(Canvas *canvas, int32_t x, int32_t y, int32_t align_x, int32_t align_y, const char *str, uint16_t color) 66 | { 67 | canvas->text(Vector(x, y), str, color); 68 | } 69 | 70 | size_t canvas_height(Canvas *canvas) 71 | { 72 | return canvas->getSize().y; 73 | } 74 | 75 | void canvas_set_bitmap_mode(Canvas *canvas, bool alpha) 76 | { 77 | // nothing to do 78 | } 79 | 80 | void canvas_set_color(Canvas *canvas, FlipperColor color) 81 | { 82 | // nothing to do 83 | } 84 | 85 | void canvas_set_font(Canvas *canvas, FlipperFont font) 86 | { 87 | // nothing to do 88 | } 89 | 90 | uint16_t canvas_string_width(Canvas *canvas, const char *str) 91 | { 92 | // width in pixels. 93 | return strlen(str) * 8; 94 | } 95 | 96 | size_t canvas_width(Canvas *canvas) 97 | { 98 | return canvas->getSize().x; 99 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FuriousBirds/flipper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define furi_get_tick() (millis() / 10) 8 | #define furi_kernel_get_tick_frequency() 200 9 | #define furi_hal_random_get() random(256) 10 | void furi_hal_random_fill_buf(void *buffer, size_t len); 11 | 12 | typedef enum 13 | { 14 | AlignLeft, 15 | AlignRight, 16 | AlignTop, 17 | AlignBottom, 18 | AlignCenter, 19 | } FlipperAlign; 20 | 21 | typedef enum 22 | { 23 | ColorWhite = 0x00, 24 | ColorBlack = 0x01, 25 | ColorXOR = 0x02, 26 | } FlipperColor; 27 | 28 | typedef enum 29 | { 30 | FontPrimary, 31 | FontSecondary, 32 | FontKeyboard, 33 | FontBigNumbers, 34 | } FlipperFont; 35 | 36 | typedef enum 37 | { 38 | InputKeyRight = BUTTON_RIGHT, 39 | InputKeyLeft = BUTTON_LEFT, 40 | InputKeyUp = BUTTON_UP, 41 | InputKeyDown = BUTTON_DOWN, 42 | InputKeyOk = BUTTON_CENTER, 43 | InputKeyBack = BUTTON_BACK 44 | } FlipperInputKey; 45 | 46 | // https://doc-tft-espi.readthedocs.io/tft_espi/colors/ 47 | #ifndef TFT_DARKCYAN 48 | #define TFT_DARKCYAN 0x03EF 49 | #endif 50 | #ifndef TFT_DARKGREEN 51 | #define TFT_DARKGREEN 0x03E0 52 | #endif 53 | #ifndef TFT_BLACK 54 | #define TFT_BLACK 0x0000 55 | #endif 56 | #ifndef TFT_WHITE 57 | #define TFT_WHITE 0xFFFF 58 | #endif 59 | #ifndef TFT_BLUE 60 | #define TFT_BLUE 0x001F 61 | #endif 62 | #ifndef TFT_RED 63 | #define TFT_RED 0xF800 64 | #endif 65 | #ifndef TFT_SKYBLUE 66 | #define TFT_SKYBLUE 0x867D 67 | #endif 68 | #ifndef TFT_VIOLET 69 | #define TFT_VIOLET 0x915C 70 | #endif 71 | #ifndef TFT_BROWN 72 | #define TFT_BROWN 0x9A60 73 | #endif 74 | #ifndef TFT_TRANSPARENT 75 | #define TFT_TRANSPARENT 0x0120 76 | #endif 77 | #ifndef TFT_YELLOW 78 | #define TFT_YELLOW 0xFFE0 79 | #endif 80 | #ifndef TFT_ORANGE 81 | #define TFT_ORANGE 0xFDA0 82 | #endif 83 | #ifndef TFT_PINK 84 | #define TFT_PINK 0xFE19 85 | #endif 86 | 87 | #ifndef FLIPPER_SCREEN_WIDTH 88 | #define FLIPPER_SCREEN_WIDTH 128 89 | #endif 90 | #ifndef FLIPPER_SCREEN_HEIGHT 91 | #define FLIPPER_SCREEN_HEIGHT 64 92 | #endif 93 | 94 | #ifndef FLIPPER_SCREEN_SIZE 95 | #define FLIPPER_SCREEN_SIZE Vector(FLIPPER_SCREEN_WIDTH, FLIPPER_SCREEN_HEIGHT) 96 | #endif 97 | 98 | typedef Draw Canvas; 99 | 100 | void canvas_clear(Canvas *canvas, uint16_t color = TFT_WHITE); 101 | size_t canvas_current_font_height(const Canvas *canvas); 102 | void canvas_draw_box(Canvas *canvas, int32_t x, int32_t y, size_t width, size_t height, uint16_t color = TFT_BLACK); 103 | void canvas_draw_dot(Canvas *canvas, int32_t x, int32_t y, uint16_t color = TFT_BLACK); 104 | void canvas_draw_frame(Canvas *canvas, int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color = TFT_BLACK); 105 | void canvas_draw_icon(Canvas *canvas, int32_t x, int32_t y, const uint8_t *icon, int32_t w, int32_t h, uint16_t color = TFT_BLACK); 106 | void canvas_draw_line(Canvas *canvas, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color = TFT_BLACK); 107 | void canvas_draw_rframe(Canvas *canvas, int32_t x, int32_t y, size_t width, size_t height, size_t radius, uint16_t color = TFT_BLACK); 108 | void canvas_draw_str(Canvas *canvas, int32_t x, int32_t y, const char *str, uint16_t color = TFT_BLACK); 109 | void canvas_draw_str_aligned(Canvas *canvas, int32_t x, int32_t y, int32_t align_x, int32_t align_y, const char *str, uint16_t color = TFT_BLACK); 110 | size_t canvas_height(Canvas *canvas); 111 | void canvas_set_bitmap_mode(Canvas *canvas, bool alpha); 112 | void canvas_set_color(Canvas *canvas, FlipperColor color); 113 | void canvas_set_font(Canvas *canvas, FlipperFont font); 114 | uint16_t canvas_string_width(Canvas *canvas, const char *str); 115 | size_t canvas_width(Canvas *canvas); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/FuriousBirds/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PicoGameEngine.h" 4 | void player_spawn(Level *level, Game *game); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Hawaii/Hawaii.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "frames.hpp" 4 | #include 5 | // from https://ia801300.us.archive.org/23/items/Architects_of_Tomorrow/2007-01-23-02-11-17_512kb.mp4 6 | /* 7 | - Board Manager: Raspberry Pi Pico 8 | - Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 9 | - CPU Speed: 200MHz 10 | */ 11 | int next_frame = 0; 12 | int max_frame = 24; 13 | int direction = 1; 14 | const PROGMEM uint8_t *frame_data(uint8_t index) 15 | { 16 | return index == 1 ? frame_1 : index == 2 ? frame_2 17 | : index == 3 ? frame_3 18 | : index == 4 ? frame_4 19 | : index == 5 ? frame_5 20 | : index == 6 ? frame_6 21 | : index == 7 ? frame_7 22 | : index == 8 ? frame_8 23 | : index == 9 ? frame_9 24 | : index == 10 ? frame_10 25 | : index == 11 ? frame_11 26 | : index == 12 ? frame_12 27 | : index == 13 ? frame_13 28 | : index == 14 ? frame_14 29 | : index == 15 ? frame_15 30 | : index == 16 ? frame_16 31 | : index == 17 ? frame_17 32 | : index == 18 ? frame_18 33 | : index == 19 ? frame_19 34 | : index == 20 ? frame_20 35 | : index == 21 ? frame_21 36 | : index == 22 ? frame_22 37 | : index == 23 ? frame_23 38 | : frame_24; 39 | } 40 | 41 | static void player_render(Entity *self, Draw *draw, Game *game) 42 | { 43 | // draw frame 44 | draw->image(Vector(0, 0), frame_data(next_frame), Vector(320, 240)); 45 | 46 | next_frame += direction; 47 | if (next_frame == max_frame) 48 | { 49 | direction = -1; 50 | } 51 | if (next_frame == 1) 52 | { 53 | direction = 1; 54 | } 55 | } 56 | static void animation(Game *game, Level *level) 57 | { 58 | // Create a blank entity 59 | Entity *player = new Entity("Player", 60 | ENTITY_PLAYER, 61 | Vector(-100, -100), 62 | Vector(0, 0), 63 | NULL, NULL, NULL, NULL, NULL, 64 | NULL, 65 | player_render, 66 | NULL, 67 | true); 68 | level->entity_add(player); 69 | next_frame = 1; 70 | } 71 | void setup() 72 | { 73 | // Setup file system (must be called in setup) 74 | setup_fs(); 75 | 76 | // Create the game instance with its name, start/stop callbacks, and colors. 77 | Game *game = new Game("Hawaii", Vector(320, 240), NULL, NULL, TFT_RED, TFT_WHITE, true); 78 | 79 | // set world size 80 | game->world_size = Vector(320, 240); 81 | 82 | // Create and add a level to the game. 83 | Level *level = new Level("Level 1", Vector(320, 240), game); 84 | game->level_add(level); 85 | 86 | // Add the player entity to the level 87 | animation(game, level); 88 | 89 | // Create the game engine (with target set at the amount of available frames) 90 | GameEngine *engine = new GameEngine("VGM Game Engine", max_frame, game); 91 | 92 | // Run the game engine's main loop. 93 | engine->run(); 94 | } 95 | 96 | void loop() 97 | { 98 | // nothing to do here 99 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Pong/Pong.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | // Translated from https://github.com/xMasterX/all-the-plugins/blob/dev/apps_source_code/flipper_pong/flipper_pong.c 5 | // All credits to @nmrr @SimplyMinimal @xMasterX 6 | /* 7 | Board Manager: Raspberry Pi Pico 8 | Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 9 | CPU Speed: 200MHz 10 | */ 11 | auto board = VGMConfig; // Video Game Module Configuration 12 | void setup() 13 | { 14 | // Setup file system (must be called in setup) 15 | setup_fs(); 16 | 17 | // Create the game instance with its name, start/stop callbacks, and colors. 18 | Game *game = new Game( 19 | "Pong", // Game name 20 | Vector(board.width, board.height), // Game size 21 | NULL, // start callback 22 | NULL, // stop callback 23 | TFT_BLACK, // Foreground color 24 | TFT_WHITE, // Background color 25 | true, // Use 8-bit graphics? 26 | board, // Board configuration 27 | true // Use double buffering for TFT? 28 | ); 29 | 30 | // set world size 31 | game->world_size = Vector(board.width, board.height); 32 | 33 | // UART buttons 34 | ButtonUART *uart = new ButtonUART(); 35 | 36 | // Add input buttons 37 | game->input_add(new Input(uart)); 38 | 39 | // Create and add a level to the game. 40 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 41 | game->level_add(level); 42 | 43 | // Add the player entity to the level 44 | player_spawn(level, game); 45 | 46 | // Create the game engine (with 60 frames per second target). 47 | GameEngine *engine = new GameEngine("VGM Game Engine", 60, game); 48 | 49 | // Run the game engine's main loop. 50 | engine->run(); 51 | } 52 | 53 | void loop() 54 | { 55 | // nothing to do here 56 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Pong/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PicoGameEngine.h" 4 | void player_spawn(Level *level, Game *game); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/T-Rex-Runner/T-Rex-Runner.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "assets.hpp" 4 | #include "player.hpp" 5 | // Translated from https://github.com/xMasterX/all-the-plugins/tree/dev/apps_source_code/t-rex-runner 6 | // All credits to @Rrycbarm and @xMasterX 7 | /* 8 | Board Manager: Raspberry Pi Pico 9 | Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 10 | CPU Speed: 200MHz 11 | */ 12 | auto board = VGMConfig; // Video Game Module Configuration 13 | void setup() 14 | { 15 | // Setup file system (must be called in setup) 16 | setup_fs(); 17 | 18 | // Create the game instance with its name, start/stop callbacks, and colors. 19 | Game *game = new Game( 20 | "T-Rex", // Game name 21 | Vector(board.width, board.height), // Game size 22 | NULL, // start callback 23 | NULL, // stop callback 24 | TFT_BLACK, // Foreground color 25 | TFT_WHITE, // Background color 26 | true, // Use 8-bit graphics? 27 | board, // Board configuration 28 | true // Use double buffering for TFT? 29 | ); 30 | 31 | // set world size 32 | game->world_size = Vector(board.width, board.height); 33 | 34 | // UART buttons 35 | ButtonUART *uart = new ButtonUART(); 36 | 37 | // Add input buttons 38 | game->input_add(new Input(uart)); 39 | 40 | // Create and add a level to the game. 41 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 42 | game->level_add(level); 43 | 44 | // Add the player entity to the level 45 | player_spawn(level); 46 | 47 | // Create the game engine (with 30 frames per second target). 48 | GameEngine *engine = new GameEngine("VGM Game Engine", 30, game); 49 | 50 | // Run the game engine's main loop. 51 | engine->run(); 52 | } 53 | 54 | void loop() 55 | { 56 | // nothing to do here 57 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/T-Rex-Runner/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PicoGameEngine.h" 4 | void player_spawn(Level *level); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Tetris/Tetris.ino: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "PicoGameEngine.h" 3 | #include "player.hpp" 4 | // Translated from https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/tetris_game 5 | // All credits to @xMasterX @jeffplang @noiob 6 | 7 | /* 8 | - Board Manager: Raspberry Pi Pico 9 | - Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 10 | - CPU Speed: 200MHz 11 | */ 12 | auto board = VGMConfig; // Video Game Module Configuration 13 | void setup() 14 | { 15 | // Setup file system (must be called in setup) 16 | setup_fs(); 17 | 18 | // Create the game instance with its name, start/stop callbacks, and colors. 19 | Game *game = new Game( 20 | "Tetris", // Game name 21 | Vector(board.width, board.height), // Game size 22 | NULL, // start callback 23 | NULL, // stop callback 24 | TFT_BLACK, // Foreground color 25 | TFT_WHITE, // Background color 26 | true, // Use 8-bit graphics? 27 | board, // Board configuration 28 | true // Use double buffering for TFT? 29 | ); 30 | 31 | // set world size 32 | game->world_size = Vector(board.width, board.height); 33 | 34 | // UART buttons 35 | ButtonUART *uart = new ButtonUART(); 36 | 37 | // Add input buttons 38 | game->input_add(new Input(uart)); 39 | 40 | // Create and add a level to the game. 41 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 42 | game->level_add(level); 43 | 44 | // Add the player entity to the level 45 | player_spawn(level, game); 46 | 47 | // Create the game engine (with 15 frames per second target). 48 | GameEngine *engine = new GameEngine("VGM Game Engine", 15, game); 49 | 50 | // Run the game engine's main loop. 51 | engine->run(); 52 | } 53 | 54 | void loop() 55 | { 56 | // nothing to do here 57 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Tetris/flipper.cpp: -------------------------------------------------------------------------------- 1 | #include "flipper.hpp" 2 | 3 | void furi_hal_random_fill_buf(void *buffer, size_t len) 4 | { 5 | uint8_t *buf = (uint8_t *)buffer; 6 | for (size_t i = 0; i < len; i++) 7 | { 8 | buf[i] = (uint8_t)random(256); 9 | } 10 | } 11 | 12 | void canvas_clear(Canvas *canvas, uint16_t color) 13 | { 14 | canvas->clear(Vector(0, 0), canvas->getSize(), color); 15 | } 16 | 17 | size_t canvas_current_font_height(const Canvas *canvas) 18 | { 19 | return 8; 20 | } 21 | 22 | void canvas_draw_box(Canvas *canvas, int32_t x, int32_t y, size_t width, size_t height, uint16_t color) 23 | { 24 | #ifdef PicoEngine 25 | canvas->tft.drawRect(x, y, width, height, color); 26 | #else 27 | canvas->display->drawRect(x, y, width, height, color); 28 | #endif 29 | } 30 | 31 | void canvas_draw_dot(Canvas *canvas, int32_t x, int32_t y, uint16_t color) 32 | { 33 | #ifdef PicoEngine 34 | canvas->tft.drawPixel(x, y, color); 35 | #else 36 | canvas->display->drawPixel(x, y, color); 37 | #endif 38 | } 39 | 40 | void canvas_draw_frame(Canvas *canvas, int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color) 41 | { 42 | #ifdef PicoEngine 43 | canvas->tft.drawRect(x, y, w, h, color); 44 | #else 45 | canvas->display->drawRect(x, y, w, h, color); 46 | #endif 47 | } 48 | 49 | void canvas_draw_icon(Canvas *canvas, int32_t x, int32_t y, const uint8_t *icon, int32_t w, int32_t h, uint16_t color) 50 | { 51 | #ifdef PicoEngine 52 | canvas->tft.drawBitmap(x, y, icon, w, h, color); 53 | #else 54 | canvas->display->drawBitmap(x, y, icon, w, h, color); 55 | #endif 56 | } 57 | 58 | void canvas_draw_line(Canvas *canvas, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color) 59 | { 60 | #ifdef PicoEngine 61 | canvas->tft.drawLine(x1, y1, x2, y2, color); 62 | #else 63 | canvas->display->drawLine(x1, y1, x2, y2, color); 64 | #endif 65 | } 66 | 67 | void canvas_draw_rframe(Canvas *canvas, int32_t x, int32_t y, size_t width, size_t height, size_t radius, uint16_t color) 68 | { 69 | #ifdef PicoEngine 70 | canvas->tft.drawRoundRect(x, y, width, height, radius, color); 71 | #else 72 | canvas->display->drawRoundRect(x, y, width, height, radius, color); 73 | #endif 74 | } 75 | 76 | void canvas_draw_str(Canvas *canvas, int32_t x, int32_t y, const char *str, uint16_t color) 77 | { 78 | #ifdef PicoEngine 79 | canvas->text(Vector(x, y), str, 1, color); 80 | #else 81 | canvas->text(Vector(x, y), str, color); 82 | #endif 83 | } 84 | 85 | void canvas_draw_str_aligned(Canvas *canvas, int32_t x, int32_t y, int32_t align_x, int32_t align_y, const char *str, uint16_t color) 86 | { 87 | #ifdef PicoEngine 88 | canvas->text(Vector(x, y), str, 1, color); 89 | #else 90 | canvas->text(Vector(x, y), str, color); 91 | #endif 92 | } 93 | 94 | size_t canvas_height(Canvas *canvas) 95 | { 96 | return canvas->getSize().y; 97 | } 98 | 99 | void canvas_set_bitmap_mode(Canvas *canvas, bool alpha) 100 | { 101 | // nothing to do 102 | } 103 | 104 | void canvas_set_color(Canvas *canvas, FlipperColor color) 105 | { 106 | // nothing to do 107 | } 108 | 109 | void canvas_set_font(Canvas *canvas, FlipperFont font) 110 | { 111 | // nothing to do 112 | } 113 | 114 | uint16_t canvas_string_width(Canvas *canvas, const char *str) 115 | { 116 | // width in pixels. 117 | return strlen(str) * 8; 118 | } 119 | 120 | size_t canvas_width(Canvas *canvas) 121 | { 122 | return canvas->getSize().x; 123 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Tetris/flipper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define furi_assert(x) assert(x) 8 | #define furi_delay_ms(x) delay(x) 9 | #define furi_get_tick() (millis() / 10) 10 | #define furi_kernel_get_tick_frequency() 200 11 | #define furi_hal_random_get() random(256) 12 | void furi_hal_random_fill_buf(void *buffer, size_t len); 13 | 14 | typedef enum 15 | { 16 | AlignLeft, 17 | AlignRight, 18 | AlignTop, 19 | AlignBottom, 20 | AlignCenter, 21 | } FlipperAlign; 22 | 23 | typedef enum 24 | { 25 | ColorWhite = 0x00, 26 | ColorBlack = 0x01, 27 | ColorXOR = 0x02, 28 | } FlipperColor; 29 | 30 | typedef enum 31 | { 32 | FontPrimary, 33 | FontSecondary, 34 | FontKeyboard, 35 | FontBigNumbers, 36 | } FlipperFont; 37 | 38 | typedef enum 39 | { 40 | InputKeyRight = BUTTON_RIGHT, 41 | InputKeyLeft = BUTTON_LEFT, 42 | InputKeyUp = BUTTON_UP, 43 | InputKeyDown = BUTTON_DOWN, 44 | InputKeyOk = BUTTON_CENTER, 45 | InputKeyBack = BUTTON_BACK 46 | } FlipperInputKey; 47 | 48 | // https://doc-tft-espi.readthedocs.io/tft_espi/colors/ 49 | #ifndef TFT_DARKCYAN 50 | #define TFT_DARKCYAN 0x03EF 51 | #endif 52 | #ifndef TFT_DARKGREEN 53 | #define TFT_DARKGREEN 0x03E0 54 | #endif 55 | #ifndef TFT_BLACK 56 | #define TFT_BLACK 0x0000 57 | #endif 58 | #ifndef TFT_WHITE 59 | #define TFT_WHITE 0xFFFF 60 | #endif 61 | #ifndef TFT_BLUE 62 | #define TFT_BLUE 0x001F 63 | #endif 64 | #ifndef TFT_RED 65 | #define TFT_RED 0xF800 66 | #endif 67 | #ifndef TFT_SKYBLUE 68 | #define TFT_SKYBLUE 0x867D 69 | #endif 70 | #ifndef TFT_VIOLET 71 | #define TFT_VIOLET 0x915C 72 | #endif 73 | #ifndef TFT_BROWN 74 | #define TFT_BROWN 0x9A60 75 | #endif 76 | #ifndef TFT_TRANSPARENT 77 | #define TFT_TRANSPARENT 0x0120 78 | #endif 79 | #ifndef TFT_YELLOW 80 | #define TFT_YELLOW 0xFFE0 81 | #endif 82 | #ifndef TFT_ORANGE 83 | #define TFT_ORANGE 0xFDA0 84 | #endif 85 | #ifndef TFT_PINK 86 | #define TFT_PINK 0xFE19 87 | #endif 88 | 89 | #ifndef FLIPPER_SCREEN_WIDTH 90 | #define FLIPPER_SCREEN_WIDTH 128 91 | #endif 92 | #ifndef FLIPPER_SCREEN_HEIGHT 93 | #define FLIPPER_SCREEN_HEIGHT 64 94 | #endif 95 | 96 | #ifndef FLIPPER_SCREEN_SIZE 97 | #define FLIPPER_SCREEN_SIZE Vector(FLIPPER_SCREEN_WIDTH, FLIPPER_SCREEN_HEIGHT) 98 | #endif 99 | 100 | typedef Draw Canvas; 101 | 102 | void canvas_clear(Canvas *canvas, uint16_t color = TFT_WHITE); 103 | size_t canvas_current_font_height(const Canvas *canvas); 104 | void canvas_draw_box(Canvas *canvas, int32_t x, int32_t y, size_t width, size_t height, uint16_t color = TFT_BLACK); 105 | void canvas_draw_dot(Canvas *canvas, int32_t x, int32_t y, uint16_t color = TFT_BLACK); 106 | void canvas_draw_frame(Canvas *canvas, int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color = TFT_BLACK); 107 | void canvas_draw_icon(Canvas *canvas, int32_t x, int32_t y, const uint8_t *icon, int32_t w, int32_t h, uint16_t color = TFT_BLACK); 108 | void canvas_draw_line(Canvas *canvas, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color = TFT_BLACK); 109 | void canvas_draw_rframe(Canvas *canvas, int32_t x, int32_t y, size_t width, size_t height, size_t radius, uint16_t color = TFT_BLACK); 110 | void canvas_draw_str(Canvas *canvas, int32_t x, int32_t y, const char *str, uint16_t color = TFT_BLACK); 111 | void canvas_draw_str_aligned(Canvas *canvas, int32_t x, int32_t y, int32_t align_x, int32_t align_y, const char *str, uint16_t color = TFT_BLACK); 112 | size_t canvas_height(Canvas *canvas); 113 | void canvas_set_bitmap_mode(Canvas *canvas, bool alpha); 114 | void canvas_set_color(Canvas *canvas, FlipperColor color); 115 | void canvas_set_font(Canvas *canvas, FlipperFont font); 116 | uint16_t canvas_string_width(Canvas *canvas, const char *str); 117 | size_t canvas_width(Canvas *canvas); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/Tetris/player.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "PicoGameEngine.h" 4 | void player_spawn(Level *level, Game *game); -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/examples/example/example.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "PicoGameEngine.h" 3 | // Note: Use the example_8bit for no screen flickering/tearing 4 | /* 5 | Board Manager: Raspberry Pi Pico 6 | Flash Size: 2MB (Sketch: 1984KB, FS: 64KB) 7 | CPU Speed: 200MHz 8 | */ 9 | auto board = VGMConfig; // Video Game Module Configuration 10 | const PROGMEM uint8_t player_10x10px[200] = { 11 | 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 13 | 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 14 | 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 15 | 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 16 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 17 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 21 | 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 23 | 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 25 | 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 27 | 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 29 | 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}; 31 | 32 | /* Update the player entity using current game input */ 33 | void player_update(Entity *self, Game *game) 34 | { 35 | Vector oldPos = self->position; 36 | Vector newPos = oldPos; 37 | 38 | // Move according to input 39 | if (game->input == BUTTON_UP) 40 | newPos.y -= 10; 41 | else if (game->input == BUTTON_DOWN) 42 | newPos.y += 10; 43 | else if (game->input == BUTTON_LEFT) 44 | newPos.x -= 10; 45 | else if (game->input == BUTTON_RIGHT) 46 | newPos.x += 10; 47 | 48 | // set new position 49 | self->position_set(newPos); 50 | 51 | // check if new position is within the level boundaries 52 | if (newPos.x < 0 || newPos.x + self->size.x > game->current_level->size.x || 53 | newPos.y < 0 || newPos.y + self->size.y > game->current_level->size.y) 54 | { 55 | // restore old position 56 | self->position_set(oldPos); 57 | } 58 | 59 | // Store the current camera position before updating 60 | game->old_pos = game->pos; 61 | 62 | // Update camera position to center the player 63 | float camera_x = self->position.x - (game->size.x / 2); 64 | float camera_y = self->position.y - (game->size.y / 2); 65 | 66 | // Clamp camera position to the world boundaries 67 | camera_x = constrain(camera_x, 0, game->current_level->size.x - game->size.x); 68 | camera_y = constrain(camera_y, 0, game->current_level->size.y - game->size.y); 69 | 70 | // Set the new camera position 71 | game->pos = Vector(camera_x, camera_y); 72 | } 73 | 74 | /* Render the player entity along with game information */ 75 | void player_render(Entity *player, Draw *draw, Game *game) 76 | { 77 | /* 78 | Draw anything extra here 79 | The engine will draw the player entity 80 | */ 81 | game->draw->text(Vector(110, 10), "VGM Game Engine"); 82 | } 83 | 84 | void setup() 85 | { 86 | // Setup file system (must be called in setup) 87 | setup_fs(); 88 | 89 | // Create the game instance with its name, start/stop callbacks, and colors. 90 | Game *game = new Game( 91 | "VGM Game Engine", // Game name 92 | Vector(board.width, board.height), // Game size 93 | NULL, // start callback 94 | NULL, // stop callback 95 | TFT_RED, // Foreground color 96 | TFT_WHITE, // Background color 97 | false, // Use 8-bit graphics? 98 | board, // Board configuration 99 | false // Use double buffering for TFT 100 | ); 101 | 102 | // set world size 103 | game->world_size = game->size; 104 | 105 | // UART buttons 106 | ButtonUART *uart = new ButtonUART(); 107 | 108 | // Add input buttons 109 | game->input_add(new Input(uart)); 110 | 111 | // Create and add a level to the game. 112 | Level *level = new Level("Level 1", Vector(board.width, board.height), game); 113 | game->level_add(level); 114 | 115 | Entity *player = new Entity( 116 | "Player", 117 | ENTITY_PLAYER, 118 | Vector(board.width / 2, board.height / 2), // Initial position 119 | Vector(10, 10), 120 | player_10x10px, 121 | NULL, // No sprite left 122 | NULL, // No sprite right 123 | NULL, // No custom initialization routine 124 | NULL, // No custom destruction routine 125 | player_update, // Update callback 126 | player_render, // Render callback 127 | NULL, // No collision callback 128 | false // 8-bit sprite? 129 | ); 130 | 131 | // Add the player entity to the level 132 | level->entity_add(player); 133 | 134 | // Create the game engine (with 30 frames per second target). 135 | GameEngine *engine = new GameEngine("VGM Game Engine", 30, game); 136 | 137 | // Run the game engine's main loop. 138 | engine->run(); 139 | } 140 | 141 | void loop() 142 | { 143 | // nothing to do here 144 | } 145 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/library.properties: -------------------------------------------------------------------------------- 1 | name=Pico Game Engine 2 | version=1.0.2 3 | author=JBlanked 4 | maintainer=JBlanked 5 | sentence=Arduino library for the Pico Game Engine. 6 | paragraph=Arduino library for the Pico Game Engine. 7 | category=Display 8 | url=https://github.com/jblanked/pico-game-engine 9 | architectures=* 10 | includes=PicoGameEngine.h 11 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/PicoGameEngine.h: -------------------------------------------------------------------------------- 1 | /* 2 | Pico Game Engine 3 | Author: JBlanked 4 | Github: https://github.com/jblanked/pico-game-engine 5 | Info: A simple game engine for Raspberry Pi Pico devices. 6 | Created: 2025-01-29 7 | Updated: 2025-05-23 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "internal/boards.hpp" 13 | #include "internal/vector.hpp" 14 | #include "internal/entity.hpp" 15 | #include "internal/level.hpp" 16 | #include "internal/input.hpp" 17 | #include "internal/image.hpp" 18 | #include "internal/draw.hpp" 19 | #include "internal/game.hpp" 20 | #include "internal/engine.hpp" 21 | 22 | using namespace PicoGameEngine; 23 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/boards.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /* Pico Calc 5 | #define PicoGameEngine_SCK 10 // 6 | #define PicoGameEngine_MOSI 11 // TX 7 | #define PicoGameEngine_MISO 12 // RX 8 | #define PicoGameEngine_CS 13 // 9 | #define PicoGameEngine_DC 14 10 | #define PicoGameEngine_RST 15 11 | */ 12 | 13 | /* VGM 14 | #define PicoGameEngine_SCK 8 15 | #define PicoGameEngine_MOSI 11 16 | #define PicoGameEngine_MISO 12 17 | #define PicoGameEngine_CS 13 18 | #define PicoGameEngine_DC 14 19 | #define PicoGameEngine_RST 15 20 | 21 | static const struct dvi_serialiser_cfg picodvi_dvi_cfg = { 22 | .pio = DVI_DEFAULT_PIO_INST, 23 | .sm_tmds = {0, 1, 2}, 24 | .pins_tmds = {10, 12, 14}, 25 | .pins_clk = 8, 26 | .invert_diffpairs = true 27 | }; 28 | 29 | */ 30 | 31 | // additional colors 32 | // https://doc-tft-espi.readthedocs.io/tft_espi/colors/ 33 | #ifndef TFT_BLACK 34 | #define TFT_BLACK 0x0000 35 | #endif 36 | 37 | #ifndef TFT_RED 38 | #define TFT_RED 0xF800 39 | #endif 40 | 41 | #ifndef TFT_WHITE 42 | #define TFT_WHITE 0xFFFF 43 | #endif 44 | 45 | namespace PicoGameEngine 46 | { 47 | typedef enum 48 | { 49 | BOARD_TYPE_PICO_CALC = 0, 50 | BOARD_TYPE_VGM = 1, 51 | BOARD_TYPE_JBLANKED = 2, 52 | } BoardType; 53 | 54 | typedef enum 55 | { 56 | PICO_TYPE_PICO = 0, // Raspberry Pi Pico 57 | PICO_TYPE_PICO_W = 1, // Raspberry Pi Pico W 58 | PICO_TYPE_PICO_2 = 2, // Raspberry Pi Pico 2 59 | PICO_TYPE_PICO_2_W = 3 // Raspberry Pi Pico 2 W 60 | } PicoType; 61 | 62 | typedef enum 63 | { 64 | LIBRARY_TYPE_PICO_DVI = 0, 65 | LIBRARY_TYPE_TFT = 1 66 | } LibraryType; 67 | 68 | typedef struct 69 | { 70 | uint8_t sck; 71 | uint8_t mosi; 72 | uint8_t miso; 73 | uint8_t cs; 74 | uint8_t dc; 75 | uint8_t rst; 76 | } BoardPins; 77 | 78 | typedef struct 79 | { 80 | BoardType boardType; 81 | PicoType picoType; 82 | LibraryType libraryType; 83 | BoardPins pins; 84 | uint16_t width; 85 | uint16_t height; 86 | uint8_t rotation; 87 | const char *name; 88 | bool hasWiFi; 89 | } Board; 90 | 91 | static const Board VGMConfig = { 92 | .boardType = BOARD_TYPE_VGM, 93 | .picoType = PICO_TYPE_PICO, 94 | .libraryType = LIBRARY_TYPE_PICO_DVI, 95 | .pins = { 96 | .sck = 8, 97 | .mosi = 11, 98 | .miso = 12, 99 | .cs = 13, 100 | .dc = 14, 101 | .rst = 15}, 102 | .width = 320, 103 | .height = 240, 104 | .rotation = 0, 105 | .name = "Video Game Module", 106 | .hasWiFi = false}; 107 | 108 | static const Board PicoCalcConfigPico = { 109 | .boardType = BOARD_TYPE_PICO_CALC, 110 | .picoType = PICO_TYPE_PICO, 111 | .libraryType = LIBRARY_TYPE_TFT, 112 | .pins = { 113 | .sck = 10, 114 | .mosi = 11, 115 | .miso = 12, 116 | .cs = 13, 117 | .dc = 14, 118 | .rst = 15}, 119 | .width = 320, 120 | .height = 320, 121 | .rotation = 0, 122 | .name = "PicoCalc - Pico", 123 | .hasWiFi = false}; 124 | 125 | static const Board PicoCalcConfigPicoW = { 126 | .boardType = BOARD_TYPE_PICO_CALC, 127 | .picoType = PICO_TYPE_PICO_W, 128 | .libraryType = LIBRARY_TYPE_TFT, 129 | .pins = { 130 | .sck = 10, 131 | .mosi = 11, 132 | .miso = 12, 133 | .cs = 13, 134 | .dc = 14, 135 | .rst = 15}, 136 | .width = 320, 137 | .height = 320, 138 | .rotation = 0, 139 | .name = "PicoCalc - Pico W", 140 | .hasWiFi = true}; 141 | 142 | static const Board PicoCalcConfigPico2 = { 143 | .boardType = BOARD_TYPE_PICO_CALC, 144 | .picoType = PICO_TYPE_PICO_2, 145 | .libraryType = LIBRARY_TYPE_TFT, 146 | .pins = { 147 | .sck = 10, 148 | .mosi = 11, 149 | .miso = 12, 150 | .cs = 13, 151 | .dc = 14, 152 | .rst = 15}, 153 | .width = 320, 154 | .height = 320, 155 | .rotation = 0, 156 | .name = "PicoCalc - Pico 2", 157 | .hasWiFi = false}; 158 | 159 | static const Board PicoCalcConfigPico2W = { 160 | .boardType = BOARD_TYPE_PICO_CALC, 161 | .picoType = PICO_TYPE_PICO_2_W, 162 | .libraryType = LIBRARY_TYPE_TFT, 163 | .pins = { 164 | .sck = 10, 165 | .mosi = 11, 166 | .miso = 12, 167 | .cs = 13, 168 | .dc = 14, 169 | .rst = 15}, 170 | .width = 320, 171 | .height = 320, 172 | .rotation = 0, 173 | .name = "PicoCalc - Pico 2 W", 174 | .hasWiFi = true}; 175 | 176 | static const Board JBlankedPicoConfig = { 177 | .boardType = BOARD_TYPE_JBLANKED, 178 | .picoType = PICO_TYPE_PICO_W, 179 | .libraryType = LIBRARY_TYPE_TFT, 180 | .pins = { 181 | .sck = 6, 182 | .mosi = 7, 183 | .miso = 4, 184 | .cs = 5, 185 | .dc = 11, 186 | .rst = 10}, 187 | .width = 320, 188 | .height = 240, 189 | .rotation = 3, 190 | .name = "JBlanked Pico", 191 | .hasWiFi = true}; 192 | 193 | }; 194 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/engine.cpp: -------------------------------------------------------------------------------- 1 | #include "engine.hpp" 2 | namespace PicoGameEngine 3 | { 4 | GameEngine::GameEngine() 5 | : name("Game Engine"), fps(60), game(nullptr) 6 | { 7 | } 8 | 9 | GameEngine::GameEngine(String name, float fps, Game *game) 10 | : name(name), fps(fps), game(game) 11 | { 12 | } 13 | 14 | void GameEngine::run() 15 | { 16 | // Initialize the game if not already active. 17 | if (!game->is_active) 18 | { 19 | game->start(); 20 | } 21 | 22 | while (game->is_active) 23 | { 24 | // Update the game 25 | game->update(); 26 | 27 | // Render the game 28 | game->render(); 29 | 30 | delay(1000 / fps); 31 | } 32 | 33 | // Stop the game 34 | game->stop(); 35 | 36 | // clear the screen 37 | game->draw->clear(Vector(0, 0), game->size, game->bg_color); 38 | 39 | // cleanup 40 | delete game; 41 | game = nullptr; 42 | } 43 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/engine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | #include "game.hpp" 4 | namespace PicoGameEngine 5 | { 6 | // Represents a game engine. 7 | class GameEngine 8 | { 9 | public: 10 | String name; // The name of the game engine. 11 | float fps; // The frames per second of the game engine. 12 | Game *game; // The game to run. 13 | 14 | GameEngine(); // Default constructor. 15 | GameEngine(String name, float fps, Game *game); // Constructor. 16 | void run(); // Runs the game engine. 17 | }; 18 | 19 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/game.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Arduino.h" 4 | #include "level.hpp" 5 | #include "input.hpp" 6 | #include "vector.hpp" 7 | #include "draw.hpp" 8 | 9 | namespace PicoGameEngine 10 | { 11 | 12 | #define MAX_LEVELS 10 13 | 14 | class Game 15 | { 16 | public: 17 | Game(); 18 | Game( 19 | const char *name, 20 | Vector size = Vector(320, 240), 21 | void (*start)() = NULL, 22 | void (*stop)() = NULL, 23 | uint16_t fg_color = 0xFFFF, 24 | uint16_t bg_color = 0x0000, 25 | bool use_8bit = false, 26 | Board board = VGMConfig, 27 | bool tftDoubleBuffer = false); 28 | ~Game(); 29 | // Clamp a value between a lower and upper bound. 30 | void clamp(float &value, float min, float max); 31 | void input_add(Input *input); // Add an input to the game 32 | void input_remove(Input *input); // Remove an input from the game 33 | void level_add(Level *level); // Add a level to the game 34 | void level_remove(Level *level); // Remove a level from the game 35 | void level_switch(const char *name); // Switch to a level by name 36 | void level_switch(int index); // Switch to a level by index 37 | void manage_input(); // Check for input from the user 38 | void render(); // Called every frame to render the game 39 | void start(); // Called when the game starts 40 | void stop(); // Called when the game stops 41 | void update(); // Called every frame to update the game 42 | 43 | const char *name; // Name of the game 44 | Level *levels[MAX_LEVELS]; // Array of levels 45 | Level *current_level; // Current level 46 | 47 | // Input pointers 48 | Input *button_up; // Input for up button 49 | Input *button_down; // Input for down button 50 | Input *button_left; // Input for left button 51 | Input *button_right; // Input for right button 52 | Input *button_center; // Input for center button 53 | Input *button_back; // Input for back button 54 | Input *uart; // Input for UART 55 | 56 | int input; // Last input (e.g., one of the BUTTON_ constants) 57 | Draw *draw; // Draw object for rendering 58 | Vector camera; // Camera position 59 | Vector pos; // Player position 60 | Vector old_pos; // Previous position 61 | Vector size; // Screen size 62 | Vector world_size; // World size 63 | bool is_active; // Whether the game is active 64 | uint16_t bg_color; // Background color 65 | uint16_t fg_color; // Foreground color 66 | 67 | bool is_8bit = false; // Flag to indicate if the game uses 8-bit graphics 68 | private: 69 | void (*_start)(); 70 | void (*_stop)(); 71 | 72 | bool is_uart_input = false; 73 | int button_uart = -1; 74 | }; 75 | 76 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/image.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | #include "boards.hpp" 4 | #include "vector.hpp" 5 | #include // storage 6 | #include 7 | #include 8 | 9 | namespace PicoGameEngine 10 | { 11 | void setup_fs(); // must be called in setup 12 | 13 | class EasyFile 14 | { 15 | public: 16 | EasyFile() {}; 17 | String read(const char *path); 18 | uint8_t *read_bytes(const char *path, size_t &size); 19 | bool write(const char *path, String data); 20 | 21 | private: 22 | File file; 23 | bool open(const char *path, const char *mode = "r"); 24 | }; 25 | 26 | class Image 27 | { 28 | public: 29 | Vector size; // Image dimensions (width, height) 30 | uint16_t *buffer; // Pointer to the image buffer (each pixel is 2 bytes, RGB565) 31 | uint8_t *data; // Raw image data (e.g. the BMP pixel data in file order) 32 | bool is_8bit; // Flag to indicate if the image is 8-bit or not 33 | Board board; // Board configuration 34 | 35 | Image(bool is_8bit = false, Board board = VGMConfig) : size(0, 0), buffer(nullptr), data(nullptr), 36 | is_8bit(is_8bit), board(board) 37 | { 38 | 39 | } // Constructor 40 | ~Image(); 41 | 42 | bool from_path(const char *path); // Create image from path: load the BMP file then create the image buffer. 43 | bool from_byte_array(uint8_t *data, Vector size); // Create an image from a provided byte array. Data must be width * height * 2 bytes long. 44 | bool from_byte_array(const uint8_t *data, Vector size); // Create an image from a provided byte array. Data must be width * height * 2 bytes long. 45 | bool from_string(String data); // Create an image from a string with string format ('\n' separates rows, each character maps to a color - ignoring spaces). 46 | bool open_image(const char *path); // Open a 16-bit BMP file from a given path and load its pixel data. 47 | bool create_image_buffer(); // Create an image buffer by flipping the BMP’s bottom-up data into a top-down buffer. 48 | }; 49 | 50 | class ImageManager 51 | { 52 | public: 53 | static ImageManager &getInstance() 54 | { 55 | static ImageManager instance; 56 | return instance; 57 | } 58 | 59 | Image *getImage(const char *name, uint8_t *data, Vector size, bool is_8bit = false) 60 | { 61 | std::string key(name); 62 | if (images.find(key) == images.end()) 63 | { 64 | Image *img = new Image(is_8bit); 65 | if (!img->from_byte_array(data, size)) 66 | { 67 | delete img; 68 | return nullptr; 69 | } 70 | images[key] = img; 71 | } 72 | return images[key]; 73 | } 74 | 75 | Image *getImage(const char *name, const uint8_t *data, Vector size, bool is_8bit = false) 76 | { 77 | std::string key(name); 78 | if (images.find(key) == images.end()) 79 | { 80 | Image *img = new Image(is_8bit); 81 | if (!img->from_byte_array(data, size)) 82 | { 83 | delete img; 84 | return nullptr; 85 | } 86 | images[key] = img; 87 | } 88 | return images[key]; 89 | } 90 | 91 | ~ImageManager() 92 | { 93 | for (auto &pair : images) 94 | { 95 | delete pair.second; 96 | } 97 | } 98 | 99 | private: 100 | std::map images; 101 | 102 | // Private constructor to enforce singleton pattern 103 | ImageManager() {} 104 | ImageManager(const ImageManager &) = delete; 105 | void operator=(const ImageManager &) = delete; 106 | }; 107 | 108 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/input.cpp: -------------------------------------------------------------------------------- 1 | #include "input.hpp" 2 | #include 3 | namespace PicoGameEngine 4 | { 5 | 6 | HW504::HW504(int x_pin, int y_pin, int button_pin, int orientation) 7 | { 8 | this->x_axis = x_pin; 9 | this->y_axis = y_pin; 10 | this->button = button_pin; 11 | this->orientation = orientation; 12 | pinMode(this->x_axis, INPUT); 13 | pinMode(this->y_axis, INPUT); 14 | pinMode(this->button, INPUT_PULLUP); 15 | } 16 | 17 | Vector HW504::axes() 18 | { 19 | Vector v; 20 | v.x = analogRead(this->x_axis); 21 | v.y = analogRead(this->y_axis); 22 | 23 | switch (this->orientation) 24 | { 25 | case HW_ORIENTATION_NORMAL: 26 | break; 27 | case HW_ORIENTATION_90: 28 | v = Vector(v.y, 1023 - v.x); 29 | break; 30 | case HW_ORIENTATION_180: 31 | v = Vector(1023 - v.x, 1023 - v.y); 32 | break; 33 | case HW_ORIENTATION_270: 34 | v = Vector(1023 - v.y, v.x); 35 | break; 36 | default: 37 | break; 38 | } 39 | return v; 40 | } 41 | 42 | int HW504::_button() 43 | { 44 | return digitalRead(this->button); 45 | } 46 | 47 | int HW504::_x_axis() 48 | { 49 | Vector v = this->axes(); 50 | return v.x; 51 | } 52 | 53 | int HW504::_y_axis() 54 | { 55 | Vector v = this->axes(); 56 | return v.y; 57 | } 58 | 59 | bool HW504::value(int button) 60 | { 61 | switch (button) 62 | { 63 | case HW_LEFT_BUTTON: 64 | return this->_x_axis() < 100; 65 | case HW_RIGHT_BUTTON: 66 | return this->_x_axis() > 1000; 67 | case HW_UP_BUTTON: 68 | return this->_y_axis() < 100; 69 | case HW_DOWN_BUTTON: 70 | return this->_y_axis() > 1000; 71 | case HW_CENTER_BUTTON: 72 | return this->_button() == LOW; 73 | default: 74 | return false; 75 | } 76 | } 77 | 78 | ButtonUART::ButtonUART(float debounce) 79 | { 80 | this->serial = new SerialPIO(0, 1); 81 | this->serial->begin(115200); 82 | this->debounce = debounce; 83 | this->startTime = millis(); 84 | } 85 | 86 | void ButtonUART::run() 87 | { 88 | if (millis() - this->startTime > this->debounce) 89 | { 90 | this->last_button = -1; 91 | this->startTime = millis(); 92 | // Check if data is available to read 93 | if (this->serial->available() > 0) 94 | { 95 | // Read the incoming byte as a character 96 | char incomingChar = this->serial->read(); 97 | switch ((int)incomingChar) 98 | { 99 | case 48: 100 | this->last_button = BUTTON_UP; 101 | break; 102 | case 49: 103 | this->last_button = BUTTON_DOWN; 104 | break; 105 | case 50: 106 | this->last_button = BUTTON_LEFT; 107 | break; 108 | case 51: 109 | this->last_button = BUTTON_RIGHT; 110 | break; 111 | case 52: 112 | this->last_button = BUTTON_CENTER; 113 | break; 114 | case 53: 115 | this->last_button = BUTTON_BACK; 116 | break; 117 | case 54: 118 | this->last_button = BUTTON_START; 119 | break; 120 | default: 121 | this->last_button = -1; 122 | break; 123 | } 124 | } 125 | } 126 | } 127 | 128 | Input::Input() 129 | : pin(-1), button(-1), elapsed_time(0), was_pressed(false), hw(nullptr), bt(nullptr) 130 | { 131 | } 132 | 133 | Input::Input(int pin, int button) 134 | { 135 | this->pin = pin; 136 | this->button = button; 137 | this->elapsed_time = 0; 138 | this->was_pressed = false; 139 | this->hw = nullptr; 140 | this->bt = nullptr; 141 | pinMode(this->pin, INPUT_PULLUP); 142 | } 143 | 144 | Input::Input(HW504 *hw, int button) 145 | { 146 | this->pin = -1; 147 | this->button = button; 148 | this->elapsed_time = 0; 149 | this->was_pressed = false; 150 | this->hw = hw; 151 | this->bt = nullptr; 152 | } 153 | 154 | Input::Input(ButtonUART *bt) 155 | { 156 | this->pin = -1; 157 | this->button = -1; // not needed 158 | this->elapsed_time = 0; 159 | this->was_pressed = false; 160 | this->hw = nullptr; 161 | this->bt = bt; 162 | } 163 | 164 | bool Input::is_pressed() 165 | { 166 | if (this->hw) 167 | return this->hw->value(this->button); 168 | else if (!this->bt) 169 | return digitalRead(this->pin) == LOW; 170 | return false; 171 | } 172 | 173 | bool Input::is_held(int duration) 174 | { 175 | return this->is_pressed() && this->elapsed_time >= duration; 176 | } 177 | 178 | void Input::run() 179 | { 180 | if (this->bt) 181 | this->bt->run(); 182 | if (this->is_pressed()) 183 | this->elapsed_time++; 184 | else 185 | this->elapsed_time = 0; 186 | 187 | if (this->is_pressed() && !this->was_pressed) 188 | this->was_pressed = true; 189 | else if (!this->is_pressed()) 190 | this->was_pressed = false; 191 | } 192 | Input::operator bool() const 193 | { 194 | return this->hw ? this->hw != nullptr : this->bt ? this->bt != nullptr 195 | : this->pin != -1; 196 | } 197 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/input.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Arduino.h" 3 | #include "vector.hpp" 4 | 5 | namespace PicoGameEngine 6 | { 7 | 8 | #define BUTTON_UP 0 9 | #define BUTTON_DOWN 1 10 | #define BUTTON_LEFT 2 11 | #define BUTTON_RIGHT 3 12 | #define BUTTON_CENTER 4 13 | #define BUTTON_BACK 5 14 | #define BUTTON_START 6 15 | 16 | /* 17 | # Wiring (HW504 -> VGM): 18 | # SW -> GP21 19 | # VRx -> GP27 20 | # VRy -> GP26 21 | # GND -> GND 22 | # 5V -> 3v3 23 | */ 24 | 25 | #define HW_LEFT_BUTTON 0 26 | #define HW_RIGHT_BUTTON 1 27 | #define HW_UP_BUTTON 2 28 | #define HW_DOWN_BUTTON 3 29 | #define HW_CENTER_BUTTON 4 30 | 31 | #define HW_ORIENTATION_NORMAL 0 32 | #define HW_ORIENTATION_90 1 33 | #define HW_ORIENTATION_180 2 34 | #define HW_ORIENTATION_270 3 35 | 36 | class HW504 37 | { 38 | public: 39 | int x_axis; 40 | int y_axis; 41 | int orientation; 42 | int button; 43 | 44 | HW504(int x_pin = 26, int y_pin = 27, int button_pin = 21, int orientation = HW_ORIENTATION_NORMAL); 45 | Vector axes(); // Read the raw ADC values from both axes and transform them based on the current orientation. 46 | bool value(int button = HW_CENTER_BUTTON); // Return the state of a button based on the transformed x,y axis values. 47 | 48 | private: 49 | int _button(); // Read the button value from the joystick 50 | int _x_axis(); // Return the transformed x-axis value 51 | int _y_axis(); // Return the transformed y-axis value 52 | }; 53 | 54 | class ButtonUART 55 | { 56 | public: 57 | ButtonUART(float debounce = 0.05); 58 | void run(); 59 | int last_button; 60 | 61 | private: 62 | SerialPIO *serial; 63 | float debounce; 64 | unsigned long startTime; 65 | }; 66 | 67 | class Input 68 | { 69 | public: 70 | int pin; 71 | int button; 72 | float elapsed_time; 73 | bool was_pressed; 74 | HW504 *hw; 75 | ButtonUART *bt; 76 | Input(); 77 | Input(int pin, int button); 78 | Input(HW504 *hw, int button); 79 | Input(ButtonUART *bt); 80 | bool is_pressed(); 81 | bool is_held(int duration = 3); 82 | void run(); 83 | operator bool() const; 84 | }; 85 | 86 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/level.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Arduino.h" 4 | #include "vector.hpp" 5 | 6 | namespace PicoGameEngine 7 | { 8 | // Forward declarations 9 | class Game; 10 | class Entity; 11 | 12 | class Level 13 | { 14 | public: 15 | // Constructors and Destructor 16 | Level(); // Default constructor 17 | Level(const char *name, 18 | const Vector &size, 19 | Game *game, 20 | void (*start)(Level &) = nullptr, 21 | void (*stop)(Level &) = nullptr); 22 | ~Level(); 23 | 24 | // Member Functions 25 | void clear(); 26 | Entity **collision_list(Entity *entity, int &count) const; 27 | void entity_add(Entity *entity); 28 | void entity_remove(Entity *entity); 29 | bool has_collided(Entity *entity) const; 30 | bool is_collision(const Entity *a, const Entity *b) const; 31 | void render(Game *game); 32 | void start(); 33 | void stop(); 34 | void update(Game *game); 35 | 36 | const char *name; 37 | Game *game; 38 | Vector size; 39 | int entity_count; 40 | Entity **entities; 41 | 42 | private: 43 | // Callback Functions 44 | void (*_start)(Level &); 45 | void (*_stop)(Level &); 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/vector.cpp: -------------------------------------------------------------------------------- 1 | #include "vector.hpp" 2 | 3 | namespace PicoGameEngine 4 | { 5 | Vector Vector::add(Vector a, Vector b) 6 | { 7 | return (Vector){this->x = a.x + b.x, this->y = a.y + b.y}; 8 | } 9 | Vector Vector::sub(Vector a, Vector b) 10 | { 11 | return (Vector){this->x = a.x - b.x, this->y = a.y - b.y}; 12 | } 13 | 14 | Vector Vector::mul(Vector a, Vector b) 15 | { 16 | return (Vector){this->x = a.x * b.x, this->y = a.y * b.y}; 17 | } 18 | 19 | Vector Vector::div(Vector a, Vector b) 20 | { 21 | return (Vector){this->x = a.x / b.x, this->y = a.y / b.y}; 22 | } 23 | 24 | Vector Vector::addf(Vector a, float b) 25 | { 26 | return (Vector){this->x = a.x + b, this->y = a.y + b}; 27 | } 28 | 29 | Vector Vector::subf(Vector a, float b) 30 | { 31 | return (Vector){this->x = a.x - b, this->y = a.y - b}; 32 | } 33 | 34 | Vector Vector::mulf(Vector a, float b) 35 | { 36 | return (Vector){this->x = a.x * b, this->y = a.y * b}; 37 | } 38 | 39 | Vector Vector::divf(Vector a, float b) 40 | { 41 | return (Vector){this->x = a.x / b, this->y = a.y / b}; 42 | } 43 | } -------------------------------------------------------------------------------- /src/ArduinoIDE/pico_game_engine/src/internal/vector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace PicoGameEngine 4 | { 5 | 6 | class Vector 7 | { 8 | public: 9 | float x; 10 | float y; 11 | 12 | Vector() {}; 13 | 14 | Vector(float x, float y) 15 | { 16 | this->x = x; 17 | this->y = y; 18 | } 19 | 20 | Vector add(Vector a, Vector b); 21 | Vector sub(Vector a, Vector b); 22 | Vector mul(Vector a, Vector b); 23 | Vector div(Vector a, Vector b); 24 | Vector addf(Vector a, float b); 25 | Vector subf(Vector a, float b); 26 | Vector mulf(Vector a, float b); 27 | Vector divf(Vector a, float b); 28 | 29 | bool operator!=(const Vector &other) const 30 | { 31 | return (x != other.x) || (y != other.y); 32 | } 33 | }; 34 | 35 | } -------------------------------------------------------------------------------- /src/CircuitPython/examples/air_labyrinth/player.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/examples/air_labyrinth/player.bmp -------------------------------------------------------------------------------- /src/CircuitPython/examples/example/Dino.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/examples/example/Dino.bmp -------------------------------------------------------------------------------- /src/CircuitPython/examples/example/example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pico Game Engine 3 | Author: JBlanked 4 | Github: https://github.com/jblanked/pico-game-engine 5 | Info: Create games and animations for Raspberry Pi Pico devices 6 | Created: 2025-05-11 7 | Updated: 2025-05-11 8 | """ 9 | 10 | # This is just an example of how to use the Pico Game Engine 11 | 12 | # import all the modules 13 | from gc import collect, mem_free 14 | 15 | 16 | def report(): 17 | collect() 18 | print(mem_free()) 19 | 20 | 21 | from pico_game_engine.game import Game 22 | from pico_game_engine.engine import GameEngine 23 | from pico_game_engine.entity import Entity 24 | from pico_game_engine.level import Level 25 | from pico_game_engine.input import ( 26 | BUTTON_UP, 27 | BUTTON_DOWN, 28 | BUTTON_RIGHT, 29 | BUTTON_LEFT, 30 | BUTTON_UART, 31 | ) 32 | from picogui.vector import Vector 33 | from picogui.image import Image 34 | from picogui.draw import Draw, TFT_BLACK, TFT_WHITE 35 | from picogui.boards import VGM_BOARD_CONFIG, JBLANKED_BOARD_CONFIG 36 | from uart import UART 37 | 38 | 39 | class PicoGameEngine: 40 | """ 41 | Pico Game Engine 42 | """ 43 | 44 | def __init__(self): 45 | # create a new TFT display 46 | self.screen = Draw(VGM_BOARD_CONFIG) 47 | # create a new game 48 | self.game: Game = None 49 | 50 | # set the current level to None 51 | self.current_level: Level = None 52 | 53 | # set the engine to None 54 | self.engine = None 55 | 56 | def add_level(self): 57 | """Add a new level""" 58 | self.current_level = Level( 59 | name="Level 1", size=Vector(320, 240), game=self.game 60 | ) 61 | self.game.level_add(self.current_level) 62 | 63 | def add_player(self): 64 | """Add a new player entity""" 65 | 66 | player_entity = Entity( 67 | name="Player", 68 | position=Vector(160, 120), 69 | sprite_file_path="/sd/Dino.bmp", 70 | sprite_size=Vector(16, 16), 71 | update=self.player_update, 72 | render=self.player_render, 73 | is_player=True, 74 | ) 75 | 76 | self.current_level.entity_add(player_entity) 77 | 78 | def game_input(self, player: Entity, game: Game): 79 | """Move the player entity based on input""" 80 | if game.input == BUTTON_UP: 81 | player.position += Vector(0, -5) 82 | elif game.input == BUTTON_RIGHT: 83 | player.position += Vector(5, 0) 84 | elif game.input == BUTTON_DOWN: 85 | player.position += Vector(0, 5) 86 | elif game.input == BUTTON_LEFT: 87 | player.position += Vector(-5, 0) 88 | 89 | def game_start(self, game: Game): 90 | """Handle your game start logic here""" 91 | 92 | def game_stop(self, game: Game): 93 | """Handle your game stop logic here""" 94 | 95 | def player_update(self, player: Entity, game: Game): 96 | """Update the player entity""" 97 | 98 | # move the player entity based on input 99 | self.game_input(player, game) 100 | 101 | def player_render(self, player: Entity, draw: Draw, game: Game): 102 | """Render the player entity""" 103 | 104 | # draw name of the game 105 | draw.text(Vector(110, 10), game.name, TFT_BLACK) 106 | 107 | def run(self): 108 | """Run the game engine""" 109 | 110 | # create a new game 111 | self.game = Game( 112 | name="Example", 113 | start=self.game_start, 114 | stop=self.game_stop, 115 | draw=self.screen, 116 | foreground_color=TFT_BLACK, 117 | background_color=TFT_WHITE, 118 | ) 119 | 120 | # add controls, level, and player 121 | self.add_level() 122 | self.add_player() 123 | 124 | # create a new game engine 125 | self.engine = GameEngine( 126 | fps=30, 127 | game=self.game, 128 | ) 129 | 130 | # run the game engine 131 | try: 132 | self.engine.run() 133 | except KeyboardInterrupt: 134 | self.game.is_running = False 135 | finally: 136 | self.game.stop() 137 | 138 | 139 | # run the main function 140 | if __name__ == "__main__": 141 | report() 142 | pico_game_engine = PicoGameEngine() 143 | report() 144 | pico_game_engine.run() 145 | -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_display_text/__init__.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_display_text/__init__.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_display_text/bitmap_label.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_display_text/bitmap_label.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_display_text/label.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_display_text/label.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_display_text/outlined_label.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_display_text/outlined_label.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_display_text/scrolling_label.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_display_text/scrolling_label.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_display_text/text_box.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_display_text/text_box.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_framebuf.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_framebuf.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/__init__.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/__init__.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/bmp/__init__.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/bmp/__init__.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/bmp/indexed.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/bmp/indexed.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/bmp/negative_height_check.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/bmp/negative_height_check.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/bmp/truecolor.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/bmp/truecolor.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/displayio_types.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/displayio_types.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/gif.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/gif.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/jpg.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/jpg.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/png.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/png.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/pnm/__init__.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/pnm/__init__.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/pnm/pbm_ascii.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/pnm/pbm_ascii.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/pnm/pbm_binary.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/pnm/pbm_binary.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/pnm/pgm/__init__.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/pnm/pgm/__init__.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/pnm/pgm/ascii.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/pnm/pgm/ascii.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/pnm/pgm/binary.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/pnm/pgm/binary.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/pnm/ppm_ascii.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/pnm/ppm_ascii.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/pnm/ppm_binary.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/pnm/ppm_binary.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/adafruit_imageload/tilegrid_inflator.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/adafruit_imageload/tilegrid_inflator.mpy -------------------------------------------------------------------------------- /src/CircuitPython/src/pico_game_engine/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/pico_game_engine/__init__.py -------------------------------------------------------------------------------- /src/CircuitPython/src/pico_game_engine/engine.py: -------------------------------------------------------------------------------- 1 | from gc import collect as free 2 | from time import sleep 3 | from .game import Game 4 | 5 | 6 | class GameEngine: 7 | """ 8 | Represents a game engine. 9 | 10 | Parameters: 11 | - name: str - the name of the game engine 12 | - fps: int - the frames per second 13 | - game: Game - the game to run 14 | """ 15 | 16 | def __init__(self, fps: int, game: Game): 17 | self.fps = fps 18 | self.game = game 19 | 20 | def run(self): 21 | """Run the game engine""" 22 | free() 23 | 24 | # start the game 25 | if not self.game.is_active: 26 | self.game.start() 27 | 28 | # start the game loop 29 | while True: 30 | free() 31 | self.game.update() # update positions, input, etc. 32 | self.game.render() # update graphics 33 | 34 | # check if the game is over 35 | if not self.game.is_running: 36 | break 37 | 38 | sleep(1 / self.fps) 39 | 40 | # stop the game 41 | self.game.stop() 42 | # clear the screen 43 | self.game.draw.clear() 44 | free() 45 | -------------------------------------------------------------------------------- /src/CircuitPython/src/pico_game_engine/entity.py: -------------------------------------------------------------------------------- 1 | from picogui.vector import Vector 2 | from displayio import TileGrid, OnDiskBitmap 3 | 4 | 5 | class Entity: 6 | """ 7 | Represents an entity in the game. 8 | 9 | Parameters: 10 | - name: str - the name of the entity 11 | - sprite: Image - the image representing the entity 12 | - position: Vector - the position of the entity 13 | - start: function(Entity, Game) - the function called when the entity is created 14 | - stop: function(Entity, Game) - the function called when the entity is destroyed 15 | - update: function(Entity, Game) - the function called every frame 16 | - render: function(Entity, Draw, Game) - the function called every frame to render the entity 17 | - collision: function(Entity, Entity, Game) - the function called when the entity collides with another entity 18 | - is_player: bool - whether the entity is the player 19 | """ 20 | 21 | def __init__( 22 | self, 23 | name: str, # name is a string that represents the name of the entity 24 | position: Vector, # position is a Vector that represents the x and y coordinates of the entity 25 | sprite_file_path: str, # sprite_file_path is a string that represents the path to the image or the image data 26 | sprite_size: Vector, # sprite_size is a Vector that represents the width and height of the image 27 | start=None, # start is a function that is called when the entity is created 28 | stop=None, # stop is a function that is called when the entity is destroyed 29 | update=None, # update is a function that is called every frame 30 | render=None, # render is a function that is called every frame 31 | collision=None, # collision is a function that is called when the entity collides with another entity 32 | is_player: bool = False, # is_player is a boolean that specifies whether the entity is the player 33 | ): 34 | self.name = name 35 | self.__position = position 36 | self.position_old = position 37 | self.sprite_path = sprite_file_path 38 | self.tile_grid = None 39 | if sprite_file_path != "": 40 | bitmap = OnDiskBitmap(sprite_file_path) 41 | self.tile_grid = TileGrid( 42 | bitmap, 43 | pixel_shader=bitmap.pixel_shader, 44 | x=int(position.x), 45 | y=int(position.y), 46 | ) 47 | del bitmap 48 | self.size = sprite_size 49 | self._start = start 50 | self._stop = stop 51 | self._update = update 52 | self._render = render 53 | self._collision = collision 54 | self.is_player = is_player 55 | self.is_active = False 56 | 57 | def collision(self, other, game): 58 | """Called when the entity collides with another entity.""" 59 | if self._collision: 60 | self._collision(self, other, game) 61 | 62 | @property 63 | def position(self) -> Vector: 64 | """Used by the engine to get the position of the entity.""" 65 | return Vector(self.__position.x, self.__position.y) 66 | 67 | @position.setter 68 | def position(self, value: Vector): 69 | """Used by the engine to set the position of the entity.""" 70 | self.position_old = Vector(self.__position.x, self.__position.y) 71 | self.__position = Vector(value.x, value.y) 72 | self.tile_grid.x = int(self.__position.x) 73 | self.tile_grid.y = int(self.__position.y) 74 | 75 | def render(self, draw, game): 76 | """Called every frame to render the entity.""" 77 | if self._render: 78 | self._render(self, draw, game) 79 | 80 | def start(self, game): 81 | """Called when the entity is created.""" 82 | if self._start: 83 | self._start(self, game) 84 | 85 | def stop(self, game): 86 | """Called when the entity is destroyed.""" 87 | if self._stop: 88 | self._stop(self, game) 89 | 90 | def update(self, game): 91 | """Called every frame.""" 92 | if self._update: 93 | self._update(self, game) 94 | -------------------------------------------------------------------------------- /src/CircuitPython/src/pico_game_engine/game.py: -------------------------------------------------------------------------------- 1 | from gc import collect as free 2 | from picogui.vector import Vector 3 | from picogui.draw import Draw 4 | from .level import Level 5 | from .input_manager import InputManager 6 | 7 | 8 | class Game: 9 | """ 10 | Represents a game. 11 | 12 | Parameters: 13 | - name: str - the name of the game 14 | - draw: Draw - the draw object to be used for rendering 15 | - foreground_color: int - the color of the foreground 16 | - background_color: int - the color of the background 17 | - start: function() - the function called when the game is created 18 | - stop: function() - the function called when the game is destroyed 19 | """ 20 | 21 | def __init__( 22 | self, 23 | name: str, 24 | draw: Draw, 25 | foreground_color: int, 26 | background_color: int, 27 | start=None, 28 | stop=None, 29 | ): 30 | self.name = name 31 | self._start = start 32 | self._stop = stop 33 | self.levels: list[Level] = [] # List of levels in the game 34 | self.current_level: Level = None # holds the current level 35 | self.input_manager = InputManager(draw.board) 36 | self.input: int = -1 # last button pressed 37 | self.draw = draw 38 | self.camera = Vector(0, 0) 39 | self.position = Vector(0, 0) 40 | self.size = Vector(draw.size.x, draw.size.y) 41 | self.world_size = Vector(draw.size.x, draw.size.y) 42 | self.is_active = False 43 | self.foreground_color = foreground_color 44 | self.background_color = background_color 45 | self.is_uart_input = False 46 | 47 | def clamp(self, value, lower, upper): 48 | """Clamp a value between a lower and upper bound.""" 49 | return min(max(value, lower), upper) 50 | 51 | @property 52 | def is_running(self) -> bool: 53 | """Return the running state of the game""" 54 | return self.is_active 55 | 56 | @is_running.setter 57 | def is_running(self, value: bool): 58 | """Set the running state of the game""" 59 | self.is_active = value 60 | 61 | def level_add(self, level: Level): 62 | """Add a level to the game""" 63 | self.levels.append(level) 64 | 65 | def level_remove(self, level: Level): 66 | """Remove a level from the game""" 67 | self.levels.remove(level) 68 | 69 | def level_switch(self, level: Level): 70 | """Switch to a new level""" 71 | if not level: 72 | print("Level is not valid.") 73 | return 74 | old_level = self.current_level 75 | self.current_level = level 76 | old_level.stop() 77 | old_level.clear() 78 | self.current_level.start() 79 | 80 | def render(self): 81 | """Render the current level""" 82 | if self.current_level: 83 | self.current_level.render() 84 | 85 | def start(self) -> bool: 86 | """Start the game""" 87 | if not self.levels: 88 | print("The game has no levels.") 89 | return False 90 | self.current_level = self.levels[0] 91 | if self._start: 92 | self._start(self) 93 | self.draw.fill(self.background_color) 94 | self.current_level.start() 95 | self.is_active = True 96 | free() 97 | return True 98 | 99 | def stop(self): 100 | """Stop the game""" 101 | 102 | if not self.is_active: 103 | return 104 | 105 | if self._stop: 106 | self._stop(self) 107 | 108 | self.is_active = False 109 | 110 | for level in self.levels: 111 | if level: 112 | level.clear() 113 | level = None 114 | self.levels = [] 115 | 116 | self.draw.fill(self.background_color) 117 | free() 118 | 119 | def update(self): 120 | """Update the game input and entity positions in a thread-safe manner.""" 121 | self.input_manager.run() 122 | self.input = self.input_manager.input 123 | 124 | # Run user-defined update functions for each entity. 125 | for entity in self.current_level.entities: 126 | if entity.update: 127 | entity.update(self) 128 | 129 | # Calculate camera offset to center the player. 130 | self.camera.x = self.position.x - (self.size.x // 2) 131 | self.camera.y = self.position.y - (self.size.y // 2) 132 | 133 | # Clamp camera position to prevent going outside the world. 134 | self.camera.x = self.clamp(self.camera.x, 0, self.world_size.x - self.size.x) 135 | self.camera.y = self.clamp(self.camera.y, 0, self.world_size.y - self.size.y) 136 | 137 | # update the level 138 | if self.current_level: 139 | self.current_level.update() 140 | -------------------------------------------------------------------------------- /src/CircuitPython/src/pico_game_engine/input.py: -------------------------------------------------------------------------------- 1 | from time import monotonic 2 | from digitalio import DigitalInOut, Direction 3 | from microcontroller import Pin 4 | from uart import UART 5 | 6 | 7 | BUTTON_UART = -1 8 | BUTTON_UP = 0 9 | BUTTON_DOWN = 1 10 | BUTTON_LEFT = 2 11 | BUTTON_RIGHT = 3 12 | BUTTON_CENTER = 4 13 | BUTTON_BACK = 5 14 | BUTTON_START = 6 15 | 16 | 17 | class Input: 18 | """ 19 | Input class to handle the button presses and joystick input. 20 | 21 | @param pin: The pin number of the button. 22 | @param button: The button to be checked. 23 | """ 24 | 25 | def __init__( 26 | self, button: int, pin: Pin = None, uart: UART = None, debounce: float = 0.05 27 | ): 28 | if not pin and not uart: 29 | raise ValueError("Either pin or uart must be provided.") 30 | elif pin and uart is None: 31 | self.pin = DigitalInOut(pin) 32 | self.pin.direction = Direction.OUTPUT 33 | self.uart = None 34 | else: 35 | self.pin = None 36 | self.uart = uart 37 | 38 | self.button = button 39 | self.elapsed_time = 0 40 | self.was_pressed = False 41 | self.last_button = 0 42 | self.start_time = monotonic() 43 | self.debounce = debounce 44 | 45 | def is_pressed(self) -> bool: 46 | """ 47 | Return True if the button is pressed. 48 | Since we are pulling up the button, we need to check for a LOW value. 49 | """ 50 | return self.pin.value 51 | 52 | def is_held(self, duration: int) -> bool: 53 | """ 54 | Return True if the button is held for a certain duration. 55 | """ 56 | 57 | return self.elapsed_time >= duration 58 | 59 | def run(self): 60 | """ 61 | Track the button state and the elapsed time it was pressed. 62 | This should be looped in the main loop. 63 | """ 64 | if self.uart: 65 | # check if it's been more than the debounce time since the last button press 66 | if monotonic() - self.start_time > self.debounce: 67 | self.last_button = -1 68 | self.start_time = monotonic() 69 | # Check if data is available to read 70 | if self.uart.available() > 0: 71 | # Read the incoming byte as a character 72 | incoming_char = ord(self.uart.read()) 73 | if incoming_char == 48: 74 | self.last_button = BUTTON_UP 75 | elif incoming_char == 49: 76 | self.last_button = BUTTON_DOWN 77 | elif incoming_char == 50: 78 | self.last_button = BUTTON_LEFT 79 | elif incoming_char == 51: 80 | self.last_button = BUTTON_RIGHT 81 | elif incoming_char == 52: 82 | self.last_button = BUTTON_CENTER 83 | elif incoming_char == 53: 84 | self.last_button = BUTTON_BACK 85 | elif incoming_char == 54: 86 | self.last_button = BUTTON_START 87 | return 88 | if self.is_pressed(): 89 | self.elapsed_time += 1 90 | self.was_pressed = True 91 | else: 92 | self.elapsed_time = 0 93 | self.was_pressed = False 94 | -------------------------------------------------------------------------------- /src/CircuitPython/src/pico_game_engine/input_manager.py: -------------------------------------------------------------------------------- 1 | import board 2 | from .input import ( 3 | Input, 4 | BUTTON_UART, 5 | BUTTON_UP, 6 | BUTTON_DOWN, 7 | BUTTON_LEFT, 8 | BUTTON_RIGHT, 9 | BUTTON_CENTER, 10 | BUTTON_BACK, 11 | BUTTON_START, 12 | ) 13 | from .boards import Board, BOARD_TYPE_VGM, JBLANKED_BOARD_CONFIG 14 | 15 | 16 | class InputManager: 17 | """ 18 | InputManager class to handle the input events. 19 | 20 | @param inputs: The list of inputs to be managed. 21 | """ 22 | 23 | def __init__(self, board_type: Board): 24 | self.input: int = -1 25 | self.inputs: list[Input] = [] 26 | self.board: Board = board_type 27 | self.is_uart_input: bool = False 28 | if board_type.board_type == BOARD_TYPE_VGM: 29 | self.inputs.append(Input(button=BUTTON_UART)) 30 | self.is_uart_input = True 31 | elif board_type.board_type == JBLANKED_BOARD_CONFIG: 32 | self.inputs.append(Input(BUTTON_UP, board.GP16)) 33 | self.inputs.append(Input(BUTTON_RIGHT, board.GP17)) 34 | self.inputs.append(Input(BUTTON_DOWN, board.GP18)) 35 | self.inputs.append(Input(BUTTON_LEFT, board.GP19)) 36 | else: 37 | pass # PicoCalc 38 | 39 | def reset(self): 40 | """Reset input.""" 41 | self.input = -1 42 | for inp in self.inputs: 43 | inp.last_button = -1 44 | 45 | def run(self): 46 | """ 47 | Run the input manager and check for input events. 48 | """ 49 | for inp in self.inputs: 50 | inp.run() 51 | if self.is_uart_input: 52 | self.input = self.inputs[0].last_button 53 | return 54 | for inp in self.inputs: 55 | if inp.is_pressed(): 56 | self.input = inp.button 57 | break 58 | -------------------------------------------------------------------------------- /src/CircuitPython/src/pico_game_engine/level.py: -------------------------------------------------------------------------------- 1 | from picogui.vector import Vector 2 | from .entity import Entity 3 | 4 | 5 | class Level: 6 | """ 7 | Represents a level in the game. 8 | 9 | Parameters: 10 | - name: str - the name of the level 11 | - size: Vector - the size of the level 12 | - game: Game - the game to which the level belongs 13 | - start: function(Level) - the function called when the level is created 14 | - stop: function(Level) - the function called when the level is destroyed 15 | """ 16 | 17 | def __init__( 18 | self, 19 | name: str, 20 | size: Vector, 21 | game, 22 | start=None, # start is a function that is called when the level is created 23 | stop=None, # stop is a function that is called when the level is destroyed 24 | ): 25 | self.name = name 26 | self.size = size 27 | self.game = game 28 | self.entities: list[Entity] = [] # List of entities in the level 29 | self._start = start 30 | self._stop = stop 31 | 32 | def clear(self): 33 | """Clear the level""" 34 | self.entities = [] 35 | 36 | def collision_list(self, entity: Entity) -> list: 37 | """Return a list of entities that the entity collided with""" 38 | collided = [] 39 | for other in self.entities: 40 | if entity != other and self.is_collision(entity, other): 41 | collided.append(other) 42 | return collided 43 | 44 | def entity_add(self, entity: Entity): 45 | """Add an entity to the level""" 46 | self.entities.append(entity) 47 | entity.start(self.game) 48 | entity.is_active = True 49 | 50 | def entity_remove(self, entity: Entity): 51 | """Remove an entity from the level""" 52 | self.entities.remove(entity) 53 | 54 | def has_collided(self, entity: Entity) -> bool: 55 | """Check for collisions with other entities""" 56 | for other in self.entities: 57 | if entity != other and self.is_collision(entity, other): 58 | return True 59 | return False 60 | 61 | def is_collision(self, entity: Entity, other: Entity) -> bool: 62 | """Check if two entities collided using AABB logic""" 63 | entity_pos = entity.position 64 | other_pos = other.position 65 | entity_size = entity.size 66 | other_size = other.size 67 | return ( 68 | entity_pos.x < other_pos.x + other_size.x 69 | and entity_pos.x + entity_size.x > other_pos.x 70 | and entity_pos.y < other_pos.y + other_size.y 71 | and entity_pos.y + entity_size.y > other_pos.y 72 | ) 73 | 74 | def render(self): 75 | """Render the level""" 76 | for entity in self.entities: 77 | if entity.is_active: 78 | entity.render(self.game.draw, self.game) 79 | if entity.tile_grid and entity.sprite_path != "": 80 | self.game.draw.tile_grid( 81 | Vector( 82 | entity.position.x - self.game.position.x, 83 | entity.position.y - self.game.position.y, 84 | ), 85 | entity.tile_grid, 86 | ) 87 | self.game.draw.swap() 88 | 89 | def start(self): 90 | """Start the level""" 91 | if self._start: 92 | self._start(self) 93 | 94 | def stop(self): 95 | """Stop the level""" 96 | if self._stop: 97 | self._stop(self) 98 | 99 | def update(self): 100 | """Update the level""" 101 | for entity in self.entities: 102 | if entity.is_active: 103 | entity.update(self.game) 104 | collided = self.collision_list(entity) 105 | for other in collided: 106 | entity.collision(other, self.game) 107 | -------------------------------------------------------------------------------- /src/CircuitPython/src/picogui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/CircuitPython/src/picogui/__init__.py -------------------------------------------------------------------------------- /src/CircuitPython/src/picogui/boards.py: -------------------------------------------------------------------------------- 1 | # Enumeration for the different board types. 2 | BOARD_TYPE_PICO_CALC = 0 3 | BOARD_TYPE_VGM = 1 4 | BOARD_TYPE_JBLANKED = 2 5 | 6 | 7 | # Enumeration for the different Pico types. 8 | PICO_TYPE_PICO = 0 # Raspberry Pi Pico 9 | PICO_TYPE_PICO_W = 1 # Raspberry Pi Pico W 10 | PICO_TYPE_PICO_2 = 2 # Raspberry Pi Pico 2 11 | PICO_TYPE_PICO_2_W = 3 # Raspberry Pi Pico 2 W 12 | 13 | 14 | # Enumeration for the different library types. 15 | LIBRARY_TYPE_PICO_DVI = 0 16 | LIBRARY_TYPE_TFT = 1 17 | 18 | 19 | class BoardPins: 20 | """Set of pins for the board""" 21 | 22 | def __init__(self, sck: int, mosi: int, miso: int, cs: int, dc: int, rst: int): 23 | self.sck = sck 24 | self.mosi = mosi 25 | self.miso = miso 26 | self.cs = cs 27 | self.dc = dc 28 | self.rst = rst 29 | 30 | 31 | class Board: 32 | """Class to represent the board.""" 33 | 34 | def __init__( 35 | self, 36 | board_type: int, 37 | pico_type: int, 38 | library_type: int, 39 | pins: BoardPins, 40 | width: int, 41 | height: int, 42 | rotation: int, 43 | name: str, 44 | has_wifi: bool, 45 | ): 46 | self.board_type = board_type 47 | self.pico_type = pico_type 48 | self.library_type = library_type 49 | self.pins = pins 50 | self.width = width 51 | self.height = height 52 | self.rotation = rotation 53 | self.name = name 54 | self.has_wifi = has_wifi 55 | 56 | 57 | # Define the board types 58 | VGM_BOARD_CONFIG: Board = Board( 59 | board_type=BOARD_TYPE_VGM, 60 | pico_type=PICO_TYPE_PICO, 61 | library_type=LIBRARY_TYPE_PICO_DVI, 62 | pins=BoardPins(sck=8, mosi=11, miso=12, cs=13, dc=14, rst=15), 63 | width=320, 64 | height=240, 65 | rotation=0, 66 | name="Video Game Module", 67 | has_wifi=False, 68 | ) 69 | 70 | PICO_CALC_BOARD_CONFIG_PICO: Board = Board( 71 | board_type=BOARD_TYPE_PICO_CALC, 72 | pico_type=PICO_TYPE_PICO, 73 | library_type=LIBRARY_TYPE_TFT, 74 | pins=BoardPins(sck=10, mosi=11, miso=12, cs=13, dc=14, rst=15), 75 | width=320, 76 | height=320, 77 | rotation=0, 78 | name="PicoCalc - Pico", 79 | has_wifi=False, 80 | ) 81 | 82 | PICO_CALC_BOARD_CONFIG_PICO_W: Board = Board( 83 | board_type=BOARD_TYPE_PICO_CALC, 84 | pico_type=PICO_TYPE_PICO_W, 85 | library_type=LIBRARY_TYPE_TFT, 86 | pins=BoardPins(sck=10, mosi=11, miso=12, cs=13, dc=14, rst=15), 87 | width=320, 88 | height=320, 89 | rotation=0, 90 | name="PicoCalc - Pico W", 91 | has_wifi=True, 92 | ) 93 | 94 | PICO_CALC_BOARD_CONFIG_PICO_2: Board = Board( 95 | board_type=BOARD_TYPE_PICO_CALC, 96 | pico_type=PICO_TYPE_PICO_2, 97 | library_type=LIBRARY_TYPE_TFT, 98 | pins=BoardPins(sck=10, mosi=11, miso=12, cs=13, dc=14, rst=15), 99 | width=320, 100 | height=320, 101 | rotation=0, 102 | name="PicoCalc - Pico 2", 103 | has_wifi=False, 104 | ) 105 | 106 | PICO_CALC_BOARD_CONFIG_PICO_2W: Board = Board( 107 | board_type=BOARD_TYPE_PICO_CALC, 108 | pico_type=PICO_TYPE_PICO_2_W, 109 | library_type=LIBRARY_TYPE_TFT, 110 | pins=BoardPins(sck=10, mosi=11, miso=12, cs=13, dc=14, rst=15), 111 | width=320, 112 | height=320, 113 | rotation=0, 114 | name="PicoCalc - Pico 2 W", 115 | has_wifi=True, 116 | ) 117 | 118 | JBLANKED_BOARD_CONFIG: Board = Board( 119 | board_type=BOARD_TYPE_JBLANKED, 120 | pico_type=PICO_TYPE_PICO_W, 121 | library_type=LIBRARY_TYPE_TFT, 122 | pins=BoardPins(sck=6, mosi=7, miso=4, cs=5, dc=11, rst=10), 123 | width=320, 124 | height=240, 125 | rotation=3, 126 | name="JBlanked Pico", 127 | has_wifi=True, 128 | ) 129 | -------------------------------------------------------------------------------- /src/CircuitPython/src/picogui/image.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from gc import collect as free 3 | from displayio import release_displays 4 | from framebufferio import FramebufferDisplay 5 | from adafruit_framebuf import ( 6 | FrameBuffer, 7 | RGB565, 8 | ) # https://circuitpython.org/libraries - add to the /lib folder 9 | 10 | from .vector import Vector 11 | 12 | SEEK_CUR = 1 13 | 14 | 15 | class Image: 16 | """ 17 | Represents an image. On CircuitPython this uses adafruit_framebuf.FrameBuffer 18 | for pixel manipulation, and displayio + framebufferio to put it on-screen. 19 | """ 20 | 21 | def __init__(self): 22 | self.size = Vector(0, 0) 23 | self.buffer = None 24 | self._raw = None # bytearray for framebuf 25 | 26 | def from_path(self, path: str) -> bool: 27 | """Load a 16‑bit BMP from disk into a FrameBuffer.""" 28 | try: 29 | self._load_bmp(path) 30 | self._make_framebuffer() 31 | return True 32 | except Exception as e: 33 | print(e) 34 | return False 35 | 36 | def from_byte_array(self, data: bytearray, size: Vector) -> bool: 37 | """ 38 | Create an image from a raw RGB565 byte array. 39 | 40 | data: bytes or bytearray length == width*height*2 41 | """ 42 | expected = size.x * size.y * 2 43 | if len(data) != expected: 44 | raise ValueError( 45 | f"Data length {len(data)} != expected {expected} for {size.x}×{size.y}" 46 | ) 47 | 48 | self.size = Vector(size.x, size.y) 49 | 50 | # ensure little‑endian 51 | buf = bytearray(data) 52 | if sys.byteorder != "little": 53 | for i in range(0, len(buf), 2): 54 | buf[i], buf[i + 1] = buf[i + 1], buf[i] 55 | free() 56 | 57 | self._raw = buf 58 | self.buffer = FrameBuffer( 59 | buf, size.x, size.y, RGB565 60 | ) # :contentReference[oaicite:0]{index=0} 61 | return True 62 | 63 | def _load_bmp(self, path): 64 | """Read BMP header + pixel data into self._raw as little‑endian RGB565 bytes.""" 65 | with open(path, "rb") as f: 66 | if f.read(2) != b"BM": 67 | raise ValueError("Not a BMP file") 68 | _ = f.read(8) # file size + reserved 69 | data_offset = int.from_bytes(f.read(4), "little") 70 | header_size = int.from_bytes(f.read(4), "little") 71 | w = int.from_bytes(f.read(4), "little") 72 | h = int.from_bytes(f.read(4), "little") 73 | self.size = Vector(w, h) 74 | # skip remaining header 75 | f.seek(data_offset, SEEK_CUR) 76 | 77 | # BMP rows are padded to 4-byte boundaries. We'll just read 2 bytes per pixel. 78 | raw = bytearray(w * h * 2) 79 | idx = 0 80 | row_bytes = w * 2 81 | padding = (4 - (row_bytes % 4)) % 4 82 | for row in range(h): 83 | # BMP is bottom‑up 84 | row_data = f.read(row_bytes) 85 | f.read(padding) 86 | # write into raw so that (0,0) is top‑left 87 | dest = (h - 1 - row) * row_bytes 88 | raw[dest : dest + row_bytes] = row_data 89 | self._raw = raw 90 | 91 | def _make_framebuffer(self): 92 | """Wrap self._raw in a FrameBuffer for drawing operations.""" 93 | w, h = self.size.x, self.size.y 94 | self.buffer = FrameBuffer( 95 | self._raw, w, h, RGB565 96 | ) # :contentReference[oaicite:1]{index=1} 97 | 98 | def from_string(self, image_str: str): 99 | """ 100 | Create a tiny monochrome‑style RGB565 image from ASCII art: 101 | \".\" or \"f\" → 0x0000, \"1\" → 0xFFFF, etc. 102 | """ 103 | rows = image_str.strip("\n").split("\n") 104 | h = len(rows) 105 | w = len(rows[0]) 106 | self.size = Vector(w, h) 107 | raw = bytearray(w * h * 2) 108 | 109 | def encode(c): 110 | return { 111 | ".": 0x0000, 112 | "f": 0x0000, 113 | "1": 0xFFFF, 114 | "2": 0xF904, 115 | "3": 0xFC98, 116 | "4": 0xFC06, 117 | "5": 0xFFA1, 118 | "6": 0x24F4, 119 | "7": 0x7ECA, 120 | "8": 0x0215, 121 | "9": 0x879F, 122 | "a": 0xC05E, 123 | "b": 0xFC9F, 124 | "c": 0x50CA, 125 | "d": 0xACF0, 126 | "e": 0x7B07, 127 | }.get(c, 0x0020) 128 | 129 | for y, row in enumerate(rows): 130 | for x, ch in enumerate(row): 131 | v = encode(ch) 132 | idx = 2 * (y * w + x) 133 | raw[idx] = v & 0xFF 134 | raw[idx + 1] = v >> 8 135 | 136 | self._raw = raw 137 | self.buffer = FrameBuffer( 138 | raw, w, h, RGB565 139 | ) # :contentReference[oaicite:2]{index=2} 140 | 141 | def show_on(self, fb_native): 142 | """ 143 | Copy this FrameBuffer into a native framebuffer (e.g. a picodvi.Framebuffer), 144 | then attach it to the display. 145 | """ 146 | # fb_native must implement .pixel(x,y,color) or expose a buffer protocol 147 | for y in range(self.size.y): 148 | for x in range(self.size.x): 149 | fb_native.pixel(x, y, self.buffer.pixel(x, y)) 150 | 151 | release_displays() # :contentReference[oaicite:3]{index=3} 152 | disp = FramebufferDisplay(fb_native, auto_refresh=True) 153 | return disp 154 | -------------------------------------------------------------------------------- /src/CircuitPython/src/picogui/loading.py: -------------------------------------------------------------------------------- 1 | from time import monotonic 2 | from math import cos, sin 3 | from .draw import Draw 4 | from .vector import Vector 5 | 6 | PI = 3.14159265358979323846 7 | RAD_FACTOR = PI / 180.0 8 | 9 | 10 | class Loading: 11 | """A memory-optimized loading class for a GUI.""" 12 | 13 | def __init__(self, draw: Draw, spinner_color: int, background_color: int): 14 | self.display = draw 15 | self.spinner_color = spinner_color 16 | self.background_color = background_color 17 | self.spinner_position = 0 18 | self.time_elapsed = 0 19 | self.time_start = 0 20 | self.animating = False 21 | self.current_text = "Loading..." 22 | self.center_x = self.display.board.width / 2 23 | self.center_y = self.display.board.height / 2 24 | self.radius = 20 # spinner radius 25 | self.span = 280 # degrees of arc 26 | self.step = 5 # degrees between segments 27 | 28 | # Pre-calculate sin/cos values for common angles to avoid repeated calculations 29 | self._cos_cache = {} 30 | self._sin_cache = {} 31 | for angle in range(0, 360, 5): 32 | self._cos_cache[angle] = cos(angle * RAD_FACTOR) 33 | self._sin_cache[angle] = sin(angle * RAD_FACTOR) 34 | 35 | def _get_cos(self, angle): 36 | """Get cosine value from cache or calculate it.""" 37 | angle = angle % 360 38 | # Round to nearest 5 degrees for cache lookup 39 | cache_key = (angle // 5) * 5 40 | return self._cos_cache.get(cache_key, cos(angle * RAD_FACTOR)) 41 | 42 | def _get_sin(self, angle): 43 | """Get sine value from cache or calculate it.""" 44 | angle = angle % 360 45 | # Round to nearest 5 degrees for cache lookup 46 | cache_key = (angle // 5) * 5 47 | return self._sin_cache.get(cache_key, sin(angle * RAD_FACTOR)) 48 | 49 | def animate(self) -> None: 50 | """Animate the loading spinner with proper buffer management.""" 51 | if not self.animating: 52 | self.animating = True 53 | self.time_start = monotonic() 54 | 55 | self.clear() 56 | self.draw_spinner() 57 | self.display.text(Vector(130, 20), self.current_text, self.spinner_color) 58 | self.display.swap() 59 | self.time_elapsed = monotonic() - self.time_start 60 | self.spinner_position = ( 61 | self.spinner_position + 10 62 | ) % 360 # Rotate by 10 degrees each frame 63 | 64 | def clear(self) -> None: 65 | """Clear the loading screen.""" 66 | self.display.fill(self.background_color) 67 | 68 | def draw_spinner(self) -> None: 69 | """Draw the loading spinner with memory optimization.""" 70 | # Use pre-calculated values 71 | center_x = self.center_x 72 | center_y = self.center_y 73 | radius = self.radius 74 | span = self.span 75 | step = self.step 76 | start_angle = self.spinner_position 77 | offset = 0 78 | 79 | # Calculate all points first to avoid tearing 80 | points = [] 81 | colors = [] 82 | 83 | while offset < span: 84 | angle = (start_angle + offset) % 360 85 | next_angle = (angle + step) % 360 86 | 87 | # Use cached or calculated values 88 | x1 = center_x + int(radius * self._get_cos(angle)) 89 | y1 = center_y + int(radius * self._get_sin(angle)) 90 | x2 = center_x + int(radius * self._get_cos(next_angle)) 91 | y2 = center_y + int(radius * self._get_sin(next_angle)) 92 | 93 | # Calculate fade color 94 | opacity = 255 - ((offset * 200) // span) 95 | color = self.fade_color(self.spinner_color, opacity) 96 | 97 | points.append((Vector(x1, y1), Vector(x2, y2))) 98 | colors.append(color) 99 | 100 | offset += step 101 | 102 | # Draw all segments at once 103 | for i, (point_pair, color) in enumerate(zip(points, colors)): 104 | self.display.line(point_pair[0], point_pair[1], color) 105 | 106 | def fade_color(self, color: int, opacity: int) -> int: 107 | """ 108 | Optimized version of fade color function. 109 | `opacity` is 0–255. 110 | """ 111 | # Skip calculation if no fade needed 112 | if opacity >= 255: 113 | return color 114 | 115 | # Quick bounds check using bitwise operations 116 | opacity = opacity & 0xFF # clamp 0-255 117 | 118 | # Extract 5‑6‑5 channels 119 | r = (color >> 11) & 0x1F 120 | g = (color >> 5) & 0x3F 121 | b = color & 0x1F 122 | 123 | # Use integer division for better performance 124 | r = (r * opacity) // 255 125 | g = (g * opacity) // 255 126 | b = (b * opacity) // 255 127 | 128 | # Recombine to 16‑bit 129 | return (r << 11) | (g << 5) | b 130 | 131 | def stop(self) -> None: 132 | """Stop the loading animation.""" 133 | self.clear() 134 | self.display.swap() 135 | self.animating = False 136 | self.time_elapsed = 0 137 | self.time_start = 0 138 | -------------------------------------------------------------------------------- /src/CircuitPython/src/picogui/menu.py: -------------------------------------------------------------------------------- 1 | from .draw import Draw 2 | from .vector import Vector 3 | from .list import List 4 | 5 | 6 | class Menu: 7 | """A simple menu class for a GUI.""" 8 | 9 | def __init__( 10 | self, 11 | draw: Draw, 12 | title: str, 13 | y: int, 14 | height: int, 15 | text_color: int, 16 | background_color: int, 17 | selected_color: int, 18 | border_color: int, 19 | border_width: int = 2, 20 | ): 21 | self.text_color = text_color 22 | self.background_color = background_color 23 | self.title = title 24 | self.list = List( 25 | draw, 26 | y + 20, 27 | height - 20, 28 | text_color, 29 | background_color, 30 | selected_color, 31 | border_color, 32 | border_width, 33 | ) 34 | self.position = Vector(0, y) 35 | self.size = Vector(draw.size.x, height) 36 | draw.clear(self.position, self.size, self.background_color) 37 | draw.swap() 38 | 39 | def add_item(self, item: str) -> None: 40 | """Add an item to the menu.""" 41 | self.list.add_item(item) 42 | 43 | def clear(self) -> None: 44 | """Clear the menu.""" 45 | self.list.clear() 46 | 47 | def draw(self) -> None: 48 | """Draw the menu.""" 49 | self.draw_title() 50 | self.list.draw() 51 | 52 | def draw_title(self) -> None: 53 | '''Draw the title of the menu."""''' 54 | self.list.scrollbar.display.text(Vector(2, 8), self.title, self.text_color, 1) 55 | 56 | def get_current_item(self) -> str: 57 | """Get the current item in the menu.""" 58 | return self.list.get_current_item() 59 | 60 | def get_item(self, index: int) -> str: 61 | """Get the item at the specified index.""" 62 | return self.list.get_item(index) 63 | 64 | def get_item_count(self) -> int: 65 | """Get the number of items in the menu.""" 66 | return self.list.get_item_count() 67 | 68 | def get_list_height(self) -> int: 69 | """Get the height of the list.""" 70 | return self.list.get_list_height() 71 | 72 | def get_selected_index(self) -> int: 73 | """Get the index of the selected item.""" 74 | return self.list.selected_index 75 | 76 | def remove_item(self, index: int) -> None: 77 | """Remove an item from the menu.""" 78 | self.list.remove_item(index) 79 | 80 | def scroll_down(self) -> None: 81 | """Scroll down the menu.""" 82 | self.draw_title() 83 | self.list.scroll_down() 84 | 85 | def scroll_up(self) -> None: 86 | """Scroll up the menu.""" 87 | self.draw_title() 88 | self.list.scroll_up() 89 | 90 | def set_selected(self, index: int) -> None: 91 | """Set the selected item in the menu.""" 92 | self.draw_title() 93 | self.list.set_selected(index) 94 | -------------------------------------------------------------------------------- /src/CircuitPython/src/picogui/scrollbar.py: -------------------------------------------------------------------------------- 1 | from .draw import Draw 2 | from .vector import Vector 3 | 4 | 5 | class ScrollBar: 6 | """A simple scrollbar class for a GUI.""" 7 | 8 | def __init__( 9 | self, 10 | draw: Draw, 11 | position: Vector, 12 | size: Vector, 13 | outline_color: int, 14 | fill_color: int, 15 | ): 16 | self.display = draw 17 | self.position = position 18 | self.size = size 19 | self.outline_color = outline_color 20 | self.fill_color = fill_color 21 | 22 | def clear(self): 23 | """Clear the scrollbar.""" 24 | self.display.clear(self.position, self.size, self.fill_color) 25 | 26 | def draw(self): 27 | """Draw the scrollbar.""" 28 | self.display.rect(self.position, self.size, self.outline_color) 29 | self.display.rect_fill( 30 | Vector(self.position.x + 1, self.position.y + 1), 31 | Vector(self.size.x - 2, self.size.y - 2), 32 | self.fill_color, 33 | ) 34 | 35 | def set_all( 36 | self, 37 | position: Vector, 38 | size: Vector, 39 | outline_color: int, 40 | fill_color: int, 41 | should_draw: bool = True, 42 | should_clear: bool = True, 43 | ): 44 | """Set the properties of the scrollbar.""" 45 | if should_clear: 46 | self.clear() 47 | self.position = position 48 | self.size = size 49 | self.outline_color = outline_color 50 | self.fill_color = fill_color 51 | if should_draw: 52 | self.draw() 53 | -------------------------------------------------------------------------------- /src/CircuitPython/src/picogui/vector.py: -------------------------------------------------------------------------------- 1 | class Vector: 2 | """ 3 | A simple 2D vector class. 4 | 5 | @param x: The x-coordinate of the vector. 6 | @param y: The y-coordinate of the vector. 7 | """ 8 | 9 | def __init__(self, x=0, y=0): 10 | # If x is a tuple, unpack it; otherwise, use the two arguments. 11 | if isinstance(x, tuple): 12 | self.x, self.y = x 13 | else: 14 | self.x = x 15 | self.y = y 16 | 17 | @classmethod 18 | def from_val(cls, value): 19 | """Ensure the value is a Vector. If it's a tuple, convert it.""" 20 | if isinstance(value, tuple): 21 | return cls(*value) 22 | if isinstance(value, cls): 23 | return value 24 | raise TypeError("Expected a tuple or a Vector.") 25 | 26 | def __add__(self, other): 27 | other = Vector.from_val(other) 28 | return Vector(self.x + other.x, self.y + other.y) 29 | 30 | def __mul__(self, scalar): 31 | return Vector(self.x * scalar, self.y * scalar) 32 | 33 | __rmul__ = __mul__ 34 | 35 | def __str__(self): 36 | return "({}, {})".format(self.x, self.y) 37 | -------------------------------------------------------------------------------- /src/CircuitPython/src/uart.py: -------------------------------------------------------------------------------- 1 | from board import GP0, GP1 2 | from microcontroller import Pin 3 | from busio import UART as busio_UART 4 | from time import monotonic, sleep 5 | 6 | 7 | class UART: 8 | """Class to control a UART interface on a Raspberry Pi Pico device.""" 9 | 10 | def __init__( 11 | self, 12 | tx_pin: Pin = GP0, 13 | rx_pin: Pin = GP1, 14 | baudrate: int = 115200, 15 | timeout: float = 5.0, 16 | ): 17 | self.uart = busio_UART(tx=tx_pin, rx=rx_pin, baudrate=baudrate, timeout=timeout) 18 | 19 | def available(self) -> int: 20 | """Return the number of bytes available to read from the UART interface.""" 21 | return self.uart.in_waiting 22 | 23 | def clear_buffer(self) -> None: 24 | """Clear the UART buffer.""" 25 | while self.available() > 0: 26 | self.uart.read(1) 27 | 28 | def read(self, size=1) -> bytes: 29 | """Read data from the UART interface.""" 30 | return self.uart.read(size) 31 | 32 | def read_line(self) -> str: 33 | """Read a line of data from the UART interface.""" 34 | line = self.uart.readline() 35 | if line: 36 | return line.decode("utf-8").strip() 37 | return "" 38 | 39 | def read_string(self, size=1) -> str: 40 | """Read a string of data from the UART interface.""" 41 | return self.uart.read(size).decode("utf-8") 42 | 43 | def read_until(self, terminator: str, timeout: float = 5.0) -> str: 44 | """Read data from the UART interface until a terminator string is found.""" 45 | received_data = "" 46 | start_time = monotonic() 47 | while monotonic() - start_time < timeout: 48 | if self.available() > 0: 49 | char = self.uart.read(1).decode("utf-8") 50 | received_data += char 51 | if received_data.endswith(terminator): 52 | received_data = received_data[: -len(terminator)] 53 | break 54 | else: 55 | sleep(0.01) 56 | return received_data.strip() 57 | 58 | def print(self, data: str) -> None: 59 | """Write a string of data to the UART interface.""" 60 | self.uart.write(data.encode("utf-8")) 61 | 62 | def println(self, data: str) -> None: 63 | """Write a string of data to the UART interface with a newline.""" 64 | self.uart.write((data + "\n").encode("utf-8")) 65 | 66 | def write(self, data: bytes) -> None: 67 | """Write bytes to the UART interface.""" 68 | self.uart.write(data) 69 | -------------------------------------------------------------------------------- /src/FlipperZeroApp/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/FlipperZeroApp/app.png -------------------------------------------------------------------------------- /src/FlipperZeroApp/application.fam: -------------------------------------------------------------------------------- 1 | App( 2 | appid="vgm_game_remote", 3 | name="[VGM] Game Remote", 4 | apptype=FlipperAppType.EXTERNAL, 5 | entry_point="vgm_game_remote_main", 6 | stack_size=4 * 1024, 7 | fap_icon="app.png", 8 | fap_category="GPIO", 9 | fap_icon_assets="assets", 10 | fap_description="Companion app for the VGM Game Engine", 11 | fap_author="JBlanked", 12 | fap_weburl="https://github.com/jblanked/VGM-Library/tree/main/engine/app", 13 | fap_version="1.1", 14 | ) 15 | -------------------------------------------------------------------------------- /src/FlipperZeroApp/assets/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jblanked/pico-game-engine/ae1bef429ef75226bab625f438f954d47c96205d/src/FlipperZeroApp/assets/main.png --------------------------------------------------------------------------------