Countdown starting at random negative number - c#

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

Related

Object don't exclude from list

Me need exclude object from the list, i make it as other variants, but it don't work? Why?
I make destroy block as minecraft, but i can see just no thing =/ and i make as can doing.
Code:
using System.Collections.Generic;
using System.Collections;
using UnityEngine;
public class GetTarget : MonoBehaviour
{
[Header("Координаты блока")] // positions blocks
public int positionX;
public int positionY;
public int positionZ;
[Header("Получение цели")] // get target
public static List<GameObject> target = new List<GameObject>();
private void OnTriggerStay(Collider collider) // check on touch to trigger
{
if(collider.gameObject.name == "WorldMap") { return; }
else if(collider.gameObject.name == "Water") { return; }
else if(collider.gameObject.name == "Berries") { return; } // this details in the code don't work now.
else
{
target.Insert(0, collider.gameObject);
Debug.Log("Вы получили " + target[0]); // Вы получите = You can have target[0] now
}
}
private void Update() // set round position
{
Vector3 positions = transform.position;
positionX = (int)Mathf.Round(gameObject.transform.position.x);
positionY = (int)Mathf.Round(gameObject.transform.position.y);
positionZ = (int)Mathf.Round(gameObject.transform.position.z);
positions.x = positionX;
positions.y = positionY;
positions.z = positionZ;
}
}
Thank you to everyone!

How can I get reference to none static variables and use them inside static methods?

