Skills based system

This one is one I’ve tried avoiding for a long time, but I believe it will eventually have to be done, at least for the style of my game, so here it goes:

The way the combat works in this game, is that you gain experience and level up, right? I want to implement this for other systems as well, such as woodcutting trees, mining rocks, fishing for food, smithing the mined products, cooking the hunted fish/animals, maybe hunting down animals, etc.

So far I’m absolutely enjoying the combat system in this game, but I don’t see the beauty of, for example, being limited off cutting a tree by getting my combat to 60, or crafting a weapon at combat 60… What does the combat level have to do with my ability to craft, maybe build a house, mine a rock… all those skills, you get my idea?

For test purposes, I want to split my magic, ranged and melee attacks into their own XP-levelling based systems, and then each time all 3 add up, I want the final level to increase as a result, and then using the same logic, I can have Crafting XP, Woodcutting XP, Mining XP, hunting XP, etc… Unlike the melee, ranged and mage attacks, these ones can just increase on their own without affecting anyone else. So it’s in 4 components:

  • Combat Skills (Ranged, Mage, Melee, and when they all get levelled up, we add our total Combat level to +1, and that’s when we get our trait points)
  • Non-combat level skills. Similar to the current progression of our combat level, these grow by performing specific tasks (for mining, it’s mining rocks. For smithing/crafting, crafting stuff, woodcutting = cutting down trees, etc… XP is given, and then you level up)
  • More importantly, everytime we level a combat skill, we need to be able to wield a new weapon (that’s taken care of in the final course, but I want to add to the enum the specific combat skills for them… I don’t want a melee level unlocking a ranged level for example, and non-combat skills can unlock new resource gathering stuff like rocks for mining, better crafting tables for crafting, smithing for better furnaces, etc)
  • Above all else, the sense of progression is important, so just like how damage improves as we grow in level, I want the ability of the player to mine/woodcut/cook food/hunt/etc… to improve as his level improves (so the higher your level, the easier it is to gather resources that require lower levels, and the better resources you can collect in the game (E.G: cut down better resource trees, mine better rocks, etc))

So, my question is, what would I need to consider to change them? I don’t want to remove the Traits system (not yet at least, I like some of its features. The traits will rely on the total level of mage, range and melee attacks combined to give you points… basically it’ll rely on the COMBINED combat level of mage, ranged and melee attacks), but I want to add this one as well on the side. One that does not rely on combat and allocated points, but it relies on XP that you gain to improve skills BY DOING THESE SKILLS

I know for a fact that Inheritance/Polymorphism will probably play a huge role in this one, but I’m keen on developing it as well. It’s another rewarding system I want involved in my game :slight_smile: (I found a few YouTube tutorials, but boy will they all screw the entire system up… This one was interesting though: https://www.youtube.com/watch?v=H3L0JRtqWP8)

Just do it. It’s the same as the combat xp and leveling, except that it’s for other things. Punch a guy, update combat xp. Punch a tree, update tree punching xp

Don’t we need an Enumerator for that? The inspector and where and how to add this is a bit baffling

‘Tree punching xp’ gave me a good laugh tho, NGL

Pretend you don’t have a combat xp and leveling system. Then, do everything you did when you did the combat xp and leveling, but call it tree punching instead. When you’re done with all that, you will now have 2 identical systems; one for combat, one for tree punching. Repeat for everything else.

This is, of course, the simple way. The more complicated way would probably be to go through the whole combat system and change it to hold multiple, identifiable values. You’d change it from a combat system to a skills system. It would still be exactly the same, but places where you now get the level - for instance - would be changed to get the level for a specific thing; GetLevel(SkillType.Combat) or GetLevel(SkillType.TreePunching). So everything would still be the way it is now, but you’d be identifying specific skills instead of just the one that’s currently there

On a scale of 1-10, how “complicated” is this solution? I find this one more desirable, because not only does it make selecting values off an enum better, but it’s also probably better performance-wise… I want the “complicated” way :confused:

I don’t know, I haven’t tried it

It’s probably not. The first way deals with one value all the time. This way needs to find that value in a list/dictionary first. Every time

So do it. You have everything you need in the course. Everything is the same, you’re just adding more values to it, and a way to identify those values

Considering what you just helped me accomplish, I wouldn’t be surprised if you come up with one tomorrow morning… It’s going to be a neat little fun system though. Regardless, I’ll see what I can come up with based on the course content

Considering what you just said about performance, then I think I’ll go with the ‘simpler’ approach. Let’s see what @Brian_Trotter thinks of this as well (Brian, if I were to develop another skilling system, what do I need to consider to make it work properly? I might create multiple ones, one for crafting, woodcutting, mining, maybe smithing, firemaking, etc… so it’s gonna be generic)

After we fix the arrow placing glitch, we can get to work on this next :slight_smile:

You mean you want it to be just like Runescape? :slight_smile: Once you’ve identified the skills that added up get a level, you just have to add up those skills.

Probably a 5…

Wouldn’t be my first approach…

First up, this is a case that I’m not going to write, because believe it or not, the skills you need to write this system have all been taught throughout the course.

I will give you a nudge, because I don’t want you writing a new component for each experience type.

First, you’re going to need a way to identify the skill… You have two choices here: You can use an enum based system:

public enum SkillType
{
    Melee,
    Magic,
    Ranged,
    TreePunching, //Sorry, couldn't resist, Bixario
    FlowerPlucking, //Really, just can't stop with them now
    RockBashing, //Last one, I promise
    Crafting,
    Jumping,
    Smithing,
    Cooking,
    FireStarting,
    Bartering,
    Convincing
}

You could also simply use magic strings. The advantage of magic strings are that you can simply use them in the Editor without recompiling. The disadvantage is that medical science has proven that aside from a little girl in Peoria Illinois who won the spelling bee in 1972, not a single human on the planet can consistenly spell anything, and magic strings are a constant source of bugs within programs when used. I know they are when I use them. @bixarrio can probably attest to times they’ve tripped him up as well.

Once you have your skills defined, then you need to improve your experience.

Actually, in this case, I’d probably bypass experience altogether in lieu of a SkillExperience component…
This component needs a couple of simple things:

  • A Dictionary<skill, int> experiences.
  • A GetExperience() method taking in a skill, returning an experience. If the Dictionary doesn’t have the skill, set the experiences[skill] to 0 and return 0
  • An AddExperience() method, taking in a skill, adding to the experience. Again, if the Dictionary doesn’t have the experience, set the experiences[skill] to the incoming experience.
  • A way to save/load the dictionary.
  • An event that indicates that experience has changed.

You need a Skills component with the level of the skills. For NPCs, you’ll just set these in some sort of array. For players, you’ll calculate them based on the attached SkillExperience.
The Skills component will need:

  • A GetLevel(skill) which returns either a hard coded level or one calculated from the SkillExperience
  • A component similar to Progression that defines the experience required for each level of experience for each given skill. You might consider simply having a formula for this that is applied to all skills.

Whenever an action needs some sort of skill for success or effectiveness, get the level from the Skills.

For example: I want to cut down a Yew tree, but this requires level 50 TreeBashing, and I only have level 2 TreeBashing. I don’t cut the tree down, but I might earn a small amount of experience in TreeBashing

Whenever an action should give some sort of experience, simply find the SkillExperience and AddExperience(the relevant skill).
For example: I cut down the Yew Tree, and in addition to getting a Yew log, I also get 50 experience in TreeBashing.

Oh come on… RuneScape was a lot of fun. Who doesn’t want that :stuck_out_tongue_winking_eye: (and ironically, I found it last night, the levelling formula is in the link in the end of my question) - anyway… I’ll go continue reading the answer and ask for help accordingly, if I get stuck :slight_smile:

I think you meant ‘TreePunching’ :stuck_out_tongue_winking_eye: - the Bashing is for ‘RockBashing’ xD

I’m genuinely surprised you labelled this as a 5. I would’ve said it’s nothing less than a straight 10, or at least harder than our Equipment drop system… it sounds too hard, but I asked anyway…

They’re not exactly what I would define as ‘simple’ (I still get heavily baffled by scary-looking code), but here is my attempt (I created a ‘Skill.cs’ script on the side, it’s currently empty, to stop ‘Skill’ from complaining… It’s on the next comment):

P.S: I edited the script according to @bixarrio 's changes below :slight_smile:

using System;
using System.Collections.Generic;
using GameDevTV.Saving;
using UnityEngine;
using Newtonsoft.Json.Linq;

namespace RPG.Stats.Skills {

public class SkillExperience : MonoBehaviour, IJsonSaveable
{
    
    // A dictionary for experiences
    private Dictionary<SkillType, int> experiences = new Dictionary<SkillType, int>();

    // Event action to indicate the change of the skill
    public event Action onExperienceGained;

    public int GetExperience(SkillType skillType) {

        if (!experiences.ContainsKey(skillType)) {

            experiences[skillType] = 0;

        }

        return experiences[skillType];

        }

    public void AddExperience(SkillType skillType, int experience) {

            if (experiences.ContainsKey(skillType)) {

                experiences[skillType] += experience;

            }

            else experiences[skillType] = experience;

            onExperienceGained?.Invoke();

        }

        public JToken CaptureAsJToken()
        {
            return JToken.FromObject(experiences);
        }

        public void RestoreFromJToken(JToken state)
        {
            // Not sure what to do here, but the line below is not working:
            // experiences = state.ToObject<SkillType, int>();
        }
    }

}

No compiler errors, but is the logic alright? (P.S: Don’t get me started with the JSON, this really REALLY REALLY baffles me, so I didn’t want to get there yet. I did watch the binaryformatter videos, and whilst they made sense, JSON was just a mindblow… I know we will need that. Please help me and I’ll do my best to follow up :slight_smile: )

and this is my ‘SkillExperience.cs’ script (again, this one was a little too confusing for me, but I gave it a go anyway… I don’t think I’m on the right track though):

P.S: I edited the script according to @bixarrio 's recommended changes below as well :slight_smile:

using System.Collections.Generic;
using UnityEngine;

namespace RPG.Stats.Skills {

public class Skill : MonoBehaviour {
    
    private Dictionary<SkillType, int> skillLevels = new Dictionary<SkillType, int>();

    [SerializeField] int startingLevel;
    [SerializeField] CharacterClass characterClass;
    [SerializeField] Progression progression;

    public int GetLevel(SkillType skillType) {

            if (skillLevels.ContainsKey(skillType)) {
                return skillLevels[skillType];
            }

            else return CalculateLevel(skillType);

        }

        private int CalculateLevel(SkillType skillType) {

            SkillExperience skillExperience = GetComponent<SkillExperience>();

            if (skillExperience == null) return startingLevel;

            float currentXP = skillExperience.GetExperience(skillType);
            int maxLevel = 200;

            for (int level = 0; level <= maxLevel; level++) {

                float XPToLevelUp = progression.GetStat(Stat.ExperienceToLevelUp, characterClass, level);
                if (XPToLevelUp > currentXP) return level;

            }

            return maxLevel + 1;

        }

    }

}

Values seem a little off, especially with this whole “maxLevel = 200;” thing, as I was not sure how to accumulate that tbh. Please help me out :slight_smile: (P.S: I’m not sure of how to use these scripts, assuming they’re right…)

This looks fine to me, except that the dictionary key should probably be the SkillType enum if you are using that, or a string if you want to try and not make typos.


Little tip - doesn’t matter, just something:

    public int GetExperience(Skill skill) {

        if (!experiences.ContainsKey(skill)) {

            experiences[skill] = 0;
            return 0;

        }

        return experiences[skill];

        }

You don’t need to return 0; If the entry doesn’t exist, you will add it with an initial value of 0. The code will then continue and return that newly added entry that is equal to 0

public int GetExperience(Skill skill)
{
    if (!experiences.ContainsKey(skill))
    {
        experiences[skill] = 0;
    }

    return experiences[skill];
}

The json saving system is nearly exactly the same as the binary system. The only difference is that you are returning a JToken object instead of the C# object. You do everything the same way you would for the binary system and in the end, instead of returning the data, you return the JToken.FromObject(data). When you restore, you don’t cast the C# way (type)state, you get it from the JToken again state.ToObject<type>(). That’s really all there is to it. Everything else is still the same

This, too (at first glance), looks fine. Again, you probably want the enum/string as the dictionary key. I personally would calculate the experience with a formula instead of getting it from a list, but that’s just me. There are many ways. You can check this article on converting levels to xp and vice versa

Check the link in my post, it’s a 21-minute video that goes through the formula used by RuneScape, and in the stuff below the video there’s a link to the RuneScape XP formula. My plan was to use a similar formula but to fine tune it, so it’s easy at the start, but it gets harder as time goes by. Alls good though, I’m still trying to figure out how do we even use that script and what changes/addons do we need, let alone the formula for now

I’ll go through the changes you mentioned though

For now I came up with my own list of skills I want involved in the game xD (some of them will require the development of sophisticated systems, but overall… they’re mostly there already in one way or the other) - but let’s wait for @Brian_Trotter so we know what’s next

experiences = state.ToObject<Dictionary<SkillType, int>>();

I tend towards formulas rather than the course’s progression as well… There are actually many many many paths to a good experience per level formula…

Here’s one I’ve been using that provides a summation based curve

int baseCost = 10; //This is the seed cost

int GetNeededExperience(int level)
{
     int result = 0;
     for(int i=1;i<l=level;i++) result+=baseCost*i;
}

this formula uses a summation of all of the values that came before it.
So assuming our BaseCost was 10, then

1=10
2=30
3=60
4=100
5=150
6=210
7=280
200 = 201000

Now in my case, I prefer the experience to be cost for the current level… in other words, to advance from level 1 to 2, you need 10 points. Then experience is reset, and to advance from level 2 to level 3, experience is 30 points (so technically, you really need 40 points to get to level , but experience is always displayed for just that level). With that in mind, 201000 isn’t that unreasonable for level 200. If you wanted to keep a simple linear experience, you might want higher numbers.

OK so… how do we use all of that to make this whole thing work? Like how do we get the game to overhaul our current XP-based system for this one (we are overhauling the old XP system for the new one, right?), what changes do we need to do? (I’m guessing there’s a challenge hidden for me here :stuck_out_tongue_winking_eye: - please give me hints xD)

and the script above this sentence goes into ‘SkillExperience.cs’ right? (Compiler is lowkey complaining of unreachable code for some reason… I’ll play with the formula a bit though). How do we also get the remaining XP till the next level to show up…?

Nah in my case I would want the value to keep incrementing rather than resetting itself, so the players can get a little more motivated to want to see how far they got (so it doesn’t feel that bad when they still got work until the next level)

and was ‘SkillExperience.cs’ correct? I have my suspicions on this script, along with ‘CalculateLevel’ on ‘Skill.cs’… I just want to double check them both

and finally (bear with me, I know I’m asking a bit too many questions now), how do we factor in failure into the formula? In other words, how do we make it harder for the player to actually get something at a low level (assuming it’s unlocked), but the probability gets easier as the levels increase? Same goes for high level stuff. So let’s say for example if you’re cutting down a Yew Tree, how do we ensure your probability of success remains low at level 60 (that’s the unlocking level, so it’s expected to be hard), but for example gets (somewhat) easier at level 80, and then a little easier at level 90, and a bit easier at 100, etc…?

That’s a lot of questions packed in there…

Let’s start with a freebie… how I would store and implement skills:

SkillExperience.cs
using System.Collections.Generic;
using GameDevTV.Saving;
using Newtonsoft.Json.Linq;
using UnityEngine;

namespace RPG.Skills
{
    public enum Skill
    {
        Melee,
        Casting,
        Ranged,
        Dodging,
        Resisting,
        Skinning,
        Herbalism,
        LumberJacking,
        Mining,
        Smelting,
        Blacksmithing,
        LeatherWorking,
        PotionMaking,
        Fishing,
    }
    
    /// <summary>
    /// Convenient container for all things skill related.  What value represents
    /// depends on context, and could represent experience, a level, or a base value per level. 
    /// </summary>
    [System.Serializable]
    public struct SkillRecord
    {
        public Skill skill; 
        public int value;
    }
    
    public class SkillExperience : MonoBehaviour, IJsonSaveable
    {
        private Dictionary<Skill, int> experiences = new();

        public event System.Action skillExperienceUpdated;

        /// <summary>
        /// Awards experience points towards a specific skill.
        /// This will trigger skillExperienceUpdated event
        /// </summary>
        /// <param name="skill">Skill to increase</param>
        /// <param name="experience">Amount to award</param>
        public void GainExperience(Skill skill, int experience)
        {
            if(!experiences.TryAdd(skill, experience))
            {
                experiences[skill] += experience;
            }
            skillExperienceUpdated?.Invoke();
        }

        /// <summary>
        /// Returns current experience points of a given skill.  
        /// </summary>
        /// <param name="skill">Skill to retrieve</param>
        /// <returns>Current experience.  0 if the skill has not been added to the experiences.</returns>
        public int GetExperience(Skill skill)
        {
            return experiences.TryGetValue(skill, out int result) ? result : 0;
        }

        /// <summary>
        /// Gets a list of all experience values in experience as SkillRecords.   
        /// </summary>
        /// <returns>Collection of SkillRecords where value = experience</returns>
        public IEnumerable<SkillRecord> GetSkillRecords()
        {
            foreach (KeyValuePair<Skill,int> pair in experiences)
            {
                yield return new SkillRecord() { skill = pair.Key, value = pair.Value };
            }
        }
        
        
        public JToken CaptureAsJToken()
        {
            return JToken.FromObject(experiences);
        }

        public void RestoreFromJToken(JToken state)
        {
            experiences = state.ToObject<Dictionary<Skill, int>>();
            skillExperienceUpdated?.Invoke();
        }

        public int SavePriority()
        {
            return 0;
        }
    }
}
SkillFormula.cs
using System.Collections.Generic;
using UnityEngine;

namespace RPG.Skills
{
    /// <summary>
    /// ScriptableObject to hold base costs for given skills.  This class holds only the base cost, which is
    /// then used in the SkillStore to calculate a given value per level using level! * cost.
    /// </summary>
    [CreateAssetMenu(fileName = "SkillCosts", menuName = "Skills/SkillFormula")]
    public class SkillFormula : ScriptableObject
    {
        [Header("Default")]
        [SerializeField] 
        [Tooltip("Base cost for any skill not in specific skill Base Costs")]private int defaultBaseCost = 10;
        [Header("Overrides")]
        [Tooltip("Enter any specific skills with custom base costs here")]
        [SerializeField] private List<SkillRecord> specificSkillBaseCosts = new List<SkillRecord>();
        
        /// <summary>
        /// Do not use this, it is the backing field for baseCosts
        /// </summary>
        private Dictionary<Skill, int> _baseCosts;

        /// <summary>
        /// Dictionary with cached values for customized base costs.  Uses lazy initialization.
        /// </summary>
        Dictionary<Skill, int> baseCosts
        {
            get
            {
                if (_baseCosts == null)
                {
                    _baseCosts = new Dictionary<Skill, int>();
                    foreach (SkillRecord record in specificSkillBaseCosts)
                    {
                        _baseCosts[record.skill] = record.value;
                    }
                }
                return _baseCosts;
            }
        }
        /// <summary>
        /// This is a little syntatic sugar trick.  Rather than using a public GetValue() method, this overrides
        /// the [] indexer, allowing you to get a specific record using [].  Indexer is safe for both when there is and
        /// isn't an entry in the skill, the default base cost is returned.
        /// </summary>
        /// <param name="skill">Skill to retrieve the base cost.</param>
        public int this[Skill skill] => baseCosts.TryGetValue(skill, out int result) ? result : defaultBaseCost;
    }
}
SkillStore.cs
using System.Collections.Generic;
using GameDevTV.Saving;
using Newtonsoft.Json.Linq;
using UnityEngine;

namespace RPG.Skills
{
    public class SkillStore : MonoBehaviour, IJsonSaveable
    {
        [Header("Starting Skills")]
        [Tooltip("Enter any starting skills here.  Any skill not in this list will start at level 1")]
        [SerializeField] private List<SkillRecord> startingSkills;
        [Header("Base Requirements")]
        [Tooltip("If there is no formula attached, defaultBaseRequirement is used.")]
        [SerializeField] private int defaultBaseRequirement=10;
        [Tooltip("Attach a SkillFormula here denoting base requirements for each skill (or a default value for ones not listed")]
        [SerializeField] private SkillFormula formulas;
        
        public event System.Action skillsUpdated;
        private SkillExperience skillExperience;
        
        //Backing field, do not use!
        private Dictionary<Skill, int> _skills;
        
        /// <summary>
        /// Use this skills Dictionary, not the backing field.  Dictionary of all skills with levels set.  
        /// </summary>
        Dictionary<Skill, int> skills
        {
            get
            {
                if (_skills==null)
                {
                    _skills = new Dictionary<Skill, int>();
                    foreach (SkillRecord skill in startingSkills)
                    {
                        _skills.Add(skill.skill, skill.value);
                    }
                }
                return _skills;
            }
        }

        
        
        void Awake()
        {
            if (TryGetComponent(out skillExperience))
            {
                skillExperience.skillExperienceUpdated += CheckExperiences;
            }

            for (int i = 1; i < 201; i++)
            {
                Debug.Log($"Cost at level {i} = {ExperienceNeeded(Skill.Blacksmithing)}");
                GainLevel(Skill.Blacksmithing);
            }
        }

        /// <summary>
        /// Event handler.  Since this method can only be called by an event on an attached skillExperience
        /// component, it can be safely assumed that skillExperience is not null.
        /// Goes through each skill within the SkillExperience component, checking to see if the exerience
        /// is high enough to level any given skill.
        /// </summary>
        private void CheckExperiences()
        {
            foreach (SkillRecord skillRecord in skillExperience.GetSkillRecords())
            {
                int experience = skillRecord.value;
                while (experience > ExperienceNeeded(skillRecord.skill))
                {
                    GainLevel(skillRecord.skill);
                }
                
            }
        }

        /// <summary>
        /// Increases the passed in skill level by 1.  Should only be called by CheckExperiences.
        /// </summary>
        /// <param name="skill">Skill to level up</param>
        private void GainLevel(Skill skill)
        {
            skills[skill] = GetSkillLevel(skill) + 1;
            skillsUpdated?.Invoke();
        }

        
        
        /// <summary>
        /// This method will calculate a skill's required level based on a set base requirement.  The Base requirement is
        /// what the skill will cost to advance from level 1 to level 2.  Thereafter, the formula multiplies the level and adds it to
        /// the base requirement.  This is functionally an n factorial method, but with a multiplier for the final result. 
        /// </summary>
        /// <param name="skill">Skill to check</param>
        /// <returns>Amount of experience needed for this level.</returns>
        private int ExperienceNeeded(Skill skill)
        {
            skills.TryAdd(skill, 1);
            int level = skills[skill];
            int baseRequirement = GetBaseRequirement(skill);
            int result = 0;
            for (int i = 1; i <= level; i++)
            {
                result += i * baseRequirement;
            }
            return result;
        }

        /// <summary>
        /// Gets the current level for the passed skill. All level start at 1 unless otherwise specified in the
        /// starting Skills list.
        /// </summary>
        /// <param name="skill">Skill to locate</param>
        /// <returns>Current level of that skill</returns>
        public int GetSkillLevel(Skill skill)
        {
            return skills.TryGetValue(skill, out int result) ? result : 1;
        }

        /// <summary>
        /// Uses an attached SkillFormula to derive a base value for the given skill.  If no SkillFormula is attached,
        /// then all skills will use the value in defaultBaseRequirement.
        /// </summary>
        /// <param name="skill">Skill to get base requirement</param>
        /// <returns>Value used to seed ExerienceNeeded</returns>
        int GetBaseRequirement(Skill skill)
        {
            if (formulas) return formulas[skill];
            return defaultBaseRequirement;
        }
        
        
        public JToken CaptureAsJToken()
        {
            return JToken.FromObject(_skills);
        }

        public void RestoreFromJToken(JToken state)
        {
            _skills = state.ToObject<Dictionary<Skill, int>>();
            skillsUpdated?.Invoke();
        }

        public int SavePriority()
        {
            return -1;
        }
    }
}

I’m sure these classes will add more questions than answers… but this lays down the basic framework. In this case, the experience → level is setup as one large experience for each skill rather than resetting, so that matches your preference. I commented the functions, as well.

With randomness. There are lots of skill based models to choose from. Here’s the simplest, although it’s probably a bit strict… at level 60, you would have a 1 in 60 chance of pulling off the chop, and by level 100, that’s still only 40%…

public bool SkillCheck(int skillLevel, int levelRequired)
{
    return Random.Range(1,skillLevel+1)>=levelRequired;
}

So it might help to give a little boost…

public bool SkillCheck(int skillLevel, int levelRequired)
{
    int bonus =  (levelRequired - skillLevel) * 2;
    return Random.Range(1, skilllevel)+bonus<levelRequired;
}

In this case, the chances rise quicker. It’s just a matter of playing around with the formulas.

2 Likes

Ahh I’ll have to go through all of this properly, and I’m heading to school now… Alright I’ll give this a thorough read between classes and try implementing them into my systems, might have a few other questions though (I didn’t properly read them yet)

On the fly though, which script goes where…?

OK Umm… I tried writing down the scripts on my own, just to try follow along with what’s going on. I understood a little bit (just a little bit) of what’s going on here, but I still have a few questions…

Where does this script go to? (I’m guessing SkillFormula, since it seems like a… well… formula). More importantly, where is it called?

  1. Let’s say if I want to use another mathematical formula, possibly one from the page that @bixarrio shared, what changes do we need to do? I’m still searching for a formula, something that’s not too hard, not too easy, and not easily guessable

  2. How do I use all these scripts? Which script goes where? (again, I understood just a little bit, so I’m a bit more baffled, still)

  3. If I develop another skill down the line in the development cycle (I added some skills as placeholders for future game systems of mine, I didn’t blindly copy paste the skills), where can I go to add experience for that skill when it happens?

  4. For debugging and demo purposes, if I want to display the text, do I do it like we did for ‘BaseStats.cs’? Or something better is available out there (I’m not sure what to display tbh for the experience gain, especially for choosing a single skill from an Enum of skills)

Thank you for taking the time and placing the energy to making sure I understand what’s going on, it’s always highly appreciated :smiley:

It could be a method in SkillStore (because that’s where the actual level is stored). In fact, if it’s in SkillStore, then rather than starting with skillLevel, just pass in the skill.

public bool SkillCheck(Skill skill, int levelRequired)
{
    int skillLevel = GetSkillLevel(skill);
    //rest of formula
}

Almost all formulas are going to take in a skill level and a levelRequired. From there, it’s just plugging in the formula. The same thing with the Experience to level formulas. Just plug in the formula.

SkillExperience and SkillStore would go on the Player. SkillFormula is a ScriptableObject that you would create just like an Inventory Item, and attach to the SkillStore. If one is not attached, it will use the default base set in the SkillStore for all skills.

For enemies, if you want them to make skill type checks, just put a SkillStore on them and in the default skills, add the skills and levels you want them to have.

First of all, hey, I worked hard on those skills! (ok, I didn’t, use the skills you want to use!).
To add a new skill, simply adding the skill to the skill enum will automatically cause it to be tracked by the system.

You could create a Skill UI that shows the values of all the skills, and perhaps even experience/experience required to level the skills.
You could simply Debug.Log() when experience in a skill is gained or the skill gains a level to check the skills.

OK so my new ‘SkillCheck()’ function is written like this in ‘SkillStore.cs’, right? (for now… I completely eliminated it from ‘SkillFormula.cs’). I still don’t see where it’s being called though

public bool SkillCheck(Skill skill, int levelRequired) {

            int skillLevel = GetSkillLevel(skill); 
            int bonus = (levelRequired - skillLevel) * 2;
            return Random.Range(1, skillLevel) + bonus < levelRequired;


        }

OK so let’s say I want to blend in the melee, ranged, defence and magic attacks into my systems, to ensure I can’t wield a specific bow and arrow, or a magic weapon, or melee or a piece of armor unless we get to a specific level. Can we do a short demonstration of that, so I can get a better idea of how to implement all these codes into my systems? (Part of me is literally screaming "We’ll be fine-tuning the ‘Fighter.cs’ script for that)… Something that shows us the levels individually (I can set the UI up for that), and then does not allow us to equip specific ammo and weapons unless we reach that Skill level.

Again, by the end of this, I’m hoping we can eliminate the trait system and all of it’s equipment conditions and other effects. Nothing wrong with it, but the XP-advanced system is probably going to end up way more advanced than the trait system.

By the end of this game I’ll probably have other stuff involved, since I got like 20 new skills I want to add (Just so I don’t sound like a lunatic, I just need a simple demo for the moment, we’ll deal with these on the long run, step by step), so a demo of how to blend these in would be nice :slight_smile: - because it still baffles me where ‘SkillCheck’, now shifted to ‘SkillStore.cs’ is called

(Quietly runs away :laughing: )

as for this one:

[quote=“Brian_Trotter, post:16, topic:234607”]

or (int i = 1; i < 201; i++)
            {
                Debug.Log($"Cost at level {i} = {ExperienceNeeded(Skill.Blacksmithing)}");
                GainLevel(Skill.Blacksmithing);
            }

Was this a demo? I noticed it’s a little too specific xD

Oh, and one last question, just so I’m not baffled, ‘SkillStore.cs’ is where I tune the formula to determine how much XP each level gives us, right? The ‘for’ loop in this:

private int ExperienceNeeded(Skill skill) {

            skills.TryAdd(skill, 1);
            int level = skills[skill];
            int baseRequirement = GetBaseRequirement(skill);
            int result = 0;

            for (int i = 1; i <= level; i++) {

                result += i * baseRequirement;

            }

            return result;

        }

Privacy & Terms