- Home /
How to fast forward time (or just skip it) properly?
Not sure what is exactly going wrong here.
I have a world time that runs on update:
if (!worldInteractions.worldIsPaused && !worldInteractions.showMainMenu){
worldTime += Time.deltaTime;
}
and I use that time to drive the game world clock :
function GameTime () {
if (worldTime - lastChange > 10) { //Decides how many seconds a game 'minute' lasts.
minutes++;
if (minutes == 60) {
minutes = 0;
hour++;
if (hour == 12) {
if (ampm == "am") {
ampm = "pm";
}
}
if (hour == 13) {
hour = 1;
}
}
lastChange = worldTime;
}
}
In my project, when the player is in a dialog, he is able to skip a dialog line and also advance the time it would take to display it(e.g. if a line of text would display for 5 seconds and the player skips it, then it will be 5 seconds later).
This is how a dialog is currently displayed (I'm doing a typewriter effect from a script shown in Unity wiki I think):
function DoTextLine (){
currentDisplayLine = null;
for (var letter in currentScriptStep.textLine.ToCharArray()) {
currentDisplayLine += letter;
yield WaitForSeconds (letterPause);
}
yield WaitForSeconds (endDialogPause);
currentState = DialogStates.Idle;
}
And when the player presses a key to fast forward that dialog I do this:
function SkipDialogLineAction (){
if (dialogInteractions.currentState == DialogStates.ShowTextLine){
Time.timeScale = 90;
}
}
and once the textLine has been fully displayed in faster speed I go back to the normal timeScale:
if(currentState == DialogStates.Idle && Time.timeScale == 90){
Time.timeScale = 1;
}
I thought this was working all correctly but I noticed that time passes much faster than it should. If I skip a dialog line of 5 secs I end up passing almost 30 secs from the worldTime variable. I'm wondering what I could be doing wrong. I'm not totally comfortable with Time.time and Time.deltaTime and wondering if there is a error there? Or if maybe using yield on the Typewriter effect could be causing the issues with the timeScale difference...
Or maybe there is a much better way to achieve this?
Any help very appreciated!
Answer by robertbu · Sep 30, 2013 at 09:39 PM
When you set 'Time.timeScale = 90', time will pass 90 times as fast. This means that Time.deltaTime will also be accelerated by 90x. So if it takes 1/3 of a second to skip the dialog, your worldTime will advance by 30 seconds. I see two possible fixes. First, you could change your letterPause variable to something small to cause the typing to go quickly rather than increasing Time.timeScale. Or you can save and restore worldTime. For the latter I mean something like:
function SkipDialogLineAction (){
if (dialogInteractions.currentState == DialogStates.ShowTextLine){
Time.timeScale = 90;
worldTimeSaved = worldTime;
}
}
if(currentState == DialogStates.Idle && Time.timeScale == 90){
Time.timeScale = 1;
worldTime = worldTimeSaved + 5.0;
}
Thanks for the suggestions. I still don't quite understand why its bad that the deltaTime goes faster.
So if it takes 1/3 of a second to skip the dialog, your worldTime will advance by 30 seconds.
Just for me to understand, lets say the dialog line would normally take 10 seconds. When I do the fast forward time will go faster 90x so ins$$anonymous$$d of 10 sec it would be 0.1sec right? What I don't understand is why the worldTime doesn't also just go faster so that in 0.1 secs it adds 10 seconds to its total time (just like it would have it I had waited for the dialog line to go).
As for the suggestions, I have other things attached to the worldTime (sun angle and rotation and other events dependent on what time it is on the clock) so if I only speed up the letterPause I won't really get the speed up that I need for the other things in the world.
The save/restore worldTime seems like a good suggestion but still makes me wonder why its "wrong" in the first place. I think I need to understand why there is a discrepancy between both!
Your logic is backwards. Here is a script to show you what is going on. Attach it to an empty game object, set factor as appropriate, and hit play:
#pragma strict
var factor = 90.0;
private var sumTime = 0.0;
function Start() {
Time.timeScale = factor;
}
function Update() {
sumTime += Time.deltaTime;
}
function OnGUI() {
GUI.Label(Rect(0,0,500, 50), "Time since startup: "+Time.realtimeSinceStartup);
GUI.Label(Rect(0,75,500, 50), "Time.time: "+Time.time);
GUI.Label(Rect(0,150,500, 50), "Sum of deltaTime "+sumTime);
}
The first number is real world time. That is, it would match a stop watch or the second hand on your watch. The second number is Time.time which is the game time. The third number is the sum of all deltaTimes which will be very close to Time.time.
With the default value of 90 for 'factor', you will see that when one second passes in the real world, you end up with 90 seconds passing in the game world. At 10 seconds real time, you have 900 seconds of game time.
Think about what happens in a game. If we are scaling things by deltaTime for movement, and we speed up Time.timeScale by a factor of 90, then each deltaTime will also be increased by a factor of 90 causing things to move further each frame.
Thanks for taking the time to explain me this!
I think I now understand the concept that time.deltaTime will increase exponentially depending on how much I multiply it by the timeScale.
This makes sense to me :
With the default value of 90 for 'factor', you will see that when one second passes in the real world, you end up with 90 seconds passing in the game world. At 10 seconds real time, you have 900 seconds of game time.
Now I made a little experiment on my side. I scripted a character walking to another character and say a line. Once the line is said I paused the editor window programmatically to see the times I get.
This are the additions I did to the gui times (the game clock starts at 7.32pm):
function OnGUI() {
GUI.Label(Rect(0,0,500, 50), "Time since startup: "+Time.realtimeSinceStartup.ToString("f2") + "secs");
GUI.Label(Rect(0,20,500, 50), "Time.time: "+Time.time.ToString("f2") + "secs");
GUI.Label(Rect(0,40,500, 50), "Sum of deltaTime "+sumTime.ToString("f2") + "secs");
GUI.Label(Rect(0,60,500, 50), " gameTime " + worldTime.ToString("f2") + "secs");
GUI.Label(Rect(0,80,500, 50), " gameClock " + hour + ":" + $$anonymous$$utes);
}
Case 1: TimeScale is always 1 through the whole thing. This is what i get when the game is paused at the end of the dialog line:
Case 2: Here I speed up the timeScale to 90 the moment the character starts to say the line. This are the results at the end of the dialog line:
And this is the exact moment the character starts to say the line and I change the timeScale from 1 to 90 (2.13secs into the game):
Now what I would assume is that the second example would have Time.time/Sum of deltaTime/gameTime just like the first case BUT with a shorter Time since startup since I made the dialog that originally took 3 seconds to take only 0.7 secs.
Thanks for the patience so far :)
What I keep thinking is if I'm staying at timeScale = 90 for too long. $$anonymous$$aybe what is happening is that the "speed up" dialog line is actually taking longer than it should?
What happens to this function that I use for the time it takes to say a line? Does it get scaled correctly?
function DoTextLine (){
currentDisplayLine = null;
for (var letter in currentScriptStep.textLine.ToCharArray()) {
currentDisplayLine += letter;
yield WaitForSeconds (letterPause);
}
yield WaitForSeconds (endDialogPause);
currentState = DialogStates.Idle;
}
If the dialog line takes 3.13 secs (5.26 - 2.13 secs) at timeScale =1, then if it would take 0.03 secs at timeScale = 90 right?
So on the Case 2 the Time since startup should read 2.13/2.14 secs and not 2.92secs, its like the function DoTextLine is taking a lot longer than 0.03 seconds to execute.
Case 2: Here I speed up the timeScale to 90 the moment the character starts to say the line. This are the results at the end of the dialog line:
Now what I would assume is that the second example would have Time.time/Sum of deltaTime/gameTime just like the first case BUT with a shorter Time since startup since I made the dialog that originally took 3 seconds to take only 0.7 secs.
The difference between Time.time and Time startup is approximately what I would expect. If your dialog is taking .25 seconds and you set Time.timeScale = 90, then Time.time will increase by approximately 22.5 during the time the dialog is up. I don't know how you are capturing your data, but 21 seconds different is approximately what I would expect to see.
But as mentioned earlier, saving and restoring worldTime is a solution regardless of how these numbers pan out.
Answer by castor · Oct 01, 2013 at 08:58 PM
In case it helps future people (and feel free to correct it Robertbu!) here is the solution I'm using based on the discussion we had, seems to be working only being about 1 to 2 seconds off.
function SkipDialogLineAction (){
if (dialogInteractions.currentState == DialogStates.ShowTextLine){
//Store the current world time and the dialogTimeLeft
var currentWorldTime = scriptWorld.worldTime;
var dialogTimeLeft = dialogInteractions.textLineDisplayTimeLeft + dialogInteractions.endDialogPause;
//Cancel current dialog
dialogInteractions.StopCoroutine("DoTextLine");
dialogInteractions.currentDisplayLine = null;
dialogInteractions.currentState = DialogStates.Idle;
//Advance game time based on how much of the dialog was left
Time.timeScale = 90;
yield WaitForSeconds (dialogTimeLeft);
//Bring timescale back to 1 and correct worldTime
Time.timeScale = 1;
scriptWorld.worldTime = currentWorldTime + dialogTimeLeft;
}
}
Your answer
Follow this Question
Related Questions
Force mode and delta timing 1 Answer
When to use Time.deltaTime? 3 Answers
Different behavior between editor and standalone changing time step 0 Answers
High Speed 1 Answer