Coroutine not starting due to inactive game object - c#

I'm getting an error message and I'm not exactly sure how to solve. I'm trying to start a countdown after a short period of idleness that then kicks off a second countdown that is paired with a visual warning. As soon as the coroutine kicks on I'm getting this error:
Coroutine couldn't be started because the the game object '_CountdownTimer' is inactive!
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
CountdownTimer:StartPreCountTimer() (at Assets/_Components/_Scripts/CountdownTimer.cs:38)
GameManager:CheckUserActivity() (at Assets/_Components/_Scripts/GameManager.cs:68)
What am I missing? Where would I need to set the active state of _CountdownTimer? Thank you!!
GameManager.cs
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public static GameManager gameManagerInstance = null; // Create Singleton
public float checkUserActivityInterval;
public GameObject loader;
public GameObject countdownTimer;
private GameObject gameManager;
private Vector3 currentMousePosition;
private Vector3 prevMousePosition;
private CountdownTimer countdownInstance;
private Scene currentScene;
public Color defaultBackgroundColor;
public Object startingScene;
public static bool userActive;
public static bool preCountActive;
public static bool restartWarningActive;
public static string animalDataFilePathJSON;
public static string animalDataFilePathTex;
void Awake ()
{
if (CountdownTimer.countdownTimerInstance == null)
Instantiate(countdownTimer);
if (gameManagerInstance == null)
gameManagerInstance = this;
else if (gameManagerInstance != null)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
void Start()
{
prevMousePosition = Input.mousePosition;
countdownInstance = countdownTimer.GetComponent<CountdownTimer>(); // Create an instance of CountdownTimer
InvokeRepeating("CheckUserActivity", 0, checkUserActivityInterval);
InvokeRepeating("SetPrevMousePosition", 0, checkUserActivityInterval);
}
void Update()
{
currentScene = SceneManager.GetActiveScene();
currentMousePosition = Input.mousePosition;
}
void CheckUserActivity()
{
if (currentScene.name != startingScene.name)
{
if (currentMousePosition == prevMousePosition)
{
Debug.Log("MOUSE HAS NOT MOVED!!");
userActive = false;
if (!userActive && !preCountActive)
countdownInstance.StartPreCountTimer();
}
if (currentMousePosition != prevMousePosition)
{
Debug.Log("MOUSE HAS MOVED!!");
userActive = true;
if (preCountActive == true)
countdownInstance.RestartPreCountTimer();
}
}
}
void SetPrevMousePosition()
{
prevMousePosition = Input.mousePosition;
}
}
CountdownTimer.cs
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;
void Awake()
{
if (countdownTimerInstance == null)
countdownTimerInstance = this;
else if (countdownTimerInstance != null)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
public void StartPreCountTimer()
{
GameManager.preCountActive = true;
GameManager.restartWarningActive = false;
counter = RunTimer(countdownDelay); // create new reference to counter
StartCoroutine(counter);
}
public void RestartPreCountTimer()
{
GameManager.preCountActive = false;
StopTimer();
}
void ShowRestartWarning()
{
GameManager.preCountActive = false;
GameManager.restartWarningActive = true;
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(StopTimer); // add button listener
if (timerDialogBoxInstance.activeInHierarchy == true)
{
counter = RunTimer(countdownLength); // create new reference to counter, resets countdown to countdownLength
StartCoroutine(counter);
}
}
IEnumerator RunTimer(float seconds)
{
float s = seconds;
while (s > -1)
{
if (GameManager.restartWarningActive == true)
if (timerTextField != null)
timerTextField.text = s.ToString();
yield return new WaitForSeconds(countdownInterval);
s -= countdownInterval;
}
if (s == -1)
{
if (GameManager.restartWarningActive == true)
RestartGame();
}
}
void StopTimer()
{
Debug.Log("Restart Cancelled");
StopCoroutine(counter);
Destroy(timerDialogBoxInstance);
}
void RestartGame()
{
SceneManager.LoadScene(startingScene.name);
}
}

I think I know where your error is. When you create an instance of the countdownTimer. You have to store a reference to it in order to get the underlying CountdownTimer class.
Try doing this, in your GameManager Class
private GameObject countDownTimerInstance;
Awake()
countDownTimerInstance = Instantiate(countdownTimer);
and in the Start() do,
countdownInstance = countdownTimerInstance.GetComponent<CountdownTimer>();
I think the problem was you were directly accessing the prefab's getComponent() instead of the instantiated gameObject's CountdownTimer!.

