Unity Timer goes into negative? - c#

So, I am trying to make a "Merchant" system for my game. After 5 minutes(timer is ticking) Merchant will be available and 2nd timer will start ticking, but the 2nd timer is in -.
void Update()
{
timeremaining -= Time.deltaTime;
int minutes = Mathf.FloorToInt(timeremaining / 60F);
int seconds = Mathf.FloorToInt(timeremaining - minutes * 60);
string niceTime = string.Format("{0:0}:{1:00}", minutes, seconds);
howlonghere -= Time.deltaTime;
int minutes2 = Mathf.FloorToInt(howlonghere / 60F);
int seconds2 = Mathf.FloorToInt(howlonghere - minutes * 60);
string niceTime2 = string.Format("{0:0}:{1:00}", minutes, seconds);
if (timeremaining > 0)
{
Merchanthuman.enabled = false;
Merchanthuman.interactable = false;
Tiimer.text = "Merchant will be here: " + niceTime;
}
else
{
Tiimer.text = "Merchant is here for: " + niceTime2;
Merchanthuman.enabled = true;
Merchanthuman.interactable = true;
}
}
That "Merchant is here for: " there should start new count down which would be 2nd time. Like the Will be here is 5 minutes & is here for is 2 minutes.

Let's split it up a little:
public const float TravelTime = 5.0f;
public const float VisitTime = 4.0f;
public float timeremaining;
public float howlonghere;
//Setup initial timers
void Start()
{
timeremaining = TravelTime;
howlonghere = VisitTime;
}
//Check each frame for the scenario
void Update()
{
if (timeremaining > 0)
{
string niceTime = ElapseTravel();
Merchanthuman.enabled = false;
Merchanthuman.interactable = false;
Tiimer.text = "Merchant will be here: " + niceTime;
}
else
{
string niceTime2 = ElapseVisit();
Tiimer.text = "Merchant is here for: " + niceTime2;
Merchanthuman.enabled = true;
Merchanthuman.interactable = true;
}
}
//Elapse remaining time when merchant travels
private string ElapseTravel()
{
timeremaining -= Time.deltaTime;
int minutes = Mathf.FloorToInt(timeremaining / 60F);
int seconds = Mathf.FloorToInt(timeremaining - minutes * 60);
return string.Format("{0:0}:{1:00}", minutes, seconds);
}
//Elapse stay time when merchant is here
private string ElapseVisit()
{
howlonghere -= Time.deltaTime;
int minutes2 = Mathf.FloorToInt(howlonghere / 60F);
int seconds2 = Mathf.FloorToInt(howlonghere - minutes2 * 60);
if (howlonghere <= 0)
{
timeremaining = TravelTime;
howlonghere = VisitTime;
}
return string.Format("{0:0}:{1:00}", minutes2, seconds2);
}
You were decreasing both timeremaining and howlonghere regardless of the situation. You need to split both scenarios and elapse (decrease) only one of the values depending on the fact that merchant is travelling to location or if he's already here.

Unity Timer is not negative. It is your own variable that is going negative.
What actually happening here is you are checking for 5 minutes and executing timeremaining -= Time.deltaTime; that will constantly decreasing your timeremaining variable.
Second thing is your are decreasing howlonghere at the same time by which it is going negative. Because howlonghere is less than timeremaining.
So when your first timer becomes timeremaining <= 0 then your second timer howlonghere will become howlonghere = -3 minutes. (Assuming that timeremaining = 5 and howlonghere = 2 initially).
What you can do if you don't want to change your logic here.
void Update()
{
timeremaining -= Time.deltaTime;
int minutes = Mathf.FloorToInt(timeremaining / 60F);
int seconds = Mathf.FloorToInt(timeremaining - minutes * 60);
string niceTime = string.Format("{0:0}:{1:00}", minutes, seconds);
if (timeremaining > 0)
{
Merchanthuman.enabled = false;
Merchanthuman.interactable = false;
Tiimer.text = "Merchant will be here: " + niceTime;
}
else {
howlonghere -= Time.deltaTime;
int minutes2 = Mathf.FloorToInt(howlonghere / 60F);
int seconds2 = Mathf.FloorToInt(howlonghere - minutes * 60);
string niceTime2 = string.Format("{0:0}:{1:00}", minutes, seconds);
Tiimer.text = "Merchant is here for: " + niceTime2;
Merchanthuman.enabled = true;
Merchanthuman.interactable = true;
}
}
Obviously you will customize it more accordingly. But it is just to make you clear.

