Endergaming2546
Endergaming2546

Reputation: 29

Is there a way to make something take a set amount of time c#

The title isn't really descriptive, I know, but I couldn't think of another way to explain it. Basically, I have a script which controls the date of something, and I want to increase the days by 1 every couple of seconds, depending on the speed. Basically there are 5 speeds, 1-5 of course, and on speed 1, I want it to increase the days by one every 2.5 seconds, on speed 2 every 1.5, speed 3 every 0.5, speed 4 every 0.09, and speed 5 every 0.04.

I wrote some code a bit ago, but I deleted it and I can't remember how it went, and what I have is what I remember.

My current code is:

    public class Date : MonoBehaviour
    {

        public TextMeshProUGUI dateText;
        
        static DateTime date = new DateTime(1, 1, 1);

        public static int speed = 1;
        public static int year;
        public static int month;
        public static int day;

        public static bool isPaused = false;

        private void Update()
        {
            
            if (isPaused == false)
            {
                if (speed == 1)
                {
                    //first number is seconds per day
                    //second is frames per second (default 60 because vsync)
                    //third is hours in a day
                    date = date.AddHours(2.5 * 60 / 24);
                }
                else if (speed == 2)
                {
                    date = date.AddHours(1.5 * 60 / 24);
                }
                else if (speed == 3)
                {
                    date = date.AddHours(0.5 * 60 / 24);
                }
                else if (speed == 4)
                {
                    date = date.AddHours(0.09 * 60 / 24);
                }
                else if (speed == 5)
                {
                    date = date.AddHours(0.04 * 60 / 24);
                }
            }

            year = date.Year;
            month = date.Month;
            day = date.Day;

            dateText.text = year.ToString() + "." + month.ToString() + "." + day.ToString();
        }

    }

but instead of getting faster as it goes up, it gets slower, and on speed 5 it takes like 3 seconds, and speed 1 goes way to fast. Can anyone help me? Tried moving around the numbers, dividing, multiplying by Time.deltaTime, but nothing works.

Upvotes: 1

Views: 142

Answers (2)

John Wu
John Wu

Reputation: 52280

Rather than thinking of this problem as increasing the value each iteration, I suggest you think of it a different way. If you know the time the animation started, and you know the current system time, your code can compute the value to display, independent of any notion of iteration.

This kind of approach is generally more stable as it corrects for rounding errors and/or iterations that are delayed for some reason (e.g. if your game is doing something else in parallel).

Essentially you compute how much time has elapsed and multiply it by some magic value which converts real elapsed seconds to elapsed displayed days. You then add this to the initial date time that was displayed at the start of the animation. The formula for the magic value is simple:

real seconds per day / seconds between day increases

Since the computation does not rely on number of iterations, you can set your loop delay to any value you want, depending on how often you want to refresh the display.

var multipliers = new Dictionary<int, float>
{
    { 1, 2.5F },
    { 2, 1.5F },
    { 3, 0.5F },
    { 4, 0.9F },
    { 5, 0.4F }
};

var initialDisplayedDate = DateTime.Parse( dateText.Text );
var animationStartTime = DateTime.Now;

const float secondsPerDay = (24 * 60 * 60);
var multiplier = secondsPerDay / multipliers[speed];

while (!canceled)
{
    TimeSpan elapsed = DateTime.Now - animationStartTime;
    int millisecondsToAdd = elapsed.TotalMilliseconds * multiplier;
    DateTime dateToShow = initialDisplayedDate.AddMilliseconds( millisecondsToAdd );

    dateText.Text = string.Format("{0:yyyy-MM-dd HH:mm:ss}", dateToShow); 

    await Task.Delay(100); //You can set this to any value. A value of 100 will update the display every 100 milliseconds. This will have no effect on the value that is displayed.
}

Upvotes: 1

Ruzihm
Ruzihm

Reputation: 20259

You're multiplying by real seconds per day when you should be dividing. Also, your intuition to multiply by Time.deltaTime was correct but as you can see isn't enough. Also, you should be multiplying by 24, not dividing. Also, multiplying by 60 isn't correct, it's the opposite of multiplying by Time.deltaTime.

Essentially, you are adding the reciprocol of what you should be adding.

Walking through:

// one frame passes for each call to Update
float deltaFrames = 1f;

// Convert to real seconds
float deltaRealSeconds = deltaFrames * Time.deltaTime;

// Convert to game days
float realSecondsPerDay = 2.5f;
switch (speed) // this should probably be an enum, but int is fine for now
{
    default:
    case 1:
        realSecondsPerDay = 2.5f;
        break;
    case 2:
        realSecondsPerDay = 1.5f;
        break;
    case 3:
        realSecondsPerDay = 0.5f;
        break;
    case 4:
        realSecondsPerDay = 0.09f;
        break;
    case 5:
        realSecondsPerDay = 0.04f;
}
float deltaGameDays = deltaRealSeconds / realSecondsPerDay;

// Convert to game hours
float deltaGameHours = deltaGameDays * 24f;

date = date.AddHours(deltaGameHours);

Or, simply:

float realSecondsPerDay = 2.5f;
switch (speed) // this should probably be an enum, but int is fine for now
{
    default:
    case 1:
        realSecondsPerDay = 2.5f;
        break;
    case 2:
        realSecondsPerDay = 1.5f;
        break;
    case 3:
        realSecondsPerDay = 0.5f;
        break;
    case 4:
        realSecondsPerDay = 0.09f;
        break;
    case 5:
        realSecondsPerDay = 0.04f;
}

date = date.AddHours(24f * Time.deltaTime / realSecondsPerDay);

Upvotes: 1

Related Questions