How I can make "Time" to be update constantly - c#

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);
}
}

Related

How to save in Unity the time obtained in the game timer?

I have created a timer for my game and I need to save in a variable or a function, the time elapsed until the moment of finishing, and then show it on the screen as "Record".
I want to display on screen the shortest time achieved, when a shorter time is achieved, replace, (overwrite) the time that was until that moment.
So far I have managed to show the clock time and a text to show "Record" that only shows "00:00" since I don't know how to put my code together.
The stopwatch works and restarts every time a new game starts, but I don't know how to save the time achieved at the end of the game
How do I get the shortest time achieved to display?
My experience is sparse in C#, I have started with a simple template to experiment and make changes.
I have built the code of the clock, following an example.
On the other hand, looking for information, I see that with the PlayerPrefs method you can save and delete data.
I found another example and studied the logic to use that method on my watch, but I don't quite understand the logic and I can't get it to work.
How can I get to save the time to display it as text on the screen?
How to make it always rewrite with the shortest time obtained at the end of the game?
I show the code of my Reloj.cs which works and is displayed on the screen.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Reloj : MonoBehaviour
{
[Tooltip("Tiempo inicial")]
public int tiempoInicial;
[Tooltip("Escala de tiempo del relog")]
[Range(-10.0f, 10.0f)]
public float escalaDeTiempo = 1;
private Text myText;
private float tiempoDelFrameConTimeScale = 0f;
private float tiempoAMostrarEnSegundos = 0f;
private float escalaDeTiempoAlPausar, escalaDeTiempoInicial;
private bool estaPausado = false;
void Start()
{
// set the timeline
escalaDeTiempoInicial = escalaDeTiempo;
myText = GetComponent<Text>();
//we start the variable
tiempoAMostrarEnSegundos = tiempoInicial;
ActualizarRelog(tiempoInicial);
}
// Update is called once per frame
void Update()
{
if (!estaPausado)
{
// the following represents the time of each frame considered the time scale
tiempoDelFrameConTimeScale = Time.deltaTime * escalaDeTiempo;
// the following variable accumulates the elapsed time to show it in the Relog
tiempoAMostrarEnSegundos += tiempoDelFrameConTimeScale;
ActualizarRelog(tiempoAMostrarEnSegundos);
}
}
public void ActualizarRelog(float tiempoEnSegundos)
{
int minutos = 0;
int segundos = 0;
string textoDelReloj;
// ensure that the time is not negative
if (tiempoEnSegundos <= 0) tiempoEnSegundos = 0;
// calculate seconds and minutes
minutos = (int)tiempoEnSegundos / 60;
tiempoEnSegundos = (int)tiempoEnSegundos % 60;
// create the string of digital characters that form the relog
textoDelReloj = minutos.ToString("00") + ':' + tiempoEnSegundos.ToString("00");
// update UI text element with character string
myText.text = textoDelReloj;
}
public void Pausar()
{
if (!estaPausado)
{
estaPausado = true;
escalaDeTiempoAlPausar = escalaDeTiempo;
escalaDeTiempo = 0;
}
}
}
On the other hand, I have been doing tests to try to implement the following example in my Clock, but I can't. Before asking the question here, I have looked for a solution and I have tried to learn how to do it, I have seen videos, examples, and I do not get it.
The following example, I create it by watching a video, and I get it to save the data on the screen, and delete it from a button, but I don't know how to implement it in my Clock. I imagine it would be very simple, but I can't understand the logic.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LogicaPuntuaje : MonoBehaviour
{
public Text textoPuntaje;
public int numPuntaje;
// The start is called before the first table update
public Text textoRecord;
void Start()
{
numPuntaje = 0;
textoRecord.text = PlayerPrefs.GetInt("PuntajeRecord", 0).ToString();
}
// The update is called once per frame
void Update()
{
}
// button to create points by doing that I don't need
public void PuntajeAlAzaro()
{
numPuntaje = Random.Range(0, 11);
textoPuntaje.text = numPuntaje.ToString();
if (numPuntaje > PlayerPrefs.GetInt("PuntajeRecord", 0))
{
PlayerPrefs.SetInt("PuntajeRecord", numPuntaje);
textoRecord.text = numPuntaje.ToString();
}
}
// function to delete the record data
public void BorrarDatos()
{
PlayerPrefs.DeleteKey("PuntajeRecord");
textoRecord.text = "00:00";
}
}
// here is the solution:
float currentTime; // your current timer (without logic)
float highscore = 99999; // set it to a really high value by default
void start()
{
highscore = PlayerPrefs.GetFloat("best"); // get your previous highscore
}
//on level completed:
{
if(currentTime <= highscore //your best time
{
highscore = currentTime;
PlayerPrefs.SetFloat("best", highscore); // save your score
}
}

Unity PlayerPrefs is not updating my 'high score'

I'm making a 2D game where you're in the middle of the screen and you move round an endless green (screen) world and white cubes spawn randomly around you, and I have finished the game mechanics and a main menu and game over screens. The one thing I'm trying to add now is a high score. I did a bit of research and found PlayerPrefs is probably the way to do it. I have a seperate scene for my main menu and my gameplay level (which includes the game over screen). I have no error messages. I have created a HSSetter (High Score Setter) script on the high score text in the main menu screen:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HSSetter : MonoBehaviour
{
public Text highScoreText;
// Start is called before the first frame update
void Start()
{
highScoreText.text = "High Score: " + PlayerPrefs.GetInt("HighScore").ToString();
}
// Update is called once per frame
void Update()
{
highScoreText.text = "High Score: " + PlayerPrefs.GetInt("HighScore").ToString();
}
}
and in my score script which is in my actual game level, here's the bit where I try to create the high score:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class score : MonoBehaviour
{
public int scoreCount = 0;
public int highScoreIFA;
void Start()
{
highScoreIFA = PlayerPrefs.GetInt("HighScore");
}
void Update()
{
if (scoreCount >= highScoreIFA)
{
PlayerPrefs.SetInt("HighScore", scoreCount);
}
}
public void AddToScore()
{
if (isHit == true) // i know this if loop works
{
scoreCount += 1; // and this, I use it to change the score text in-game.
isHit = false;
}
}
}
In AddToScore(), I increment scoreCount.
Through some debugging, I have found that everything in the HSSetter script works - when I change the highScoreText.text, the text on screen changes, which led me to believe the issue might be with the change of scenes? Thanks!
Multiple things you should do here
The first you already updated in your question afterwards: You had the condition wrong and always updated only
if(highScoreIFA > scoreCount)
which would almost always be the case.
Now you have changed it to
if(scoreCount >= highScoreIFA)
which still is not good since if the score is equal there is no reason to update it, yet.
I would rather use
if(scoreCount > highScoreIFA)
so only really update it when needed.
Secondly in both scripts do not use Update at all! That is extremely inefficient.
I would rather use event driven approach and only change and set stuff in the one single moment it actually happens.
You should only one single class (e.g. the score) be responsible and allowed to read and write the PlayerPrefs for this. I know lot of people tent to use the PlayerPrefs for quick and dirty cross access to variables. But it is exactly this: Quick but very dirty and error prone.
If you change the keyname in the future you'll have to do it in multiple scripts.
Instead rather let only the score do it but then let other scripts reference it and retrieve the values directly from that script instead
And finally you should use
PlayerPrefs.Save();
to create checkpoints. It is automatically done in OnApplicationQuit, bit in case your app is force closed or crashes the User would lose progress ;)
Might look like
public class score : MonoBehaviour
{
public int scoreCount = 0;
// Use an event so every other script that is interested
// can just register callbacks to this
public event Action<int> onHighScoreChanged;
// Use a property that simply always invoked the event whenever
// the value of the backing field is changed
public int highScoreIFA
{
get => _highScoreIFA;
set
{
_highScoreIFA = value;
onHighScoreChanged?.Invoke(value);
}
}
// backing field for the public property
private int _highScoreIFA;
private void Start()
{
highScoreIFA = PlayerPrefs.GetInt("HighScore");
}
public void AddToScore()
{
if (isHit == true) // i know this if loop works
{
scoreCount += 1; // and this, I use it to change the score text in-game.
isHit = false;
// Only update the Highscore if it is greater
// not greater or equal
if (scoreCount > highScoreIFA)
{
PlayerPrefs.SetInt("HighScore", scoreCount);
// Save is called automatically in OnApplicationQuit
// On certain checkpoints you should call it anyway to avoid data loss
// in case the app is force closed or crashes for some reason
PlayerPrefs.Save();
}
}
}
}
Then your other script only listens to the event and updates its display accordingly. It is even questionable if both scripts should not rather simply be one ;)
public class HSSetter : MonoBehaviour
{
public Text highScoreText;
// Reference your Score script here
[SerializeField] private score _score;
private void Awake ()
{
// Find it on runtime as fallback
if(!_score) _score = FindObjectOfType<score>();
// Register a callback to be invoked everytime there is a new Highscore
// Including the loaded one from Start
_score.onHighScoreChanged += OnHighScoreChanged;
}
private void OnDestroy()
{
_score.onHighScoreChanged += OnHighScoreChanged;
}
private void OnHighScoreChanged(int newHighScore)
{
highScoreText.text = $"High Score: {newHighScore}";
}
}

