This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 2 years ago.
I have a game where I have a queue matchup system.
I would like to show the player how long they been in the current queue. It works well, until the player presses the menu/app overview button on their phone, which basically freezes the timer, and it only continues counting when the player switches back to full screen mode on their phone.
I tried looking for an app lifecycle method (somewhat like onApplicationPause, but it didn't work for me)
I also tried syncing the time by saving it in the db and then loading from the database actually, but Firebase puts on some delay, so it won't be exact.
How could I solve this, so it will keep counting when the user presses their app overview/menu button on their phone?
For now, I have this code which counts the user's queue time:
private void Update() {
if(startedCounting) {
timer += Time.deltaTime;
int seconds = Mathf.FloorToInt(timer % 60);
int minutes = Mathf.FloorToInt(timer / 60);
queueStatusText.text = "You are in the queue\n"
+ string.Format("{0:00}:{1:00}", minutes, seconds);
}
}
There are different aproaches, some using static class or singleton pattern. It is better to not update this time variable each time on Update() as it takes the computation time each update (if you don't need this time for anything else). Also user doesn't need to have exact time by frames so you can avoid things like adding Time.deltaTime.
I'll show you example with static class, it can hold this information. Also note that this script is only added as C# file, but you don't attach it to any GameObject :
public static class QueueTimerInformation //It is not inheriting from MonoBehavior!
{
private static DateTime dt;
private static bool isRunning = false;
//Save current DateTime when user did the action
public static void Start()
{
if(!isRunning)
{
dt = DateTime.Now;
isRunning = true;
}
}
public static void Reset()
{
isRunning = false;
}
// This gets the actual time in String value
// Usually it is better to return just `elapsedTime` and format it later
public static string GetTimeElapsed()
{
if(!isRunning) return "00:00"; //Not running, return some default
var elapsedTime = (DateTime.Now - dt);
return $"{elapsedTime:mm\\:ss}";
}
}
Usage
//On 1st time enter lobby
QueueTimerInformation.Start();
//In update method
var result = QueueTimerInformation.GetTimeElapsed();
Related
I am beginner game dev in unity and i start new little project "alone", and in my game i have a little problem.
My problem is that the time, i.e. the clock in the game is not synchronized and does not synchronize and I have some suspicions.
I know maybe I'm doing another wrong step, maybe I put a wrong equation in the whole function and nothing works anymore, but to tell you more briefly, I want to make a game in which the game interface is a desktop and the clock on I coded it below, it doesn't work to be updated with time, I waited and tried many solutions that I applied and they didn't work, so I'll leave the code below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class DateTime : MonoBehaviour
{
public Text timeLockScreenText;
public Text dateLockScreenText;
public Text timeTaskBarText;
string time = System.DateTime.UtcNow.ToLocalTime().ToString("HH:mm");
string date = System.DateTime.UtcNow.ToLocalTime().ToString("MM-dd-yyyy");
// Update is called once per frame
void Update()
{
InvokeRepeating("DateTimeV",0f,1f);
}
public void DateTimeV()
{
timeLockScreenText.text = time;
dateLockScreenText.text = date;
timeTaskBarText.text = timeLockScreenText.text;
}
}
Now I have some theories:
one is that in the game, just like in real life, it's like with any personal computer user when we open it there is a lockscreen with a clock and we press enter to enter all the time to access the internet and other files on the desktop, so I have the theory that the lockscreen being a GameObject which is like visibility opposite to the desktop, i.e. if the lockscreen is active the desktop is not visible and vice versa, the clock does not update because let's say if we are on the desktop the lockscreen is disabled and that's why the script doesn't really work and you have to to make two separate ones, one for the lockscreen and one for the desktop, actually for the clock on the desktop
or I don't know how to code well
I tried to put in Update() function, in InvokeRepeting, StartCourutine with IEnumerator function, and i dont have the solutions
I don't know anything about Unity, but it seems that you're never updating the values of time and date after they're initially assigned. Perhaps something like this would do the trick, where you just assign the correct values in the method:
public void DateTimeV()
{
DateTime utcNow = System.DateTime.UtcNow.ToLocalTime();
timeLockScreenText.text = utcNow.ToString("HH:mm");
dateLockScreenText.text = utcNow.ToString("MM-dd-yyyy");
timeTaskBarText.text = timeLockScreenText.text;
}
Use fixed update, and the code from Rufus L:
void FixedUpdate() => DateTimeV();
public void DateTimeV()
{
DateTime utcNow = System.DateTime.UtcNow.ToLocalTime();
timeLockScreenText.text = utcNow.ToString("HH:mm");
dateLockScreenText.text = utcNow.ToString("MM-dd-yyyy");
timeTaskBarText.text = timeLockScreenText.text;
}
What Rufus L said + you would use InvokeRepeating only once e.g. in
private void Start()
{
InvokeRepeating(nameof(DateTimeV), 0f, 1f);
}
or use a simple counter
private float timer;
private void Update ()
{
timer += Time.deltaTime;
if(timer > 1f)
{
timer -= 1f;
DateTimeV();
}
}
or a coroutine
private IEnumerator Start()
{
while(true)
{
DateTimeV();
yield return new WaitForSeconds(1f);
}
}
I want to make countdown timer that will return value of bool when he is active , so I can check on other script if is active give double points if is not then you need to give normal points..
I want to make it more complicated and I want to add time on timer if the timer is active, if is not then we use default time on countdown...
I don't know how to use courutine specially when I need to add time if the timer is not over..
Lets say like a example:
I pick up power up and timer starts for 5 seconds counting to 0.
If i pick up again powerup and timer is on lets say 3 , Power up need to have now 8 seconds. When powerup is over he must go from 5 seconds when player pick up new one..
Here is my code that doesn't work how I want also my code doesn't have a function to add time to power up when power up is active.. In other words I don't know how i can check if powerup is active and if yes just to add to counter 5 more seconds..
Here is code that doesn't contain adding time it only contains working counter..
void startDoublePoints()
{
StartCoroutine("doublePoints");
Time.timeScale = 1;
}
//Simple courutine
IEnumerator doublePoints()
{
while (true)
{
yield return new WaitForSeconds(1);
timeLeft--;
}
}
I hope someone will explain me more about how I can achieve my goal.. I hope I explained what I need to achieve.. If you do not understand something please ask on comment and I will try to explain it again..
Thank you so much community I don't know how would I learn anything without this great place :)
float powerUpTimer;
bool isDoublePoints = false;
void Update()
{
// Check timer only when Power up time
if(isDoublePoints)
{
// Countdown the timer with update time
powerUpTimer -= Time.deltaTime;
if(powerUpTimer <= 0)
{
// End of power up time
isDoublePoints = false;
powerUpTimer = 0;
}
}
}
// Add any time player picks to timer
public void OnPickPowerUp(float buffTime)
{
isDoublePoints = true;
powerUpTimer += buffTime;
}
I need to keep up to an X (changeable amount) instances of countdown timers, each being added by the user, once a single one of them reaches 0 it calls for a function while the other counters keep counting.
for that purpose i tried to create a queue of them, but as it turns out, I can't change the value of an iteration variable in foreach:
public struct Debuff
{
public float Timer;
public int Stack;
public int MaxStack;
...
}
public Debuff Poisoned;
public void CalcDamage(...)
{
...
if (Poisoned.Stack < Poisoned.MaxStack)
{
Poisoned.Stack++;
PoisonStacksTimer.Enqueue(Poisoned.Timer);
InvokeRepeating("Poisoning", 0.1F, 1F);
}
else
{
PoisonStacksTimer.Dequeue();
PoisonStacksTimer.Enqueue(Poisoned.Timer);
}
}
public void Poisoning()
{
foreach(float PTimer in PoisonStacksTimer)
{
TakeDamage(Poisoned.DmgTranslate);
PTimer -= Time.deltaTime; // here lies at least one of the problems
if (PTimer <= 0)
{
Poisoned.Stack--;
PoisonStacksTimer.Dequeue();
CancelInvoke("Poisoning");
}
}
}
is there any other way to use countdown timers in queue? or maybe a better way to achieve my objective?
this is my first time asking a question here so I hope I explained my problem clearly.
Off the top my head, I'd use coroutines.
Every time you need to start a timer, you start an associated coroutine, so if you, at a specific moment, have for example 4 active timers, there'll be 4 coroutines.
In order to keep a reference to every single coroutine, use a List<IEnumerator>: every time a timer needs to be started, add the new IEnumerator reference and start the coroutine, so when a coroutine ends naturally or needs to be stopped, you can remove that reference from the list while keeping the other alive coroutines' references in the list.
Hi I have a C# script written which I can then call inside another script to keep track of the amount of time remaining between different scenes, and then load a scene when the timer reaches 0. I beleive that both scripts are written correctly, but there is no action when the timer reaches 0, I think the issue is that I'm not placing the code in the correct location but I'm not really sure, I've tried adding it to both my start function and my update function.
Here is the script for the static class;
public static class GlobalCountDown
{
static DateTime TimeStarted;
static TimeSpan TotalTime;
public static void StartCountDown(TimeSpan totalTime)
{
TimeStarted = DateTime.UtcNow;
TotalTime = totalTime;
}
public static TimeSpan TimeLeft
{
get
{
var result = DateTime.UtcNow - TimeStarted;
if (result.TotalSeconds <= 0)
SceneManager.LoadScene("Lose");
return TimeSpan.Zero;
//return result;
}
}
}
And here is the call I make in each game scene to check the time remaining;
GlobalCountDown.StartCountDown (TimeSpan.FromSeconds (5));
I get a warning when compiling the script 'The private field `GlobalCountDown.TotalTime' is assigned but its value is never used' but am not sure how to fix it.
Any help or advice would be appreciated.
Change this
var result = DateTime.UtcNow - TimeStarted;
To this
var result = TotalTime - (DateTime.UtcNow - TimeStarted);
In your behaviour do this
void Start()
{
GlobalCountDown.StartCountDown (TimeSpan.FromSeconds (5));
}
void Update()
{
if (GlobalCountDown.TimeLeft == TimeSpan.Zero)
SceneManager.LoadScene("Lose");
}
and remove the SceneManager code from your GlobalCountDown class
a very simple question here.
I'm coding a multi-player game in XNA, what would be a better option here - and why?
(Line 7 is the only changed line in each option)
Option 1:
const float SyncFrequency = (1 / 30f);
float Sync;
void Sync(GameTime GT)
{
Sync += (float)GT.ElapsedGameTime.TotalSeconds;
if (Sync >= SyncFrequency) { Sync = 0; SyncAll(); }
}
void SyncAll() { /*Syncing Code Here*/ }
Option 2:
const float SyncFrequency = (1 / 30f);
float Sync;
void Sync(GameTime GT)
{
Sync += (float)GT.ElapsedGameTime.TotalSeconds;
if (Sync >= SyncFrequency) { Sync -= SyncFrequency; SyncAll(); }
}
void SyncAll() { /*Syncing Code Here*/ }
Also:
Notice I use floats:
const float SyncFrequency = (1 / 30f);
Would using extra-precision be needed at all? E.g:
const double SyncFrequency = (1 / 30d);
Mathematically it makes sense to subtract instead of resetting.
Example: Standard 60fps means updating approximately every 16.666ms. If you have a network connection that you poll on every 30ms, upon the second Update, the time passed will have been 33.333ms, and this means that if you reset, you will be back at square one again. Whereas if you subtract 30ms from the current, you're down on 3.333ms. So over the course of a few seconds, you'll have run a more accurate amount of polls on the network connection.
It all depends on the situation really. In the above mentioned instance, you might run into situation where the application will poll the network connection 33 times, and other times it may do it 32, or 34 times, due to the time from the previous runs. All this can of course be calculated.
This may lead to uneven (probably unnoticeable, but still) gameplay, if it is actually used for network polling.
However for things such as Animating, it makes sense, since you'll most likely ALWAYS want the smoothest animating your hardware can deliver.
Especially for movement, since some PCs can't update 60 times per second (either because of hardware limitations, or the software simply only supports 30 or maybe 24fps). This will also lead to uneven movement, if you don't actually use the time passed (16.666ms in 60fps) to calculate movement and the likes.
Rant ended
To further expand on Bjarke's great explanation, I coded this for my use, but feel free to use it:
public static class Timers
{
private static Dictionary<string, Timer> Array;
public static void Add(string Name, double Interval)
{
if (Array == null) Array = new Dictionary<string, Timer>();
if (!Array.ContainsKey(Name)) Array.Add(Name, new Timer(Interval));
}
public static void Remove(string Name) { if ((Array != null) && Array.ContainsKey(Name)) Array.Remove(Name); }
public static void Clear() { Array.Clear(); Array = null; }
public static void Update(GameTime GameTime)
{
if (Array != null)
foreach (Timer Timer in Array.Values)
{
if (Timer.Time >= Timer.Interval) Timer.Time -= Timer.Interval;
Timer.Time += GameTime.ElapsedGameTime.TotalSeconds;
}
}
public static bool Tick(string Name) { return ((Array != null) && Array.ContainsKey(Name) && (Array[Name].Time >= Array[Name].Interval)); }
private class Timer
{
public double Interval, Time;
public Timer(double Interval) { this.Interval = Interval; }
}
}
To add a timer:
//Parameter 1: The name of the timer for your use.
//Parameter 2: The time (of interval) for each tick.
Timers.Add("Name of your timer!", (1 / 30d));
// (1 / 30d) = 30 times a second
(add your timer in the load function of your game)
To use your timer:
In your game update method (at the start of it), call:
Timers.Update(gameTime);
Then you can use any timers that you've added like so:
if (Tick("Name of your timer!"))
{
//Code here to execute on the tick of the timer
}