- Home /
See if a time span is bigger than X
Ok so im trying to make a reward you can get after some hours. I can get the last time you get the reward and in the start get the difference in a time span. But the problem is that i cant see if the time span is bigger than 6 hours. I have tried difference.Minutes
(So i didnt have to wait hours and then find out it didnt work) but it didnt work. I have searched around on the internet in hours but there is nothing that tells how to do it. I really want to get it working.
Any help apreciated.
Thanks
Answer by SomeGuy22 · Jan 12, 2019 at 06:24 PM
While Invoke might be the most straightforward approach, there are better options out there are easier to test for your time span. Here are two solutions that I believe are more reliable depending on how you want to actually implement this system:
Option 1: System Time Comparison
As a programmer, you have access to a variety of information provided by the operating system. This information can be accessed from the System class in the .Net framework, and can be used within your C# code very easily.
I'm not sure if you want the reward to appear after waiting 6 hours of realtime, or of the game running. (I.E. did someoone close the game and wait 6 hours before opening?) If you want this behavior, then you can simply retrieve the current time from the OS and compare it to the time you want. So at the start of whenever you want the waiting to begin, you can cache the current OS time by saving System.DateTime.Now. Then every so often (or whenever you want to check for the reward) you can compare System.DateTime.Now to the cached result.
void CheckReward() {
if ((System.DateTime.Now - cachedTime) >= 6)
GiveReward();
}
However, I don't think you can just do a direct comparison like this because .Now returns a type DateTime. You'll have to look up how comparisons work between this type of class and extract the relevant information (the hours). Now you might have a problem if someone tries to cheat by advancing their system clock but that also becomes a useful tool for you to test if it works. However, I wouldn't recommend this method if you want to avoid cheating and need a more reliable system.
Option 2: Manually Track Time
For some cases it's better to have full control over the comparison values so that you can easily test the results without worrying about how the player might interfere on a built game. The easiest way to manually track the time spent is to either look at Time.time or Time.deltaTime. For the least amount of confusion, let's look at Time.deltaTime.
Every time a frame is rendered, the Update() function is called. Since this happens at a variable time-frame, Unity has built in deltaTime as a way to keep track of how long it's been since the last frame. If we continuously add the time from each frame to one variable, we are now keeping track of how many seconds have passed:
void Update() {
seconds += Time.deltaTime;
}
Okay great, but we're looking on the scale of hours, not seconds. And what happens if our seconds get so large that we exceed the floating point maximum value? (Unlikely, but always something to worry about) Well then, let's just use a second variable called "minutes" and refresh our seconds every time we hit 60:
void Update() {
seconds += Time.deltaTime;
if (seconds >= 60) {
minutes++;
seconds = 0.0f;
}
}
Great, we've pushed back our problem so now we only have to deal with minutes, and it'll take a lot longer for minutes to reach the maximum integer value. But we can do even better.
void Update() {
seconds += Time.deltaTime;
if (seconds >= 60) {
minutes++;
seconds = 0.0f;
}
if (minutes >= 60) {
hours++;
minutes = 0;
}
if (hours >= 6)
GiveReward;
}
Now, if we want to test that our code works, we simply have to observe what happens when seconds ticks over 60. If our minutes increment, we can be pretty sure that they will count up to 60. Same for hours. If we want to test if GiveReward is called, we merely need to set our hours to 5 and minutes to 59 in the Inspector. Then we watch what happens when the seconds ticks over.
The best part about this method is that seconds, minutes, and hours are all serializable, and can be saved if you wish to continue keeping track of time across play sessions, like if someone saves the game and continues later.
Possible Optimization
A lot of people make the mistake of checking things in Update() unnecessarily. Remember that more work your code does on each frame, the slower your game will run. For example, if you know that you will only check rewards at the end of mission or something, you do the comparisons by checking Time.time instead and only then will you check if the hours are >= 6.
Another good optimization is to have the above code run in FixedUpdate instead. If you don't need frame-perfect precision and can settle for 50 times a second or however many you've set, then you can replace Time.deltaTime with Time.fixedDeltaTime (once it's in FixedUpdate). You'll be doing less work if it's run on the FixedUpdate cycle, since it won't run thousands of times if you're getting a high framerate.
If any of this was confusing you should look into learning more about how Unity and the .Net framework works. There are tons of subtle tricks to getting the most out of the engine, but thinking up broad-scale approaches like this is a critical skill in all types of programming.
As far as i understood the question, he already uses your first option as he already has the timespan that is returned when you subtract two datetime values from each other
You're right, if that's true then your answer would be correct. The overloaded DateTime subtraction function would return TimeSpan class, which has the function TotalHours(). TotalHours returns the hours in a double for some reason (could that lead to comparison imprecision?)
Answer by aardappel156 · Jan 12, 2019 at 04:03 PM
you could do a Invoke and then just convert your 6hours into second. You can test this by doing 10 seconds first like
void Start()
{
Invoke("GiveReward", 10);
}
void GiveReward()
{
//give the player reward code here
}
There's a significant problem with both Invoke() and the WaitForSeconds yielding trick that have caused me to almost never use them again. This really depends on how you want your game to work, but you're going to run into a serious issue if you plan on having the game state saved and loaded again for a different play session. Invoke() might internally keep track of the time spent, but you can't easily pause and resume the waiting from a custom time value.
If we were waiting 3 hours for the reward and then saved and quit, we'd lose all the Invoke progress. Same for WaitForSeconds. We can manually keep track of the time and then Invoke() again with the adjusted time, but at that point we're already keeping track of the time... so you might as well just manually keep track of the time in a variable and then serialize that variable when it's time to save. When it's time to load a game, you can set that variable to it's loaded value and resume where you left off. Again, it depends on how you want the system to work but this is something to consider for anyone looking for waiting behavior.
Yes as @SomeGuy22 its not good. Because i want to save the progress even though i quit.
Answer by badadam · Jan 12, 2019 at 05:50 PM
public class WaitTime : MonoBehaviour {
public float rewardHour;
void Start()
{
//start rewarding
StartCoroutine(giveReward());
}
public IEnumerator giveReward()
{
yield return new WaitForSeconds(hourToSec(rewardHour));
//giving rewards codes
}
private float hourToSec(float hour)
{
return hour * 60 * 60;
}
}