Why does why C# code keep crashing Unity? (I'm a beginner to Unity and C#)

Whenever I run my game it freezes, but it doesn't without this C# script.
I've tried changing around my code, and it works outside of Unity, in .NET (with some tweaks to certain functions) but when it's in Unity it crashes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Throw : MonoBehaviour
{
public Rigidbody rb;
string final = "final:";
public float force = 1;
public float accuracy = 0;
void incto(float amount)
{
while (force < amount)
{
Debug.Log(force);
force++;
}
}
void decto(float amount)
{
while (force > amount)
{
Debug.Log(force);
force--;
}
}
void fstart()
{
while (true)
{
force = 1;
incto(200);
decto(1);
if(Input.GetKey(KeyCode.E))
{
Debug.Log(final + force);
break;
}
}
}
// Start is called before the first frame update
void Start()
{
fstart();
}
// Update is called once per frame
void FixedUpdate()
{
Debug.Log(force);
}
}
It should decrease and increase the force value, then stop when you press E, but Unity just crashes.
Unity takes care of the while(true) for you. Unity's while(true) calls your FixedUpdate, you just need to fill it in.
Unity only captures keystrokes once per frame, so Input.GetKey(KeyCode.E) will always return the same value. Unity crashes because of your while(true) is an infinite loop.
More info: https://docs.unity3d.com/Manual/ExecutionOrder.html
i belive that unity starts catching key strokes after the first frame, not before, try moving fstart() behind a first run bool in the FixedUpdate function
oh and this will hang the entire program every time it executes a frame.....
The code crashes because you have an infinite loop here:
while (true)
{
}
It will never exit the loop so nothing more happens. Just put that code into Update() method, which gets called by the engine on every frame, it will do the trick

