Lesson Learned from RLDDTCRT: UI Architecture Design

2025-08-18

I'm currently just about to start part 10, but I think it might be a good timing to review and redesign my UI architecture design, as it has caused too much trouble in my previous parts.

Current Design

Basically, the game loop is

LOOP
    WHILE need to draw
        draw UI, and handle the previous input, and generate potentially a single output
        apply output back to the UI state and game state, which can potentially produce the need to draw
    IF has key/mouse input right now
        take the input and produce the need to draw

There are 2 big problems:

Drawing UI and handling input should really be separated processes
Input handling should be from top to bottom, while drawing should be from bottom to top. I was not doing it because I separated UI and game logic as much as I can, so it was during UI drawing that I could related things on the screen back to things in the game (for example, which grid on the screen corresponding to which mob). It turns out that this is still doable with the separation: just store the needed information after each draw.
Input and output does not need to be separated structure, and they shouldn't
This is obvious as drawing can produce an output back to itself, which then could trigger another output. Given that the draw process only takes "inputs", this output has to be adapted into an input. You can see how terrible this code can be.
Each iteration should be able to generate multiple events
There is one kind of events that cause the pop-up (e.g. inventory) to be closed. However, when using a item, the UI produce actually 2 events at the end: close the inventory, and produce a game action. Currently this is handled in my code by a clever hack.

UI Features and Their Challenges

Input that produces input

For example, In my game in the "look" mode, the player can hit ENTER to query the thing at cursor. This is implemented by mimicking a mouse click to reuse the logic.

This challenge gives another example on why separating input and output can be a bad idea.

Hint on the action effect

Suppose the player cast a fireball. In modern games, the UI should draw an area that marks the affected tiles, and when the player confirm all mobs on these tiles, and exactly only these mobs, will be damaged by the fireball.

The challenge here is that the UI should know something from the game logic. It can either query it every time it need this information, or it can produce the result itself by using exactly the same algorithm as the game.

Multi-step input

When use an item, there are several steps:

  • the player tap i to open the inventory
  • the player use some key sequences to select an item
  • the player then select a place to use the item on
  • the item is finally consumed and activated.

Ideally, the real action sent to the game only at the last step (after selecting the place).

Animation

Animation is probably the hardest, due to moving.

(OffColorCommentary 2016) gave some wise thought on this topic. Generally, we want to minimize the time during which animation is played, otherwise our targeted audience can be bored while waiting. One core way to do this is to play all the animations that move each mobs at the same time. But, if One of them affects another, there is a logical order introduced: A mob should not attack a tile at the same time a mob is moving away from it.

That is, the system should work out the topological orders of the animations, and play animations that don't depend on each other at the same time as many as possible.

Proposed Design

The design is simple:

LOOP
    IF need to draw
        draw UI, and cache the information needed by the outer UI logic
    IF has key/mouse input right now
        produce a UI event to FIFO
    WHILE there is some UI event in FIFO
        Handle a UI event, which can
            produce zero or more UI events to FIFO
            request the game to do something, and get a feedback

Let's see how far this design can go.

Reference

OffColorCommentary. 2016. “Reply on Faq Friday #31: Pain Points About Animation.” February 5, 2016. https://old.reddit.com/r/roguelikedev/comments/448jw6/faq_friday_31_pain_points/czoiapx/.