Or maybe to make the variables static and get reference to them in the editor.
This script sit in my Game scene.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class SceneFader : MonoBehaviour
{
#region FIELDS
public GameObject fadeOutUIGameobjectImage;
public float fadeSpeed = 0.8f;
public bool loaded = false;
private static Image fadeOutUIImage;
private void Start()
{
}
public enum FadeDirection
{
In, //Alpha = 1
Out // Alpha = 0
}
#endregion
#region FADE
public static IEnumerator Fade(FadeDirection fadeDirection)
{
fadeOutUIGameobjectImage.SetActive(true);
float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
if (fadeDirection == FadeDirection.Out)
{
while (alpha >= fadeEndValue)
{
SetColorImage(ref alpha, fadeDirection);
yield return null;
}
fadeOutUIGameobjectImage.SetActive(false);
}
else
{
fadeOutUIGameobjectImage.SetActive(true);
while (alpha <= fadeEndValue)
{
SetColorImage(ref alpha, fadeDirection);
yield return null;
}
}
}
#endregion
#region HELPERS
public static IEnumerator FadeAndLoadSceneNewGame(FadeDirection fadeDirection, string sceneToLoad)
{
yield return Fade(fadeDirection);
loaded = false;
SceneManager.LoadScene(sceneToLoad);
}
public static IEnumerator FadeAndLoadSceneLoadGame(FadeDirection fadeDirection, string sceneToLoad)
{
yield return Fade(fadeDirection);
loaded = true;
SceneManager.LoadScene(sceneToLoad);
var saveLoad = GameObject.Find("Save System").GetComponent<SaveLoad>();
saveLoad.Load();
}
private static void SetColorImage(ref float alpha, FadeDirection fadeDirection)
{
if(fadeOutUIImage == null)
{
fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
}
fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
}
#endregion
}
Now the variables in the tope are public but not static so I can't use them in the rest of the script and if I make them static I can't reference to them in the editor I tried to use Find in the Start when they are static but they are null.
And this script sit in my Main Menu scene I thought make the two methods in the Game scene public static will be easier to call them from the Main Menu scene :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEditor;
using Cinemachine;
using UnityStandardAssets.Characters.ThirdPerson;
public class MenuController : MonoBehaviour
{
#region Default Values
[Header("Default Menu Values")]
[SerializeField] private float defaultVolume;
[SerializeField] private int defaultSen;
[SerializeField] private bool defaultInvertY;
[Header("Levels To Load")]
public string _newGameButtonLevel;
private string levelToLoad;
public GameObject player;
private int menuNumber;
#endregion
#region Menu Dialogs
[Header("Main Menu Components")]
[SerializeField] private GameObject menuDefaultCanvas;
[SerializeField] private GameObject GeneralSettingsCanvas;
[SerializeField] private GameObject graphicsMenu;
[SerializeField] private GameObject soundMenu;
[SerializeField] private GameObject controlsMenu;
[SerializeField] private GameObject confirmationMenu;
[Space(10)]
[Header("Menu Popout Dialogs")]
[SerializeField] private GameObject noSaveDialog;
[SerializeField] private GameObject newGameDialog;
[SerializeField] private GameObject loadGameDialog;
#endregion
#region Slider Linking
[Header("Menu Sliders")]
[SerializeField] private Text controllerSenText;
[SerializeField] private Slider controllerSenSlider;
public float controlSenFloat = 2f;
[Space(10)]
[SerializeField] private Text volumeText;
[SerializeField] private Slider volumeSlider;
[Space(10)]
[SerializeField] private Toggle invertYToggle;
#endregion
#region Initialisation - Button Selection & Menu Order
private void Start()
{
menuNumber = 1;
}
#endregion
//MAIN SECTION
public IEnumerator ConfirmationBox()
{
confirmationMenu.SetActive(true);
yield return new WaitForSeconds(2);
confirmationMenu.SetActive(false);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (menuNumber == 2 || menuNumber == 7 || menuNumber == 8)
{
GoBackToMainMenu();
ClickSound();
}
else if (menuNumber == 3 || menuNumber == 4 || menuNumber == 5)
{
GoBackToOptionsMenu();
ClickSound();
}
else if (menuNumber == 6) //CONTROLS MENU
{
ClickSound();
}
}
}
private void ClickSound()
{
GetComponent<AudioSource>().Play();
}
#region Menu Mouse Clicks
public void MouseClick(string buttonType)
{
if (buttonType == "Controls")
{
controlsMenu.SetActive(true);
menuNumber = 6;
}
if (buttonType == "Graphics")
{
GeneralSettingsCanvas.SetActive(false);
graphicsMenu.SetActive(true);
menuNumber = 3;
}
if (buttonType == "Sound")
{
GeneralSettingsCanvas.SetActive(false);
soundMenu.SetActive(true);
menuNumber = 4;
}
if (buttonType == "Exit")
{
Debug.Log("YES QUIT!");
Application.Quit();
}
if (buttonType == "Options")
{
menuDefaultCanvas.SetActive(false);
GeneralSettingsCanvas.SetActive(true);
menuNumber = 2;
}
if (buttonType == "LoadGame")
{
menuDefaultCanvas.SetActive(false);
loadGameDialog.SetActive(true);
menuNumber = 8;
}
if (buttonType == "NewGame")
{
menuDefaultCanvas.SetActive(false);
newGameDialog.SetActive(true);
menuNumber = 7;
}
}
#endregion
public void VolumeSlider(float volume)
{
AudioListener.volume = volume;
volumeText.text = volume.ToString("0.0");
}
public void VolumeApply()
{
PlayerPrefs.SetFloat("masterVolume", AudioListener.volume);
Debug.Log(PlayerPrefs.GetFloat("masterVolume"));
StartCoroutine(ConfirmationBox());
}
public void ControllerSen()
{
controllerSenText.text = controllerSenSlider.value.ToString("0");
controlSenFloat = controllerSenSlider.value;
}
#region ResetButton
public void ResetButton(string GraphicsMenu)
{
if (GraphicsMenu == "Audio")
{
AudioListener.volume = defaultVolume;
volumeSlider.value = defaultVolume;
volumeText.text = defaultVolume.ToString("0.0");
VolumeApply();
}
if (GraphicsMenu == "Graphics")
{
controllerSenText.text = defaultSen.ToString("0");
controllerSenSlider.value = defaultSen;
controlSenFloat = defaultSen;
invertYToggle.isOn = false;
}
}
#endregion
#region Dialog Options - This is where we load what has been saved in player prefs!
public void ClickNewGameDialog(string ButtonType)
{
if (ButtonType == "Yes")
{
newGameDialog.SetActive(false);
StartCoroutine(SceneFader.FadeAndLoadSceneNewGame(SceneFader.FadeDirection.In, _newGameButtonLevel));
}
if (ButtonType == "No")
{
GoBackToMainMenu();
}
}
public void ClickLoadGameDialog(string ButtonType)
{
if (ButtonType == "Yes")
{
newGameDialog.SetActive(false);
StartCoroutine(SceneFader.FadeAndLoadSceneLoadGame(SceneFader.FadeDirection.In, _newGameButtonLevel));
}
if (ButtonType == "No")
{
GoBackToMainMenu();
}
}
#endregion
#region Back to Menus
public void GoBackToOptionsMenu()
{
GeneralSettingsCanvas.SetActive(true);
graphicsMenu.SetActive(false);
soundMenu.SetActive(false);
VolumeApply();
menuNumber = 2;
}
public void GoBackToMainMenu()
{
menuDefaultCanvas.SetActive(true);
newGameDialog.SetActive(false);
loadGameDialog.SetActive(false);
noSaveDialog.SetActive(false);
GeneralSettingsCanvas.SetActive(false);
graphicsMenu.SetActive(false);
soundMenu.SetActive(false);
menuNumber = 1;
}
public void ClickQuitOptions()
{
GoBackToMainMenu();
}
public void ClickNoSaveDialog()
{
GoBackToMainMenu();
}
#endregion
}
In this script I have two events :
ClickNewGameDialog and ClickLoadGameDialog that I'm calling them from OnClick buttons in the main menu.
I created this script and added it to an empty GameObject in the Game scene :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class NewGame : MonoBehaviour
{
// Here you store the actual instance
private static NewGame _instance;
// Public read-only access property
public static NewGame Instance
{
get
{
// if already set simply return directly
if (_instance) return _instance;
// Otherwise try to find it in the scene
_instance = FindObjectOfType<NewGame>();
if (_instance) return _instance;
// Otherwise create it now
_instance = new GameObject(nameof(NewGame)).AddComponent<NewGame>();
return _instance;
}
}
private bool _gameStarted;
public static bool GameStarted => Instance._gameStarted;
private void Awake()
{
if (_instance && _instance != this)
{
// There already exist another instance
Destroy(this.gameObject);
return;
}
// Otherwise this is the active instance and should not be destroyed
_instance = this;
DontDestroyOnLoad(this.gameObject);
SceneManager.sceneLoaded += OnSceneLoaded;
// update it once now
_gameStarted = SceneManager.GetActiveScene().buildIndex != 0;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
_gameStarted = scene.buildIndex != 0;
}
}
but when I tried to use it for exmaple in the Game scene like this :
if(NewGame.GameStarted != true)
GameStarted is all the time true never false so it's all the time staring a new game even if the player clicked the Load Game button.
Use Singletone Design Pattern. Create a static variable that refers to your script like this:
public static YourCodeBehaviour Instance;
Then in Awake method you just check if it null and set it if true, like this.
private void Awake() {
if(Instance == null)
Instance = this;
else {
Destroy(gameObject);
}
}
Then you got an Instance that you can access from your static methods, cause Instance is static and got the actual alive instance.
Good luck!