Related

C# Time Management (with Unity 2D)

I ran into a little issue whilst using C#/Unity in combination with a litte countdown timer. The countdown is working fine an as expected, as long as the number of total timeToDisplay is not too big (e.g. more than a day)
r/Unity2D - Asking for Help: Countdown strangely stops when there is too much remaining time
As you can see, the user has the possibility to add time to that countdown, which (again) works fine, until it's too much.
Using TextMeshPro and TextMeshPro-Buttons.
Image of Countdown + Buttons to add Seconds/Minutes/Hours/Days
However... here's the code:
using UnityEngine;
using TMPro;
public class Controller : MonoBehaviour
{
public float timeValue = 78000;
public TMP_Text timerText;
// I also tried FixedUpdate, but error still occured
void Update()
{
if (timeValue > 0)
{
timeValue -= Time.deltaTime;
}
else
{
timeValue = 0;
}
DisplayTime(timeValue);
}
void DisplayTime(float timeToDisplay)
{
float days = Mathf.FloorToInt(timeToDisplay / 86400);
timeToDisplay = timeToDisplay % 86400;
float hours = Mathf.FloorToInt(timeToDisplay / 3600);
timeToDisplay = timeToDisplay % 3600;
float minutes = Mathf.FloorToInt(timeToDisplay / 60);
timeToDisplay = timeToDisplay % 60;
float seconds = Mathf.FloorToInt(timeToDisplay);
if (seconds < 0)
{
seconds = 0;
}
timerText.text = string.Format("{0:00} days {1:00} hours {2:00} minutes {3:00} seconds", days, hours, minutes, seconds);
}
public void AddDay()
{
/* 86400 seconds/day */
timeValue += 86400;
}
public void AddHour()
{
/* 3600 seconds/hour */
timeValue += 3600;
}
public void AddMinute()
{
timeValue += 60;
}
public void AddSecond()
{
timeValue += 1;
}
}
Does anybody know what I'm missing here?
problem on here: timeValue -= Time.deltaTime , float have a little deviation
public float timeValue = 78000;
float beginTime = 0;
void Start()
{
beginTime = Time.time;
}
// I also tried FixedUpdate, but error still occured
void Update()
{
float usedTime = Time.time - beginTime;
if( timeValue - usedTime > 0 )
{
DisplayTime(timeValue - usedTime);
}
else
{
DisplayTime(0);
}
}

C# Trying to create a timer. It doesn't seem to work

