Im making a parkour game and im trying to get a stopwatch to time the player.
i have a canvas with the crosshair of the player and the stopwatch there and when switching between scenes, i use a singleton pattern to only have one canvas.
this is the code that i am using to go to the next level
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
public class NxtLvl : MonoBehaviour
{
public GameObject carrotGFX;
public GameObject panel;
public bool gameOver;
public Timer timer;
public TMP_Text timeText;
[SerializeField]
private Canvas canvas;
public GameObject crosshairTimerCanvas;
Vector3 timerStartPos;
void Awake()
{
timerStartPos = timer.transform.position;
canvas.gameObject.SetActive(false);
}
void OnTriggerEnter(Collider other)
{
gameOver = true;
canvas.gameObject.SetActive(true);
carrotGFX.SetActive(false);
timer.transform.SetParent(panel.transform);
timer.transform.position = new Vector3(771, 400, 0);
timer.StopCoroutine("StopWatch");
Debug.Log("Stopwatch paused");
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
public void NextLevel(string scene)
{
Debug.Log("Next Level");
SceneManager.LoadScene(scene);
gameOver = false;
canvas.gameObject.SetActive(false);
timer = GameObject.FindGameObjectWithTag("CrosshairTimer");
timer.transform.SetParent(crosshairTimerCanvas.transform);
timeText.gameObject.SetActive(true);
timer.transform.position = timerStartPos;
Debug.Log("Waiting");
Debug.Log("Wait time complete, resuming stopwatch");
timer.StartCoroutine("StopWatch");
Debug.Log("Stopwatch resumed");
}
}
FindGameObjectWithTag returns a gameobject, not a Timer. If you want the Timer component, you need to use GetComponent().
Next, you donât reference things from other scenes if youâre just switching scenes. If something is a Singleton, itâs usually floating around and you need to access it through the Singleton script. Like. Singleton.Instance.Whatever, which is sort of the point of a singleton.
timer = GameObject.FindGameObjectWithTag(âCrosshairTimerâ).GetComponent();
will get the Timer script attached to the CrosshairTimer-tagged GameObject.
now it says
NullReferenceException: Object reference not set to an instance of an object
NxtLvl.NextLevel (System.String scene) (at Assets/Scripts/NxtLvl.cs:64)
ok so what happens now is that the timer gets put under tha panel in scene 1 so when i go to scene 2, the timer is still in panel in scene 1 so how do i get timer to move from scene 1 to scene 2 crosshair timer canvas
That most likely means the GetComponent call on line 59 is returning null. GetComponent is designed to return null when the GameObject doesnât have an active component of that type attached. The line with the error is the first time youâre trying to use the reference returned by the GetComponent call, which would be why you see the null reference error on that line.
edit: Also, I see in the earlier code you are calling LoadScene. If the timer is supposed to be in the new scene, and not the old scene, note that this wonât work. LoadScene doesnât complete immediately. It schedules the scene to be loaded after the current frame update completes. So all code for this frame will execute still in the old scene, even after LoadScene is called. (If you were already aware of that, then ignore this comment)
i fixed it so it works but when i finish level 2, the timer doesnt stop. i think this is because i use the same timer in all my scenes and it has dontdestroyonload on it.
in the gamemanager/carrot of level 2, i cant reference the timer through code bc its in the dontdestroyonload scene
FindGameObjectWithTag will find the GameObject across all active scenes, including the DontDestroyOnLoad scene. So you must be running into some other problem if youâre unable to reference it.
i edited the code so ik that i dont have a timer so heres what i got
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
public class NxtLvl : MonoBehaviour
{
public GameObject carrotGFX;
public bool gameOver;
public Timer timer;
public TMP_Text timeText;
[SerializeField]
private Canvas canvas;
public GameObject crosshairTimerCanvas;
Vector3 timerStartPos;
void Awake()
{
timerStartPos = timer.transform.position;
canvas.gameObject.SetActive(false);
}
void OnTriggerEnter(Collider other)
{
//Initiate game over and turn on game over canvas
gameOver = true;
canvas.gameObject.SetActive(true);
carrotGFX.SetActive(false);
if(timer != null) //Check if we have a timer or not
{
//if we do have timer, set position to center of the screen
timer.transform.position = new Vector3(771, 400, 0);
}
else
{
Debug.Log("No timer");
return; //If no timer, donât do anything
}
//Stop timer
timer.StopCoroutine("StopWatch");
Debug.Log("Stopwatch paused");
//Show mouse
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
public void NextLevel(string scene)
{
//Load the next level
Debug.Log("Next Level");
SceneManager.LoadScene(scene);
gameOver = false;
//Disable gameover canvas
canvas.gameObject.SetActive(false);
//Enable timer text and resume timer
timeText.gameObject.SetActive(true);
timer.StartCoroutine("StopWatch");
//Set timer text to original position
timeText.transform.position = timerStartPos;
}
}
now i know that i have no timer but when i try to add the getcomponent stuff, it still doesnt work
can i just send u the unity package and u try to edit it to work?
When youâve got the GetComponent stuff in, you should be looking at the scene hierarchy when it fails. Verify there really is a GameObject active in the hierarchy with that specific tag, written exactly. Since youâre using FindGameObjectWithTag, it will only return 1 object with that tag. So you need to also verify there is only exactly 1 GameObject in the entire hierarchy with that tag. If you have another GameObject with that tag assigned, and it doesnât have the Timer component, well that is probably the problem.
After you have verified all that, then verify that 1 GameObject actually has an active Timer component. If youâve verified all of that, then the GetComponent call will not return null. So something in that list of things I just mentioned is the problem.
As for troubleshooting, you can use FindGameObjectsWithTag and check the length of the returned array. If the length of the returned array is anything other than 1, then there is your problem. If the GameObject is in the scene but inactive, then it wonât be found. If the script is attached but inactive then GetComponent wonât find it.
If youâre still having trouble using tags with GetComponent, you can drop this and go an entirely other route. Instead create a static reference to the Timer script, and just use that everywhere you need it.
Example of that last thing:
Inside your Timer class add the following
public static Timer Instance = null;
void Awake()
{
if (Instance == null)
{
Instance = this;
}
else
{
Debug.Log("OOPS! There is already a Timer! Destroying this new one");
Destroy(gameObject);
}
}
Everywhere else you access the Timer via Timer.Instance now instead of your Timer reference youâve been trying to use. No more FindGameObjectWithTag or GetComponent.
the canvas that the timer sits on is marked as crosshaircanvas but it has no timer component, the actual timer text has the component but no tag, do u think this is the problem?
Yeah. When you FindGameObjectWithTag, you get exactly the GameObject with that specific tag. If the Timer component is on a different GameObject, then youâre getting getting the wrong GameObject, which would explain why GetComponent doesnât find an instance of Timer.