Hello. I can’t seem to get my timer to stop. I want it to stop after I have gathered my last collectable, in this instance there is 8 of them. The text below is for my timer in general and it works with no errors but it is not stopping when it gets to the end of the level, or my You Win! screen.
I have this set up on my Timer Text UI. Should I put it on my player? What am I doing wrong?
Any help is appreciated. Let me know what I am doing wrong.
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
public class myTimer : MonoBehaviour {
public Text TimerText;
private float startTime;
private int count;
bool keepTiming;
float timer;
void Start (){
StartTimer();
}
void Update () {
if(count>=8){
Debug.Log ("Timer has stopped at " + TimeToString (StopTimer ()));
}
if(keepTiming) {
UpdateTime ();
}
}
void UpdateTime (){
timer = Time.time + startTime;
TimerText.text = TimeToString (timer);
}
float StopTimer () {
keepTiming = false;
return timer;
}
void ResumeTimer () {
keepTiming = true;
startTime = Time.time + timer;
}
void StartTimer () {
keepTiming = true;
startTime = Time.time;
}
string TimeToString (float t){
string minutes = ((int)t / 60).ToString ();
string seconds = (t % 60).ToString (“f2”);
return minutes + “:” + seconds;
}
}
Well…nothing ever sets count, for starters.
Okay. I will look at that. I added that to be a part of my if statement. I am fairly new at this.
The count is in my Player Control.
A good way to do that would be like this:
Whatever is keeping track of your collectables should have a reference to your timer. Maybe add a “public myTimer gameTimer;” variable to your PlayerControl, and drag the timer component into that field in the unity inspector. Then when you pick up the last collectable, you call “gameTimer.StopTimer()”, which you would have to make a public function.
Example:Example PlayerControl.cs
public class PlayerControl : MonoBehaviour {
public int amountToCollect = 8;
public myTimer timer;
private int collected;
// logic for picking stuff up (do however you want)
private void OnTriggerEnter2D(Collider2D other) {
// if the other object has the right component
if(other.GetComponent<Collectable>() != null) {
collected++;
if(collected >= amountToCollect && timer != null) {
timer.StopTimer();
}
}
}
}
That should be enough to get it going if your timer is working.
Now I’m going to throw a completely different way to implement that timer at you, to show you some other options you have when programming. I tried to comment it to make it understandable. Key things to note are the use of TimeSpan, Time.deltaTime, and Coroutine.
Different Timer Implementation
using System.Collections;
using UnityEngine.UI;
using UnityEngine;
using System;
public class myTimer : MonoBehaviour {
public Text TimerText;
private bool paused;
private float timer;
private Coroutine activeTimer; // reference to the running timer
private void Start() {
StartTimer();
}
public void ResumeTimer() {
paused = false;
}
public void PauseTimer() {
paused = true;
}
public void StartTimer() {
// if the timer is active, stop it
if(activeTimer != null) {
StopTimer();
}
// start the timer coroutine
activeTimer = StartCoroutine(timerCoroutine());
}
public void StopTimer() {
StopCoroutine(timerCoroutine());
activeTimer = null;
}
public string TimeToString(float t) {
// TimeSpan: handy class for time tracking
// create a timespan using total seconds
TimeSpan timeSpan = TimeSpan.FromSeconds(t);
// express the timespan in minutes and seconds
return timeSpan.Minutes + ":" + timeSpan.Seconds;
}
// runs just like a normal function
// but can use 'yield' to delay continuing the code
// in this case, the delay is one frame to act like an Update loop
private IEnumerator timerCoroutine() {
// keep looping while this component is enabled
while(enabled) {
// inner loop while paused
while(paused) {
// do nothing, and
// continue from here next frame (keeps looping until paused is false)
yield return null;
}
// add the time passed since last frame
timer += Time.deltaTime;
// update text
TimerText.text = TimeToString(timer);
// continue from here next frame
// (keeps looping until the coroutine is stopped or component disabled)
yield return null;
}
}
}
Yeah. So the count you define in myTimer is unaffected by one defined in PlayerControl. Those are instance-scoped fields. Which means that each instance of either class gets its own copy of the field…kind of like how you probably have a wallet and I have a wallet but they are different wallets: one is yours and one is mine.
@LiterallyJeff is exactly right. You should decouple your collectables-tracker from your timer. The collectables-tracker should make method calls to the timer to start it and stop it. Otherwise, each should be only about what it is about and not the other thing.
@LiterallyJeff @MaxGuernseyIII thank you for answering my noob question. I will try it out soon.