refactor game speed to provide a constant frame rate
This commit is contained in:
parent
3ba7987ffe
commit
15986db050
14 changed files with 80 additions and 99 deletions
|
@ -1205,21 +1205,20 @@ Sets the resolution of the video display, valid options are 640x480, 800x600, 10
|
|||
<a name="SetVideoSyncSpeed"></a>
|
||||
<h3>SetVideoSyncSpeed(speed)</h3>
|
||||
|
||||
Sets the video sync speed for the game. 100 is equivilent to 30 frames/second. 200
|
||||
is equivilent to 60 frames/second.
|
||||
Sets the video render speed for the game. 30 means 30 frames/second. 60 means 60 frames/second. Default is the native monitor refresh rate.
|
||||
|
||||
<dl>
|
||||
<dt>speed</dt>
|
||||
<dd>The speed to set the game to as described, 100 is default</dd>
|
||||
<dd>The speed to set the game to as described, default is the monitor refresh rate.</dd>
|
||||
<dt><i>RETURNS</i></dt>
|
||||
<dd>Nothing</dd>
|
||||
</dl>
|
||||
|
||||
<h4>Exmaple</h4>
|
||||
<h4>Example</h4>
|
||||
|
||||
<pre>
|
||||
-- Set Speed to 60 frames/second
|
||||
SetVideoSyncSpeed(200)
|
||||
SetVideoSyncSpeed(60)
|
||||
</pre>
|
||||
|
||||
<a name="ShowEnergySelectedOnly"></a>
|
||||
|
|
|
@ -2105,7 +2105,7 @@ void EditorMainLoop()
|
|||
bool start = true;
|
||||
|
||||
while (Editor.Running) {
|
||||
if (FrameCounter % FRAMES_PER_SECOND == 0) {
|
||||
if (FrameCounter % CYCLES_PER_SECOND == 0) {
|
||||
if (UpdateMinimap) {
|
||||
UI.Minimap.Update();
|
||||
UpdateMinimap = false;
|
||||
|
|
|
@ -635,14 +635,14 @@ bool GetGamePaused()
|
|||
}
|
||||
|
||||
/**
|
||||
** Set the game speed
|
||||
** Set the game speed in range 0 .. 100
|
||||
**
|
||||
** @param speed New game speed.
|
||||
*/
|
||||
void SetGameSpeed(int speed)
|
||||
{
|
||||
if (GameCycle == 0 || FastForwardCycle < GameCycle) {
|
||||
VideoSyncSpeed = speed * 100 / CYCLES_PER_SECOND;
|
||||
CyclesPerSecond = (static_cast<double>(speed) / 100) * CYCLES_PER_SECOND + static_cast<double>(CYCLES_PER_SECOND) / 3;
|
||||
SetVideoSync();
|
||||
}
|
||||
}
|
||||
|
@ -654,7 +654,7 @@ void SetGameSpeed(int speed)
|
|||
*/
|
||||
int GetGameSpeed()
|
||||
{
|
||||
return CYCLES_PER_SECOND * VideoSyncSpeed / 100;
|
||||
return ((static_cast<double>(CyclesPerSecond) - static_cast<double>(CYCLES_PER_SECOND) / 3) / CYCLES_PER_SECOND) * 100;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
|
|
|
@ -207,8 +207,8 @@ extern bool GamePaused;
|
|||
extern bool GameObserve;
|
||||
/// Flag telling if the game is in establishing mode
|
||||
extern bool GameEstablishing;
|
||||
/// Flag telling not to advance to the next game cycle
|
||||
extern char SkipGameCycle;
|
||||
/// Counter of how many game cycles to skip for each rendered frame
|
||||
extern double SkipGameCycle;
|
||||
/// Invincibility cheat
|
||||
extern bool GodMode;
|
||||
/// Whether the map is the only thing displayed or not
|
||||
|
|
|
@ -65,8 +65,6 @@ constexpr unsigned short UpgradeMax = 2048; /// How many upgrades
|
|||
constexpr unsigned char MAX_RACES = 8;
|
||||
constexpr unsigned char PlayerNumNeutral = PlayerMax - 1; /// this is the neutral player slot
|
||||
|
||||
/// Frames per second to display (original 30-40)
|
||||
constexpr unsigned char FRAMES_PER_SECOND = 30; // 1/30s
|
||||
/// Game cycles per second to simulate (original 30-40)
|
||||
constexpr unsigned char CYCLES_PER_SECOND = 30; // 1/30s 0.33ms
|
||||
|
||||
|
|
|
@ -405,15 +405,12 @@ public:
|
|||
extern CVideo Video;
|
||||
|
||||
/**
|
||||
** Video synchronization speed. Synchronization time in percent.
|
||||
** If =0, video framerate is not synchronized. 100 is exact
|
||||
** CYCLES_PER_SECOND (30). Game will try to redraw screen within
|
||||
** intervals of VideoSyncSpeed, not more, not less.
|
||||
** Target CyclesPerSecond that are simulated. The default is CYCLES_PER_SECOND.
|
||||
** @see CYCLES_PER_SECOND
|
||||
*/
|
||||
extern int VideoSyncSpeed;
|
||||
extern int CyclesPerSecond;
|
||||
|
||||
extern int SkipFrames;
|
||||
extern double SkipCycles;
|
||||
|
||||
/// Fullscreen or windowed set from commandline.
|
||||
extern char VideoForceFullScreen;
|
||||
|
@ -421,6 +418,9 @@ extern char VideoForceFullScreen;
|
|||
/// Next frame ticks
|
||||
extern double NextFrameTicks;
|
||||
|
||||
/// Target refresh rate for renderer
|
||||
extern int RefreshRate;
|
||||
|
||||
/// Counts frames
|
||||
extern unsigned long FrameCounter;
|
||||
|
||||
|
|
|
@ -370,7 +370,7 @@ void CMinimap::Update()
|
|||
{
|
||||
static int red_phase;
|
||||
|
||||
int red_phase_changed = red_phase != (int)((FrameCounter / FRAMES_PER_SECOND) & 1);
|
||||
int red_phase_changed = red_phase != (int)((FrameCounter / CYCLES_PER_SECOND) & 1);
|
||||
if (red_phase_changed) {
|
||||
red_phase = !red_phase;
|
||||
}
|
||||
|
|
|
@ -1148,11 +1148,10 @@ static void CheckPlayerThatTimeOut(int hostIndex)
|
|||
if (!lastFrame) {
|
||||
return;
|
||||
}
|
||||
const int framesPerSecond = FRAMES_PER_SECOND * VideoSyncSpeed / 100;
|
||||
const int secs = (FrameCounter - lastFrame) / framesPerSecond;
|
||||
const int secs = (FrameCounter - lastFrame) / CyclesPerSecond;
|
||||
// FIXME: display a menu while we wait
|
||||
const int timeoutInS = CNetworkParameter::Instance.timeoutInS;
|
||||
if (3 <= secs && secs < timeoutInS && FrameCounter % framesPerSecond == 0) {
|
||||
if (3 <= secs && secs < timeoutInS && FrameCounter % CyclesPerSecond == 0) {
|
||||
SetMessage(_("Waiting for player \"%s\": %d:%02d"), Hosts[hostIndex].PlyName,
|
||||
(timeoutInS - secs) / 60, (timeoutInS - secs) % 60);
|
||||
}
|
||||
|
|
|
@ -110,15 +110,15 @@ void DoScrollArea(int state, bool fast, bool isKeyboard)
|
|||
vp = UI.SelectedViewport;
|
||||
|
||||
if (fast) {
|
||||
stepx = (int)(speed * vp->MapWidth / 2 * PixelTileSize.x * FRAMES_PER_SECOND / 4);
|
||||
stepy = (int)(speed * vp->MapHeight / 2 * PixelTileSize.y * FRAMES_PER_SECOND / 4);
|
||||
stepx = (int)(speed * vp->MapWidth / 2 * PixelTileSize.x * CYCLES_PER_SECOND / 4);
|
||||
stepy = (int)(speed * vp->MapHeight / 2 * PixelTileSize.y * CYCLES_PER_SECOND / 4);
|
||||
} else {// dynamic: let these variables increase up to fast..
|
||||
// FIXME: pixels per second should be configurable
|
||||
stepx = (int)(speed * PixelTileSize.x * FRAMES_PER_SECOND / 4);
|
||||
stepy = (int)(speed * PixelTileSize.y * FRAMES_PER_SECOND / 4);
|
||||
stepx = (int)(speed * PixelTileSize.x * CYCLES_PER_SECOND / 4);
|
||||
stepy = (int)(speed * PixelTileSize.y * CYCLES_PER_SECOND / 4);
|
||||
}
|
||||
if ((state & (ScrollLeft | ScrollRight)) && (state & (ScrollLeft | ScrollRight)) != (ScrollLeft | ScrollRight)) {
|
||||
stepx = stepx * 100 * 100 / VideoSyncSpeed / FRAMES_PER_SECOND / (SkipFrames + 1);
|
||||
stepx = stepx * 3;
|
||||
remx += stepx - (stepx / 100) * 100;
|
||||
stepx /= 100;
|
||||
if (remx > 100) {
|
||||
|
@ -129,7 +129,7 @@ void DoScrollArea(int state, bool fast, bool isKeyboard)
|
|||
stepx = 0;
|
||||
}
|
||||
if ((state & (ScrollUp | ScrollDown)) && (state & (ScrollUp | ScrollDown)) != (ScrollUp | ScrollDown)) {
|
||||
stepy = stepy * 100 * 100 / VideoSyncSpeed / FRAMES_PER_SECOND / (SkipFrames + 1);
|
||||
stepy = stepy * 3;
|
||||
remy += stepy - (stepy / 100) * 100;
|
||||
stepy /= 100;
|
||||
if (remy > 100) {
|
||||
|
@ -255,7 +255,7 @@ static void GameLogicLoop()
|
|||
//
|
||||
// Game logic part
|
||||
//
|
||||
if (!GamePaused && NetworkInSync && !SkipGameCycle) {
|
||||
if (!GamePaused && NetworkInSync && SkipGameCycle < 1) {
|
||||
SinglePlayerReplayEachCycle();
|
||||
++GameCycle;
|
||||
MultiPlayerReplayEachCycle();
|
||||
|
@ -333,11 +333,6 @@ static void GameLogicLoop()
|
|||
#endif
|
||||
}
|
||||
|
||||
//#define REALVIDEO
|
||||
#ifdef REALVIDEO
|
||||
static int RealVideoSyncSpeed;
|
||||
#endif
|
||||
|
||||
static void DisplayLoop()
|
||||
{
|
||||
/* update only if viewmode changed */
|
||||
|
@ -359,12 +354,6 @@ static void DisplayLoop()
|
|||
|
||||
ColorCycle();
|
||||
|
||||
#ifdef REALVIDEO
|
||||
if (FastForwardCycle > GameCycle && RealVideoSyncSpeed != VideoSyncSpeed) {
|
||||
RealVideoSyncSpeed = VideoSyncSpeed;
|
||||
VideoSyncSpeed = 3000;
|
||||
}
|
||||
#endif
|
||||
if (FastForwardCycle <= GameCycle || GameCycle <= 10 || !(GameCycle & CallPeriod::cEvery256th)) {
|
||||
//FIXME: this might be better placed somewhere at front of the
|
||||
// program, as we now still have a game on the background and
|
||||
|
@ -375,11 +364,6 @@ static void DisplayLoop()
|
|||
UpdateDisplay();
|
||||
RealizeVideoMemory();
|
||||
}
|
||||
#ifdef REALVIDEO
|
||||
if (FastForwardCycle == GameCycle) {
|
||||
VideoSyncSpeed = RealVideoSyncSpeed;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void SingleGameLoop()
|
||||
|
@ -416,10 +400,6 @@ void GameMainLoop()
|
|||
|
||||
CParticleManager::init();
|
||||
|
||||
#ifdef REALVIDEO
|
||||
RealVideoSyncSpeed = VideoSyncSpeed;
|
||||
#endif
|
||||
|
||||
CclCommand("if (GameStarting ~= nil) then GameStarting() end");
|
||||
|
||||
long ticks = SDL_GetTicks();
|
||||
|
@ -440,11 +420,6 @@ void GameMainLoop()
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef REALVIDEO
|
||||
if (FastForwardCycle > GameCycle) {
|
||||
VideoSyncSpeed = RealVideoSyncSpeed;
|
||||
}
|
||||
#endif
|
||||
NetworkQuitGame();
|
||||
EndReplayLog();
|
||||
|
||||
|
|
|
@ -590,7 +590,7 @@ void ParseCommandLine(int argc, char **argv, Parameters ¶meters)
|
|||
AiSleepCycles = atoi(optarg);
|
||||
continue;
|
||||
case 'S':
|
||||
VideoSyncSpeed = atoi(optarg);
|
||||
RefreshRate = atoi(optarg);
|
||||
continue;
|
||||
case 'u':
|
||||
if (!strcmp(optarg, "userhome")) {
|
||||
|
|
|
@ -84,7 +84,7 @@ bool GameRunning; /// Current running state
|
|||
bool GamePaused; /// Current pause state
|
||||
bool GameObserve; /// Observe mode
|
||||
bool GameEstablishing; /// Game establishing mode
|
||||
char SkipGameCycle; /// Skip the next game cycle
|
||||
double SkipGameCycle; /// Skip the next n game cycles
|
||||
char BigMapMode; /// Show only the map
|
||||
enum _iface_state_ InterfaceState; /// Current interface state
|
||||
bool GodMode; /// Invincibility cheat
|
||||
|
@ -349,7 +349,7 @@ static void UiIncreaseGameSpeed()
|
|||
if (FastForwardCycle >= GameCycle) {
|
||||
return;
|
||||
}
|
||||
VideoSyncSpeed += 10;
|
||||
CyclesPerSecond++;
|
||||
SetVideoSync();
|
||||
UI.StatusLine.Set(_("Faster"));
|
||||
}
|
||||
|
@ -362,12 +362,8 @@ static void UiDecreaseGameSpeed()
|
|||
if (FastForwardCycle >= GameCycle) {
|
||||
return;
|
||||
}
|
||||
if (VideoSyncSpeed <= 10) {
|
||||
if (VideoSyncSpeed > 1) {
|
||||
--VideoSyncSpeed;
|
||||
}
|
||||
} else {
|
||||
VideoSyncSpeed -= 10;
|
||||
if (CyclesPerSecond > 1) {
|
||||
--CyclesPerSecond;
|
||||
}
|
||||
SetVideoSync();
|
||||
UI.StatusLine.Set(_("Slower"));
|
||||
|
@ -381,7 +377,7 @@ static void UiSetDefaultGameSpeed()
|
|||
if (FastForwardCycle >= GameCycle) {
|
||||
return;
|
||||
}
|
||||
VideoSyncSpeed = 100;
|
||||
CyclesPerSecond = CYCLES_PER_SECOND;
|
||||
SetVideoSync();
|
||||
UI.StatusLine.Set(_("Set default game speed"));
|
||||
}
|
||||
|
|
|
@ -1151,7 +1151,7 @@ static void InfoPanel_draw_no_selection()
|
|||
y += 16;
|
||||
label.Draw(x, y, _("Cycle:"));
|
||||
label.Draw(x + 48, y, GameCycle);
|
||||
label.Draw(x + 110, y, CYCLES_PER_SECOND * VideoSyncSpeed / 100);
|
||||
label.Draw(x + 110, y, CyclesPerSecond);
|
||||
y += 20;
|
||||
|
||||
std::string nc;
|
||||
|
|
|
@ -99,7 +99,11 @@ static int NumRects;
|
|||
static std::map<int, std::string> Key2Str;
|
||||
static std::map<std::string, int> Str2Key;
|
||||
|
||||
double FrameTicks; /// Frame length in ms
|
||||
/// Frame length in ms
|
||||
static double FrameTicks;
|
||||
|
||||
/// Target refresh rate for renderer
|
||||
int RefreshRate = 0;
|
||||
|
||||
const EventCallback *Callbacks;
|
||||
|
||||
|
@ -116,29 +120,47 @@ uint32_t SDL_CUSTOM_KEY_UP;
|
|||
-- Sync
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
static int GetRefreshRate()
|
||||
{
|
||||
if (!RefreshRate) {
|
||||
int displayCount = SDL_GetNumVideoDisplays();
|
||||
SDL_DisplayMode mode;
|
||||
for (int i = 0; i < displayCount; i++) {
|
||||
SDL_GetDesktopDisplayMode(0, &mode);
|
||||
if (mode.refresh_rate > RefreshRate) {
|
||||
RefreshRate = mode.refresh_rate;
|
||||
}
|
||||
}
|
||||
if (!RefreshRate) {
|
||||
RefreshRate = 60;
|
||||
}
|
||||
}
|
||||
return RefreshRate;
|
||||
}
|
||||
|
||||
/**
|
||||
** Initialise video sync.
|
||||
** Calculate the length of video frame and any simulation skips.
|
||||
**
|
||||
** @see VideoSyncSpeed @see SkipFrames @see FrameTicks
|
||||
** @see CyclesPerSecond @see SkipCycles @see SkipFrames @see FrameTicks
|
||||
*/
|
||||
void SetVideoSync()
|
||||
{
|
||||
double ms;
|
||||
|
||||
if (VideoSyncSpeed) {
|
||||
ms = (1000.0 * 1000.0 / CYCLES_PER_SECOND) / VideoSyncSpeed;
|
||||
int fps = GetRefreshRate();
|
||||
int nativeFps = fps;
|
||||
if (fps < CyclesPerSecond) {
|
||||
fprintf(stdout, "WARNING: Game speed is faster than monitor refresh rate.\n");
|
||||
FrameTicks = 1000.0 / CyclesPerSecond;
|
||||
SDL_GL_SetSwapInterval(0); // disable vsync, so we can run faster than the refresh
|
||||
} else {
|
||||
ms = (double)INT_MAX;
|
||||
FrameTicks = 1000.0 / fps;
|
||||
if (SDL_GL_SetSwapInterval(-1) < 0) { // try to set adaptive vsync
|
||||
SDL_GL_SetSwapInterval(1); // if it failed, set vsync
|
||||
}
|
||||
}
|
||||
SkipFrames = ms / 400;
|
||||
while (SkipFrames && ms / SkipFrames < 200) {
|
||||
--SkipFrames;
|
||||
}
|
||||
ms /= SkipFrames + 1;
|
||||
SkipCycles = (static_cast<double>(fps) / CyclesPerSecond) - 1;
|
||||
|
||||
FrameTicks = ms / 10;
|
||||
DebugPrint("frames %d - %5.2fms\n" _C_ SkipFrames _C_ ms / 10);
|
||||
DebugPrint("native fps: %d, render frame skip: %d, game cycle skip: %f\n" _C_ nativeFps _C_ Preference.FrameSkip _C_ SkipCycles);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
|
@ -785,8 +807,10 @@ void WaitEventsOneFrame()
|
|||
}
|
||||
handleInput(NULL);
|
||||
|
||||
if (!SkipGameCycle--) {
|
||||
SkipGameCycle = SkipFrames;
|
||||
if (SkipGameCycle < 0) {
|
||||
SkipGameCycle += SkipCycles;
|
||||
} else {
|
||||
SkipGameCycle--;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -795,21 +819,10 @@ void WaitEventsOneFrame()
|
|||
*/
|
||||
|
||||
static Uint32 LastTick = 0;
|
||||
static int RefreshRate = 0;
|
||||
|
||||
static void RenderBenchmarkOverlay()
|
||||
{
|
||||
if (!RefreshRate) {
|
||||
int displayCount = SDL_GetNumVideoDisplays();
|
||||
SDL_DisplayMode mode;
|
||||
for (int i = 0; i < displayCount; i++) {
|
||||
SDL_GetDesktopDisplayMode(0, &mode);
|
||||
if (mode.refresh_rate > RefreshRate) {
|
||||
RefreshRate = mode.refresh_rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int RefreshRate = GetRefreshRate();
|
||||
// show a bar representing fps, where the entire bar is the max refresh rate of attached displays
|
||||
Uint32 nextTick = SDL_GetTicks();
|
||||
Uint32 frameTime = nextTick - LastTick;
|
||||
|
|
|
@ -168,8 +168,8 @@ int ClipY2; /// current clipping bottom right
|
|||
|
||||
static std::vector<Clip> Clips;
|
||||
|
||||
int VideoSyncSpeed = 100; /// 0 disable interrupts
|
||||
int SkipFrames; /// Skip this frames
|
||||
int CyclesPerSecond = CYCLES_PER_SECOND;
|
||||
double SkipCycles; /// Skip this frames
|
||||
|
||||
Uint32 ColorBlack;
|
||||
Uint32 ColorDarkGreen;
|
||||
|
@ -348,9 +348,10 @@ void DeInitVideo()
|
|||
*/
|
||||
static int CclSetVideoSyncSpeed(lua_State *l)
|
||||
{
|
||||
LuaCheckArgs(l, 1);
|
||||
VideoSyncSpeed = LuaToNumber(l, 1);
|
||||
return 0;
|
||||
LuaCheckArgs(l, 1);
|
||||
RefreshRate = LuaToNumber(l, 1);
|
||||
SetVideoSync();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VideoCclRegister()
|
||||
|
|
Loading…
Reference in a new issue