Zixel: The Miscellaneous Sprint
Sprint 4
Sprint 4 took a little bit of an unexpected detour. I started working on adding some core features to the room system.
Notably, transition effects between rooms. After the room transitions were functioning I took a brief detour
to add some 2D lighting to the game.
My goal is to start working on The Vendor’s game sometime soon. Without a UI system, that wasn’t going to be possible.
Detour #2 for this sprint: a UI system! This turned out to be a lot more work (and fun) than expected. It’s still a work in progress,
but I’m pretty happy with the progress so far.
Room Transitions
The Room class is one of the major building blocks for Zixel. Each room is a collection of Object instances.
It’s a way to break apart functionality and also keep the code organized. Examples of a room include:
- The main menu
- The vendor’s shop
- The game’s main gameplay room
- The game’s end screen
Moving between rooms is a very common operation that requires a nice transition effect. That’s what this feature is
for! Moving between rooms is designed to be a simple function call against the room manager.
In the example below, the room manager will transition to the ParticleTestRoom room using a wipe transition effect.
roomMgr.ChangeRoom(
std::make_shared<ParticleTestRoom>(),
zixel::TransitionType::Wipe,
0.6f
);
Demo
2D Lighting
Another random thought that turned into an afternoon of workβsimple 2D lighting! This isn’t a full-blown lighting
system, but it’s a start. The idea here is that light sources are drawn to an overlay texture that is applied (with
a blending shader) to the scene.
Here’s an example of a simple light that we attach to a test game object, “Frank”.
zixel::Light frankLight;
frankLight.position = Vector2{m_pFrank->GetPosition().x, m_pFrank->GetPosition().y};
frankLight.radius = 150.0f;
frankLight.color = Color{255, 220, 180, 255}; // Warm white
frankLight.intensity = 1.2f;
frankLight.falloff = 1.5f;
frankLight.allowCulling = false; // Always render player light
m_frankLightIndex = lighting.AddLight(frankLight);
Demo
Font Scaling (SDF)
Font scaling is an interesting challenge for any UI system. Traditional scaling techniques only work well for
monospaced fonts, but SDF fonts are much more flexible. The idea is to render the font to a texture, then scale the
texture based on the font size.
I won’t go into the details of how this works, but there’s an expertly written article on the topic here.
Demo
Shader management system
The rabbit hole goes deeper! When working on the SDF shader, I realized that Zixel didn’t have a unified shader
management system. Rather than simply adding a shader manager, I wanted to ensure that every Object had the
ability to use its own custom shader and not be forced to use a default sprite renderer.
All the shader effects will be added to Zixel to keep the game logic as simple as possible. Using them is as
simple as overriding the Object::OnDraw() virtual method. Inside of it, call shaderMgr.EnableEffect(effect);
and then invoke the base Object::OnDraw() method.
Demo
UI Theme & Basic Components
FINALLY! After all the precursor work, it’s time to get cracking on a UI system. The Vendor will be very UI heavy. Lots of inventory management, dialog, and other UI elements will be needed. Designing the UI system with Claude, I was able to put together a retained mode UI system for my needs. Base plumbing for the system is in place, including texture support, modals, and controls. Beyond the base work, I’d like to add as much “juice” as possible. Making it very visually engaging.
Demo
GitHub CoPilot Agent gets smart(er)
While doing the UI work, some debugging was necessary to fix issues that came up. My workflow at this point is as
follows:
- Create a GitHub issues ticket describing the required feature as if it were a product requirements document
- Assign the ticket to GitHub CoPilot Agent
- CoPilot will automatically create a pull request with the required changes
- Pull down the PR branch and test it locally
- (optionally) fix any issues locally or tag @copilot in the PR and ask it to address
- Iterate until the PR is ready to merge
During the initial implementation of the UI panel component, CoPilot finished the implementation by updating the
PR description with…A SCREENSHOT. That’s notable because CoPilot operates in a headless environment within the
GitHub Actions CI pipeline. The model figured out a way to capture screenshots and use its multimodal ability to
determine if it had completed the task.
Below is the screenshot of my interaction with CoPilot, asking how it obtained the screenshot.
Needless to say, I’m very impressed with that little bit of functionality. So much so, that it was added to the
CoPilot agent definition file that is used for the Zixel repo.
Sprint 5
Next up - I’d like to round out the UI functionality, keeping in mind that it’d be great to stub out some of the
shop management functionality for The Vendor.
Additional Controls:
- Slider
- Checkbox
- Dropdown
- Tooltips
- List
- Scrollbar (for lists)
- Radio buttons
- Image
- Tabs
Animations: - In/Out - Easing - Bouncing - Wiggle
Sounds: - Button clicks - Error/Success
Ensuring that the UI system is robust and complete will enable moving on to The Vendor.