public class TimerCountdown : MonoBehaviour
{
float currentTime = 0f;
float startingTime = 70f;
float hours = 0;
float minutes = 0;
float seconds = 0;
[SerializeField] Text countdownText;
void Start()
{
currentTime = startingTime;
float hours = currentTime / 3600 ;
float minutes = currentTime % 3600 / 60 ;
float seconds = currentTime % 60 ;
}
void Update()
{
currentTime -=1 * Time.deltaTime;
hours = currentTime / 3600 ;
minutes = currentTime % 3600 / 60 ;
seconds = currentTime % 60 ;
countdownText.text = hours.ToString("00") + ":" + minutes.ToString("00") + ":" + seconds.ToString("00") + " --- " + currentTime.ToString("0");
}
}
So this is my function. When I run this in unity, the counting down works, but the conversion to minutes seem to be wrong. As in, when it gets lower, the minute doesn't change only every 2 minute (180s passes). Why is that? What am I doing wrong?
Also, if i increment this for 9000f (2h 30min), it does the same, even if its 59seconds, it still stays 30, instead of 29. Any help? Is there any mistake in my logic?
What is happening is that your float is getting rounded when you are converting it to a string.
If you take something as simple as 1 minute, 33 seconds = 93f and run your function you will get:
float floatTime = 93f;
var floatMinutes = floatTime / 60; // floatMinutes = 1.55
Console.WriteLine(floatMinutes.ToString("00")); // Outputs: 02
However if you are using integer math, the remainder is thrown away:
int intTime = 93;
var intMinutes = intTime / 60; // intMinutes = 1
Console.WriteLine(intMinutes.ToString("00")); // Outputs: 01
To make floats display property:
public void Update()
{
currentTime -= 1 * Time.deltaTime;
hours = (float)Math.Floor(currentTime / 3600);
minutes = (float)Math.Floor(currentTime % 3600 / 60);
seconds = currentTime % 60;
CountdownText = hours.ToString("00") + ":" + minutes.ToString("00") + ":" + seconds.ToString("00") + " --- " + currentTime.ToString("0");
}
the minute doesn't change only every 2 minute (180s passes). Why is that?
There's nothing in the code you posted that would suggest a reason for the minutes value getting stuck for two minutes at a time (or three...180 seconds is three minutes, not two, so it's not really clear what you mean).
The author of this answer suggests that your description is simply wrong, and that there's not a two minute delay between changes in the minute value. Their assumption may or may not be correct; it's hard to tell, given the wording of the question.
That said, it seems to me that your whole approach is wrong anyway. Doing all this math is pointless, given that .NET has very good support for dealing with time values built-in. I would change your code to look like this:
public class TimerCountdown : MonoBehaviour
{
float currentTime = 0f;
const float startingTime = 70f;
[SerializeField] Text countdownText;
void Start()
{
currentTime = startingTime;
}
void Update()
{
currentTime -= Time.deltaTime;
countdownText.text = $"{TimeSpan.FromSeconds(currentTime):hh\\:mm\\:ss}" + " --- " + currentTime.ToString("0");
}
}
The above also uses string interpolation for the string formatting. This may or may not be available in the version of Unity3d you're using. If not, you can use the older ToString() approach:
countdownText.text = TimeSpan.FromSeconds(currentTime).ToString("hh\\:mm\\:ss") + " --- " + currentTime.ToString("0");

Unity - Trying to Calculate In-Game Time

