Code in loop freezing unity

public void ExpIncreased(SkillTypes stat, int exp) {
        skills[stat].currentExp += exp;
        skills[stat].expRemaining = skills[stat].nextLevelAt - skills[stat].currentExp;
        if (skills[stat].expRemaining < 0) {
            skills[stat].expRemaining = 0;
        }
        if (skills[stat].totalLevel < 100) {
            while (skills[stat].currentExp > skills[stat].nextLevelAt) {

                if(skills[stat].totalLevel < 100) {
                    skills[stat].totalLevel += 1; print("Total level: " + skills[stat].totalLevel);
                    skills[stat].nextLevelAt += ((skills[stat].totalLevel * 50 * skills[stat].totalLevel * skills[stat].totalLevel) / 4); print("Exp to level: " + skills[stat].nextLevelAt);
                }
               
                if (skills[stat].currentLevel < 100) {
                    skills[stat].currentLevel += 1; print("Current level: " + skills[stat].currentLevel);
                }
                //skills[stat].nextLevelAt += ((skills[stat].totalLevel * 50 * skills[stat].totalLevel * skills[stat].totalLevel) / 4); print("Exp to level: " + skills[stat].nextLevelAt);
            }
        }
        if (skills[stat].totalLevel > 100) {
            skills[stat].totalLevel = 100;
            print("Real total level: " + stat + ": " + skills[stat].totalLevel);
        }
    }

The code that’s in a comment is what makes it freeze when I place that same code outside of the if statement it doesn’t freeze, however, I don’t want to update the skills exp to level if the skill is already at 100.

P.S. I know the code is super messy, I’ll be tidying it up when I figure this problem out.

First of all, is ‘skills’ a dictionary? What’s the reason that you look it up all the time?
That code would be way more efficient if you get it once in the beginning and cache it locally.

Other than that, you prevent the first if statement from progressing once the total level has reached 100 within that loop. Your implementation does neither allow a break out, nor any other way to increase the ‘nextLevelAt’ value.
That is, once you reach 100 for your totalLevel, you’ll get stuck.

Last but not least, there’s probably a simple formular you could use to achieve the same effect. There doesn’t seem to be a real use for the loop, as one-time calculation would be as effective, but hundreds of times more efficient.

1 Like

I’m always 1 or 2 minutes too late today. :rage:

1 Like

