Week 15: Finished platform-graph save/load and started learning Unity

A screen recording showing the platform-graph precalculation screen.
Precalculating platform graphs and saving them to JSON files!
(I know, very exciting stuff)

tl;dr: I finished implementing platform-graph save/load support, and I started learning Unity.

What happened last week?


  • Finished implementing save/load support of platform graphs.
    • This gives a huge improvement to load times, since the graph has already been calculated.
A screen recording showing the platform-graph calculation loading screen at play-time.
If the platform graph file isn't found, the graph is calculated on-demand.

A screen recording showing the contents of a platfrom-graph save file.
Soooo many lines in one platform-graph save file.

  • Started learning how to use Unity!
    • I'll talk more about my impressions with Unity in a separate post.

Laundry list:

  • Spent a couple hours debugging a strange issue with Godot’s collision engine giving inconsistent (and bogus) results in my precompute screen rather than in the normal game screen.
    • The bug:
      • Godot shows the collision position as being in the middle of the air, nowhere near any tile.
      • However, Godot is detecting the collision as happening in the correct frame—it just reports the wrong position.
    • Only repros in precompute mode.
      • Level rendered within GUI Control node wrapper.
      • Wrapper set to have width/height of zero, clip contents, and have opacity of 0.
    • If I change the precompute screen node hierarchy to instead place the level within the same viewport arrangement as in the GameScreen, then Godot's collision calculations work correctly again.
    • That led me to believe that the problem stems from me not having the outer viewport configured correctly for Godot’s 2D collision engine to work.
    • I think this isn't the first time I've seen issues like this when I had the viewport misconfigured for 3D.
  • Added new fonts.
  • Added better app-lifecycle events for running things after the initial window size has been set.
  • Added some folder-opening utils.
    • Include a button in the platform-graph precompute screen for opening the folder where the platform-graph encoding was saved.
    • Auto-open the screenshot folder when closing the app if a screenshot was taken.
  • Renamed some lingering things from “Scaffold” to “Scaffolder”.
  • Finished implementing the first working version of platform-graph precalculation.
  • Added support for configuring platform graphs to omit trajectory frame-by-frame position/velocity state.
    • This then requires the player to use the mode of instruction-based movement and Godot physics, rather than trajectory state-matching movement.
  • I occasionally see a bizarre bug with Godot’s debugger:
    • I set a breakpoint somewhere.
    • Then Godot infinitely loops at some unrelated getter within that file.
    • The loop causes the debugger stacktrace to grow for a few minutes until Godot presumably has a stackoverflow.
    • The stacktrace only shows the one getter line repeated.
    • The editor and debugger are sorta responsive during this time, but they quickly snap back to the problem line, so it’s not really usable.
    • This makes it very difficult to debug things.
    • I have a new theory about why this happens:
      • Probably during object instantiation, Godot tries to call these getters internally for some reason (even though there is no explicit call from any GDScript lines)
      • This has become a problem, since I’ve updated my Edge classes to now sometimes be instantiated with null state, when populating them from JSON state.
      • The getters weren’t previously set up to allow this null state.
    • So I spent some time replacing getters in my Edge.gd with proper function calls.
  • Spent some time debugging how Godot’s JSON parsing creates floats instead of ints for all values.
  • Added float-to-int parsing as needed throughout my graph-decoding logic.
  • Added a new minimal-string encoding syntax for low-level objects, such as Vector2, Vector3, Rect2, and Color.
    • For example, output for Vector2:
      • Before: {x:2.2,y:3.3}
      • After: "2.2,3.3"
  • Some platform-graph encoding metrics:
    • "Level 7":
      • Old encoding syntax (using JSON objects): 8.46 MB
      • New encoding syntax (using minimal strings): 7.99 MB
      • New encoding syntax (with trajectory frame-by-frame position/velocity state omitted): 4.34 MB
  • Added support for graph-calculation progress bars:
    • Updated the precompute screen, the platform-graph parser, and the platform-graph class to all support forcing calculations to split each iteration into a separate frame.
    • Then we can update the UI between frames, since the CPU isn’t locked on just the graph calculation.
  • GDScript/Python newline cleanup:
    • Removed redundant \ when inside parens.
    • Removed redundant + for concatenating strings across newlines.
  • Created a progress bar screen to show when calculating the level at play time.
  • Adjusted some GUI spacing.
  • Updated Scaffolder window positioning to always move game window to other monitor if there is a second monitor.
  • Fixed a bug in my time calculations when any physics frame takes longer than the expected “fixed” physics framerate.
  • Added profiler timing metrics to the platform-graph precompute screen.
  • Fixed platform-graph file-loading in HTML exports.
  • Fixed issues with initial screen size.

What's next?

🎉 Cheers!

This is a simple icon representing my sabbatical.