Exp Checking

Is this a good way to check my skills for what exp they’re on, and running ExpIncreased(); everytime my player gains experience?

public void ExpIncreased() {
        for (int i = 0; i < skills.Count; i++) {
            if (skills[(SkillTypes)i].currentExp >= skills[(SkillTypes)i].expToLevel) {
                skills[(SkillTypes)i].total += 1;
                print("i = " + i); //know what's been leveled up
            }
        }
    }

You can have an event handler for the player that calls ExperienceGained everytime player gains experience.

Given that your skills are a class when a player has a new skill added to his skill-list, that skill subscribes to that event and executes OnExperienceGained, which in turn would check if experience to level up has been reached.

1 Like

The players have all the skills from the start, would your method still be usable as you said about skills being added.

Also, what would an example be if possible? I learn best from examples so I can break down the code and learn it, thanks in advance!

Here’s my entire code, if you know anyways of improving it could you say please, I want to learn how to make my code more efficient.:slight_smile:

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

[System.Serializable]
public class PlayerStats : MonoBehaviour {

    public bool gameStarted = false;

    //Health
    public int healthRegenAmount; public float healthRegenTimer;
    [SerializeField] private bool isRegeningHealth = false;

    bool statsUpdateRunning;

    [Header("Stats")]
    public int[] currentStats;
    public int[] totalStats;

    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() }
    };

    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].current;
                totalStats[i] = skills[(SkillTypes)i].total;
            }
        }
    }

    // Use this for initialization
    void Start() {
        if (!gameStarted) {
            skills[SkillTypes.Health].current = skills[SkillTypes.Health].total;
            gameStarted = true;
            StartCoroutine(UpdateStats());
        }
    }

    // Update is called once per frame
    void Update() {
        //Regen health
        if (skills[SkillTypes.Health].current != skills[SkillTypes.Health].total && !isRegeningHealth) {
            StartCoroutine(HealthRegen());
        }

        if (Input.GetKeyDown(KeyCode.A)) {
            skills[SkillTypes.Construction].current += 1;
            skills[SkillTypes.Construction].total = skills[SkillTypes.Construction].current;
            StatsToUpdate();
        }

        if (Input.GetKeyDown(KeyCode.S)) {
            CheckExpAmount();
        }

        if (!statsUpdateRunning) {
            StartCoroutine(UpdateStats());
        }
    }

    IEnumerator HealthRegen() {
        isRegeningHealth = true;
        while (skills[SkillTypes.Health].current < skills[SkillTypes.Health].total) {
            skills[SkillTypes.Health].current += healthRegenAmount;
            if (skills[SkillTypes.Health].current > skills[SkillTypes.Health].total) {
                skills[SkillTypes.Health].current = skills[SkillTypes.Health].total;
            }
            yield return new WaitForSeconds(healthRegenTimer);
        }
        isRegeningHealth = false;
    }

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

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

    public void CheckExpAmount() {
        for (int i = 0; i < skills.Count; i++) {
            if (skills[(SkillTypes)i].currentExp >= skills[(SkillTypes)i].expToLevel) {
                skills[(SkillTypes)i].total += 1;
                print("i = " + i); //know what's been leveled up
            }
        }
    }
}

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 current = 1;
    public int total = 1;
    public int currentExp = 110;
    public int expToLevel = 100;
}

You should simply read a book or do some general C# tutorials- no amount of examples make up for a lack of basic programming knowledge. That’s not a judgment, just trying to help you out- take the time and do the work and you’ll be far better off in the end than being spoonfed answers piecemeal whenever you run into a problem.

To clarify a bit though, you make an event handler which is a list of delegates to call when something happens. UnityEvents are a pretty good example. You set up the event “ExpGainedEvent” in your status script, and then other systems/scripts subscribe to that event as listeners. This means that when the event is fired (invoked), then all listeners (registered functions) are called- anyone interested in knowing when EXP is increased can subscribe to the event with a function to call when that event occurs.