So I wrote a day-night cycle script the other day and I have the sun/moon cycle working (its a really rough script, not perfect yet) but one of the other things I wanted to do was to be able to calculate the current time inside the game working off of that day/night cycle.
I have a ticker that is working so far but it is not scaling correctly to the percentage of the day.
Can anyone help me out with this because I think this is beyond my skillset with maths right now.
Basically I just want to solve the current time of day as relative to the % we are moving through the day/night cycle.
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
public class DayNightCycle : MonoBehaviour
{
public static DayNightCycle instance;
public Light sun, moon;
public float secondsInFullDay = 3600f;
[Range(0, 1)]
public float currentTimeOfDay = 0f;
[HideInInspector]
public float timeMultiplier = 1f;
float sunInitialIntensity;
public Camera mainCam;
public Material skyboxDay, skyBoxNight;
public float gameTime;
public float fSeconds;
public int totalSeconds, iSeconds, minutes, hours, days;
public int currentSecond, currentMinute, currentHour, currentDay;
public float previousTime;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
sunInitialIntensity = sun.intensity;
}
// Update is called once per frame
void Update()
{
UpdateSun();
currentTimeOfDay += (Time.deltaTime / secondsInFullDay) * timeMultiplier;
if (currentTimeOfDay >= 1)
{
currentTimeOfDay = 0;
}
gameTime += Time.deltaTime;
// seconds / total seconds = percentage
// percentage * seconds = total seconds
// seconds = total seconds * percentage
totalSeconds = (int)gameTime;
fSeconds = (secondsInFullDay * currentTimeOfDay);
currentSecond = (int)fSeconds;
if (currentSecond >= 60)
IncrementMinutes();
if (currentMinute >= 60)
IncrementHours();
if (currentHour >= 24)
IncrementDays();
previousTime = (int)gameTime;
}
void UpdateSun()
{
sun.transform.localRotation = Quaternion.Euler((currentTimeOfDay * 360f) - 90, 170, 0);
moon.transform.localRotation = Quaternion.Euler((currentTimeOfDay * 360f) - 90, 170, 0);
float intensityMultiplier = 1f;
float moonIntensityMult = 0.025f;
if (currentTimeOfDay <= 0.23f || currentTimeOfDay >= 0.75f)
{
RenderSettings.skybox = skyBoxNight;
intensityMultiplier = 0f;
moonIntensityMult = 0.025f;
}
else if (currentTimeOfDay <= 0.25f)
{
RenderSettings.skybox = skyBoxNight;
intensityMultiplier = Mathf.Clamp01((currentTimeOfDay - 0.23f) * (1 / 0.02f));
moonIntensityMult = 0f;
}
else if (currentTimeOfDay >= 0.73f)
{
RenderSettings.skybox = skyboxDay;
intensityMultiplier = Mathf.Clamp01(1 - ((currentTimeOfDay - 0.73f) * (1 / 0.02f)));
moonIntensityMult = 0f;
}
sun.intensity = sunInitialIntensity * intensityMultiplier;
moon.intensity = moonIntensityMult;
}
public float GetTimeOfDayInSeconds
{
get { return currentTimeOfDay; }
set { return; }
}
void IncrementMinutes()
{
currentMinute++;
currentSecond = 0;
}
void IncrementHours()
{
currentHour++;
currentSecond = 0;
currentMinute = 0;
}
void IncrementDays()
{
currentDay++;
currentSecond = 0;
currentMinute = 0;
currentHour = 0;
}
}
I think keeping track of second, minute hour and increment each separtedly is not the right approach. You need to now the scale or proprotion factor between your game time and the real time, and handle the one time variable at once.
Find this trial function to obtain the sacaled time I think you need.
using System;
using UnityEngine;
public class DayNightCycle : MonoBehaviour {
// This is public to check with manual input if the obtained time is the one we expect.
// In the real method, this should not exist and should be calculated with the elapsed time of the game,
// commented belowin the getGameTime(int secodsDayDurationInGame) method
public double elapsedRealTime;
float startingGameTime;
DateTime startingGameDate;
private void Start() {
startingGameTime = Time.time;
startingGameDate = DateTime.Now; // choose the starting date you like
}
private float secondsOfARealDay = 24 * 60 * 60;
DateTime getGameTime(int secodsDayDurationInGame) {
float scaledElapsedSecondInGame = secondsOfARealDay / secodsDayDurationInGame; // second equivalent in your game
//float elapsedRealTime = Time.time - startingGameTime; // uncomment to calculate with elapsed real time.
DateTime gateDateTime = startingGameDate.AddSeconds(elapsedRealTime * scaledElapsedSecondInGame);
return gateDateTime;
}
void OnMouseDown() { // this makes the cube clickable
Debug.LogError(getGameTime(3600).ToString());
}
}
You can try it making a cube clickable to print the output when you update the elapsed time in the public variable.
It is for 3600 second day in your game, but if you whant another game day duration you can just make that variabel puclic and try.
Important to have a collider, if not, cube wont be clickable. However It is added by default when you add the primitive right click in the scene -> 3D Object -> Cube.
You can check for example that if you add 3600 to the elapsed time public variable, and you click the cube you obtain tomorrows date in the console.
When you check the function works according to your needs you can uncomment the line //float elapsedRealTime = Time.time - startingGameTime; to use the real time elapsed or the one you wish for the date calculation.
Okay so I got it to where it is correctly synched now with the day/night cycle I just need to clean this code up quite a bit. Note: The way I'm increasing days will start to fail if you use an exceptionally long in game day I think. The boolean might work but it might also give you a new day before the end of the current day. I was testing this on a 3600 second long day.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
public class DayNightCycle : MonoBehaviour
{
public static DayNightCycle instance;
public Light sun, moon;
public float secondsInFullDay = 3600f;
[Range(0, 1)]
public float currentTimeOfDay = 0f;
[HideInInspector]
public float timeMultiplier = 1f;
float sunInitialIntensity;
public Material skyboxDay, skyBoxNight;
public double elapsedRealTime;
public float secondsPerDayMultiplier;
[SerializeField]
private float speedOfGameSecond, speedOfGameMinute, speedOfGameHour, speedOfGameDay;
[SerializeField]
private float currentGameSecond, currentGameMinute, currentGameHour, currentGameDay;
private bool stopIncrementingDay;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
speedOfGameSecond = secondsInFullDay / 24 / 60 / 60;
speedOfGameMinute = secondsInFullDay / 24 / 60;
speedOfGameHour = secondsInFullDay / 24;
speedOfGameDay = secondsInFullDay;
sunInitialIntensity = sun.intensity;
}
// Update is called once per frame
void Update()
{
UpdateSun();
currentTimeOfDay += (Time.deltaTime / secondsInFullDay) * timeMultiplier;
if (currentTimeOfDay >= 1)
{
currentTimeOfDay = 0;
}
secondsPerDayMultiplier = currentTimeOfDay * secondsInFullDay;
// seconds / total seconds = percentage
// percentage * seconds = total seconds
// seconds = total seconds * percentage
currentGameSecond = secondsPerDayMultiplier / speedOfGameSecond;
currentGameMinute = secondsPerDayMultiplier / speedOfGameMinute;
currentGameHour = secondsPerDayMultiplier / speedOfGameHour;
if(!stopIncrementingDay && currentGameHour >= 23.999)
{
IncrementDay();
stopIncrementingDay = true;
} else if(currentGameHour <= 23.999)
{
stopIncrementingDay = false;
}
elapsedRealTime += Time.deltaTime;
previousTime = Time.deltaTime;
}
void UpdateSun()
{
sun.transform.localRotation = Quaternion.Euler((currentTimeOfDay * 360f) - 90, 170, 0);
moon.transform.localRotation = Quaternion.Euler((currentTimeOfDay * 360f) - 90, 170, 0);
float intensityMultiplier = 1f;
float moonIntensityMult = 0.025f;
if (currentTimeOfDay <= 0.23f || currentTimeOfDay >= 0.85f)
{
RenderSettings.skybox = skyBoxNight;
intensityMultiplier = 0f;
moonIntensityMult = 0.025f;
}
else if (currentTimeOfDay <= 0.25f)
{
RenderSettings.skybox = skyBoxNight;
intensityMultiplier = Mathf.Clamp01((currentTimeOfDay - 0.23f) * (1 / 0.02f));
moonIntensityMult = 0f;
}
else if (currentTimeOfDay >= 0.83f)
{
RenderSettings.skybox = skyboxDay;
intensityMultiplier = Mathf.Clamp01(1 - ((currentTimeOfDay - 0.83f) * (1 / 0.02f)));
moonIntensityMult = 0f;
}
sun.intensity = sunInitialIntensity * intensityMultiplier;
moon.intensity = moonIntensityMult;
}
public float GetTimeOfDayPercent
{
get { return currentTimeOfDay; }
set { return; }
}
public float GetSecondsPerDay()
{
return secondsInFullDay;
}
private void IncrementDay()
{
currentGameSecond = 0;
currentGameMinute = 0;
currentGameHour = 0;
currentGameDay++;
}
public void GetTimeOfDay()
{
// now to work on this
}
}