C# Unity My code won't allow for one class to edit the variables in another

I have a small game where the player flies a rocket to a green platform to progress. If they crash, they start over. The levels simply loop and the gameplay works fine. The issue I'm having is with the timer. I'm trying to make a system that keeps track of how long it takes the player to beat every level, and then save their fastest time under the record variable. I tried adding a hotkey in the Timer script to reset the timer, but if I run the same ResetTimer function from the Rocket script, the currentTime variable will not change. It still prints "Timer reset" but the variable stays the same. Any ideas?
Rocket.cs
using UnityEngine.SceneManagement;
public class Rocket : MonoBehaviour
{
Timer timer = new Timer();
[SerializeField] float rotationThrust = 100f;
[SerializeField] float mainThrust = 50f;
[SerializeField] float levelLoadDelay = 2f;
[SerializeField] AudioClip mainEngine;
[SerializeField] AudioClip success;
[SerializeField] AudioClip death;
[SerializeField] ParticleSystem mainEngineParticles;
[SerializeField] ParticleSystem successParticles;
[SerializeField] ParticleSystem deathParticles;
Rigidbody rigidBody;
AudioSource audioSource;
enum State { Alive, Dying, Transcending }
State state = State.Alive;
bool collisionsEnabled = true;
void Start()
{
rigidBody = GetComponent<Rigidbody>();
audioSource = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.Escape))
{
Application.Quit();
}
if (state == State.Alive)
{
RespondToThrustInput();
RespondToRotateInput();
}
if (Debug.isDebugBuild)
{
RespondToDebugKeys();
}
}
private void RespondToDebugKeys()
{
if (Input.GetKeyDown(KeyCode.L))
{
LoadNextLevel();
}
else if (Input.GetKeyDown(KeyCode.K))
{
LoadFirstLevel();
}
else if (Input.GetKeyDown(KeyCode.C))
{
collisionsEnabled = !collisionsEnabled;
}
else if (Input.GetKeyDown(KeyCode.P))
{
timer.ResetRecord();
}
}
private void RespondToThrustInput()
{
if (Input.GetKey(KeyCode.Space) || Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
ApplyThrust();
}
else
{
audioSource.Stop();
mainEngineParticles.Stop();
}
}
private void ApplyThrust()
{
rigidBody.AddRelativeForce(Vector3.up * mainThrust);
if (!audioSource.isPlaying)
{
audioSource.PlayOneShot(mainEngine);
}
mainEngineParticles.Play();
}
private void RespondToRotateInput()
{
rigidBody.freezeRotation = true;
float rotationSpeed = rotationThrust * Time.deltaTime;
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
transform.Rotate(Vector3.forward * rotationSpeed);
}
else if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
transform.Rotate(Vector3.back * rotationSpeed);
}
rigidBody.freezeRotation = false;
}
void OnCollisionEnter(Collision collision)
{
if (state != State.Alive || !collisionsEnabled)
{
return;
}
switch (collision.gameObject.tag)
{
case "Friendly":
break;
case "Finish":
StartSuccessSequence();
break;
default:
StartDeathSequence();
break;
}
}
private void StartSuccessSequence()
{
state = State.Transcending;
audioSource.PlayOneShot(success);
successParticles.Play();
Invoke("LoadNextLevel", levelLoadDelay);
}
private void StartDeathSequence()
{
state = State.Dying;
audioSource.Stop();
deathParticles.Play();
audioSource.PlayOneShot(death);
Invoke("LoadFirstLevel", levelLoadDelay);
}
public void LoadFirstLevel()
{
timer.ResetTimer();
SceneManager.LoadScene(1);
}
private void LoadNextLevel()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
if (currentSceneIndex + 1 == SceneManager.sceneCountInBuildSettings)
{
timer.Save();
LoadFirstLevel();
}
else
{
SceneManager.LoadScene(currentSceneIndex + 1);
}
}
}
Timer.cs
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
using UnityEngine.UI;
using System;
public class Timer : MonoBehaviour
{
float currentTime;
float record = -1f;
public Text currentText;
public Text recordText;
private void Start()
{
DontDestroyOnLoad(gameObject);
}
public void Save()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/playerInfo.dat");
PlayerData data = new PlayerData();
if (RecordTest() == true)
{
if (currentTime < record)
{
record = currentTime;
data.recordSave = record;
}
}
else record = currentTime;
data.recordSave = record;
bf.Serialize(file, data);
file.Close();
}
public void Load()
{
if(File.Exists(Application.persistentDataPath + "/playerInfo.dat"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/playerInfo.dat", FileMode.Open);
PlayerData data = (PlayerData)bf.Deserialize(file);
file.Close();
record = data.recordSave;
}
}
private void Update()
{
CheckForResetInput();
SetCurrentTimeText();
SetRecordTimeText();
}
private void SetRecordTimeText()
{
if (record == -1)
{
recordText.text = "N/A";
}
else
{
string minutesRecord = Mathf.Floor(record / 60).ToString("00");
string secondsRecord = Mathf.Floor(record % 60).ToString("00");
recordText.text = minutesRecord + ":" + secondsRecord;
}
}
private void CheckForResetInput()
{
if (Input.GetKeyDown(KeyCode.R))
{
ResetTimer();
}
}
private void SetCurrentTimeText()
{
currentTime += Time.deltaTime;
string minutes = Mathf.Floor(currentTime / 60).ToString("00");
string seconds = (currentTime % 60).ToString("00");
currentText.text = minutes + ":" + seconds;
}
public void ResetTimer()
{
print("Timer reset");
currentTime = 0;
}
public void ResetRecord()
{
record = -1f;
}
public bool RecordTest()
{
if (record == -1)
{
return false;
}
else return true;
}
public void Collided()
{
print("Ouch");
}
}
[Serializable]
class PlayerData
{
public float recordSave;
}
Serializedfields with no other modifier are PRIVATE to the class they are in. Serializedfields are purely for them to show in the unity inspector, they need to be public for you to access them from other classes
So
[SerializeField] float rotationThrust = 100f; is private to Rocket
[SerializeField] public float rotationThrust = 100f; will be visible to other classes with a reference to a Rocket instance - Technically you dont need to serialize the field as public will always show, but it makes it look nice in your code if they all line up :D
You are creating a new instance of Timer for your Rocket component.
Timer timer = new Timer();
This means that when rocket calls timer.ResetTimer() is resetting the newly instanced Timer class, but NOT the MonoBehaviour attached to your scene object.
Rather than declare a new timer in your rocket class, create a reference to it in the inspector via:
[SerializeField] Timer timer;
and drag/drop your Timer object to that reference.
The above solution is probably the best pattern, but if you want a quick and easy solution, change your currentTime to static:
private static float currentTime
Making this value static means that all Timer instances will share the same value. Since the variable is static, it is not specific to a single class instance. With this solution you are not eliminating the multiple instances of Timer, but you are forcing them to share a currentTime variable. This doesn't fix the design that caused your problem, but it does fix the symptom. This is why I say it is not the best solution.