This is my entire code;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StatsHandler : MonoBehaviour {

    EventManager eventManager = new EventManager();

    public bool gameStarted = false;

    public int[] currentStats;
    public int[] totalStats;

    void Awake() {
        currentStats = new int[skills.Count];
        totalStats = new int[skills.Count];

        if (!gameStarted) {
            for (int i = 0; i < skills.Count; i++) {
                currentStats[i] = skills[(SkillTypes)i].currentLevel;
                totalStats[i] = skills[(SkillTypes)i].totalLevel;
            }
        }
    }

    void Start() {
        if (!gameStarted) {
            skills[SkillTypes.Health].currentLevel = skills[SkillTypes.Health].totalLevel;
            gameStarted = true;
            //StartCoroutine(UpdateStats());
        }
    }

    void Update() {
        if(Input.GetKeyDown(KeyCode.P)) {
            ExpIncreased(SkillTypes.Magic, 173857000);
        }
    }

    private void StatsToUpdate() {
        for (int i = 0; i < skills.Count; i++) {
            currentStats[i] = skills[(SkillTypes)i].currentLevel;
            totalStats[i] = skills[(SkillTypes)i].totalLevel;
        }
        print("Updated stats!");
    }

    /*IEnumerator UpdateStats() {
        statsUpdateRunning = true;
        while (gameStarted) {
            StatsToUpdate();
            yield return new WaitForSeconds(60f);
        }  
        statsUpdateRunning = false;
    }*/

    public void ExpIncreased(SkillTypes stat, int exp) {
        skills[stat].currentExp += exp;
        skills[stat].expRemaining = skills[stat].nextLevelAt - skills[stat].currentExp;
        if (skills[stat].expRemaining < 0) {
            skills[stat].expRemaining = 0;
        }

        if (skills[stat].totalLevel < 100) {
            while (skills[stat].currentExp > skills[stat].nextLevelAt) {

                if(skills[stat].totalLevel < 100) {
                    skills[stat].totalLevel += 1; print("Total level: " + skills[stat].totalLevel);
                    //print("Next level at: " + stat + ": " + skills[stat].nextLevelAt); print("Current exp: " + stat + ": " + skills[stat].currentExp); print("Exp remaining: " + stat + ": " + skills[stat].expRemaining);
                    //skills[stat].nextLevelAt += ((skills[stat].totalLevel * 50 * skills[stat].totalLevel * skills[stat].totalLevel) / 4); print("Exp to level: " + skills[stat].nextLevelAt);
                }
               
                if (skills[stat].currentLevel < 100) {
                    skills[stat].currentLevel += 1; print("Current level: " + skills[stat].currentLevel);
                }
                skills[stat].nextLevelAt += ((skills[stat].totalLevel * 50 * skills[stat].totalLevel * skills[stat].totalLevel) / 4); print("Exp to level: " + skills[stat].nextLevelAt);
            }
        }
        if (skills[stat].totalLevel > 100) {
            skills[stat].totalLevel = 100;
            print("Real total level: " + stat + ": " + skills[stat].totalLevel);
        }
    }


    public enum SkillTypes {
        //Combat Skills
        Health, Melee, Range, Magic, Defence, Prayer, Slayer,

        //Gathering Skills
        Mining, Fishing, Woodcutting, Hunting, Farming, Thieving,

        //Production Skills
        Cooking, Smithing, Fletching, Firemaking, Herblore, Crafting, Construction
    }

    public class Skill {
        public int currentLevel = 1;
        public int totalLevel = 1;

        public int currentExp = 0;
        public int nextLevelAt = 10000;

        public int expRemaining;
    }

    public Dictionary<SkillTypes, Skill> skills = new Dictionary<SkillTypes, Skill>() {
        //Combat Skills
        { SkillTypes.Health, new Skill() }, { SkillTypes.Melee, new Skill() }, { SkillTypes.Range, new Skill() }, { SkillTypes.Magic, new Skill() },
        { SkillTypes.Defence, new Skill() }, { SkillTypes.Prayer, new Skill() }, { SkillTypes.Slayer, new Skill() },

        //Gathering Skills
        { SkillTypes.Mining, new Skill() }, { SkillTypes.Fishing, new Skill() }, { SkillTypes.Woodcutting, new Skill() },
        { SkillTypes.Hunting, new Skill() }, { SkillTypes.Farming, new Skill() }, { SkillTypes.Thieving, new Skill() },

        //Production Skills
        { SkillTypes.Cooking, new Skill() }, { SkillTypes.Smithing, new Skill() }, { SkillTypes.Fletching, new Skill() }, { SkillTypes.Firemaking, new Skill() },
        { SkillTypes.Herblore, new Skill() }, { SkillTypes.Crafting, new Skill() }, { SkillTypes.Construction, new Skill() }
    };
}
  1. Yeah, it’s a dictionary, and what do you mean I keep looking it up?

  2. I realized that too when the level goes over 100 the exp doesn’t increase so the while loop just keeps going until the crash.

  3. What would I need to do so I wouldn’t have to loop it? I thought I needed the loop there so that if you get a large amount of exp that should level you up 3 times, it would only do it once without a loop, any suggestions?

Thanks for the help!

No, you were only late with the freezing part, I’m still interested in what you said about the other problems if you could help that would be great. :slight_smile:

When you do this:

skills[stat] ...

it’ll have to look up the value for the specified key.

You usually won’t notice this, but you actually do that lookup at least 7 times in your method, it’s more likely that it happens perhaps 15-20 times or if you level the skill up to let’s say 100 levels… it’ll do that hundreds or thousands of times - for each skill.
It involves serveral method calls each time you look it up.

Just do this

//start of your method
Skill currentSkill = skills[stat];
//or
var currentSkill = skills[stat];

and use ‘currentSkill’ everywhere in that method instead of ‘skills[stat]’.

If that’s a plain ‘I’ve got an amount X of exp and i simply need to determine the level it maps to’ approach, there’ll surely be a formula that expresses the whole algorithm with the same result.
These formulas are sometimes not as trivial, especially when something grows exponentially (well - that might still be easy) or irregularly (that’s a little more difficult), but once you figured them out, it’s usually a one time cost without any loops - most-likely just a straight calculation.

1 Like

Every time you use SomeDict[SomeKey] it’s performing a lookup of the data needed. This is pretty fast, but not completely instant. When you’re going to be performing a lookup more than once in the same frame, you should cache the result to a local variable so that you can just use that for the operations, so like Skill tempSkill = skills[stat]; or Skill tempSkill = skills[(SkillTypes)i];

1 Like

1 - So like this?:

public void ExpIncreased(SkillTypes stat, int exp) {
        var skill = skills[stat];

        skill.currentExp += exp;
        skill.expRemaining = skill.nextLevelAt - skill.currentExp;
        if (skill.expRemaining < 0) {
            skill.expRemaining = 0;
        }

        if (skill.totalLevel < 100) {
            while (skill.currentExp > skill.nextLevelAt) {

                if(skill.totalLevel < 100) {
                    skill.totalLevel += 1; print("Total level: " + skill.totalLevel);
                    print("Next level at: " + stat + ": " + skill.nextLevelAt); print("Current exp: " + stat + ": " + skill.currentExp); print("Exp remaining: " + stat + ": " + skill.expRemaining);
                    //skill.nextLevelAt += ((skill.totalLevel * 50 * skill.totalLevel * skill.totalLevel) / 4); print("Exp to level: " + skill.nextLevelAt);
                }
              
                if (skill.currentLevel < 100) {
                    skill.currentLevel += 1; print("Current level: " + skill.currentLevel);
                }
                skill.nextLevelAt += ((skill.totalLevel * 50 * skill.totalLevel * skill.totalLevel) / 4); print("Exp to level: " + skill.nextLevelAt);
            }
        }
        if (skill.totalLevel > 100) {
            skill.totalLevel = 100;
            print("Real total level: " + stat + ": " + skill.totalLevel);
        }
    }

I don’t have mine set up as predefined values for exp, it will add the exp once it levels and then recheck each time it gets exp to see if the cur exp is now higher than exp needed to level. Is this not a good, or at least not an efficient idea at all?

Yes, it’s effectively the same, but better in regards to performance.

That doesn’t necessarily mean you cannot have a formula for it. As I said, it’s sometimes not easy to extract a formula from an algorithm. It might be way simplier to take the cost and run it in a loop instead of spending time finding one. :smile:

1 Like

Yeah, I think for simplicity’s sake I’ll keep it in a loop, but if I wanted to make a formula for it, do you have any examples of a good one? I would like to get my head around it in the future but if you don’t that’s okay.

Not really. It’s also something you need to find and test, as this is very application specific.

1 Like

So the formula for exp increase? Or what for?

I was talking about a formula that determines the actual level when you throw in a certain amount of experience. A formula for exp-increase would logically be a part of it - otherwise that wouldn’t work at all, as the former depends on the latter. :slight_smile:
For complex ones you’d most-likely code it anyway - like stated before - so if that’s more efficient (in terms of your own time you wanna spend on it) leave it like that, as long as it works.
That’d be some kind of optimzation anyway which will be more important once everything else works as expected.

I was just poiting out that this is possible, so you may remember that once you feel like heading towards some major optimization phase.

1 Like

He’s saying the formula used to determine the amount of exp the character/skill needs to gain to increase from one level to the next- in some cases this doesn’t scale at all (so just “100 exp per level, always”), other times it’s a linear or simple exponential progression (like “it’s 1.2x more exp to level, each level”), but other times it’s really quite complex, for balance reasons.

For Final Fantasy VII, you can find the formula used here, to give an example. Using the formula, when applying large amounts of experience, you can jump right to the result (so like, “+5000 exp goes from level 1 to level 12, with 152 remainder”) without looping over and applying experience to each level individually.

2 Likes

Okay so this is one I found, it’s for a game called Runescape:

int experienceForLevel(int level)
{
    double total = 0;
    for (int i = 1; i < level; i++)
    {
        total += floor(i + 300 * pow(2, i / 7.0));
    }

    return floor(total / 4);
}

This has a for loop in it though, how would I do this sort of one without a for loop? Sorry about this too.

He wanted you to know that an optimization exists, not that you need to handle that optimization now. Premature optimization is a huge problem. I would just keep the loop, personally- For loops aren’t evil as long as you aren’t creating any variables inside of one. Cache your lookups (GetComponents, dictionary values, etc), create variables outside of (before) the loops instead of inside of them, and you’ll probably be fine.

I’m not a math expert, so I can’t tell you how to calculate this. Even if I could, I probably wouldn’t, because then what happens when you need to adjust this later for balance reasons? Easier to adjust the For loop, where the logic is simpler.

2 Likes

Alright thanks, I’ll keep my while loop as it is then, and modify how much exp is added when leveling up.

Maybe my posts were a bit misleading. Complex formulas usually use math functions, like pow, sqrt… And these are often implemented using some kind of loop or recursion or sometimes just various mathematical “tricks”, approximations and… well ye. Something that’s a given and is probably faster than anything you can implement on your own.

Perhaps I scared you a little bit too much, just wanted to let you know about it in general - since I didn’t take a closer look at how you increase exp. But I’m not a mathematician myself either. :stuck_out_tongue:

Off-topic & Fun fact: Even tho this seems to be a small part, it actually takes alot of alot of factors and testing into account. It’s somewhat part of game balancing - and balancing a game can be very difficult, even when you pick just a tiny factor like that.

I’ve never been part of “test-team” an exp-formula but that would be fun tho. I could imagine you need hours or days to make first decisions for a semi-decent and reasonable formula for a specific game. It’s interesting how a tiny change of a constant causes a completely different growth that effects user experience in many ways.
Once you’re done you probably don’t wanna plot functions anymore. xD

1 Like

Yeah, I’m not too great at maths either, well on this level anyway. And yeah I’ve been struggling to think of a good steady increase in exp per level, but I’ll get there soon enough. Thanks for all your help too! :slight_smile: