1

I'm trying to reduce the number of lines of code by condensing the below into a repeatable function rather than adding multiple if else blocks for each item.

Current working logic:

So I currently have some logic that translates my game score into easier to read values as such:

1,000+ = simple formatting

10,000+ = 10K - 999k

1,000,000+ = 1M - 999M

etc

    public void ScoreDisplayLogic()
    {
        // Convert to x.xM
        if (GameManager.score >= 1000000)
        {
            scoreText.text = "Score: " + (GameManager.score / 1000000).ToString("0.0")+"M";
        }
        // Convert to x.xK 
        else if (GameManager.score >= 10000)
        {
            scoreText.text = "Score: " + (GameManager.score / 1000).ToString("0.0")+"K";
        }        
        // Format with x,xxx.xx
        else if (GameManager.score >= 1000)
        {
            scoreText.text = "Score: " + GameManager.score.ToString("0,000.00");
        }
    }

I want to do the same thing for my cost values, however I have a lot of different things with costs.

My goal is to be able to scale this, so is it possible to condense these into a singular script without writing an if else block for each one?

Trying to avoid the below:

ex) upgrade1Cost, upgrade2Cost, upgrade3Cost, idleUpgrade1Cost, etc

    public void CostDisplayLogic()
    {
        // Format upgrade1Cost
        // Convert to x.xM
        if (GameManager.upgrade1Cost>= 1000000)
        {
            upgrade1CostText.text = "Score: " + (GameManager.upgrade1Cost/ 1000000).ToString("0.0")+"M";
        }
        // Convert to x.xK 
        else if (GameManager.upgrade1Cost>= 10000)
        {
            upgrade1CostText.text = "Score: " + (GameManager.upgrade1Cost/ 1000).ToString("0.0")+"K";
        }        
        // Format with x,xxx.xx
        else if (GameManager.upgrade1Cost>= 1000)
        {
            upgrade1CostText.text = "Score: " + GameManager.upgrade1Cost.ToString("0,000.00");
        }
        // Format upgrade2Cost
        // Convert to x.xM
        if (GameManager.upgrade2Cost>= 1000000)
        {
            upgrade2CostText.text = "Score: " + (GameManager.upgrade2Cost/ 1000000).ToString("0.0")+"M";
        }
        // Convert to x.xK 
        else if (GameManager.upgrade2Cost>= 10000)
        {
            upgrade2CostText.text = "Score: " + (GameManager.upgrade2Cost/ 1000).ToString("0.0")+"K";
        }        
        // Format with x,xxx.xx
        else if (GameManager.upgrade2Cost>= 1000)
        {
            upgrade2CostText.text = "Score: " + GameManager.upgrade2Cost.ToString("0,000.00");
        }
        // etc, etc

    }


yeelowsnow
  • 11
  • 2
  • 1
    Make a static function to format a number as text. That function may end up long if you support a ton of different abbreviations, but it's all in one place. Once you have that the places where you call it all turn into one liners. You could also consider building a table with the value, what to divide by, and the format string then the formatting function is just s loop to find the right entry and apply the values. – Retired Ninja Apr 30 '23 at 19:05
  • You can use extension method as mentioned [this SO answer](https://stackoverflow.com/a/48000498/2892829). – Orifjon May 01 '23 at 00:41

3 Answers3

1

If your numbers are always ints you could do something really simple like this:

//note - this only takes values up to 999 Million, if you want to go big diablo numbers in the billions and trillions you cant 
//use an int as it's too small, you'll need to use a data type that can record more numbers. 
    public static string ConvertToReadable(int num)
    {
        string orderSuffix = "";
        int order = num.ToString().Length;
        if(order <=3) return(num.ToString());
        else if (order<=6) return (num.ToString().Substring(0, order-3) +"K");
        else if (order<=9) return (num.ToString().Substring(0, order-6) +"M");
        else return "Too Big!";
    }  

If you're using floats you'll need to split the number at the decimal and do something and then cast that to an int and use the above code. Note that due to size limitations of an int you'll need to use a long if you're going diablo style and having damage numbers in the billions and trillions.

edit: this returns 10000 as 10K, 10 as 10 or 10000000 as 10M.

Damon Vizl
  • 11
  • 2
  • Also, I like to have helper classes like this, so it can sit on a static helper class as a static method and then it can be called from anywhere in the code base to get back a readable string. You don't want this code sitting on your "shopkeeper" script and your "damage" script as you want to keep your code DRY (dont repeat yourself) – Damon Vizl Apr 30 '23 at 22:42
0

Not tested. But I think you would like to examin string.Format(...). ToString() uses the same format.

private void Format(int value)
{

    string str = "Score: {0:";
    
    if (value >= 1000000) str += "#,##0,,M"; else
    if (value >=   10000) str += "#,##0,K";  else
                          str += "#,##0";

    str += "}";

    return string.Format(CultureInfo.InvariantCulture, str, value)

}

public void CostDisplayLogic()
{

    upgrade1CostText.text = Format(GameManager.upgrade1Cost);
    upgrade2CostText.text = Format(GameManager.upgrade2Cost);

}
Willem
  • 302
  • 10
  • This is for int. Noticed you have a decimal at your last line. This would work too if you change int value to float or decimal. – Willem May 01 '23 at 00:33
  • About CultureInfo.InvariantCulture https://stackoverflow.com/a/9760339/20378590 – Willem May 01 '23 at 00:43
0

I tend to use a tuple array to remove this kind of duplication. Try this:

public void CostDisplayLogic()
{
    var levels = new (decimal level, Func<decimal, string> format)[]
    {
        (1000000, d => d.ToString("0.0") + "M"),
        (10000, d => d.ToString("0.0") + "K"),
        (1000, d => d.ToString("0,000.00")),
        (decimal.MinValue, d => d.ToString("0.00"))
    };

    string GetScore(decimal cost) =>
        levels
            .Where(l => cost >= l.level)
            .Select(l => l.format((cost / l.level)))
            .First();

    upgrade1CostText.text = $"Score: {GetScore(GameManager.upgrade1Cost)}";
    upgrade2CostText.text = $"Score: {GetScore(GameManager.upgrade2Cost)}";
}
Enigmativity
  • 113,464
  • 11
  • 89
  • 172