Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Make a text count up from zero to value; Score/Results screen for game

Discussion in 'Scripting' started by topherbwell, Jan 17, 2016.

  1. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Hello,
    I am wanting to display final score in a game on a results screen which also tracks a few statistics, add bonuses, and then the final score is displayed.

    I am wanting each field, for instance "Score", to count up from 0 to the value, then on to the next statistic, etc.

    Does anyone have an idea how to make the number count up like this? I am not asking how to pass the value to the text or display it, I am asking how to actually return a value that counts up like this.

    I am looking for something that does THIS but the example is all javaScript and I have only been learning C# as my first language. Any help appreciated!
     
  2. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    coroutines
    https://unity3d.com/learn/tutorials/modules/intermediate/scripting/coroutines

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using System.Collections;
    5.  
    6. public class TestScript : MonoBehaviour
    7. {
    8.     public Text field;
    9.     public float targetScore;
    10.     public float currentDisplayScore = 0;
    11.  
    12.  
    13.     void Start()
    14.     {
    15.         StartCoroutine(CountUpToTarget());
    16.     }
    17.  
    18.     IEnumerator CountUpToTarget()
    19.     {
    20.         while (currentDisplayScore < targetScore)
    21.         {
    22.             currentDisplayScore += Time.deltaTime; // or whatever to get the speed you like
    23.             currentDisplayScore = Mathf.Clamp(currentDisplayScore, 0f, targetScore);
    24.             field.text = currentDisplayScore + "";
    25.             yield return null;
    26.         }
    27.     }
    28.  
    29. }
    30.  
    31.  
     
  3. topherbwell

    topherbwell

    Joined:
    Jun 8, 2015
    Posts:
    180
    Hey that looks like it will do exactly what I was needing. Thanks man, I'll get it working in game tonight.
     
  4. swosko

    swosko

    Joined:
    Oct 19, 2016
    Posts:
    1
    Glad I stumbled upon this thread. I was hacking around with an overly complicated solution, when one such as the one provided is plenty sufficient. I tweaked it to be parameter driven and incrementally step the text to the target value in the input duration.

    Code (CSharp):
    1.  
    2. IEnumerator CountUpToTarget(Text label, int targetVal, float duration, float delay = 0f, string prefix = "")
    3.     {
    4.         if (delay > 0)
    5.         {
    6.             yield return new WaitForSeconds(delay);
    7.         }
    8.  
    9.         int current = 0;
    10.         while (current < targetVal)
    11.         {
    12.             // step by amount that will get us to the target value within the duration
    13.             current += (int)(targetVal / (duration/Time.deltaTime));
    14.             current = Mathf.Clamp(current, 0, targetVal);
    15.             label.text = prefix + current;
    16.             yield return null;
    17.         }
    18.     }
    19.  
     
  5. WeHeartGames

    WeHeartGames

    Joined:
    Jul 30, 2014
    Posts:
    8
    @swosko, I'm having a weird problem with this code, maybe you have some insight as to why this might be happening. Here's the code I'm using:

    Code (CSharp):
    1. StartCoroutine (CountUpToTarget(progressText, countUpTarget, 0.6f));
    2.  
    3. IEnumerator CountUpToTarget(Text label, int targetVal, float duration, float delay = 1.0f, string prefix = "")
    4.     {
    5.         if (delay > 0)
    6.         {
    7.             yield return new WaitForSeconds(delay);
    8.         }
    9.         int current = bankHighFives;
    10.         while (current < targetVal)
    11.         {
    12.             // step by amount that will get us to the target value within the duration
    13.             current += (int)(targetVal / (duration/Time.deltaTime));
    14.             current = Mathf.Clamp(current, 0, targetVal);
    15.             label.text = prefix + current;
    16.             yield return null;
    17.         }
    18. }
    The issue I'm having is this:
    - Sometimes it works perfectly.
    - Sometimes it doesn't count up at all.
    - Sometimes when it doesn't work, if I click on object names in the Hierarchy panel while it's supposed to be counting, it starts to count up again. Sometimes this kicks it all the way through, and sometimes it just counts up a couple numbers and then stalls again.

    Any idea why this might be happening? Thanks!
    -Mike
     
  6. Leuki

    Leuki

    Joined:
    Feb 27, 2014
    Posts:
    130
    Check out the answer i just provided in THIS post, may help you all out.
     
  7. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    That while loop is entirely dependent on line 13 in your code snippet not returning 0. If it does the loop is endless and the value isn't changing.

    Few ways it can do that: anything that is zeroing Time.deltaTime (i.e. a use of Time.timeScale somewhere as part of a pause function attempt), or the right combination of values that the float is less than 1 so that the int cast just truncates the value to 0.

    I'd start by checking those cases out.
     
    WeHeartGames likes this.
  8. WeHeartGames

    WeHeartGames

    Joined:
    Jul 30, 2014
    Posts:
    8
    @LeftyRighty Thank you! That was it. Here's the fix I implemented. No idea if it's hacky or not but it seems to be working, so I guess that's all that matters. :)

    Code (CSharp):
    1. IEnumerator CountUpToTarget(Text label, int targetVal, float duration, float delay = 1.0f, string prefix = "")
    2.     {
    3.         if (delay > 0)
    4.         {
    5.             yield return new WaitForSeconds(delay);
    6.         }
    7.         int current = bankHighFives;
    8.         int currentNonZero = 1;
    9.         while (current < targetVal)
    10.         {
    11.             // ensure "current" is not getting a value of zero, resulting in an infinite loop
    12.             currentNonZero = (int)(targetVal / (duration/Time.deltaTime));
    13.             if (currentNonZero == 0)
    14.             {
    15.                 currentNonZero = 1;
    16.             }
    17.             // step by amount that will get us to the target value within the duration
    18.             current += currentNonZero;
    19.             current = Mathf.Clamp(current, 0, targetVal);
    20.             label.text = prefix + current;
    21.             yield return null;
    22.         }
     
  9. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    It's still bugged.
    The reason that the original version is bugged is in fact what @LeftyRighty said.
    But all of that simply happens because the cast to an integer is done in the wrong place.

    You should take the original code, declare 'current' as a float and let the value add up as a floating point number.
    The cast can be done when you concatenate the string for the text field.
     
  10. nikoosefat

    nikoosefat

    Joined:
    Dec 3, 2015
    Posts:
    1
    Hi there
    do it by DOTween Library like this

    public void CountUp(float addScore)
    {
    float currentScore = float.Parse(scoreText.text);
    DOVirtual.Float(currentScore,addScore,1f,(v) => scoreText.text = Mathf.Floor(v).ToString());
    }
    1 is duration you can set it to whatever you like.
     
    twitchfactor likes this.
  11. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    685
    You can do it with Invoke() recursively instead of a while() and coroutines.

    BTW am curious about coroutines versus Invoke, I know there's a lot of material out there about the comparisons, would it make any appreciable difference in this case (besides not being able to pass arguments using Invoke)?

    Code (CSharp):
    1.  
    2.     int timerValue = 0;
    3.     int timerEnd = 10;
    4.     float timerTick = 1.5f;
    5.     void Start()
    6.     {
    7.         countdownTimer();
    8.     }
    9.     void countdownTimer()
    10.     {
    11.         timerValue++;
    12.         if (timerValue >= timerEnd)
    13.         {
    14.             print("Timer ended");
    15.         }
    16.         else
    17.         {
    18.             print("Timer value = " + timerValue);
    19.             Invoke("countdownTimer", timerTick);
    20.         }
    21.     }
     
    Faizan65 likes this.
  12. Faizan65

    Faizan65

    Joined:
    Sep 12, 2020
    Posts:
    5
    Here is how I did it.

    Code (CSharp):
    1.     public IEnumerator countdownTimer(int timerEnd, TMP_Text textTMP)
    2.     {
    3.         timerValue++;
    4.         if (timerValue > timerEnd)
    5.         {
    6.             StopCoroutine("countdownTimer");
    7.         }
    8.         else
    9.         {
    10.             textTMP.text = timerValue.ToString();
    11.             yield return new WaitForSeconds(0.01f);
    12.             StartCoroutine(countdownTimer(timerEnd, textTMP));
    13.         }
    14.     }
    And where I needed to call it, I just started the coroutine like this.


    StartCoroutine(lvlManager.countdownTimer(PlayerPrefs.GetInt("Score"), SuccessLevelText));