mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-23 07:43:01 +01:00
Refactor game loop to use a semi-fixed timestep. (#5848)
Fix headless game being jumpy/stuttering. Bump up network version.
This commit is contained in:
committed by
Michał Janiszewski
parent
337f15619b
commit
a8af3c7670
@@ -70,19 +70,19 @@ namespace OpenRCT2
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// Dependencies
|
// Dependencies
|
||||||
IPlatformEnvironment * const _env = nullptr;
|
IPlatformEnvironment * const _env = nullptr;
|
||||||
IAudioContext * const _audioContext = nullptr;
|
IAudioContext * const _audioContext = nullptr;
|
||||||
IUiContext * const _uiContext = nullptr;
|
IUiContext * const _uiContext = nullptr;
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
IObjectRepository * _objectRepository = nullptr;
|
IObjectRepository * _objectRepository = nullptr;
|
||||||
IObjectManager * _objectManager = nullptr;
|
IObjectManager * _objectManager = nullptr;
|
||||||
ITrackDesignRepository * _trackDesignRepository = nullptr;
|
ITrackDesignRepository * _trackDesignRepository = nullptr;
|
||||||
IScenarioRepository * _scenarioRepository = nullptr;
|
IScenarioRepository * _scenarioRepository = nullptr;
|
||||||
|
|
||||||
bool _isWindowMinimised = false;
|
bool _isWindowMinimised = false;
|
||||||
uint32 _lastTick = 0;
|
uint32 _lastTick = 0;
|
||||||
uint32 _uncapTick = 0;
|
uint32 _accumulator = 0;
|
||||||
|
|
||||||
/** If set, will end the OpenRCT2 game loop. Intentially private to this module so that the flag can not be set back to false. */
|
/** If set, will end the OpenRCT2 game loop. Intentially private to this module so that the flag can not be set back to false. */
|
||||||
bool _finished = false;
|
bool _finished = false;
|
||||||
@@ -315,7 +315,7 @@ namespace OpenRCT2
|
|||||||
}
|
}
|
||||||
network_begin_server(gNetworkStartPort, gNetworkStartAddress);
|
network_begin_server(gNetworkStartPort, gNetworkStartAddress);
|
||||||
}
|
}
|
||||||
#endif // DISABLE_NETWORK
|
#endif // DISABLE_NETWORK
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STARTUP_ACTION_EDIT:
|
case STARTUP_ACTION_EDIT:
|
||||||
@@ -344,6 +344,15 @@ namespace OpenRCT2
|
|||||||
RunGameLoop();
|
RunGameLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShouldRunVariableFrame()
|
||||||
|
{
|
||||||
|
if (!gConfigGeneral.uncap_fps) return false;
|
||||||
|
if (gGameSpeed > 4) return false;
|
||||||
|
if (gOpenRCT2Headless) return false;
|
||||||
|
if (_uiContext->IsMinimised()) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the main game loop until the finished flag is set.
|
* Run the main game loop until the finished flag is set.
|
||||||
*/
|
*/
|
||||||
@@ -351,9 +360,21 @@ namespace OpenRCT2
|
|||||||
{
|
{
|
||||||
log_verbose("begin openrct2 loop");
|
log_verbose("begin openrct2 loop");
|
||||||
_finished = false;
|
_finished = false;
|
||||||
|
|
||||||
|
bool variableFrame = ShouldRunVariableFrame();
|
||||||
|
bool useVariableFrame;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (ShouldRunVariableFrame())
|
useVariableFrame = ShouldRunVariableFrame();
|
||||||
|
// Make sure we catch the state change and reset it.
|
||||||
|
if (variableFrame != useVariableFrame)
|
||||||
|
{
|
||||||
|
_lastTick = 0;
|
||||||
|
variableFrame = useVariableFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useVariableFrame)
|
||||||
{
|
{
|
||||||
RunVariableFrame();
|
RunVariableFrame();
|
||||||
}
|
}
|
||||||
@@ -365,32 +386,32 @@ namespace OpenRCT2
|
|||||||
log_verbose("finish openrct2 loop");
|
log_verbose("finish openrct2 loop");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShouldRunVariableFrame()
|
|
||||||
{
|
|
||||||
if (!gConfigGeneral.uncap_fps) return false;
|
|
||||||
if (gGameSpeed > 4) return false;
|
|
||||||
if (gOpenRCT2Headless) return false;
|
|
||||||
if (_uiContext->IsMinimised()) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunFixedFrame()
|
void RunFixedFrame()
|
||||||
{
|
{
|
||||||
_uncapTick = 0;
|
|
||||||
uint32 currentTick = platform_get_ticks();
|
uint32 currentTick = platform_get_ticks();
|
||||||
uint32 ticksElapsed = currentTick - _lastTick;
|
|
||||||
if (ticksElapsed < UPDATE_TIME_MS)
|
if (_lastTick == 0)
|
||||||
{
|
|
||||||
platform_sleep(UPDATE_TIME_MS - ticksElapsed);
|
|
||||||
_lastTick += UPDATE_TIME_MS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
_lastTick = currentTick;
|
_lastTick = currentTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 elapsed = currentTick - _lastTick;
|
||||||
|
|
||||||
|
_lastTick = currentTick;
|
||||||
|
_accumulator += elapsed;
|
||||||
|
|
||||||
GetContext()->GetUiContext()->ProcessMessages();
|
GetContext()->GetUiContext()->ProcessMessages();
|
||||||
|
|
||||||
|
if (_accumulator < UPDATE_TIME_MS)
|
||||||
|
{
|
||||||
|
platform_sleep(UPDATE_TIME_MS - _accumulator - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_accumulator -= UPDATE_TIME_MS;
|
||||||
|
|
||||||
rct2_update();
|
rct2_update();
|
||||||
if (!_isWindowMinimised)
|
if (!_isWindowMinimised && !gOpenRCT2Headless)
|
||||||
{
|
{
|
||||||
platform_draw();
|
platform_draw();
|
||||||
}
|
}
|
||||||
@@ -399,42 +420,49 @@ namespace OpenRCT2
|
|||||||
void RunVariableFrame()
|
void RunVariableFrame()
|
||||||
{
|
{
|
||||||
uint32 currentTick = platform_get_ticks();
|
uint32 currentTick = platform_get_ticks();
|
||||||
if (_uncapTick == 0)
|
|
||||||
|
bool draw = !_isWindowMinimised && !gOpenRCT2Headless;
|
||||||
|
|
||||||
|
if (_lastTick == 0)
|
||||||
{
|
{
|
||||||
_uncapTick = currentTick;
|
|
||||||
sprite_position_tween_reset();
|
sprite_position_tween_reset();
|
||||||
|
_lastTick = currentTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit number of updates per loop (any long pauses or debugging can make this update for a very long time)
|
uint32 elapsed = currentTick - _lastTick;
|
||||||
if (currentTick - _uncapTick > UPDATE_TIME_MS * 60)
|
|
||||||
{
|
if (elapsed > UPDATE_TIME_MS)
|
||||||
_uncapTick = currentTick - UPDATE_TIME_MS - 1;
|
elapsed = UPDATE_TIME_MS;
|
||||||
}
|
|
||||||
|
_lastTick = currentTick;
|
||||||
|
_accumulator += elapsed;
|
||||||
|
|
||||||
GetContext()->GetUiContext()->ProcessMessages();
|
GetContext()->GetUiContext()->ProcessMessages();
|
||||||
|
|
||||||
while (_uncapTick <= currentTick && currentTick - _uncapTick > UPDATE_TIME_MS)
|
while (_accumulator >= UPDATE_TIME_MS)
|
||||||
{
|
{
|
||||||
// Get the original position of each sprite
|
// Get the original position of each sprite
|
||||||
sprite_position_tween_store_a();
|
if(draw)
|
||||||
|
sprite_position_tween_store_a();
|
||||||
|
|
||||||
// Update the game so the sprite positions update
|
|
||||||
rct2_update();
|
rct2_update();
|
||||||
|
|
||||||
// Get the next position of each sprite
|
_accumulator -= UPDATE_TIME_MS;
|
||||||
sprite_position_tween_store_b();
|
|
||||||
|
|
||||||
_uncapTick += UPDATE_TIME_MS;
|
// Get the next position of each sprite
|
||||||
|
if(draw)
|
||||||
|
sprite_position_tween_store_b();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tween the position of each sprite from the last position to the new position based on the time between the last
|
if (draw)
|
||||||
// tick and the next tick.
|
{
|
||||||
float nudge = 1 - ((float)(currentTick - _uncapTick) / UPDATE_TIME_MS);
|
const float alpha = (float)_accumulator / UPDATE_TIME_MS;
|
||||||
sprite_position_tween_all(nudge);
|
sprite_position_tween_all(alpha);
|
||||||
|
|
||||||
platform_draw();
|
platform_draw();
|
||||||
|
|
||||||
sprite_position_tween_restore();
|
sprite_position_tween_restore();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenParkAutoDetectFormat(IStream * stream)
|
bool OpenParkAutoDetectFormat(IStream * stream)
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ extern "C" {
|
|||||||
// This define specifies which version of network stream current build uses.
|
// This define specifies which version of network stream current build uses.
|
||||||
// It is used for making sure only compatible builds get connected, even within
|
// It is used for making sure only compatible builds get connected, even within
|
||||||
// single OpenRCT2 version.
|
// single OpenRCT2 version.
|
||||||
#define NETWORK_STREAM_VERSION "30"
|
#define NETWORK_STREAM_VERSION "31"
|
||||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -758,8 +758,10 @@ void sprite_position_tween_store_b()
|
|||||||
store_sprite_locations(_spritelocations2);
|
store_sprite_locations(_spritelocations2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sprite_position_tween_all(float nudge)
|
void sprite_position_tween_all(float alpha)
|
||||||
{
|
{
|
||||||
|
const float inv = (1.0f - alpha);
|
||||||
|
|
||||||
for (uint16 i = 0; i < MAX_SPRITES; i++) {
|
for (uint16 i = 0; i < MAX_SPRITES; i++) {
|
||||||
rct_sprite * sprite = get_sprite(i);
|
rct_sprite * sprite = get_sprite(i);
|
||||||
if (sprite_should_tween(sprite)) {
|
if (sprite_should_tween(sprite)) {
|
||||||
@@ -767,9 +769,9 @@ void sprite_position_tween_all(float nudge)
|
|||||||
rct_xyz16 posB = _spritelocations2[i];
|
rct_xyz16 posB = _spritelocations2[i];
|
||||||
|
|
||||||
sprite_set_coordinates(
|
sprite_set_coordinates(
|
||||||
posB.x + (sint16)((posA.x - posB.x) * nudge),
|
posB.x * alpha + posA.x * inv,
|
||||||
posB.y + (sint16)((posA.y - posB.y) * nudge),
|
posB.y * alpha + posA.y * inv,
|
||||||
posB.z + (sint16)((posA.z - posB.z) * nudge),
|
posB.z * alpha + posA.z * inv,
|
||||||
sprite
|
sprite
|
||||||
);
|
);
|
||||||
invalidate_sprite_2(sprite);
|
invalidate_sprite_2(sprite);
|
||||||
|
|||||||
Reference in New Issue
Block a user