Unity: How to transfer time script to 24 hours and not decimals

so I've found a script online for Unity that is a Day and Night cycle however it works in decimals, 0.1 to 1, however I want it to be 1 to 24.
The code I am using is below, I have tried fiddling around with the decimal values however I can't get it correct.
using UnityEngine;
using System.Collections;
public class DayNightCycle : MonoBehaviour {
public Light sun;
public float secondsInFullDay = 120f;
[Range(0,24)]
public float currentTimeOfDay = 0;
[HideInInspector]
public float timeMultiplier = 1f;
float sunInitialIntensity;
void Start() {
sunInitialIntensity = sun.intensity;
}
void Update() {
UpdateSun();
currentTimeOfDay += (Time.deltaTime / secondsInFullDay) * timeMultiplier;
if (currentTimeOfDay >= 24) {
currentTimeOfDay = 0;
}
}
void UpdateSun() {
sun.transform.localRotation = Quaternion.Euler((currentTimeOfDay * 360f) - 90, 170, 0);
float intensityMultiplier = 1;
if (currentTimeOfDay <= 0.23f || currentTimeOfDay >= 0.75f) {
intensityMultiplier = 0;
}
else if (currentTimeOfDay <= 0.25f) {
intensityMultiplier = Mathf.Clamp01((currentTimeOfDay - 0.23f) * (1 / 0.02f));
}
else if (currentTimeOfDay >= 0.73f) {
intensityMultiplier = Mathf.Clamp01(1 - ((currentTimeOfDay - 0.73f) * (1 / 0.02f)));
}
sun.intensity = sunInitialIntensity * intensityMultiplier;
}
}
The result I want is for the day and night cycle to work from 1 to 24 and not 0 to 1, so that there's 24 hours in the game, and that it's easier to modify when using a sleep script I've made.
To convert a value between 0 and 1 (interval [0, 1]) you just have to multiply by the size of your new interval then add the first value of your interval.
So if you have a variable value and want an interval: [MIN, MAX] the calculation is the following:
var newValue = (value * (MAX - MIN)) + MIN;
In you case you want the interval [1, 24] so it is:
var newValue = (value * 23) + 1;
Simple maths, hope it helps.