Related

List array unity

I have a ObjectController script that looking for game objects and adding them to an array and another script that draws outline around those game objects. There're few tasks that I'm trying to achieve:
From ObjectController script List array I want to check what object is currently selected (clicked) so i won't be able to select (click) on other objects.
onMouseButtonDown(1) i want to clear selections (outline) from those objects.
Can you please guide me in the right direction?
I'm new to coding so please, go easy on me :D
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ObjectController : MonoBehaviour
{
public List<SpriteOutline> objects;
private void Start()
{
List<SpriteOutline> objectList = FindObjectsOfType<SpriteOutline>().ToList<SpriteOutline>();
for (int i = 0; i < objectList.Count; i++)
{
objects.Add(objectList[i]);
}
}
public List<SpriteOutline> GetList()
{
return objects;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class SpriteOutline : MonoBehaviour {
public Color OutlineColor = Color.white;
private Color _currentColor = Color.clear;
[Range(0, 16)]
public int outlineSize = 1;
private bool clicked = false;
private SpriteRenderer spriteRenderer;
ObjectController objController;
IEnumerator Start()
{
objController = GameObject.Find("ObjectController").GetComponent<ObjectController>();
yield return new WaitForEndOfFrame();
for (int i = 0; i < objController.GetList().Count; i++)
{
Debug.Log(i);
}
}
void OnEnable() {
spriteRenderer = GetComponent<SpriteRenderer>();
UpdateOutline(true);
}
void OnDisable() {
UpdateOutline(false);
}
void Update() {
UpdateOutline(true);
onMouseDown();
}
private void onMouseDown()
{
Vector3 mousePos;
mousePos = Input.mousePosition;
mousePos = Camera.main.ScreenToWorldPoint(mousePos);
CapsuleCollider2D coll = GetComponent<CapsuleCollider2D>();
if (Input.GetMouseButtonDown(0))
{
if (coll.OverlapPoint(mousePos))
{
_currentColor = Color.clear;
if (!clicked)
{
_currentColor = OutlineColor;
clicked = true;
}
else
{
clicked = false;
}
}
}
if (Input.GetMouseButtonDown(1))
{
_currentColor = Color.clear;
}
}
void UpdateOutline(bool outline) {
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
spriteRenderer.GetPropertyBlock(mpb);
mpb.SetFloat("_Outline", outline ? 1f : 0);
mpb.SetColor("_OutlineColor", _currentColor);
mpb.SetFloat("_OutlineSize", outlineSize);
spriteRenderer.SetPropertyBlock(mpb);
}
}
hatebin
hatebin
A bit strange for me that you go through a controller class just to again find all instances of the actual class ;)
First you could simplify your code a lot:
public class ObjectController : MonoBehaviour
{
// The list is already public so no need to have a getter method ...
public List<SpriteOutline> objects;
private void Start()
{
// FindObjectsOfType returns an SpriteOutline[]
// so the easiest way of converting it to a list is
objects = FindObjectsOfType<SpriteOutline>().ToList();
}
}
Then you would later simply do
FindObjectOfType<ObjectController>().objects
However this isn't even necessary!
Instead of this manager/Singleton pattern here I would rather use a static and let your object instances register themselves and let your class handle it completely itself:
public class SpriteOutline : MonoBehaviour
{
[SerializeField] private Color OutlineColor = Color.white;
[SerializeField] [Range(0, 16)] private int outlineSize = 1;
[SerializeField] private SpriteRenderer _spriteRenderer;
// Here you actually register and unregisters instances
private static readonly List<SpriteOutline> _instances = new List<SpriteOutline>();
// If even needed public read-only access
public ReadOnlyCollection<SpriteOutline> Instances => _instances.AsReadOnly();
// The current selection
private static readonly HashSet<SpriteOutline> _currentSelection = new HashSet<SpriteOutline>();
// Again if needed a public read-only access
public static HashSet<SpriteOutline> CurrentSelection => new HashSet<SpriteOutline>(_currentSelection);
// backing field for storing the actual value of selected
private bool _isSelected;
// Property which additionally updates the outline when changed
public bool IsSelected
{
get => _isSelected;
set
{
if(value == _isSelected) return;
_isSelected = value;
if(value)
{
_currentSelection.Add(this);
}
else
{
_currentSelection.Remove(this);
}
UpdateOutline(enabled, value);
}
}
private void Awake()
{
if(!_spriteRenderer) _spriteRenderer = GetComponent<SpriteRenderer>();
// Register yourself
_instances.Add(this);
}
private void OnEnable()
{
UpdateOutline (true, _isSelected);
}
private void OnDisable()
{
UpdateOutline (false, _isSelected);
}
private void OnDestroy ()
{
// Unregister yourself
_instances.Remove(this);
if(_currentSelection.Contains(this)) _currentSelection.Remove(this);
}
// This simplifies your query a lot since this is anyway only called
// If the mouse is hovering the collider of this object
private void OnMouseDown()
{
// Toggle selection of this
if (Input.GetMouseButtonDown(0))
{
IsSelected = !IsSelected;
}
}
private void Update()
{
// Deselect this
if (Input.GetMouseButtonDown(1))
{
IsSelected = false;
}
}
void UpdateOutline(bool outline, bool selected)
{
var mpb = new MaterialPropertyBlock();
_spriteRenderer.GetPropertyBlock(mpb);
mpb.SetFloat("_Outline", outline ? 1f : 0);
mpb.SetColor("_OutlineColor", selected ? OutlineColor : Color.clear);
mpb.SetFloat("_OutlineSize", outlineSize);
spriteRenderer.SetPropertyBlock(mpb);
}
}
If you rather want to select only exactly one object at a time then I would exchange the HashSet<SpriteOutline> _currentSelection by something like
// The current selection
private static SpriteOutline _currentSelection;
// Again if needed a public read-only access
public static SpriteOutline CurrentSelection => _currentSelection;
accordingly change
public bool IsSelected
{
get => _isSelected;
set
{
if(value == _isSelected) return;
// assuming that if you set this via property it should
// overwrite the current selection
// otherwise you would here do
//if(_currentSelection && _currentSelection != this) return;
if(_currentSelection && _currentSelection != this) _currentSelection.IsSelected = false;
_isSelected = value;
_currentSelection = value ? this : null;
UpdateOutline(enabled, value);
}
}
and finally you wanted to block the input on objects that are not the selection:
private void OnMouseDown()
{
// Toggle selection of this
if (Input.GetMouseButtonDown(0))
{
if(_currentSelection && _currentSelection != this) return;
IsSelected = !IsSelected;
}
}
Note: Typed on smartphone but I hope the idea gets clear

Passing data between scenes Errors

I have made a game manager making sure my data gets passed on from the first scene on to the next scene. Within the game manager, I have added certain scripts to ensure it gets passed. As you can see in the picture underneath.
The problem is that the score adds up at the first level, let's say I have 5 points. I go to level 2 and the UI shows my score as 0 (even tho I have nothing put as text within the score text) I kill 1 monster and the UI shows 6. So how can I put the UI to be showing it at all times? (Constant refresh?)
The second problem is that while the score manager does work. The health script cancels everything out when switching levels.
The user starts with 10 health. Takes damage in the first scene, but in the second scene, the user still has 10 health for some reason?
Game Manager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
public ActionButton PopupPrefab;
private ActionButton currentlySpawnedPopup;
public static GameManager instance = null;
void Awake () {
if (instance == null) {
instance = this;
} else if (instance != this) {
Destroy (gameObject);
}
Application.targetFrameRate = 60;
}
void Update () {
//if (Input.GetKeyDown(KeyCode.R)) {
// RestartScene ();
//}
}
public void InvokeRestartScene (float time) {
Invoke ("RestartScene", time);
}
public void RestartScene () {
SceneManager.LoadScene (0);
}
public void SpawnPopup (Vector2 position) {
DespawnPopup ();
currentlySpawnedPopup = Instantiate (PopupPrefab, position, Quaternion.identity) as ActionButton;
}
public void DespawnPopup () {
if (currentlySpawnedPopup != null) {
currentlySpawnedPopup.DestroySelf();
currentlySpawnedPopup = null;
}
}
public void FadePopup () {
if (currentlySpawnedPopup != null) {
currentlySpawnedPopup.FadeMe ();
currentlySpawnedPopup = null;
}
}
}
Score Manager
using UnityEngine;
using System;
using System.Collections;
public class ScoreManager : MonoBehaviour
{
public static ScoreManager Instance { get; private set; }
public int Score { get; private set; }
public int HighScore { get; private set; }
public bool HasNewHighScore { get; private set; }
public static event Action<int> ScoreUpdated = delegate {};
public static event Action<int> HighscoreUpdated = delegate {};
private const string HIGHSCORE = "HIGHSCORE";
// key name to store high score in PlayerPrefs
void Awake()
{
if (Instance)
{
DestroyImmediate(gameObject);
}
else
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
void Start()
{
Reset();
}
public void Reset()
{
// Initialize score
Score = 0;
// Initialize highscore
HighScore = PlayerPrefs.GetInt(HIGHSCORE, 0);
HasNewHighScore = false;
}
public void AddScore(int amount)
{
Score += amount;
// Fire event
ScoreUpdated(Score);
if (Score > HighScore)
{
UpdateHighScore(Score);
HasNewHighScore = true;
}
else
{
HasNewHighScore = false;
}
}
public void UpdateHighScore(int newHighScore)
{
// Update highscore if player has made a new one
if (newHighScore > HighScore)
{
HighScore = newHighScore;
PlayerPrefs.SetInt(HIGHSCORE, HighScore);
HighscoreUpdated(HighScore);
}
}
}
Health Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class Health : MonoBehaviour {
public UnityEvent OnTakeDamageEvent;
public UnityEvent OnTakeHealEvent;
public UnityEvent OnDeathEvent;
[Header ("Max/Starting Health")]
public int maxHealth;
[Header ("Current Health")]
public int health;
[Header ("IsDeathOrNot")]
public bool dead = false;
[Header ("Invincible")]
public bool invincible = false;
public bool becomeInvincibleOnHit = false;
public float invincibleTimeOnHit = 1f;
private float invincibleTimer = 0f;
[Header ("Perform Dead Events after x time")]
public float DieEventsAfterTime = 1f;
void Start () {
health = maxHealth;
}
void Update () {
if (invincibleTimer > 0f) {
invincibleTimer -= Time.deltaTime;
if (invincibleTimer <= 0f) {
if (invincible)
invincible = false;
}
}
}
public bool TakeDamage (int amount) {
if (dead || invincible)
return false;
health = Mathf.Max (0, health - amount);
if (OnTakeDamageEvent != null)
OnTakeDamageEvent.Invoke();
if (health <= 0) {
Die ();
} else {
if (becomeInvincibleOnHit) {
invincible = true;
invincibleTimer = invincibleTimeOnHit;
}
}
return true;
}
public bool TakeHeal (int amount) {
if (dead || health == maxHealth)
return false;
health = Mathf.Min (maxHealth, health + amount);
if (OnTakeHealEvent != null)
OnTakeHealEvent.Invoke();
return true;
}
public void Die () {
dead = true;
if (CameraShaker.instance != null) {
CameraShaker.instance.InitShake(0.2f, 1f);
}
StartCoroutine (DeathEventsRoutine (DieEventsAfterTime));
}
IEnumerator DeathEventsRoutine (float time) {
yield return new WaitForSeconds (time);
if (OnDeathEvent != null)
OnDeathEvent.Invoke();
}
public void SetUIHealthBar () {
if (UIHeartsHealthBar.instance != null) {
UIHeartsHealthBar.instance.SetHearts (health);
}
}
}
I have thought of adding the following script on to my Health Script
But then I get the following error messages:
" Cannot implicitly convert type 'int' to 'bool'"
"The left-hand side of an assignment must be a variable, property or indexer"
void Awake()
{
if (health)
{
DestroyImmediate(gameObject);
}
else
{
(int)health = this;
DontDestroyOnLoad(gameObject);
}
}
The problem is that the score adds up at the first level, let's say I have 5 points. I go to level 2 and the UI shows my score as 0 (even tho I have nothing put as text within the score text) I kill 1 monster and the UI shows 6. So how can I put the UI to be showing it at all times? (Constant refresh?)
You can make one of the scripts set the UI text score when the scene is loaded.
void Start(){
// Loads the scoreText on start
scoreText.text = yourCurrentScore.ToString();
// Will work unless it has a "DontDestroyOnLoad" applied to it
}
The second problem is that while the score manager does work. The
health script cancels everything out when switching levels. The user
starts with 10 health. Takes damage in the first scene, but in the
second scene, the user still has 10 health for some reason?
In your health script, you had this:
void Start () {
health = maxHealth;
}
This resets your health everytime the scene loads and starts (Aka when you load to the next level). This causes the issue.
" Cannot implicitly convert type 'int' to 'bool'"
if (health)
The () for the if statement should be a condition (a question).
For example, doing health < 0 is valid since its saying "Is health less than 0?"
Doing health is not, since its just saying "10" (or some number).
"The left-hand side of an assignment must be a variable, property or
indexer"
(int)health = this;
If you wanted to change the value of health, just do health = 10 or health = some_Variable_That_Is_An_Integer

Why only from the second door the door close ? The first one stay opened

The first script and the second both attached to the doors.
In this case I have 12 doors.
No matter what the first door of the 12 the player controller or a NPC enter the door it will open but stay opened. The next door the player controller or the NPC will enter will open and then also will be closed and then all the doors.
But each time the first door never close. It's working only from the second door each time when running the game.
On this script HoriDoorManager I'm using public static flag exitedDoor and set it to true inside the OnTriggerExit:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class HoriDoorManager : MonoBehaviour
{
public static bool exitedDoor = false;
private bool doorLockState;
private List<DoorHori> doors = new List<DoorHori>();
private void Start()
{
if (transform.parent != null)
{
Transform parent = transform.parent;
var children = parent.GetComponentsInChildren<Transform>();
if(children != null)
{
foreach (Transform door in children)
{
if (door.name == "Door_Left" || door.name == "Door_Right")
doors.Add(door.GetComponent<DoorHori>());
}
}
}
}
void OnTriggerEnter()
{
if (doorLockState == false)
{
if (doors != null)
{
for(int i =0; i < doors.Count; i++)
{
doors[i].OpenDoor();
}
}
}
}
private void OnTriggerExit(Collider collide)
{
if (doorLockState == false)
{
exitedDoor = true;
}
}
public void ChangeLockState(bool lockState)
{
doorLockState = lockState;
}
}
In the second script I'm checking if the flag exitedDoor is true and then start closing the door: Inside the method WaitToClose:
using UnityEngine;
using System.Collections;
public class DoorHori : MonoBehaviour
{
public float translateValue;
public float easeTime;
public OTween.EaseType ease;
public float waitTime;
private Vector3 StartlocalPos;
private Vector3 endlocalPos;
private void Start()
{
StartlocalPos = transform.localPosition;
gameObject.isStatic = false;
}
public void OpenDoor()
{
OTween.ValueTo(gameObject, ease, 0.0f, -translateValue, easeTime, 0.0f, "StartOpen", "UpdateOpenDoor", "EndOpen");
GetComponent<AudioSource>().Play();
}
private void UpdateOpenDoor(float f)
{
Vector3 pos = transform.TransformDirection(new Vector3(1, 0, 0));
transform.localPosition = StartlocalPos + pos * f;
}
private void UpdateCloseDoor(float f)
{
Vector3 pos = transform.TransformDirection(new Vector3(-f, 0, 0));
transform.localPosition = endlocalPos - pos;
}
private void EndOpen()
{
endlocalPos = transform.localPosition;
StartCoroutine(WaitToClose());
}
private IEnumerator WaitToClose()
{
if (HoriDoorManager.exitedDoor == true)
{
yield return new WaitForSeconds(waitTime);
OTween.ValueTo(gameObject, ease, 0.0f, translateValue, easeTime, 0.0f, "StartClose", "UpdateCloseDoor", "EndClose");
GetComponent<AudioSource>().Play();
}
}
}
Try changing the OnTriggerEnter to this in your first script.
private void OnTriggerEnter(Collider collide){
if (doorLockState == false)
{
if (doors != null)
{
for(int i =0; i < doors.Count; i++)
{
doors[i].OpenDoor();
}
}
}
}
You are not using unity's defined method when you remove the parameters of the method, so it is no longer referencing the same OnTriggerEnter.
It can then also be used to check what is triggering the on enter flag, because I am assuming you don't want any collisions to trigger this logic.

Call function from another script only once in Update

I'm having trouble with some countdown timer functionality. In my GameManager script I'm checking the scene name and checking for idle usage (no mouse movement/no clicking) and kicking off a function in my CountdownTimer script that is counts down the amount of idle time using the function StartPreCountTimer(). Once that timer reaches zero it then instantiates a countdown dialog and kicks off IEnumerator RunTimer() that the user can either cancel to continue playing or let reach zero, at which point the game resets and returns to the intro screen.
The problem I'm having is at the precount step. The GameManager script keeps calling StartPreCountTimer() every frame which obviously prevents the countdown from ever completing because it restarts every frame. How do I fix this? How do a call the function only once from Update?
GameManager Script
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public static GameManager gameManagerInstance = null; // Create Singleton
public GameObject loader;
public GameObject countdownTimer;
public Color defaultBackgroundColor;
public Object startingScene;
public GameObject timeOutWarningDialog;
public float countdownLength;
public float countdownDelay;
private Vector3 prevMousePosition;
private CountdownTimer countdownInstance;
private Scene currentScene;
private GameObject gameManager;
private GameObject canvas;
public static string animalDataFilePathJSON;
public static string animalDataFilePathTex;
void Awake()
{
if (gameManagerInstance == null)
gameManagerInstance = this;
else if (gameManagerInstance != null)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
gameManager = GameObject.FindGameObjectWithTag("GameManager");
// get and store JSON and Tex filepaths defined in Loader script
animalDataFilePathJSON = GameObject.FindGameObjectWithTag("Loader").GetComponent<Loader>().animalDataFilePathJSON;
animalDataFilePathTex = GameObject.FindGameObjectWithTag("Loader").GetComponent<Loader>().animalDataFilePathTex;
}
void Start()
{
prevMousePosition = Input.mousePosition;
currentScene = SceneManager.GetActiveScene();
countdownInstance = countdownTimer.GetComponent<CountdownTimer>(); // Create an instance of CountdownTimer
}
void Update()
{
currentScene = SceneManager.GetActiveScene();
if (currentScene.name != startingScene.name)
{
countdownInstance.StartPreCountTimer(); // Start Pre-count Timer
if (Input.GetMouseButtonDown(0) || Input.mousePosition != prevMousePosition)
{
countdownInstance.StartPreCountTimer(); // Start Pre-count Timer
if (timeOutWarningDialog != null)
timeOutWarningDialog.SetActive(false);
}
prevMousePosition = Input.mousePosition;
}
}
}
CountdownTimer Script
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class CountdownTimer : MonoBehaviour {
public GameObject timeOutWarningDialog;
public float countdownLength;
public float countdownDelay;
public Object startingScene;
private float countdownInterval = 1;
private GameObject countdownTimer;
private IEnumerator counter;
private Button stopCountButton;
private Text timerTextField;
private GameObject timerInstance;
private GameObject canvas;
// GAME TIMER
public void StartPreCountTimer()
{
CancelInvoke();
if (GameObject.FindGameObjectWithTag("Timer") == null)
Invoke("ShowRestartWarning", countdownDelay);
}
void ShowRestartWarning()
{
canvas = GameObject.FindGameObjectWithTag("Canvas");
timerInstance = Instantiate(timeOutWarningDialog); // instantiate timeout warning dialog
timerInstance.transform.SetParent(canvas.transform, false);
timerInstance.SetActive(true);
Text[] textFields = timerInstance.GetComponentsInChildren<Text>(true); // get reference to timer textfields
timerTextField = textFields[2]; // access and assign countdown textfield
stopCountButton = timerInstance.GetComponentInChildren<Button>(); // get reference to keep playing button
stopCountButton.onClick.AddListener(StopTimer); // add button listener
if (timerInstance.activeSelf == true)
{
counter = RunTimer(countdownLength); // create new reference to counter, resets countdown to countdownLength
StartCoroutine(counter);
}
}
IEnumerator RunTimer(float seconds)
{
float s = seconds;
while (s > -1)
{
if (timerTextField != null)
timerTextField.text = s.ToString();
yield return new WaitForSeconds(countdownInterval);
s -= countdownInterval;
}
if (s == -1)
{
RestartGame();
}
}
void StopTimer()
{
StopCoroutine(counter);
Destroy(timerInstance);
}
void RestartGame()
{
SceneManager.LoadScene(startingScene.name);
}
}

