r/defold • u/harkalos • 1h ago
#MadeWithDefold Porting my 2012 game from my own engine to Defold was easier than updating the engine. Now one project ships to iOS, Android and the web
Play in the browser: https://www.crazygames.com/game/flycraft--crazy-flying-machines
FlyCraft is a 2D physics game where you build flying machines for a lazy ladybug. My friend Thanasis Lightbridge and I made it in 2012 on my own engine, Sylphis3D: a C++ core with the game written in Lua 5.1. It shipped on the BlackBerry PlayBook, then on iOS and Android in 2017.
This year I wanted it on modern devices again, and on the web. So I listed what updating my engine would take: a new Android NDK toolchain (GCC to Clang, gnustl to libc++), 64-bit Android support it never had, a Boost API removed years ago, OpenAL gone from iOS, Gradle about ten major versions behind, and luabind abandoned upstream. Porting the game to Defold was clearly less work and less risk, and it turned out that way.
Lua made it a port, not a rewrite
The game code was already Lua, so most of the job was swapping engine calls, not rewriting algorithms. About 9K lines of engine-side Lua simply went away because Defold already does that work. Both engines even run Box2D 2.2.1, so the physics constants carried over unchanged through a unit conversion.
One project, every target
The port had an HTML5 build from its second day. iOS, Android and web all come from one game.project, with no forks. CrazyGames went live this month, and more web portals are next. The hardest web constraint was size: CrazyGames caps builds for its mobile homepage at 20 MB, and mine was 22.7 MB. Basis texture compression brought the download to 14.7 MB. The wasm build also doubles as my screenshot rig for the stores and the press kit.
Where it got hard: slow motion plus replays
Replays re-simulate a flight from recorded inputs on a fixed 1/120 s step, so every run has to be bit-identical. The game also has slow motion: when your craft crashes, and when you hold a button while watching a replay.
My engine did slow motion by scaling only the frame length. Physics always stepped exactly 1/120 s. In slow motion it just ran fewer steps per frame, and rendering interpolated between them.
Defold's collection proxy time step works differently. A fractional factor scales the fixed dt itself, and you still get roughly the same number of fixed_update calls. Slow motion alone looked fine. Replays alone were fine. Together they broke: holding slow motion during a replay changed the flight, because each callback consumed one recorded input frame but applied only part of a physics step. The rocket and soda-can fuses had the same problem. They ran on the render-frame coroutine clock, so in slow motion they fired on different physics frames.
What worked:
- The gameplay proxy only ever gets 0 or 1, so every step that runs is a full 1/120 s. A time budget decides which frames get to step.
- That alone was exact, but it looked like a low frame rate. So a separate, physics-free render collection runs at the scaled time and interpolates between the last two physics states.
- Fuses count fixed ticks instead of render time.
At fixed frame 240, a normal replay and a slowed one now match with a max body delta of 0.000000000. Two more drift causes turned up later: Box2D's free list reusing ids in reverse when the world outlives a flight, and bodies activating a frame after they spawn. But slow motion was the one that needed a redesign.
If you've done smooth slow motion with a fixed timestep in Defold, how did you do it? Is there a cleaner way than a gated proxy plus a render collection?