Time.time make my Color Lerp not 'lerp'

I'm making a day/night cycle and have the lerp on Time.time, because if I use Time.deltaTime it turns night.day around so its day at 12am. However I digress.
The problem I'm running into now too is that with any Time setting it will make the lerp instant and not - well.. 'lerp'. Any idea on fixing this? I'm a C# newbie
I got it working with this as the time script:
using UnityEngine;
using System.Collections;
public class timeFlow : MonoBehaviour
{
public float Hours = 00;
public float Minutes = 00;
void Update()
{
if(Hours <= 23){
if(Minutes >= 60)
{
Minutes = 0;
if(Minutes <= 59)
{
Hours++;
}
else
{
Minutes = 0;
Hours = 0;
guiText.text = Hours.ToString("f0") + ":0" + Minutes.ToString("f0");
}
}
else
{
Minutes += UnityEngine.Time.deltaTime * 100;
}
if(Mathf.Round(Minutes) <= 9)
{
guiText.text = Hours.ToString("f0") + ":0" + Minutes.ToString("f0");
}
else
{
guiText.text = Hours.ToString("f0") + ":" + Minutes.ToString("f0");
}
}
else {
Hours = 0;
}
}
}
And this is the lerp script:
using UnityEngine;
using System.Collections;
public class cycleFlow : MonoBehaviour {
public Color32 night = new Color32(30, 30, 30, 255);
public Color32 day = new Color32(255, 255, 255, 255);
public GameObject Timer;
private timeFlow TimeFlow;
void Awake () {
TimeFlow = Timer.GetComponent<timeFlow> ();
}
void Update () {
DayNightCycle ();
}
void DayNightCycle()
{
foreach (SpriteRenderer child in transform.GetComponentsInChildren<SpriteRenderer>()) {
if (TimeFlow.Hours == 18){
child.color = Color.Lerp(day, night, Time.time);
}
if (TimeFlow.Hours == 6) {
child.color = Color.Lerp(night, day, Time.time);
}
}
}
}
You can write something like this, Lerp takes actual (from) value, final (to) value and fraction value. (here Time.deltaTime) Now when your timer will reach hour 18, your Color will change to color of night in few Update function calls (you can control change time by multiplying Time.deltaTime) :)
foreach ( SpriteRenderer child in transform.GetComponentsInChildren<SpriteRenderer>() ) {
if ( TimeFlow.Hours == 18 ) {
child.color = Color.Lerp(child.color, night, Time.deltaTime);
// here two times faster
// child.color = Color.Lerp(child.color, night, Time.deltaTime * 2.0f);
}
if ( TimeFlow.Hours == 6 ) {
child.color = Color.Lerp(child.color, day, Time.deltaTime);
// here half slower :)
// child.color = Color.Lerp(child.color, night, Time.deltaTime * 0.5f);
}
}
I had a hard time understanding lerp when used together with a time.
Maybe this example helps someone:
// lerp values between 0 and 10 in a duration of 3 seconds:
private float minValue = 0.0f;
private float maxValue = 10.0f;
private float totalDuration = 3.0f;
private float timePassed = 0.0f;
private float currentValue;
void Update()
{
timePassed += Time.deltaTime;
// Lerp expects a value between 0 and 1 as the third parameter,
// so we need to divide by the duration:
currentValue = Mathf.Lerp(minValue, maxValue, timePassed/totalDuration);
}
Note: you need to add some logic for:
handling when to start/stop lerping
when to reset timePassed

Categories