Why is my "long" breaking?

I’m prototyping an RPG engine and every time the XP gets up to the point it would break an Int it still breaks.

Also, as a side note, I can’t get it to display the numbers with commas. Did that change in 6?

using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
using UnityEditor.Rendering;

public class LevelMenu : MonoBehaviour
{
    public long
        totalLevelXPCost,
        strengthXPCost,
        agilityXPCost,
        intelligenceXPCost,
        willpowerXPCost,
        levelXPCost;

    public int
        strengthLevel,
        agilityLevel,
        intelligenceLevel,
        willpowerLevel,
        totalLevel,
        totalXP,
        hp,
        sp,
        mp,
        poise,
        recovery,
        movement,
        attack,
        awareness,
        equipCarry,
        skillCarry,
        slashRes,
        bashRes,
        pierceRes,
        bleedRes,
        poisonRes,
        diseaseRes,
        fireRes,
        iceRes,
        lightningRes,
        arcaneRes,
        lightRes,
        darkRes;

    public TextMeshProUGUI
        strengthText,
        agilityText,
        intelligenceText,
        willpowerText, 
        strengthXPCostText,
        agilityXPCostText,
        intelligenceXPCostText,
        willpowerXPCostText,
        totalLevelText,
        totalXPText;


    private void Start()
    {

    }


    public void LevelUpStrength()
    {
        strengthLevel += 1;
        strengthText.text = strengthLevel.ToString();
        UpdateCharacterSheet();
    }

    public void LevelDownStrength()
    {
        if (strengthLevel > 0) strengthLevel -= 1;
        UpdateCharacterSheet();
    }
    public void LevelUpAgility()
    {
        agilityLevel += 1;
        UpdateCharacterSheet();
    }

    public void LevelDownAgility()
    {
        if (agilityLevel > 0) agilityLevel -= 1;
        UpdateCharacterSheet();
    }
    public void LevelUpIntelligence()
    {
        intelligenceLevel += 1;
        UpdateCharacterSheet();
    }

    public void LevelDownIntelligence()
    {
        if (intelligenceLevel > 0) intelligenceLevel -= 1;
        UpdateCharacterSheet();
    }

    public void LevelUpWillpower()
    {
        willpowerLevel += 1;
        UpdateCharacterSheet();
    }

    public void LevelDownWillpower()
    {
        if (willpowerLevel > 0) willpowerLevel -= 1;
        UpdateCharacterSheet();
    }

    public void UpdateCharacterSheet()
    {
        totalLevel = strengthLevel + agilityLevel + intelligenceLevel + willpowerLevel;
        levelXPCost = CalcLevelXP();

        //Calculate XP Cost
        strengthXPCost = CalcAtributeXP(strengthLevel);
        agilityXPCost = CalcAtributeXP(agilityLevel);
        intelligenceXPCost = CalcAtributeXP(intelligenceLevel);
        willpowerXPCost = CalcAtributeXP(willpowerLevel);

        //Update UI
        strengthText.text = strengthLevel.ToString();
        agilityText.text = agilityLevel.ToString();
        intelligenceText.text = intelligenceLevel.ToString();
        willpowerText.text = willpowerLevel.ToString();
        strengthXPCostText.text = strengthXPCost.ToString() + " XP";
        agilityXPCostText.text = agilityXPCost.ToString() + " XP";
        intelligenceXPCostText.text = intelligenceXPCost.ToString() + " XP";
        willpowerXPCostText.text = willpowerXPCost.ToString() + " XP";
        totalLevelText.text = "Level : " + totalLevel.ToString();
        totalXPText.text = "Total XP : " + totalXP.ToString();
    }

    long CalcAtributeXP(int atr)
    {
        long xp = 100;
        for (int i = 0; i < atr; i++)
        {
            xp = Mathf.RoundToInt(xp * 1.1f);
        }
        xp -= 100;
        xp += levelXPCost;
        return xp;
    }

    long CalcLevelXP()
    {
        long xp = 100;
        for (int i = 0; i < totalLevel; i++)
        {
            xp = Mathf.RoundToInt(xp * 1.05f);
        }
        return xp;
    }
}

Can you define ‘break’?

Going into the negative once it passes 2B, or whatever the limit is for INT

You would do well to take a minute to simplify and establish what this all does. Simple logging statements would help confirm what is happening but when debugging removing non-essential actions would help you a ton. Clearly you could comment out most of these things and still witness the behavior right?

Your LevelUpStrength is updating the text element value but none of the others do, they rely on UpdateCharacterSheet.

I’d guess it has to do with your Calc methods but again ask yourself a) what is the calculation doing and b) how can I monitor the values? You have some Mathf.RoundToInt going on but it returns a long. You can figure this out in no time with a bit of debugging.

1 Like

Probably because you’re rounding to int in CalcAtributeXP and CalcLevelXP.

Note that your multiplication when passing a value into RoundToInt is returning a float value:

Mind you the method makes zero sense as you’re looping x amount of times to simply recalculate the value for xp x amount of times for no reason.

You might as well just calculate the value for atr rather than loop for no reason.

2 Likes

Oh, DUH!

Sorry, the code is long and boring.

As of right now the project is nothing more than menus.

Thanks!

I think you misread that.

The point of the loop is to increase the cost of an attribute level by 10% each level, while the total level increases the cost by 5%.

If you take 1 and multiply it by 1.1f, then round it to an int, you get 1

That is (I believe) what Spiney is pointing out. So each iteration you’re losing the work you’re doing, or a fraction of it in any case.

This is apart and aside from the cast-to-int problem as far as 32 vs 64 bits.

Do all your iterating in a number type that can support what you want (perhaps a double?), then cast it back to a long afterwards.

You MIGHT want to consider fixed point arithmetic and using all longs, in which case you would agree, “I’m using X decimal places all the time in my longs,” and transform them on output.

If you chose 3 decimal places, then 1 would be represented as 1000 and 1.5 would be 1500, etc.

NOTE: you can use fixed point for as large a section of your code as you like, even something as small as this one computation function, by converting the integer long value coming in, doing your lossless fixed point arithmetic, then converting it back at the end. This reduces the footprint of change if you don’t want to convert your entire world to use fixed point.

2 Likes

I ended up using a float then converting to a long.

Not the most optimal, but it works.

As for your point about rounding, it’s starting at 100 so it’s not an issue.

I’m pretty happy with the results.

Thanks again for the help.

Uhm, you are aware of the method System.Math.Pow, right? So instead of your for loop, you can simply do

xp = 100L * (long)System.Math.Pow(1.1, atr);
xp -= 100L;
// ...

Though most games which have an exponential power curve use System.Math.Exp (which is “e” to the power of x)

1 Like