In this case, if you have an event called “public UnityEvent ExpGainedEvent;” in your status script, then on Awake (or whenever), the SkillManager script can find that Status script (GetComponent), and then find that event in the Status script, and register a function to be called when that event fires- OnExpGained(int expAmount), or something. When that event is invoked with a given amount of exp as a parameter, then OnExpGained(int expAmount) will get run on the SkillManager (and any other listeners). This means you can have many different scripts registered all over the game to listen for exp being gained, like your UI (that’s a big one), skill managers, etc…

You can also have additional events for when a level is gained, when damage is taken, etc… Events are huge in good game design. Check out this video in the Learn section for more information on events and how they work in Unity.

1 Like

Thanks but to be fair I haven’t actually encountered a problem, and if I was to find a problem I would research it and ask, it helps me with doing both, I honestly didn’t just leave a post for all the code on events, I went off right away and started looking up more about them and will be doing more so.

My main question was if using a for loop like the way I did was a good idea (yes or no?) because it worked as I wanted however as I said before I’d like to make my code as efficiently as possible. Thanks again.

I agree with @DonLoquacious you should be aware of C# basics and its features in order to benefit in the long run.

I couldn’t wrap my head around the delegates and events until I watched this video.

With that said, it’s a good thing to look up the C# basics to take advantage of Interfaces, inheritance etc.

Also, from the script that you posted, it’s not a good practice to have input listener in a script like PlayerStats. Try split large scripts into smaller bits.

I recommend this book, Game Programming Patterns that you can read online for free.

To answer your question, OP, it’s not bad to use loops, but it’s not good to use loops for leveling up player’s skills. As mentioned, using events is a good approach.

The problem you encountered here was the lack of knowledge that events exist, not an issue in using them, and that’s a far greater concern and why I brought up reading/studying. You can’t know what tools are appropriate if you don’t know what tools are available. 99% of problems in the support forum here are people who “learn better by seeing examples” and so don’t ever bother with any generalized studying of programming, C#, or Unity.

For loops are fine- you need to iterate through the whole skill collection somehow if you mean to apply the exp amount to all of them. However, you probably want to just run a “level up skill” function when the exp is high enough, or reduce the current exp by the expToLevel amount when leveling up occurs, or increase the expToLevel by a given amount. As it stands, it’ll level up the skill every single time you gain exp after the threshold is met.

That’s really my only observation there- premature optimization is a massive problem though, so don’t worry too much about efficiency until efficiency becomes an actual issue, or read a book/tutorials on efficient programming.

Sorry my code wasn’t fully completed, I wanted to know if a for loop would be good for exp gains, my code would include the total exp needed to increase too so it wouldn’t always level up.

I also knew about events, but I thought of doing the code using for loops, and I thought it may not be the correct way so I asked.

Probably still not complete but this does the trick so far:

public void CheckExpAmount() {
        for (int i = 0; i < skills.Count; i++) {
            if (skills[(SkillTypes)i].currentExp >= skills[(SkillTypes)i].expToLevel && skills[(SkillTypes)i].totalLevel < 100) {
                skills[(SkillTypes)i].totalLevel += 1;
                skills[(SkillTypes)i].currentLevel += 1;
                skills[(SkillTypes)i].expToLevel += 1000; //example
                if(skills[(SkillTypes)i].totalLevel >= 100 || skills[(SkillTypes)i].currentLevel >= 100) {
                    skills[(SkillTypes)i].totalLevel = 100;
                    skills[(SkillTypes)i].currentLevel = 100;
                }
                print("i = " + i); //know what's been leveled up
            }
        }
    }

Okay thanks, I’ll look further into events.

Edit: What I don’t fully understand is how to access all my values in my dictionary without a loop?

If you’re going to use delegates, PlayersStats wouldn’t need to worry about that at all. All it has to do is to is subscribe skill’s method (which is going to level up skill levels) to an event which is being invoked everytime player gains XP.

You will understand it all once you watch the video closely.

Okay, I watched the video, still a bit confused, I’ll have more of a scan and see if I can work it out.

Edit: I’ve finally figured it all out fully, thanks all for your help.

1 Like