A Rust-based monochrome pixel game engine that can be compiled to both web assembly (javascript canvas used for display) and also to the RP2040 microcontroller (which uses the monochrome SSD1306 display over I2C). I'm mainly making this so that I can bring hardware to Daydream (the final game apparently has to be uploadable to itch.io, hence the WASM support).
BnaJns
Check their projects out: slack.fm, readmefm, BnaJns Portfolio, roblox-outfit-finder, faucet - a c++ http server, visittallinn-waffles, ralseibot
555 timer
Check their project out: DOOMkey
Ishaan
Check their projects out: Hyprdrive, HackaCLI, EncryptUI, RotatoLang, ToDoIt, SSH Portfolio
Once you ship this you can't edit the description of the project, but you'll be able to add more devlogs and re-ship it as you add new features!
I think I'll ship one more time before SoM ends, main thing I've done during this ship is fixing collisions and improving the animation system
Added the ability for player sprite animations to change depending on the state of the player, so jumping doesn't look stupid now ig. Also did some work on fixing collisions but im going to continue that tomorrow, spent a while thinking of the best way to do it and i think i came up with something half-decent perhaps
oh my god ive been debugging this for the last 30 min and it's this max here that needs to be a min instead. collisions are close to done, it says 4h44min but it's probably a bit less in terms of normal human brain coding time, i code slowly when i'm low on sleep
Static objects are a thing now, next I'll probably add level importing from LDTK, collisions and then start working on the demo! I'll probably delay font support until the third ship.
i added the ability to flip sprites in the x-axis and also a 2d camera so that can be moved around, for example when the player moves to a different part of the level the camera moves too so that the player isn't offscreen. also spritesheet animation support is almost done, just need to write the tick function. got a lot done today.
writing this thing made me feel like an absolute genius, i think i'm starting to understand properly how rust works now. non-static lifetimes can die in hell, but other than that it's a decent language
Sprite loading works now!!! I encoded the png file from my pc using https://github.com/ArcaEge/embedded-encoder (that's my #siege project, so the time spent for that bit doesn't count here). I'm basically embedding the encoded spritesheet into the binary and it seems to work pretty well
screw memory usage and reliability, i gave up with static allocation and am now using dynamic allocation everywhere (fyi the size of my heap is only 64kb, this probably won't end well...)
Sound support is done!! Well, not really, I still need to do the wasm bit but that's pretty simple and (hopefully) won't take too long. Also thanks @555 timer for permanently lending me these 15 passive buzzers
Implemented an actor-world system similar to greenfoot java, i.e. spent 6 hours building a fancy abstraction system to be able to draw this box:
Finally fixed that stupid bug causing the ssd1306 to get desynced. here's a cat because I don't have a better image to post.
Completely overhauled the input system to be a lot less stupid, this is across the last two devlogs. Also added a loading indicator to the canvas
Just did some soldering and stuff to repurpose this old controller I made a while ago, don't look at the solder joints on the back side :)
Got the demo working! Just need to compile it for the pico (I need to add the inputs to the pico HAL for that, maybe another hour or so of work, then I'm ready to deploy the demo somewhere and ship!)
Keyboard inputs in wasm work now!! That was quite annoying to do because it turns out that there's no API to get the current state of a key, you have to work it out yourself using event listeners. Time to make the demo! Then I'll do the first ship, and then add some quality-of-life features like builtin sprite rendering, but the core engine is basically done.
Display output in wasm is working! That went smoother than expected and I also made it a nice colour scheme. The canvas is pixel-perfect responsive (and all that logic is in rust because I'm not touching javascript with a metre stick if I can avoid it).
Lesson of the day: mixing async + non-async code = pure pain and suffering. I somehow made it work but it's pain. Also I still don't understand what the hell static lifetimes are but they seem to make the compiler happy. Anyway, delays in wasm work now and the code doesn't crash the main thread anymore. yay.
Next up is figuring out how to render to a canvas.
Refactored the project to be able to use wasm-pack and added a little webpack npm runner to run the devserver, printing stuff to the console works so we should be good. Just need to implement the HAL for wasm now, then I'll get inputs working, make a simple demo (maybe a dot moving around the screen controlled by wasd/arrows) and then ship the first version. I hope I can get that done by tonight.
Did some more work on individual pixel addressing and framebuffer stuff, here's a nice little pixel scanning across the screen row by row. Also ticks work now, I set the tick rate to ~60 ticks/sec. I need to work on the wasm side of things now, just need to create a HAL for web assembly.
Writing to the display now works! Here are some nice horizontal lines :D
Other stuff I've been up to:
- Added a 64KiB heap using embedded-alloc for dynamic memory allocation (don't need it now but will need it in the future)
- Implemented a generic framebuffer
- Found a bug/oversight in rp2040_hal (no good way to make two back-to-back i2c transactions without a stop/restart in between. logic analyser came in clutch for that one)
I added the function to initialise the display (the display's buffer is not cleared at startup so it makes this nice random pattern from the noise in the buffer, this isn't an issue as it goes away when you display the first frame). Next what I need to add is some function to put the pico's framebuffer data onto the display's framebuffer
I think I'm starting to figure out how Rust works but everything is taking 4x as long as it should because I'm still a clueless beginner to embedded Rust. I decided to say screw it, let's refactor the code because the old structure was stupid and hard to maintain, especially with 2 compile targets for pico and wasm. Speaking of wasm, I haven't done much in terms of that but I have started work on the ssd1306 driver code to drive the screen (I'm not using external crates for this because who likes to use libraries when you can do it yourself? It's pretty simple anyway because I'm just porting over the display driver code from my pico flappy bird project, which also used an ssd1306 but was written in C). Pictures are mandatory on devlogs, so here's a picture of the current setup I guess.
Clock and peripherals initialisations for the Pico are done, it took a while because this is one of my first projects using Rust and I was busy playing karate with the compiler
Did some boilerplate stuff, mostly just initialising the project structure and getting compilation to both RP2040 and WASM working.