I am a beginner developer and I am trying to create a timer that shows how many seconds have passed since the start of the game on Unity. I want to change the text component (I am using TextMeshPro to be specific) of the object with the script.
I have been searching on google but nothing has worked yet. I can't find a way to link the public Text to the text component, and I get an error because the text is null.
Well you should reference the text in your code and then change it with a coroutine
public class Timer : MonoBehaviour
{
[SerializeField]
private TMP_Text textComponent;
private int seconds = 0;
private IEnumerator TimeRoutine()
{
while(Application.isPlaying)
{
textComponent.text = seconds.ToString();
yield return new WaitForSeconds(1f);
seconds++;
}
}
private void Start()
{
StartCoroutine(nameof(TimeRoutine));
}
}
Code for Timer with Text mesh pro
using UnityEngine;
using TMPro;
public class Timerexample : MonoBehaviour
{
float cntdnw = 30.0f;
public TMP_Text disvar;
void Update()
{
if(cntdnw>0)
{
cntdnw -= Time.deltaTime;
}
double b = System.Math.Round (cntdnw, 2);
disvar.text = b.ToString ();
if(cntdnw < 0)
{
Debug.Log ("Completed");
}
}
}
Source: Unity timer
Related
I am recently new to c# and I need some help.
Essentially I have two scripts, one for spawning objects and one for moving an object along a path. I need a way to combine these two mechanics so that when a new object is instantiated it automatically joins the path and follows it.
The path is made using iTween.
![The objects the scripts are attached to] (https://i.stack.imgur.com/QPQn2.png)
I've tried changing the variable m_PlayerObj to the cube prefab and I've tried adding the Path script to the instantiation script but nothing seems to work.
The scripts attached do nkt include these attempts I made as I wanted to make the code very clear.
Spawner script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnerScript : MonoBehaviour
{
public GameObject cubeprefab;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(cubeprefab, transform.position, Quaternion.identity);
}
}
}
Path script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Path : MonoBehaviour
{
public GameObject m_PlayerObj;
public Transform[] positionPoint;
[Range(0, 1)]
public float value;
// Start is called before the first frame update
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
float tempTime;
// Update is called once per frame
void Update()
{
if (value < 1)
{
value += Time.deltaTime / 10;
}
iTween.PutOnPath(m_PlayerObj, positionPoint, value);
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
As stated above, any help would be greatly appreciated as I am really stuck on this conceot and since I am new to Unity I really can’t see a way around it // how to fix it.
Instead of only storing the player object in the Path script, store a collection of objects. That way, the path can keep track of more than one object.
//public GameObject m_PlayerObj; // Get rid of this
public List<GameObject> followers; // Add this
Then, in your Update loop, you can loop through all of them.
void Update()
{
for (var i = 0; i < followers.Length; ++i)
{
if (value < 1)
{
value += Time.deltaTime / 10;
}
iTween.PutOnPath(m_PlayerObj, positionPoint, value);
}
}
Of course, now, you need to make sure you pass your cube instance to the Path GameObject when you spawn it, so the path knows about the cube follower. That means your spawner also needs to know about the path.
public class SpawnerScript : MonoBehaviour
{
public GameObject cubeprefab;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var cubeInst = Instantiate(cubeprefab, transform.position, Quaternion.identity);
path.followers.Add(cubeInst);
}
}
}
Now a new problem is going to be that each object is going to be at the same position on the path, because the path only stores one value - a better term might be progress. So if they're all the same, like the cube, you won't be able to tell because they'd overlap.
So you have to decide what you want to do instead. Evenly space them? You could do that with some math. Or have them all start from the beginning and keep track of their progress separately? Then you'd need to store progress for each of them. A better place to do that is probably on the cube object, which means you need to add a new script to your cube prefab:
public class PathFollower : MonoBehaviour
{
[Range(0, 1)]
public float pathProgress;
}
And, you need to start referring to the prefab by this script, instead of just GameObject:
public class SpawnerScript : MonoBehaviour
{
public PathFollower pathFollower;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var followerInst = Instantiate(pathFollower, transform.position, Quaternion.identity);
path.followers.Add(followerInst);
}
}
}
public class Path : MonoBehaviour
{
//public GameObject m_PlayerObj; // Get rid of this
public List<PathFollower> followers; // Add this
//...
Finally, you need to make sure to use the individual progress for each path follower, rather than a single progress value like your old Path script did:
for (var i = 0; i < followers.Count; ++i)
{
if (followers[i].pathProgress < 1)
{
followers[i].pathProgress += Time.deltaTime / 10;
}
iTween.PutOnPath(followers[i].gameObject, positionPoint, followers[i].pathProgress);
}
Putting it all together (separate files of course, with their own includes!):
public class SpawnerScript : MonoBehaviour
{
public PathFollower pathFollower;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var followerInst = Instantiate(pathFollower, transform.position, Quaternion.identity);
path.followers.Add(followerInst);
}
}
}
public class Path : MonoBehaviour
{
//public GameObject m_PlayerObj; // Get rid of this
public List<PathFollower> followers; // Add this
public Transform[] positionPoint;
//[Range(0, 1)]
//public float value; // Don't need this anymore either
// Start is called before the first frame update
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
// Update is called once per frame
void Update()
{
for (var i = 0; i < followers.Count; ++i)
{
if (followers[i].pathProgress < 1)
{
followers[i].pathProgress += Time.deltaTime / 10;
}
iTween.PutOnPath(followers[i].gameObject, positionPoint, followers[i].pathProgress);
}
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
public class PathFollower : MonoBehaviour
{
[Range(0, 1)]
public float pathProgress;
}
Recently, I've been working on a racing game that requires the player to avoid Radioactive Barrels that should subtract 15 seconds if they happen to collide into them; Below is code for my 'Timer' script, and my Barrel Collision Script.
Timer Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Timer : MonoBehaviour
{
public float timeRemaining = 10;
public bool timerIsRunning = false;
public Text timeText;
public void Start()
{
// Starts the timer automatically
timerIsRunning = true;
}
public void Update()
{
if (timerIsRunning)
{
if (timeRemaining > 0)
{
timeRemaining -= Time.deltaTime;
DisplayTime(timeRemaining);
}
else
{
Debug.Log("Time has run out!");
timeRemaining = 0;
timerIsRunning = false;
}
}
}
public void DisplayTime(float timeToDisplay)
{
timeToDisplay += 1;
float minutes = Mathf.FloorToInt(timeToDisplay / 60);
float seconds = Mathf.FloorToInt(timeToDisplay % 60);
timeText.text = string.Format("{0:00}:{1:00}", minutes, seconds);
}
}
Next, is my Barrel Collision Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExplosionTrigger : MonoBehaviour
{
public AudioSource ExplosionSound;
public ParticleSystem Explosion;
public void OnTriggerEnter(Collider collider)
{
Explosion.Play();
ExplosionSound.Play();
Timer.timeRemaining += 1.0f;
}
}
Is there anyway I could subtract time by colliding into said Barrels from the Timer script?
The easiest way to get the timeRemaining exposed to other classes is using the static keyword.
public static float timeRemaining = 10;
By making the variable static, it can be referenced by other classes. If you would rather not expose the variable completely, you can make static setter/getters. The variable would then be private static float timeRemaining = 10; when using the setter/getter.
public static float TimeRemaining
{
get{ return timeRemaining;}
set { timeRemaining = value;}
}
If you happen to want to expose more variables or methods to your classes from a script, I would recommend either implementing the Singleton Pattern or possibly implement your own Event System which uses the Singleton Pattern to pass events freely in your project. That way, you can subscribe and fire events for various scripts to listen for.
Yes, you could use GetComponent<>() to access the script from a different game object. You should set a GameObject variable, and drag the game object with the script on it. Add this to your barrel script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExplosionTrigger : MonoBehaviour
{
public AudioSource ExplosionSound;
public ParticleSystem Explosion;
public GameObject Timer;
public float timeLoss;
public Timer timer;
void Start()
{
timer = Timer.GetComponent<TimerScript>();
}
public void OnTriggerEnter(Collider collider)
{
timer.timeRemaining -= 1f;
Explosion.Play();
ExplosionSound.Play();
}
}
You can change the values in the TimerScript using a period. Make sure to change TimerScript to the name of the script on the Timer game object.
I am trying to create a simple dialogue system for my game in Unity. I've set up a special trigger for a Dialogue to start and the code is passing right variable but somehow it gets stuck at clearing the queue and throws a NullReferenceException.
I've seen through debugger that all variables and triggers work perfectly fine till cs:27 inside DialogueManager.cs. I've also checked the inspector to make sure everything is correctly assigned.
Dialogue class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Dialogue
{
public string name;
[TextArea(3,10)]
public string[] sentences;
}
DialogueTrigger class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NPC_DialogTrigger : MonoBehaviour
{
// Player
public Transform player;
// GameMaager to close
public GameObject Gameplay;
// Camera and Canvas to turn on
public GameObject DialogueManager;
// NPC Details
public GameObject InteractionNPCNameTextField;
public Transform interactionTransform;
public float radius = 3f;
private bool isBusy = false;
// DialogueStart
public GameObject DialogueStart;
void Start()
{
InteractionNPCNameTextField.gameObject.SetActive(false);
}
void Update()
{
float distance = Vector3.Distance(player.position, interactionTransform.position);
if (distance <= radius)
{
if (isBusy == false)
{
InteractionNPCNameTextField.gameObject.SetActive(true);
if (Input.GetKeyDown(KeyCode.E))
{
Dialogue();
Debug.Log("Started Dialogue procedure.");
}
}
}
else if (distance > radius)
{
InteractionNPCNameTextField.gameObject.SetActive(false);
}
}
public void Dialogue()
{
Gameplay.SetActive(false);
DialogueManager.SetActive(true);
DialogueStart.GetComponent<DialogueStart>().TriggerDialogue();
Debug.Log("Triggered Dialogue.");
}
void OnDrawGizmosSelected()
{
if (interactionTransform == null)
{
interactionTransform = transform;
}
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(interactionTransform.position, radius);
}
}
DialogueStart class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DialogueStart : MonoBehaviour
{
public Dialogue dialogue;
public void TriggerDialogue()
{
FindObjectOfType<DialogueManager>().StartDialogue(dialogue);
Debug.Log("Dialogue sent to dialogue manager.");
}
}
DialogueManager class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class DialogueManager : MonoBehaviour
{
public Text nameText;
public Text DialogueText;
private Queue<string> sentences;
public GameObject DialogueManagerUI;
void Start()
{
if (sentences == null)
{
sentences = new Queue<string>();
}
}
public void StartDialogue (Dialogue dialogue)
{
Debug.Log("Received dialogues: " + dialogue);
nameText.text = dialogue.name;
Debug.Log("Start Dialogue: " + sentences.Count);
sentences.Clear();
Debug.Log("Sentences Cleared: " + sentences);
foreach (string sentence in dialogue.sentences)
{
sentences.Enqueue(sentence);
}
DisplayNextSentence();
}
public void DisplayNextSentence()
{
if(sentences.Count == 0)
{
EndDialogue();
return;
}
string sentence = sentences.Dequeue();
DialogueText.text = sentence;
}
void EndDialogue()
{
Debug.Log("End of conversation.");
}
private void Update()
{
if (DialogueManagerUI.activeInHierarchy)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else if (!DialogueManagerUI.activeInHierarchy)
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
}
Visual Studio doesn't give me any errors.
Unity error code -> Screenshot
Debugger right before issue -> Screenshot2
Seems to me like the Queue is never assigned to sentences and it remains empty.
If it is so - why?
It sounds like DialogueManager.StartDialogue probably via DialogueStart.TriggerDialogue is called from somewhere in either Awake or at least before your Start was executed.
Especially
DialogueManager.SetActive(true);
lets assume the DialogueManager object is inactive at first anyway. So maybe your stuff is called before it is set to active.
This might also be due to the Start where you set
InteractionNPCNameTextField.gameObject.SetActive(false);
so any component on this GameObject might not have its Start method getting called. Maybe you referenced the wrong GameObject here?
Debugging would help to figure out in which order your methods get called.
In general my thumb rule for initializing is
Do everything that depends only on yourself in Awake
Do everything that depends on other components being setup already in Start
This way you can (almost) always be sure that stuff is already initialized when you need it.
However, these are only guesses but
The simple solution here:
You could already solve this by simply initializing the sentences right away using
private readonly Queue<string> sentences = new Queue<string>();
now it is definitely initialized even before Start or Awake get called!
So I put the object in the scene and then I made it "invisible" (deactivate if you will) from the inspector (the checkmark box next to the object's name) and after waiting 8 seconds it doesn't become visible. I am using Unity 2d and C#.
I have the game start paused for three seconds then plays after that which works. The first script is that one. The item is supposed to reappear after 8 seconds so after the game resumes, which doesn't work.
//delay before level starts script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class countDown : MonoBehaviour
{
public GameObject CountDown;
private void Start()
{
StartCoroutine("StartDelay");
}
void Update()
{
}
IEnumerator StartDelay()
{
Time.timeScale = 0;
float pauseTime = Time.realtimeSinceStartup + 3f;
while (Time.realtimeSinceStartup < pauseTime)
yield return 0;
CountDown.gameObject.SetActive(false);
Time.timeScale = 1;
}
{
//script for the flower to appear
IEnumerator Start()
{
print(Time.time);
yield return new WaitForSeconds(8);
print(Time.time);
flowerInScene.gameObject.SetActive(true);
}
[SerializeField] Transform flowerInScene;
}
I still don't really get your two methods called Start
You can simply call a StartCoroutine at the end of another Coroutine so you can chain them together (though there are surely better ways to do what you want in general):
using System.Collections;
using UnityEngine;
public class CountDown : MonoBehaviour
{
public GameObject CountDownObject;
public GameObject flowerObject;
private void Start()
{
StartCoroutine(Delay());
}
private IEnumerator Delay()
{
yield return new WaitForSeconds(3);
HideCountdown();
StartCoroutine(FlowerDelay());
}
private void HideCountdown()
{
CountDownObject.SetActive(false);
}
private IEnumerator FlowerDelay()
{
yield return new WaitForSeconds(8);
ShowFlower();
}
private void ShowFlower()
{
flowerObject.SetActive(true);
}
}
I personaly don't like Coroutines .. they are not so easy to debug sometimes. I would prefer doing something like this with simple timers (though in the first moment it does look worse). Advantage is I can now directly watch the timer count down in the inspector:
using UnityEngine;
public class SimpleCountDown : MonoBehaviour
{
[Header("The Objects")]
public GameObject CountDownObject;
public GameObject FlowerObject;
[Header("Settings")]
// Here you can adjust the delay times
public float StartOffset = 3;
public float FlowerOffset = 8;
[Header("Debug")]
public float startTimer;
public float flowerTimer;
public bool isStartDelay;
public bool isFlowerDelay;
private void Start()
{
startTimer = StartOffset;
flowerTimer = FlowerOffset;
isStartDelay = true;
}
private void Update()
{
if (!isStartDelay && !isFlowerDelay) return;
if (isStartDelay)
{
startTimer -= Time.deltaTime;
if (startTimer <= 0)
{
HideCountdown();
isStartDelay = false;
isFlowerDelay = true;
}
}
if (isFlowerDelay)
{
flowerTimer -= Time.deltaTime;
if (flowerTimer <= 0)
{
ShowFlower();
isFlowerDelay = false;
this.enabled = false;
}
}
}
private void HideCountdown()
{
CountDownObject.SetActive(false);
}
private void ShowFlower()
{
FlowerObject.SetActive(true);
}
}
I'm trying to be able to respawn as well as reset the score counting as soon as i walk into my "Gold" Object.
As for now i'm not even able to respawn which was possible earlier before trying to implement the "Score-Stuff" (at first the "FoundGold" Script was only used to be able to respawn). Also i'm trying to make the lowest Score the High-Score.
Note that im new to C# and i kinda put everything together from the tutorials i needed so a answer with some actual code/stating where something went wrong would be much appreciated.
//GoldFound Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GoldFound : MonoBehaviour
{
private ScoreManager theScoreManager;
public Transform target;
[SerializeField] private Transform player;
[SerializeField] private Transform respawnpoint;
private void Start()
{
theScoreManager = FindObjectOfType<ScoreManager>();
}
private void OnTriggerEnter(Collider other)
{
theScoreManager.scoreIncreasing = false;
player.transform.position = respawnpoint.transform.position;
theScoreManager.scoreCount = 0;
theScoreManager.scoreIncreasing = true;
}
}
other code
//ScoreManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour
{
public Text scoreText;
public Text hiScoreText;
public float scoreCount;
public float hiScoreCount;
public float pointPerSecond;
public bool scoreIncreasing;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
if (scoreIncreasing)
{
scoreCount += pointPerSecond * Time.deltaTime;
}
if(scoreCount > hiScoreCount)
{
hiScoreCount = scoreCount;
}
scoreText.text = "Score: " + Mathf.Round (scoreCount);
hiScoreText.text = "High Score: " + Mathf.Round (hiScoreCount);
}
}
If you want to save your highscore in-between play sessions, then the easiest way to do so is to save the value to PlayerPrefs. If you want to start saving more / more complex stuff you really should save it in a file you generate yourself. But in your case, PlayerPrefs is fine.
Here's a Unity tutorial on the subject:
https://unity3d.com/learn/tutorials/topics/scripting/high-score-playerprefs
Otherwise, you can just do it like this:
public void SetHighscore (float currentScore)
{
if (PlayerPrefs.HasKey("highscore"))
{
float highscore = PlayerPrefs.GetFloat("highscore");
if (highscore > currentScore)
{
PlayerPrefs.SetFloat("highscore", currentScore);
PlayerPrefs.Save();
}
}
else
{
PlayerPrefs.SetFloat("highscore", currentScore);
PlayerPrefs.Save();
}
}
Then just write PlayerPrefs.GetKey("highscore") whenever you need it.
(Though I'd also recommend you check if it exists by using the PlayerPrefs.HasKey("highscore"))
https://docs.unity3d.com/ScriptReference/PlayerPrefs.html