C# Timer will not stop on trigger enter

I have a beautifully simple script that displays a timer on Unity 5 UI. At a finish line the racer enters a trigger and the timer should stop. It doesn't. I get no errors it just keeps running like Forrest Gump! I plan to post timer results to a leaderboard if I could stop the timer. Here is my timer script:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Timer : MonoBehaviour
{
public Text timerText;
private float startTime;
private bool finished = false;
// Use this for initialization
void Start () {
startTime = Time.time;
}
// Update is called once per frame
void Update () {
if(finished)
return;
float t = Time.time - startTime;
string minutes = ((int) t / 60).ToString();
string seconds = (t % 60).ToString("f2");
timerText.text = minutes + ":" + seconds;
}
public void finish()
{
finished = true;
timerText.color = Color.yellow;
}
}
On a triggered cube I haved placed the following script:
using UnityEngine;
using System.Collections;
public class WinBox : MonoBehaviour
{
private void onTriggerEnter(Collider other)
{
GameObject.Find("Green").SendMessage("Finish");
}
}
I've changed collision detection to continuous on the rigidbody to my it more sensitive with no luck. Made the cube thicker. Nope, it just keeps on ticking. The vehicle "Green" is in a indexed container/empty game object. It is a networked multiplayer game, so it should probably be rewritten to find the game object with the tag, player. Or somehow the first tagged game object to enter the trigger. I think the problem is 'Send Message' I've never worked with that. The text color change also didn't work, but that needs to be changed to enable confetti particles when the timer stops. Any assist would be greatly appreciated.
There are few problems in your code:
1.Your timer won't stop because the trigger callback function is not being called. The trigger callback function is not being called because onTriggerEnter is supposed to be OnTriggerEnter with the O capitalized.
When you fix this, make sure that the collider has IsTrigger property checked/enabled too.
2.Another problem is this line: GameObject.Find("Green").SendMessage("Finish");
You are calling a function called Finish but the actual name of your function is finish from the Timer class.
Finally, instead of doing GameObject.Find("Green"); in the OnTriggerEnter function, why not cache it in the Start function then re-use it? GameObject.Find is very expensive and doing it many times will slow down your game.
public class WinBox : MonoBehaviour
{
GameObject green;
void Start()
{
green = GameObject.Find("Green");
}
private void OnTriggerEnter(Collider other)
{
green.SendMessage("finish");
}
}

Slider code, doesn't work as scrubber, won't move when down?

