Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Code in loop freezing unity

Discussion in 'Scripting' started by Recable, Oct 21, 2017.

  1. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    Code (CSharp):
    1. public void ExpIncreased(SkillTypes stat, int exp) {
    2.         skills[stat].currentExp += exp;
    3.         skills[stat].expRemaining = skills[stat].nextLevelAt - skills[stat].currentExp;
    4.         if (skills[stat].expRemaining < 0) {
    5.             skills[stat].expRemaining = 0;
    6.         }
    7.         if (skills[stat].totalLevel < 100) {
    8.             while (skills[stat].currentExp > skills[stat].nextLevelAt) {
    9.  
    10.                 if(skills[stat].totalLevel < 100) {
    11.                     skills[stat].totalLevel += 1; print("Total level: " + skills[stat].totalLevel);
    12.                     skills[stat].nextLevelAt += ((skills[stat].totalLevel * 50 * skills[stat].totalLevel * skills[stat].totalLevel) / 4); print("Exp to level: " + skills[stat].nextLevelAt);
    13.                 }
    14.                
    15.                 if (skills[stat].currentLevel < 100) {
    16.                     skills[stat].currentLevel += 1; print("Current level: " + skills[stat].currentLevel);
    17.                 }
    18.                 //skills[stat].nextLevelAt += ((skills[stat].totalLevel * 50 * skills[stat].totalLevel * skills[stat].totalLevel) / 4); print("Exp to level: " + skills[stat].nextLevelAt);
    19.             }
    20.         }
    21.         if (skills[stat].totalLevel > 100) {
    22.             skills[stat].totalLevel = 100;
    23.             print("Real total level: " + stat + ": " + skills[stat].totalLevel);
    24.         }
    25.     }
    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.
     
  2. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    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.
     
    Recable likes this.
  3. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I'm always 1 or 2 minutes too late today. :mad:
     
    Recable likes this.
  4. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    This is my entire code;

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class StatsHandler : MonoBehaviour {
    6.  
    7.     EventManager eventManager = new EventManager();
    8.  
    9.     public bool gameStarted = false;
    10.  
    11.     public int[] currentStats;
    12.     public int[] totalStats;
    13.  
    14.     void Awake() {
    15.         currentStats = new int[skills.Count];
    16.         totalStats = new int[skills.Count];
    17.  
    18.         if (!gameStarted) {
    19.             for (int i = 0; i < skills.Count; i++) {
    20.                 currentStats[i] = skills[(SkillTypes)i].currentLevel;
    21.                 totalStats[i] = skills[(SkillTypes)i].totalLevel;
    22.             }
    23.         }
    24.     }
    25.  
    26.     void Start() {
    27.         if (!gameStarted) {
    28.             skills[SkillTypes.Health].currentLevel = skills[SkillTypes.Health].totalLevel;
    29.             gameStarted = true;
    30.             //StartCoroutine(UpdateStats());
    31.         }
    32.     }
    33.  
    34.     void Update() {
    35.         if(Input.GetKeyDown(KeyCode.P)) {
    36.             ExpIncreased(SkillTypes.Magic, 173857000);
    37.         }
    38.     }
    39.  
    40.     private void StatsToUpdate() {
    41.         for (int i = 0; i < skills.Count; i++) {
    42.             currentStats[i] = skills[(SkillTypes)i].currentLevel;
    43.             totalStats[i] = skills[(SkillTypes)i].totalLevel;
    44.         }
    45.         print("Updated stats!");
    46.     }
    47.  
    48.     /*IEnumerator UpdateStats() {
    49.         statsUpdateRunning = true;
    50.         while (gameStarted) {
    51.             StatsToUpdate();
    52.             yield return new WaitForSeconds(60f);
    53.         }  
    54.         statsUpdateRunning = false;
    55.     }*/
    56.  
    57.     public void ExpIncreased(SkillTypes stat, int exp) {
    58.         skills[stat].currentExp += exp;
    59.         skills[stat].expRemaining = skills[stat].nextLevelAt - skills[stat].currentExp;
    60.         if (skills[stat].expRemaining < 0) {
    61.             skills[stat].expRemaining = 0;
    62.         }
    63.  
    64.         if (skills[stat].totalLevel < 100) {
    65.             while (skills[stat].currentExp > skills[stat].nextLevelAt) {
    66.  
    67.                 if(skills[stat].totalLevel < 100) {
    68.                     skills[stat].totalLevel += 1; print("Total level: " + skills[stat].totalLevel);
    69.                     //print("Next level at: " + stat + ": " + skills[stat].nextLevelAt); print("Current exp: " + stat + ": " + skills[stat].currentExp); print("Exp remaining: " + stat + ": " + skills[stat].expRemaining);
    70.                     //skills[stat].nextLevelAt += ((skills[stat].totalLevel * 50 * skills[stat].totalLevel * skills[stat].totalLevel) / 4); print("Exp to level: " + skills[stat].nextLevelAt);
    71.                 }
    72.                
    73.                 if (skills[stat].currentLevel < 100) {
    74.                     skills[stat].currentLevel += 1; print("Current level: " + skills[stat].currentLevel);
    75.                 }
    76.                 skills[stat].nextLevelAt += ((skills[stat].totalLevel * 50 * skills[stat].totalLevel * skills[stat].totalLevel) / 4); print("Exp to level: " + skills[stat].nextLevelAt);
    77.             }
    78.         }
    79.         if (skills[stat].totalLevel > 100) {
    80.             skills[stat].totalLevel = 100;
    81.             print("Real total level: " + stat + ": " + skills[stat].totalLevel);
    82.         }
    83.     }
    84.  
    85.  
    86.     public enum SkillTypes {
    87.         //Combat Skills
    88.         Health, Melee, Range, Magic, Defence, Prayer, Slayer,
    89.  
    90.         //Gathering Skills
    91.         Mining, Fishing, Woodcutting, Hunting, Farming, Thieving,
    92.  
    93.         //Production Skills
    94.         Cooking, Smithing, Fletching, Firemaking, Herblore, Crafting, Construction
    95.     }
    96.  
    97.     public class Skill {
    98.         public int currentLevel = 1;
    99.         public int totalLevel = 1;
    100.  
    101.         public int currentExp = 0;
    102.         public int nextLevelAt = 10000;
    103.  
    104.         public int expRemaining;
    105.     }
    106.  
    107.     public Dictionary<SkillTypes, Skill> skills = new Dictionary<SkillTypes, Skill>() {
    108.         //Combat Skills
    109.         { SkillTypes.Health, new Skill() }, { SkillTypes.Melee, new Skill() }, { SkillTypes.Range, new Skill() }, { SkillTypes.Magic, new Skill() },
    110.         { SkillTypes.Defence, new Skill() }, { SkillTypes.Prayer, new Skill() }, { SkillTypes.Slayer, new Skill() },
    111.  
    112.         //Gathering Skills
    113.         { SkillTypes.Mining, new Skill() }, { SkillTypes.Fishing, new Skill() }, { SkillTypes.Woodcutting, new Skill() },
    114.         { SkillTypes.Hunting, new Skill() }, { SkillTypes.Farming, new Skill() }, { SkillTypes.Thieving, new Skill() },
    115.  
    116.         //Production Skills
    117.         { SkillTypes.Cooking, new Skill() }, { SkillTypes.Smithing, new Skill() }, { SkillTypes.Fletching, new Skill() }, { SkillTypes.Firemaking, new Skill() },
    118.         { SkillTypes.Herblore, new Skill() }, { SkillTypes.Crafting, new Skill() }, { SkillTypes.Construction, new Skill() }
    119.     };
    120. }
    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!
     
  5. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    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. :)
     
  6. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    1)
    When you do this:
    Code (csharp):
    1. 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
    Code (csharp):
    1. //start of your method
    2. Skill currentSkill = skills[stat];
    3. //or
    4. var currentSkill = skills[stat];
    and use 'currentSkill' everywhere in that method instead of 'skills[stat]'.


    3)
    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.
     
    Last edited: Oct 21, 2017
    Recable likes this.
  7. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    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];
     
    Recable likes this.
  8. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    1 - So like this?:

    Code (CSharp):
    1. public void ExpIncreased(SkillTypes stat, int exp) {
    2.         var skill = skills[stat];
    3.  
    4.         skill.currentExp += exp;
    5.         skill.expRemaining = skill.nextLevelAt - skill.currentExp;
    6.         if (skill.expRemaining < 0) {
    7.             skill.expRemaining = 0;
    8.         }
    9.  
    10.         if (skill.totalLevel < 100) {
    11.             while (skill.currentExp > skill.nextLevelAt) {
    12.  
    13.                 if(skill.totalLevel < 100) {
    14.                     skill.totalLevel += 1; print("Total level: " + skill.totalLevel);
    15.                     print("Next level at: " + stat + ": " + skill.nextLevelAt); print("Current exp: " + stat + ": " + skill.currentExp); print("Exp remaining: " + stat + ": " + skill.expRemaining);
    16.                     //skill.nextLevelAt += ((skill.totalLevel * 50 * skill.totalLevel * skill.totalLevel) / 4); print("Exp to level: " + skill.nextLevelAt);
    17.                 }
    18.              
    19.                 if (skill.currentLevel < 100) {
    20.                     skill.currentLevel += 1; print("Current level: " + skill.currentLevel);
    21.                 }
    22.                 skill.nextLevelAt += ((skill.totalLevel * 50 * skill.totalLevel * skill.totalLevel) / 4); print("Exp to level: " + skill.nextLevelAt);
    23.             }
    24.         }
    25.         if (skill.totalLevel > 100) {
    26.             skill.totalLevel = 100;
    27.             print("Real total level: " + stat + ": " + skill.totalLevel);
    28.         }
    29.     }
     
  9. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    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?
     
    Last edited: Oct 21, 2017
  10. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    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. :D
     
    Recable likes this.
  11. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    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.
     
  12. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Not really. It's also something you need to find and test, as this is very application specific.
     
    Recable likes this.
  13. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    So the formula for exp increase? Or what for?
     
  14. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    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. :)
    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.
     
    Recable likes this.
  15. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    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.
     
    Suddoha and Recable like this.
  16. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    Okay so this is one I found, it's for a game called Runescape:

    Code (CSharp):
    1. int experienceForLevel(int level)
    2. {
    3.     double total = 0;
    4.     for (int i = 1; i < level; i++)
    5.     {
    6.         total += floor(i + 300 * pow(2, i / 7.0));
    7.     }
    8.  
    9.     return floor(total / 4);
    10. }
    This has a for loop in it though, how would I do this sort of one without a for loop? Sorry about this too.
     
  17. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    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.
     
    Suddoha and Recable like this.
  18. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    Alright thanks, I'll keep my while loop as it is then, and modify how much exp is added when leveling up.
     
  19. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    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. :p

    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
     
    Last edited: Oct 21, 2017
    Recable likes this.
  20. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    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! :)
     
  21. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    Actually, alongside this, I just looked up about pow and sqrt in c#, seems good but I do have a question because it's ont defined in the code I'm wondering - if the "floor" in this code like pow/sqrt in which it has a special meaning, or if not do you possibly know what it's there for?

    Code (CSharp):
    1. int experienceForLevel(int level)
    2. {
    3.     double total = 0;
    4.     for (int i = 1; i < level; i++)
    5.     {
    6.         total += floor(i + 300 * pow(2, i / 7.0));
    7.     }
    8.     return floor(total / 4);
    9. }
     
  22. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    probably rounding down :)
     
    Recable and TaleOf4Gamers like this.
  23. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    So just placing an int floor; on the top

    Edit: or var floor;
     
  24. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    If what you said means 3.7 = 3 , then yes :)
     
    Recable likes this.
  25. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    So confused :confused::confused:
     
  26. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Floor is a function that takes a floating point number and rounds it down the nearest integer, most likely :)
     
    Recable likes this.
  27. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    Oh right yeah thanks, should have figured that out myself, haha... :oops:
     
  28. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    I do have another problem, I have been trying out many different things to test the code and make sure it works, it was all going well till I found that adding a huge amount of exp on the first exp amounts given makes unity crash. Here's the code for that specific part:

    Code (CSharp):
    1. void Update() {
    2.         if(Input.GetKeyDown(KeyCode.S)) {
    3.             ExpIncreased(SkillTypes.Magic, 4);
    4.         }
    5.  
    6.         if (Input.GetKeyDown(KeyCode.M)) {
    7.             ExpIncreased(SkillTypes.Magic, 40);
    8.         }
    9.  
    10.         if (Input.GetKeyDown(KeyCode.B)) {
    11.             ExpIncreased(SkillTypes.Magic, 400);
    12.         }
    13.  
    14.         if (Input.GetKeyDown(KeyCode.L)) {
    15.             ExpIncreased(SkillTypes.Magic, 4000);
    16.         }
    17.  
    18.         if (Input.GetKeyDown(KeyCode.H)) {
    19.             ExpIncreased(SkillTypes.Magic, 40000);
    20.         }
    21.     }
    Code (CSharp):
    1. public void ExpIncreased(SkillTypes stat, int exp) {
    2.         int maxSkillLevel = 100;
    3.         var skill = skills[stat];
    4.         var lvlNext = (((70) + skill.totalLevel * 7 * skill.totalLevel));
    5.  
    6.         if (firstLoopExpIncreased == false) {
    7.             skill.nextLevelAt += lvlNext;
    8.             //print("First exp to level: " + skill.nextLevelAt);
    9.             firstLoopExpIncreased = true;
    10.         }
    11.         skill.currentExp += exp;
    12.  
    13.         if (skill.totalLevel < maxSkillLevel) {
    14.             while (skill.currentExp > skill.nextLevelAt) {
    15.                 if(skill.totalLevel < maxSkillLevel) {
    16.                     skill.totalLevel += 1;
    17.                     skill.nextLevelAt += lvlNext;
    18.                     //print(stat + " total level: " + skill.totalLevel);
    19.                 }
    20.                 if (skill.currentLevel < maxSkillLevel) {
    21.                     skill.currentLevel += 1;
    22.                     //print(stat + " total level: " + skill.currentLevel);
    23.                 }
    24.             }
    25.         }
    26.         if (skill.totalLevel > maxSkillLevel) {
    27.             skill.totalLevel = maxSkillLevel;
    28.             //print("Max level done: " + maxSkillLevel);
    29.         }
    30.         skill.expRemaining = skill.nextLevelAt - skill.currentExp;
    31.  
    32.         if (skill.expRemaining < 0) {
    33.             skill.expRemaining = 0;
    34.         }
    35.         magicLevel.text = "Magic level: " + skill.currentLevel + "/" + skill.totalLevel;
    36.         nextLevelAt.text = "Next level at: " + skill.nextLevelAt;
    37.         curMagicExp.text = "Current magic exp: " + skill.currentExp;
    38.         remainingMagicExp.text = "Remaining magic exp: " + skill.expRemaining;
    39.     }
    When I press H for the first ExpIncrease it causes unity to crash, where if I do any of the others first, then press H it goes fine.

    Edit: Further testing it is showing that I have to press L first to add 4000 exp, then I can start using H to add 40000 exp, strange, I don't have any ideas what's causing it at the moment...
     
    Last edited: Oct 21, 2017
  29. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    Okay even more so now, when I add 4000 it jumps to level 52 when it should be on level 14, because when adding smaller amounts it goes to there... so confused now
     
  30. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    I think it might be related to the first nested if statement inside the while loop.
     
  31. Recable

    Recable

    Joined:
    Sep 12, 2017
    Posts:
    61
    Nevermind, it's all sorted now.