tweak(gui): Decouple GUI transition and world animation time step from render update#2056
Conversation
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Include/GameClient/GameWindowTransitions.h | Changed m_currentFrame from Int to Real and updated the comment to document 30fps-equivalent frame units; clean and correct. |
| Core/GameEngine/Source/GameClient/GUI/GameWindowTransitions.cpp | Replaces single-frame-per-render-update advance with a fractional accumulator scaled by getBaseOverUpdateFpsRatio(); adds a loop to step every integer state so discrete states cannot be skipped at low FPS. Logic is correct for both forward and reverse directions. |
| Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp | Removes the isGamePaused() outer guard and scales Z-rise by getActualLogicTimeScaleOverFpsRatio(); the scale returns 0 when game-halted so pause behavior is preserved. The two different FramePacer functions used across this file vs transitions are intentional. |
| GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp | Identical change to the Zero Hour variant of InGameUI; same analysis applies as Generals. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Render Frame] --> B[TransitionGroup::update]
A --> C[InGameUI::preDraw]
B --> D["timeScale = getBaseOverUpdateFpsRatio()\n= BaseFps / renderFps"]
D --> E["prevFrame = int(m_currentFrame)\nm_currentFrame += dir * timeScale\nnewFrame = int(m_currentFrame)"]
E --> F{newFrame == prevFrame?}
F -- Yes / high FPS --> G[return — hold current state]
F -- No --> H["Loop: tWin->update(frame)\nfor each integer frame crossed"]
H --> I{isFinished?}
I -- Yes --> J[break early]
I -- No --> H
C --> K[updateAndDrawWorldAnimations]
K --> L["zRiseTimeScale =\ngetActualLogicTimeScaleOverFpsRatio()\n= min(1, logicFps/renderFps)\n= 0 if game halted"]
L --> M{Game paused / halted?}
M -- Yes, zRiseTimeScale=0 --> N[Z-rise = 0, expiry check still runs]
M -- No --> O["worldPos.z +=\nzRisePerSecond / LOGICFRAMES_PER_SECOND\n* zRiseTimeScale"]
N --> P[Draw animation]
O --> P
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Render Frame] --> B[TransitionGroup::update]
A --> C[InGameUI::preDraw]
B --> D["timeScale = getBaseOverUpdateFpsRatio()\n= BaseFps / renderFps"]
D --> E["prevFrame = int(m_currentFrame)\nm_currentFrame += dir * timeScale\nnewFrame = int(m_currentFrame)"]
E --> F{newFrame == prevFrame?}
F -- Yes / high FPS --> G[return — hold current state]
F -- No --> H["Loop: tWin->update(frame)\nfor each integer frame crossed"]
H --> I{isFinished?}
I -- Yes --> J[break early]
I -- No --> H
C --> K[updateAndDrawWorldAnimations]
K --> L["zRiseTimeScale =\ngetActualLogicTimeScaleOverFpsRatio()\n= min(1, logicFps/renderFps)\n= 0 if game halted"]
L --> M{Game paused / halted?}
M -- Yes, zRiseTimeScale=0 --> N[Z-rise = 0, expiry check still runs]
M -- No --> O["worldPos.z +=\nzRisePerSecond / LOGICFRAMES_PER_SECOND\n* zRiseTimeScale"]
N --> P[Draw animation]
O --> P
Reviews (7): Last reviewed commit: "fix(gui): Replicate to Generals" | Re-trigger Greptile
Additional Comments (1)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! Prompt To Fix With AIThis is a comment left during a code review.
Path: Generals/Code/GameEngine/Source/GameClient/GUI/GameWindowTransitions.cpp
Line: 62:62
Comment:
[P2] This file still uses `NULL` (`TheTransitionHandler = NULL;`). New code in this PR also adds `FramePacer` usage; consider switching to `nullptr` for null pointer literals to match the repo’s C++ style.
```suggestion
GameWindowTransitionsHandler *TheTransitionHandler = nullptr;
```
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise. |
|
I frequently find myself increasing the render frame rate at the menu because I find the window transitions too slow at 30 fps, and would probably like it better if they played close to ~1.75x the original speed. Is there anything this PR can do in that regard? |
|
I agree with Caball. It would be nice to have a setting to adjust GUI speed (x1.0... x1.25... x1.5x... x1.75... x2.00... and so on). |
|
Feature for scaling animation speed is unrelated to decoupling step and better be follow up change. |
|
Looks good. |
276ed5c to
e6cd525
Compare
|
There are still a couple of to-dos in the PR description. |
Is this something you can reproduce? I don't think I've seen anything like this during my testing. |
|
I ran into this several times when testing. My SSD was slow, but still it was unusual behavior. I can retest if this is not reproducible for others. |
Please do. |
Can you take a look at this? I cannot reproduce it, and this PR probably shouldn't be blocked from merging if you can't reproduce it either |
|
I replicated Xezon's bug by running this on a networked drive. The logic seems to fail on long IO stalls. |
|
Added a single sleep to replicate this, the menu won't even launch on this. But it does on main. GameEngine.cpp 30 will randomly bug out the menu after a few switches. |
|
|
@bobtista Can you take a look at the stuck menu issue? |
|
I've addressed the remaining review comments and tested the menu-stall repro locally:
|
There was a problem hiding this comment.
I expect this test can now be removed because it is implicit in getActualLogicTimeScaleOverFpsRatio ?
There was a problem hiding this comment.
Yes but this block also gates animation cleanup/removal, not only the scaled Z movement. I'm not sure if that would ever make a difference though
There was a problem hiding this comment.
m_expireFrame and ANIM_2D_STATUS_COMPLETE are bound to logic frame number. As long as game is paused frame number will not advance. So this should be stable when paused. Please test.
There was a problem hiding this comment.
I just tested and was able to pause while a unit was being promoted, the animation stopped while paused, and continued when I resumed.
There was a problem hiding this comment.
Wait, the main menu is not rendering when I hit escape with fixed logic fps and uncapped render fps, the game just pauses, then it renders briefly when i resume.
There was a problem hiding this comment.
So it does not work and needs more work?
There was a problem hiding this comment.
Never mind, that was on another branch build unrelated. I rebuilt this PR and retested, 30fps render and faster, main menu animates fine, and the veterancy chevron animates consistently and stops and resumes normally while paused.
There was a problem hiding this comment.
Next thing I noticed was the "you have been promoted" stars blink faster at higher render fps, which is for another PR
Addressed in #2833 |
|
Good change, however I have a suggestion. I for my part like the menus moving a bit faster. Maybe a setting can be added to scale the GUI menu transition speeds to one's liking? |






Summary
Changes
TransitionGroup::m_currentFramechanged fromInttoRealto support fractional frame accumulationTransitionGroup::update()scales frame advancement byTheFramePacer->getActualLogicTimeScaleOverFpsRatio()InGameUIworld animation Z-rise calculation now scales by the same time factorTest plan