Time and Frames

We have all heard the term FPS, frames per second, which tells us how many frames are rendered on screen within one second. Higher FPS will result in smoother animations because those animations get more frames. The problem with high FPS is that the GPU has to keep up with this demand and be able to produce 30/60/144 or more frames within 1 second. This is where frame times, VSync and a bunch of offer stuff that I won’t cover here comes into play.

The important takeaway is that you can not reliably calculate a duration based on how many frames where rendered because not every frame takes the same amount of time. This is a problem when you want to smoothly move an object from one position to another. Engines like Unity solve this by providing a delta time which is the interval from the last to the current frame, allowing you to create smooth translations.

Looking at RPG Maker MV/MZ

So what issue does RPG Maker have? I have recently played a game for around 21 hours, but the save menu displayed over 51 hours, more than double. Actually not just more than double but around 2.4 times more than expected. This quickly made me realize that I had been playing the game on my 144Hz monitor, meaning instead of rendering at 60 FPS, the game was rendering at 144 FPS. I hope you notice that 60 x 2.4 equals 144.

To further investigate the problem I took a peek inside the js folder, did a quick search for “playtime” and found the culprits of my frustration:

1
2
3
4
Game_System.prototype.onBeforeSave = function() {
    this._framesOnSave = Graphics.frameCount;
    // ...
};
1
2
3
4
Game_System.prototype.onAfterLoad = function() {
    Graphics.frameCount = this._framesOnSave;
    // ...
};
1
2
3
Game_System.prototype.playtime = function() {
    return Math.floor(Graphics.frameCount / 60);
};
1
2
3
4
5
6
Game_System.prototype.playtimeText = function() {
    var hour = Math.floor(this.playtime() / 60 / 60);
    var min = Math.floor(this.playtime() / 60) % 60;
    var sec = this.playtime() % 60;
    return hour.padZero(2) + ':' + min.padZero(2) + ':' + sec.padZero(2);
};

A quick explanation: whenever you save, the game includes the amount of frames it has rendered in the save file because Game_System is serialized and _framesOnSave is a field of that object. When you load, the save will get deserialized and Graphics.frameCount will be set back to that value. In case you are wondering: Graphics.frameCount gets incremented on each render.

This is fine so far, but the real problems are found in the playtime and playtimeText functions where the game assumes you are constantly playing at 60 frames per second. The pure frameCount value gets divided by 60 to get the amount of seconds passed and in playtimeText that result is further processed to the get hours and minutes.

Due to the multiple issues outlined at the beginning you can see that this is not a good idea. Having said that: let’s look at how to fix this.

Possible Solution

This solution is probably the easiest and most straightforward one I can think of:

  • on load/start: set startTime to the current time
  • on save:
    1. get the current time and calculate the difference between now and startTime
    2. add the difference to a variable in the save

No need for frame count calculations or anything complex, just use Date.now() a few times and get the difference.

Changing the implementation in RPG Maker MV/MZ

Start by opening js/rpg_objects.js and look for the initialize function. Here we want to add two new fields:

1
2
3
4
5
Game_System.prototype.initialize = function() {
  this._startTime = Date.now();
  this._playtime = 0;
  // ...
}

Date.now() returns the current Unix time as an integer that looks like this: 1632928643900.

We need to set the start time in the initialize function for when the game starts and in the onAfterLoad function for when the player loads a save file:

1
2
3
4
Game_System.prototype.onAfterLoad = function() {
  this._startTime = Date.now();
  // ...
}

The onBeforeSave function gets called before the save is serialized so here we have to calculate the time difference and reset the start time:

1
2
3
4
5
Game_System.prototype.onBeforeSave = function() {
  this._playtime += Date.now() - this._startTime;
  this._startTime = Date.now();
  // ...
}

Assuming the player started at 1632928643900 and played for 1 second till 1632928644900, this._playtime will now be 1000. The only thing remaining is updating the playtime function:

1
2
3
Game_System.prototype.playtime = function() {
  return Math.floor(this._playtime / 1000);
};

Since this function returns the amount of time passed in seconds we have to divide by 1000 to convert from milliseconds to seconds. While we are at it, I also recommend updating playtimeText:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Game_System.prototype.playtimeText = function() {
  var secondsPassed = this.playtime();
  var minutesPassed = Math.floor(secondsPassed / 60);
  var hoursPassed = Math.floor(minutesPassed / 60);

  var sec = secondsPassed % 60;
  var min = minutesPassed % 60;
  var hour = hoursPassed % 24;

  return hour.padZero(2) + ':' + min.padZero(2) + ':' + sec.padZero(2);
};

The main difference here is that we cache the result from the playtime function and other operations.

Plugin for MV/MZ

If you don’t want to mess with the game files directly, which is a good idea, then I suggest taking a look at the PlaytimeFix plugin which I developed that does everything explained in this post.

Afterword

This issue has been bugging me a lot during my playthroughs of longer games because you don’t really notice it when your playtime goes from 1h to 2 hours but going from 21 hours to 50+ hours was a bit more in my face. RPG Maker MZ also doesn’t fix this, and I have not seen any discussion about this.