What breaks is the scrubbing slider, it recognises a click, but it does not move. I have interactable enable.
I'm having to reimport all assets to get it to work again but even now this isn't a sure bet it'll fix it.
This code was working perfectly fine about a week ago, I've made little changes to it since.
A lot of it is commented so I can learn that different parts of the code. Feel free to delete it if it bothers you.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class NewAnimController : MonoBehaviour {
public Slider scrubSlider; //This is the slider that controls the current position of the animation. Values have to be 0 to 1 for normalized time of the animation, called later in the script
public Slider speedSlider; //This is the slider that controls the playback speed of the animation
public Animator animator; //Animator that is attached to the model that we are looking to control the animation of
public float playbackSpeedAdjustment = 0.5f; //This is a variable that can be easily adjusted to change the total playback speed of the animation. If it's too fast make smaller and vice versa
public Text currentDateText;
public int monthsInProject;
private float rememberTheSpeedBecauseWeMightNeedIt; //Float needed to keep the speed of the animation between pausing and playing
public void Update()
{
float animationTime = animator.GetCurrentAnimatorStateInfo(0).normalizedTime; //float a new value animationTime that is equal to the animators animation state then converted to normalized time
//Debug.Log("animationTime (normalized) is " + animationTime); //logging the normalized time to be able to store it for the scrubbing slider. Doesn't need to be logged for this to work, good to log it to make sure that it's working
scrubSlider.value = animationTime; //making the slider value equal to the now logged normalized time of the animation state
}
public void ScrubSliderChanged(float ScrubSliderchangedValue) // this value has to be floated so that the scrubbing slider can be attached to in the inspector to be able to change the current frame of the animation
{
animator.Play("Take 001", -1, scrubSlider.normalizedValue);
}
public void SpeedSliderChanged(float SpeedSliderchangedValue) //value is floated to be able to change the speed of the animation playback
{
animator.speed = speedSlider.normalizedValue * playbackSpeedAdjustment; // here the speed is multiplied by the adjustment value set in the editor just in case the playback speed needs to be changed outside of normalized values
}
public void UserClickedPauseButton()
{
if (animator.speed > 0f)
{
// we need to pause animator.speed
rememberTheSpeedBecauseWeMightNeedIt = animator.speed;
animator.speed = 0f;
}
else
{
// we need to "unpause"
animator.speed = rememberTheSpeedBecauseWeMightNeedIt;
}
}
public void UserClickedBackButton()
{
scrubSlider.value = scrubSlider.value - (1f / monthsInProject);
}
public void UserClickedForwardButton()
{
scrubSlider.value = scrubSlider.value + (1f / monthsInProject);
}
public void UserClickedStartButton()
{
scrubSlider.value = 0;
}
public void UserClickedEndButton()
{
scrubSlider.value = 1;
}
}
Many thanks for all your help.
I'm pretty sure the problem is this. In Update you are doing something like this:
scrubSlider.value = animationTime
that means that every frame, "NO MATTER WHAT", YOU ARE SETTING THE SLIDER POSITION, to, where the animation is. If the user is trying to move the slider - you are moving it right back, that same frame!
It's not so easy to solve this problem. Unity did not include a trivial built-in function for this. You need a separate script which sits on the slider which determines whether or not the handle is being held down. You have to use the OnPointerDown and OnPointerUp handlers.
How to use pointer handlers in modern Unity:
Step one, make this small script called Teste.cs
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
public class Teste:MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
public Slider theSlider;
public bool sliderIsBeingHeldDown;
public void OnPointerDown(PointerEventData data)
{
sliderIsBeingHeldDown = true;
Debug.Log("holding down!");
}
public void OnPointerUp(PointerEventData data)
{
sliderIsBeingHeldDown = false;
Debug.Log("holding up!");
}
}
Don't forget the declaration...
Teste:MonoBehaviour, IPointerDownHandler, IPointerUpHandler
rather than the usual MonoBehavior.
Drag that script on to your UI.Slider.
Note that in general these only work when they are "on" the thing in question.
Run, and try clicking up and down on the slider button - check the console. It works right? Now you have to use that variable inside NewAnimController
(1) Add a public variable to NewAnimController,
public Teste teste;
(2) Be sure to drag to connect that variable in Inspector
(3) Now you can refer to teste.sliderIsBeingHeldDown to see if that slider is being held down. So instead of doing this every frame...
scrubSlider.value = animationTime;
you will have to do this every frame...
if (teste.sliderIsBeingHeldDown)
Debug.Log("don't try to work against the user!");
else
scrubSlider.value = animationTime;
I hope that works! I think that's the simplest way to know when the slider is being held-down.
You really chose a difficult project for your first project! Hell!
It's worth noting that you could also put the slider-adjusting code (I mean to say your actual code for the scrubber) "inside" the script that is actually on the slider -- but I wouldn't worry about that for now. Your solution as stands is good.
Note depending on how you implement it, you may need to make it pause more elegantly when you hold down the slider handle, but before moving the slider handle. To achieve this, one approach is arrange to call UserClickedPauseButton() or a similar concept, when, the Down/Up routines shown here in "Teste", are called. Conversely you could just not bother with ScrubSliderChanged, and instead do the whole job inside code that runs whenever the handle is "down".
(In general, you'd almost certainly use UnityEvent here to make a solid reusable solution for a slider such as this for use with something like a scrubber.)

Categories

Resources