I'm trying to create an inactivity timer for my game and I seem to have everything working except for one part. After a pre-count, I'm instantiating a prefab timer dialog and then in my coroutine I'm trying to update the text field 'timerTextField' located in the that prefab but it isn't updating.
I do have all of the appropriate variables assigned in the editor and my debug.log right after the 'timerTextField.text = s.ToString();' line is counting down correctly. What do I have wrong?
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
// Create Singleton
public static GameManager gameManagerInstance = null;
// Set Default Background Color
public Color defaultColor;
// Restart variables
private Vector3 prevMousePosition;
[SerializeField]
private Text timerTextField;
public Object startingScene;
public GameObject timeOutWarningDialog;
public float countdownLength;
public float countdownDelay;
private float seconds;
private Scene currentScene;
private GameObject gameManager;
private GameObject canvas;
private GameObject timerInstance;
void Awake()
{
if (gameManagerInstance == null)
gameManagerInstance = this;
else if (gameManagerInstance != null)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
gameManager = GameObject.FindGameObjectWithTag("GameManager");
}
void Start()
{
prevMousePosition = Input.mousePosition;
currentScene = SceneManager.GetActiveScene();
}
void Update()
{
if (Input.anyKeyDown || Input.mousePosition != prevMousePosition)
{
currentScene = SceneManager.GetActiveScene();
if (currentScene.name != startingScene.name)
{
StartPreCountTimer();
if (timeOutWarningDialog != null)
{
timeOutWarningDialog.SetActive(false);
}
}
}
prevMousePosition = Input.mousePosition;
}
// GAME TIMER
public void StartPreCountTimer()
{
// Debug.Log("Pre Count Started");
CancelInvoke();
if (GameObject.FindGameObjectWithTag("Timer") == null)
Invoke("ShowRestartWarning", countdownDelay);
}
void ShowRestartWarning()
{
canvas = GameObject.FindGameObjectWithTag("Canvas");
timerInstance = Instantiate(timeOutWarningDialog);
timerInstance.transform.SetParent(canvas.transform, false);
timerInstance.SetActive(true);
if (timerInstance.activeSelf == true)
StartTimer(countdownLength);
}
public void StartTimer(float seconds)
{
StartCoroutine("RunTimer", seconds);
}
IEnumerator RunTimer(float seconds)
{
float s = seconds;
// Debug.Log("Restart Countdown Started from: " + s);
while (s > 0)
{
if (timerTextField != null)
{
timerTextField.text = s.ToString();
Debug.Log(s);
}
yield return new WaitForSeconds(1);
s -= 1;
}
if (s == 0)
{
RestartGame();
}
}
void RestartGame()
{
SceneManager.LoadScene(startingScene.name);
// Debug.Log("Game Restarted");
}
}
Your problems comes from the timerTextField reference.
It seems like you assign the Text component located inside your prefab (which is timeOutWarningDialog I think) to the timerTextField field inside Inspector.
Because of this, after instantiating a new prefab timer dialog, when you want to change the timerTextField.text value, what is changed is the Text component inside your prefab and not the one of the instantiated object.
You check this but selecting your object containing the Text component inside your prefab (int he Project tab of Unity):
(this also explains the weird values you had on start: the previous values reached when you stopped game)
For your script to work, you simply have to reference the new Text component of the instantiated prefab using something like this:
void ShowRestartWarning()
{
timerInstance = Instantiate(timeOutWarningDialog);
timerInstance.transform.SetParent(transform, false);
timerInstance.SetActive(true);
timerTextField = timerInstance.GetComponentInChildren<Text>(); // NEW LINE
if(timerInstance.activeSelf == true)
StartTimer(countdownLength);
}
Hope this helps,
Related
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
I am new to unity and I am trying to make a flappy bird as a project but I am stuck in one place.
I created a game control script and added game over text and it worked fine, but when I tried to add to score text to a different script, it didn't work.
It's hard to explain in words, here's a pic and script.
[1]: https://i.stack.imgur.com/DLsKv.png
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameControl : MonoBehaviour
{
public static GameControl instance;
public GameObject gameOverText;
public Text scoreText;
public bool gameOver = false;
public float scrollingSpeed = -1.5f;
private int score = 0;
void Awake()
{
if(instance == null)
{
instance = this;
}
else if(instance != this)
{
Destroy(gameObject);
}
}
void Update()
{
if(gameOver == true && Input.GetMouseButtonDown(0))
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
public void PlayerScored()
{
if (gameOver)
{
return;
}
score++;
scoreText.text = "Score: " + score.ToString();
}
public void PlayerDied()
{
gameOverText.SetActive(true);
gameOver = true;
}
}
The gameobject you are trying to drag into the Text slot doesn't have a "Text" component which looks like this:
Add this component to your gameobject.
If the gameobject already has the "Text" component, there must be some other problem which is that you don't have "Event System" in your scene which probably got deleted accidentally. Right click on your hierarchy and add "Event System".
I'm trying to keep the player skin, even when reloading the scene, or moving on to a new one. The player object changes every scene.
The player skin is chosen in a pause menu, with three buttons. Each one of those buttons calls a function in the script below. I'm trying to call one of these functions, based on what value the PlayerPrefs int holds, and the function does get called; But throws the error MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it
Below is what i have already tried, but this throws an error on scene reload (death)
i don't know what i'm doing wrong here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Pausemenu : MonoBehaviour
{
public static bool gameIsPaused = false;
public GameObject pauseMenuUI;
public Material BelgianMat;
public Material Ball2;
public Material Rainbow;
public GameObject Player;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape)) {
if (gameIsPaused) {
Resume();
} else {
Pause();
}
}
}
public void Resume(){
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
gameIsPaused = false;
}
void Pause() {
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
gameIsPaused = true;
}
public void LoadMenu() {
Time.timeScale = 1f;
gameIsPaused = false;
SceneManager.LoadScene("Menu");
}
public void QuitGame() {
Debug.Log("Quitting");
Application.Quit();
}
public void ApplyBelgian() {
Player.GetComponent<Renderer>().material = BelgianMat;
PlayerPrefs.SetInt("playerMat", 0);
}
public void ApplyBall2() {
Player.GetComponent<Renderer>().material = Ball2;
Debug.Log("Applied ball two");
PlayerPrefs.SetInt("playerMat", 1);
}
public void ApplyRainbow() {
Player.GetComponent<Renderer>().material = Rainbow;
PlayerPrefs.SetInt("playerMat", 2);
}
void OnEnable()
{
Debug.Log("OnEnable called");
SceneManager.sceneLoaded += OnSceneLoaded;
}
// called second
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
Debug.Log("OnSceneLoaded: " + scene.name);
Debug.Log(mode);
if (PlayerPrefs.GetInt("playerMat") == 0) {
ApplyBelgian();
}
else if (PlayerPrefs.GetInt("playerMat") == 1) {
Debug.Log("gonna apply ball 2");
ApplyBall2();
}
else if (PlayerPrefs.GetInt("playerMat") == 2) {
ApplyRainbow();
}
}
}
I don't know why but it seems like the reference to the Player object may be broken after loading the scene. If this is the case, add a "Player" tag to your Player object and add this to the OnEnable function: Player = GameObject.FindWithTag("Player");
My countdown timer in ShowRestartDialog() is acting funky. Instead of starting at the defined countdownLength (which is set to 5) it is starting at a random negative number and going down from there. Why would that be happening? Thanks!
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class CountdownTimer : MonoBehaviour
{
public static CountdownTimer countdownTimerInstance = null; // Create Singleton
public Object startingScene;
public GameObject timeOutWarningDialog;
private GameObject timerDialogBoxInstance;
private GameObject canvas;
private IEnumerator counter;
private Button stopCountButton;
private Text timerTextField;
public float countdownLength;
public float countdownDelay;
private float countdownInterval = 1.0f;
void Awake()
{
if (countdownTimerInstance == null)
countdownTimerInstance = this;
else if (countdownTimerInstance != null)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
public void StartPreCountTimer()
{
GameManager.preCountActive = true;
Debug.Log("StartPreCountTimer Timer has Started!");
if (GameManager.restartWarningActive == false)
Invoke("ShowRestartDialog", countdownDelay);
}
public void RestartPreCountTimer()
{
GameManager.preCountActive = false;
Debug.Log("StartPreCountTimer Timer has Restarted!");
CancelInvoke("ShowRestartDialog");
}
void ShowRestartDialog()
{
GameManager.preCountActive = false;
canvas = GameObject.FindGameObjectWithTag("Canvas");
timerDialogBoxInstance = Instantiate(timeOutWarningDialog); // instantiate timeout warning dialog
timerDialogBoxInstance.transform.SetParent(canvas.transform, false);
timerDialogBoxInstance.SetActive(true);
Text[] textFields = timerDialogBoxInstance.GetComponentsInChildren<Text>(true); // get reference to timer textfields
timerTextField = textFields[2]; // access and assign countdown textfield
stopCountButton = timerDialogBoxInstance.GetComponentInChildren<Button>(); // get reference to keep playing button
stopCountButton.onClick.AddListener(StopDialogTimer); // add button listener
if (timerDialogBoxInstance.activeInHierarchy == true)
InvokeRepeating("StartDialogTimer", 0, countdownInterval);
}
void StartDialogTimer()
{
float s = countdownLength--;
Debug.Log(s);
if (timerTextField != null)
timerTextField.text = s.ToString();
if (s == -1)
{
RestartGame();
}
}
void StopDialogTimer()
{
Debug.Log("Restart Cancelled");
CancelInvoke("StartDialogTimer");
Destroy(timerDialogBoxInstance);
}
void RestartGame()
{
SceneManager.LoadScene(startingScene.name);
}
}
You initialize bad your s variable.
float s = countdownLength--;
On declaration s = 0.0f - 5 ===> -5 first value
You never reach the -1 value to restart your game.
A way to reach is changing this:
if (s <= -1)
{
RestartGame();
}
I am using the Unity Engine to make a 2D game... to give you an idea of what it looks like, there is a generic space background, 4 rocks bouncing around the screen, and a tie fighter. My goal was to make the tie fighter explode (Which I did succeed in doing), destroy itself, and have a prefab take its place. I am new to c#, so I don't know much of the API. I tried using the second script to destroy the tie fighter, then instantiate a prefab... Now, whenever I run the game, it spawns enough clones to the point where Unity crashes and I do not know how to fix it. I tried googling stuff and doing a manual fix (hence the bools), but nothing seems to be working. Any help would be greatly appreciated. Also, please don't just comment; write it as an answer so that I can mark it correct if it works. Here are the scripts (I am assuming the error is in the second one, but I included both for reference):
First Script:
using UnityEngine;
using System.Collections;
public class tfScript : MonoBehaviour
{
Vector3 tfPos;
Vector3 worldPos;
float mousePosInBlocksx;
float mousePosInBlocksy;
int lives;
public Sprite tieFight; // Drag your first sprite here
public Sprite kaboom; // Drag your second sprite here
private SpriteRenderer spriteRenderer;
float makeThingsGoBoom;
// Use this for initialization
public void Start ()
{
tfPos = new Vector3 (3f, 3f, -4f);
lives = 20;
spriteRenderer = GetComponent<SpriteRenderer>(); // we are accessing the SpriteRenderer that is attached to the Gameobject
if (spriteRenderer.sprite == null) // if the sprite on spriteRenderer is null then
{
spriteRenderer.sprite = tieFight; // set the sprite to sprite1
}
}
// Update is called once per frame
public void Update ()
{
GameObject controller = GameObject.Find ("controller");
gameController gameCon = controller.GetComponent<gameController> ();
mousePosInBlocksx = ((Input.mousePosition.x / Screen.width) * 16);
mousePosInBlocksy = ((Input.mousePosition.y / Screen.width) * 12)+2;
tfPos.x = Mathf.Clamp (mousePosInBlocksx, .5f, 15.5f);
tfPos.y = Mathf.Clamp (mousePosInBlocksy, .5f, 11.5f);
this.transform.position = tfPos;
if (makeThingsGoBoom == 0)
{
gameCon.Update();
}
}
public void ChangeTheDarnSprite ()
{
if (spriteRenderer.sprite == tieFight) { // if the spriteRenderer sprite = sprite1 then change to sprite2
spriteRenderer.sprite = kaboom;
}
else
{
spriteRenderer.sprite = tieFight;
}
}
public void OnCollisionEnter2D (Collision2D collider)
{
if (collider.gameObject.name.Contains("spacerock") )
{
lives--;
print (getLives ());
}
if (collider.gameObject.name.Contains("spacerock")) // If the space bar is pushed down
{
spriteRenderer.sprite = kaboom;
makeThingsGoBoom = 0;
}
}
public void increaseLives()
{
lives++;
}
public double getLives()
{
return lives;
}
}
Second Script:
using UnityEngine;
using System.Collections;
public class gameController : MonoBehaviour
{
public GameObject tf;
public GameObject tfpf;
public bool iBlowedUp = false;
public void Start()
{
}
public void Update ()
{
boom ();
}
public void boom()
{
iBlowedUp = true;
if (iBlowedUp = true)
{
StartCoroutine (waitForIt ());
Destroy (tf);
tfpf = Instantiate (Resources.Load ("Prefabs/tfpf")) as GameObject;
iBlowedUp = false;
}
}
public IEnumerator waitForIt()
{
print ("Bob lives #2!");
yield return new WaitForSeconds (1);
print ("John is a turtle #2!");
}
}
You are calling following method in a Update function which is executed constantly.
public void boom()
{
iBlowedUp = true;
if (iBlowedUp = true)
{
StartCoroutine (waitForIt ());
Destroy (tf);
tfpf = Instantiate (Resources.Load ("Prefabs/tfpf")) as GameObject;
iBlowedUp = false;
}
}
An if iBlowedUp = true; if (iBlowedUp = true){ doesn't make sense, because the statement is true always.
It should be similar to:
public void boom()
{
if (iBlowedUp == true)
{
iBlowedUp = false;
StartCoroutine (waitForIt ());
Destroy (tf);
tfpf = Instantiate (Resources.Load ("Prefabs/tfpf")) as GameObject;
}
}
Probably you want to set iBlowedUp to true somewhere else. As I consider in a tfScript.Update() method, instead of calling Update method.
if (makeThingsGoBoom == 0)
{
gameCon.iBlowedUp = true;
//gameCon.Update();
}
if (iBlowedUp = true)
{
iBlowedUp = false;
StartCoroutine (waitForIt ());
Destroy (tf);
tfpf = Instantiate (Resources.Load ("Prefabs/tfpf")) as GameObject;
}
before instantiate make isBlowedUp false, I cant say ı understood well this "Unity crashes". there is some complexity in your code. I hope you fix them as well