Player not moving to all waypoints.

Enemy character not moving to the 3rd waypoint. After moving to waypoint 2 it just stops and the idle animation plays. The character has a NavMeshAgent on it and it looks like the destination reached event is not being triggered when he gets to the waypoint. If anyone has had a situation like this before I would appreciate any information possible. I have been trying to figure out whats wrong for a few hours now and am starting to think it might not be any of the scripts.
here is the waypoint controller
using UnityEngine;
using UnityEngine.AI;
public class WaypointController : MonoBehaviour {
Waypoints[] waypoints;
public Transform target;
//NavMeshPath path;
int currentWaypointIndex = -1;
//private NavMeshAgent agent;
//EnemyCharacter enemy;
public event System.Action<Waypoints> OnWaypointChanged;
// Use this for initialization
void Awake () {
waypoints = GetWaypoints();
}
public void SetNextWaypoint() {
if (currentWaypointIndex != waypoints.Length)
currentWaypointIndex++;
if (currentWaypointIndex == waypoints.Length)
currentWaypointIndex = 0;
if (OnWaypointChanged != null)
OnWaypointChanged(waypoints[currentWaypointIndex]);
//Debug.Log("OnWaypointChanged == null: " + (OnWaypointChanged == null));
//Debug.Log("OnWaypointChanged != null: " + (OnWaypointChanged != null));
}
Waypoints[] GetWaypoints()
{
return GetComponentsInChildren<Waypoints>();
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Vector3 previousWaypoint = Vector3.zero;
foreach (var waypoint in GetWaypoints())
{
Vector3 waypointPosition = waypoint.transform.position;
Gizmos.DrawWireSphere(waypointPosition, .2f);
if (previousWaypoint != Vector3.zero)
Gizmos.DrawLine(previousWaypoint, waypointPosition);
previousWaypoint = waypointPosition;
}
}
}
Here is the EnemyPatrolPoints script
using UnityEngine;
[RequireComponent(typeof(AI_PathFinder))]
public class EnemyPatrolPoints : MonoBehaviour {
[SerializeField]
WaypointController waypointController;
[SerializeField]
float waitTimeMin;
[SerializeField]
float waitTimeMax;
AI_PathFinder pathfinder;
private void Start()
{
waypointController.SetNextWaypoint();
}
private void Awake()
{
pathfinder = GetComponent<AI_PathFinder>();
pathfinder.OnDestinationReached += Pathfinder_OnDestinationReached;
waypointController.OnWaypointChanged += WaypointController_OnWaypointChanged;
}
private void WaypointController_OnWaypointChanged(Waypoints waypoint)
{
pathfinder.SetTarget(waypoint.transform.position);
print("waypoint changed");
}
private void Pathfinder_OnDestinationReached()
{
SealForce_GameManager.Instance.Timer.Add(waypointController.SetNextWaypoint, Random.Range(waitTimeMin, waitTimeMax));
print("destination reached");
}
}
Here is the AI Pathfinder script`
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class AI_PathFinder : MonoBehaviour
{
[HideInInspector]
public NavMeshAgent agent;
public EnemyPatrolPoints enemyPatrolPoints;
[SerializeField] float distanceRemainingThreshold;
bool m_destinationReached;
bool destinationReached
{
get
{ return m_destinationReached; }
set
{
m_destinationReached = value;
if (m_destinationReached)
{
if (OnDestinationReached != null)
OnDestinationReached();
}
}
}
public event System.Action OnDestinationReached;
void Start()
{
agent = GetComponent<NavMeshAgent>();
//enemyPatrolPoints = GetComponent<EnemyPatrolPoints>();
}
public void SetTarget(Vector3 target)
{
agent.SetDestination(target);
}
void Update()
{
if (destinationReached)
return;
if (agent.remainingDistance < distanceRemainingThreshold)
destinationReached = true;
}
}
The lines
if (agent.remainingDistance < distanceRemainingThreshold)
destinationReached = true;
are never reached as long as destinationReached is true because of
if (destinationReached)
return;
You are setting it to true after the first waypoint is reached and then never reset it to false so your Update is always skipped in the future.
You should add it e.g. to
public void SetTarget(Vector3 target)
{
agent.SetDestination(target);
destinationReached = false;
}

Countdown text field not updating during countdown

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,

Categories

Resources