Week 25: A new pixel font, and refactoring how GUI-scaling works

A screenshot from Momma Duck showing the new pixelated font.
I created a custom pixelated font

tl;dr: I refactored Scaffolder's GUI-scaling system, and I made a custom pixelated font.

What happened last week?

Scaffolder's magical automatic GUI-scaling

My Scaffolder framework includes a system for automatically adjusting the size of all GUIs and screen layouts to match the current screen size. It's pretty powerful, and extremely handy—when it works!

Basically, the scaling works by registering all scalable GUIs elements in a central place, listening for viewport-size-change events, and then recursively looking at each registered GUI element and updating it's size for the current viewport size.

  • It makes an attempt to automatically scale some properties (like `rect_size`, `rect_min_size`, `rect_scale`).
    • This automatic scaling works by looking at the element's original size and the viewport's new scale factor, and assigning a new element size based-off of the two.
  • But any GUI element can define a `update_gui_scale` method if it needs more custom control for size calculations.
  • Additionally, some types of things are scaled independently. For example:
    • Fonts
    • Checkbox icons
    • Tree-view arrow icons
    • "Styleboxes," which are a concept Godot invented for configuring properties of a panel, like color, border width, corner radius, shadow dimensions, etc.

Unfortunately, this auto-scaling has been a recurring pain-point, and I've periodically had to re-fix scaling problems for the same set of problematic widgets.

An epiphany!

Fortunately, I finally took the time to think-through why my mental model for scaling hasn’t been working. When I try to record "original" GUI element sizes at scaled windows, it uses incorrect values, since underlying font and stylebox sizes have already changed separately.

My solution has two parts:

  • Disable almost all of the dumb GUI-element auto-scaling.
  • Use custom Scaffolder widgets which can be configured with the "original" size information.
    • Since this original size information is configured separately, it won't be contaminated by changes to underlying font or stylebox sizes.
So, after this week, I should actually be done futzing (for the most part) with getting GUI layouts just right! Phew...

Why a pixel font?

Because of consistency.

It's really important that the entire application uses a consistent style. And early on, I decided that I want to focus all of my game-development on a style that is low-resolution and pixelated, similar to classic 8-bit games. Both the GUI and the actual gameplay need to use this pixelated style.

I'm also planning to refactor other aspects of my GUI system to better match this pixelated style. E.g., fewer round corners, fewer smooth shadows, fewer color variations, fewer smooth gradients, bigger UI elements.

Why pixelated in the first place?

Because I like that art style in games!

And, very importantly, because I think I have a much better chance of eventually making half-decent art in this style.

Laundry list

  • Move level-session state into a separate class.
    • This means that the state can now persist after the level itself has been destroyed.
    • This is useful for things like showing your stats on the game-over screen.
  • Fix error messages that had been printing to the console, which were due to a bug with Godot incorrectly reporting circular dependency issues (even though the app itself would still run fine).
    • The simple printing of these error messages was really slowing down the run-time of the editor!
  • Add support for configuring the app to clear save-state on each run.
  • Add support for configuring test-only levels.
  • Move some general-purpose level-config logic into Scaffolder from Momma Duck.
  • Fix issues with level-select-item unlock animations.
  • Fix general-purpose next-level-suggestion logic and move it to Scaffolder.
  • Add a settings-screen item for toggling whether a leash is drawn for the ducks.
  • Fix a bug with screen navigation when reseting the level from the settings screen.
  • Write a mid-year-review devlog post.
  • Refactor GUI scaling to not use per-frame relative scale ratios and to instead record original per-node dimensions using Object.set_meta.
    • This simplifies a lot of logic across the codebase, and fixes a lot of strange timing bugs.
  • Fix some GUI scaling bugs.
  • Create a custom pixel font.
  • Incorporate new pixel font into Scaffolder, Surfacer, and Momma Duck.
    • Struggled for a while trying to understand why Godot was vertically-aligning my font according to the entire character range, rather than just from the top of the character to the baseline.
    • Eventually I discovered Godot’s DynamicFont.extra_spacing_top parameter to fix this!
  • Color links.
  • Simplify color configuration.
  • Fix some labeled-control-item scaling.
  • Improve time and duckling-count label text formats.
  • Add a debounce function.
  • Fix settings-item-checkbox scaling.
  • Fix inspector-legend scaling.
  • Add support for using the header text color in the accordion.
  • Add ability to configure the background color for a specific screen.
  • Add a game-over-screen image.
  • Add support for configuring accordion header centering.
  • Make it easier to globally configure screen-body, button, and accordion sizes.
  • Hide everything during layout resizing.
  • Fix auto-scroll in level-select screen.
  • Fix scaling of x-button and check-boxes in inspector.
  • Fix issues with accordion-panel height scaling.
  • Fix height in the accordion panel.
  • Add support for mouse-back-button presses.
  • Refactor screen-navigation to only keep the current screen instantiated.
  • Finally had a realization for why my mental model for scaling hasn’t been working.
    • Recording original sizes doesn't work at scaled windows, since font and box sizes have already changed.
  • Disable all automatic updates to size/scale/position; only keep auto-updates for very basic values that aren't likely to be affected by scaling to other aspects like font, styleboxes, checkbox icons.
  • Make gui-hiding during screen-resize happen earlier.
  • Create ScaffolderLabelLink and ScaffolderTextureLink.
  • Update ScaffolderButton to support multiple lines.
  • Fix the third-party-licenses-screen width.
  • Add better font-size configuration with ScaffolderButton.
  • Create a custom Spacer scene, which can better handle screen resizing.
  • Use the new Spacer scene throughout the app.
  • Move Bootstrap global link-coloring logic down into my new link class.
  • Move button-press-feedback handling down into re-usable widgets.
  • Add editor icons for ScaffolderLabelLink, ScaffolderTextureLink, and Spacer.
  • Create new ScaffolderProgressBar and ScaffolderSlider scenes.

What's next?

  • Finish refactoring Scaffolder's GUI-scaling system.
  • Finish adding other small post-game-jam fixes to both of my frameworks and to Momma Duck.
  • Work on some other improvements to Surfacer.

🎉 Cheers!

This is a simple icon representing my sabbatical.