Coroutine couldn't be started because the the game object is inactive

I'm getting an error of "Coroutine couldn't be started because the the game object 'TimeOutWarningDialog' is inactive!" but I'm unsure why I'm getting this error.
Just to give a rundown of the code:
I'm looking for inactivity in GameManger.Update()
If inactive for a period of time I call GameManager.ShowRestartWarning()
TimeOutWarningDialog gets SetActive to true
I check if the object is active before calling StartRestartTimer(), if (timerInstance.activeSelf == true) StartRestartTimer();
I call startTimer() in CountdownTimer class
I'm setting the object that I'm instatiating to 'active' before I call the startTimer function which includes the coroutine. what am I doing wrong here?
any help would be great!!
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
// Create Singleton
public static GameManager instance = null;
// Set Default Background Color
public Color defaultColor;
// Restart variables
private Vector3 prevMousePosition;
public GameObject timeOutWarningDialog;
public GameObject restartDialog;
public float countdownLength;
public float timeUntilCountdown;
// Game Controller
private GameObject canvas;
private GameObject gameManager;
public GameObject timerInstance;
public Object startingScene;
private Scene currentScene;
// File System List of Folders
public List<string> folders;
void Awake()
{
if (instance == null)
instance = this;
else if (instance != 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)
if(currentScene.name != startingScene.name)
StartGameTimer();
prevMousePosition = Input.mousePosition;
}
// GAME TIMER
void StartGameTimer()
{
// Debug.Log("Game Timer Started");
CancelInvoke();
if (GameObject.FindGameObjectWithTag("Timer") == null)
Invoke("ShowRestartWarning", timeUntilCountdown);
}
void ShowRestartWarning()
{
canvas = GameObject.FindGameObjectWithTag("Canvas");
timerInstance = Instantiate(timeOutWarningDialog);
timerInstance.transform.SetParent(canvas.transform, false);
timerInstance.SetActive(true);
if (timerInstance.activeSelf == true)
StartRestartTimer();
}
void StartRestartTimer()
{
CountdownTimer countdownTimer = timeOutWarningDialog.GetComponent<CountdownTimer>();
countdownTimer.startTimer(countdownLength);
CancelInvoke();
Invoke("RestartGame", countdownLength);
}
void RestartGame()
{
SceneManager.LoadScene(startingScene.name);
Debug.Log("Game Restarted");
Debug.Log("Current Scene is " + currentScene.name + ".");
}
void DestroyTimer()
{
Destroy(GameObject.FindGameObjectWithTag("Timer"));
}
}
then I'm calling startTimer in the CountdownTimer class below:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class CountdownTimer : MonoBehaviour
{
public float countdownLength;
public Text timerText;
public bool stop = true;
private float minutes;
private float seconds;
public void startTimer(float from)
{
stop = false;
countdownLength = from;
Update();
StartCoroutine(updateCoroutine());
}
void Update()
{
if (stop) return;
countdownLength -= Time.deltaTime;
minutes = Mathf.Floor(countdownLength / 60);
seconds = countdownLength % 60;
if (seconds > 59) seconds = 59;
if (minutes < 0)
{
stop = true;
minutes = 0;
seconds = 0;
}
}
private IEnumerator updateCoroutine()
{
while (!stop)
{
timerText.text = string.Format("{0:0}:{1:00}", minutes, seconds);
yield return new WaitForSeconds(0.2f);
Debug.Log(string.Format("{0:0}:{1:00}", minutes, seconds));
}
}
}
The problem is in this method:
void StartRestartTimer()
{
CountdownTimer countdownTimer = timeOutWarningDialog.GetComponent<CountdownTimer>();
countdownTimer.startTimer(countdownLength);
CancelInvoke();
Invoke("RestartGame", countdownLength);
}
You start the coroutine first and then invoke RestartGame to load another scene. So the object with the coroutine gets destroyed.
I can't give you the solution because it requires more knowledge regarding your scenes but you may want to try additive scene loading.

Categories