dango
A little tribute to the Dango Daikazoku from Clannad (by Key, KyoAni, et al)
Try it with your friends at http://ernestwong.nz/dango-tribute/server/
It might not work :)
demo.mp4
What is this?
Just a little experiment made using the Bevy game engine to get myself a bit more familiar with Rust.
I don't take pride in the quality of this codebase. It is full of hacks, non-idiomatic Bevy code, and more inefficient hacks. Be warned when looking through the code.
Will I continue to work on this?
No. I don't want to ruin the image of Clannad any further. Let's keep this as a small experiment.
Blob physics
The dangos are octagon meshes, simulated as FEM surfaces within nphysics.
To get variable jump heights, it applies a jump force that:
- spikes when the jump button is pressed, and
- decays until the jump button is released.
- drops to zero after the jump button is released.
By default, these FEM surfaces like to roll around. To fix their angle so they're always upright, it measures the aggregate angle and angular momentum of the whole dango using its 8 individual FEM surface elements, and then use something that resembles a PD control loop to lock it in place naturally. To re-enable rolling, we just turn off this PD control loop.
To get the wobbly walking animation, it modulates the horizontal walking force with some squishing and stretching.
Currently, the dango physics don't obey Newton's third law (i.e. the jump force is asymmetrically applied), so if two dangos ever get tangled, they can fly into the air ignoring gravity.
Blob drawing
To draw the dangos, I used a Catmull-Rom spline from the Splines crate to smooth the octagon out into 32 vertices, and then used the lyon crate to tessellate them into triangular meshes that can be rendered.
The shadows are just hard-coded octagons on the ground. The eyes are also tessellated using lyon, and are made to blink once in a while using a simple finite state machine.
Background and post-processing
The background and the hand-drawn look are done in a post-processing shader with the help of a bevy plugin that's propably not good enough to be made into its own public crate. The fragment shader script is pretty slow and unnecessarily intensive, so I had to limit the size of the screen to keep the framerate consistently smooth. The shader script itself was inspired from a shadertoy script by flockaroo.
Music
Yeah, sorry, the short background music was whipped up in a day or two, so it sounds kinda bad. I used Dorico to write it up, and then performed it with Spitfire Audio's BBC Symphony Orchestra Discover's celesta instrument, recorded inside Cakewalk.
The audio playback might not start in Chrome due to Chrome's autoplay policies, and at the moment, there might be some audio degradation at the start of the playback.
Multiplayer
The prediction/rollback logic was eventually refactored out into its own crate: CrystalOrb. This networking code probably isn't efficient, and there are still some bugs that show up when the browser tab sleeps for too long.
The player hosts the servers themselves, but it's still written in a server-client way, so it's like an inefficient version of peer-to-peer? The connections are established using WebRTC, with a WebSocket signalling server written in Rust using Actix. However, I haven't configured a TURN server, so many connections could fail. Trying to connect within a LAN might also have some issues, because web browser anonymize their IP addresses by using an mDNS address, but not all OSes support them.
Patches
To get this experiment somewhat working, I hacked together some patches for some dependencies. Some of the patches are genuine fixes and enhancements, but most of them are short-term solutions that are specific to this experiment. When I get time, I'll see if I can make proper pull-requests for some of the fixes back into the upstream repositories.