Skills based system

It’s best not to call the code in the first place… If you’re not basing level on experience, you should not be trying to set the level based on experience.

I tried multiple ways to ignore this function overall for my NPCs, but it just won’t allow me to. After reviewing that script again and going through the enemy’s inspector, I realized they don’t have an ‘experience’ component to begin with, so that line of code is not reached anyway (and it really has me thinking hard about how it got called to begin with…)

It’s called by the LazyValue’s Init() method.

When I tried to have you replace it with this method

private int CalculateLevel()
{
   return TryGetComponent(out SkillStore skillStore)? skillStore.GetCombatLevel() :  startingLevel;
}

You said that this was breaking the project (which is… um… impossible, because skillStore.CombatLevel was already mostly working at that point).

Ahh, yup that fixed it… now it’s not complaining anymore :slight_smile: (I kept tuning that method like crazy, never thought of deleting it though…)

I’m done with the defence and combat system now. Off to the Resource Gathering XP system, because this one right now is a living nightmare (and that error was just the top of the ice cake of headaches for me). I’ll give this a try and update you if I need help, thanks again Brian for pulling me out of this issue :slight_smile:

[SOLVED, but with a new problem in the end… Please skip till the end, but also check my ‘ResourceGathering.HandleRaycast()’ method]

[Main problem is in ‘ResourceGathering.cs’]

OK so now in my resource gathering system, something I coded a while ago, I tried programming a new function, known as ‘AccumulateXP()’. This function, however, does not work properly. The point of this function is that it will give you the XP for your skills, based on what tag the instantiated prefab has (that’s my way of classifying them apart), so for example a ‘ResourceTree’ tagged item that is cut down returns woodcutting XP, a ‘ResourceRock’ returns mining XP, etc… The problem is that it does not return any sort of XP, and just hovering over the tree when the commented out ‘AccumulateXP()’ functions in ‘ResourceGathering.cs’ makes the player automatically wipe the tree out and gain its resources at maximum speed, which is not what I desire.

Everytime I try integrating the ‘AccumulateXP’ function in my ‘ResourceGathering.cs’ script, into the ‘HandleRaycast()’ method, similar to my ‘Gather()’ function in ‘ResourceGathering.cs’ script, it returns an NRE, as follows:

NullReferenceException: Object reference not set to an instance of an object
RPG.ResourceManager.ResourceGathering.AccumulateXP () (at Assets/Project Backup/Scripts/ResourceManager/ResourceGathering.cs:91)
RPG.ResourceManager.ResourceGathering.HandleRaycast (RPG.Control.PlayerController callingController) (at Assets/Project Backup/Scripts/ResourceManager/ResourceGathering.cs:157)
RPG.Control.PlayerController.InteractWithComponent () (at Assets/Project Backup/Scripts/Control/PlayerController.cs:150)
RPG.Control.PlayerController.Update () (at Assets/Project Backup/Scripts/Control/PlayerController.cs:79)

I tried tracing down the NRE, but had no luck fully understand where I went wrong. If it helps in anyway, here are the scripts I coded:

ResourceTreeSpawner.cs (This one works perfectly fine for my needs):

using System.Collections;
using UnityEngine;

namespace RPG.ResourceManager
{
    public class ResourceTreeRespawner : MonoBehaviour
    {
        [SerializeField] GameObject treeToRespawn;
        [SerializeField] int hideTime;
        [SerializeField] int originalQuantity = 10;

        private GameObject currentTree;
        private ResourceGathering resourceGathering;
        private bool isRespawning = false;

        [SerializeField] int woodcuttingXPReward;
        [SerializeField] int miningXPReward;

        void Start()
        {
            // Initialize the resourceGathering component
            resourceGathering = GetComponent<ResourceGathering>();

            // Spawn the initial tree
            SpawnTree();
        }

        void Update()
        {
            // Check if the current tree is destroyed and respawn it
            if (currentTree == null && !isRespawning)
            {
                StartCoroutine(RespawnAfterDelay());
            }
        }

        IEnumerator RespawnAfterDelay()
        {
            isRespawning = true;
            yield return new WaitForSeconds(hideTime);
            SpawnTree();
            isRespawning = false;
        }

        private void SpawnTree()
        {
            
            // Instantiate a new tree ('Quaternion.identity' = no Rotation assigned - Success):
            currentTree = Instantiate(treeToRespawn, transform.position, Quaternion.identity);

            // Make the current tree a child of this GameObject (the Tree Respawner - Success):
            currentTree.transform.parent = this.transform;

            // Assign a tag to the spawned resource source (Success):
            currentTree.gameObject.tag = "ResourceTree";

            // Access the ResourceGathering script on the instantiated object and set its properties (Success):
            ResourceGathering resourceGathering = currentTree.GetComponent<ResourceGathering>();

            // Assign the XP Reward to their respective slots (Success):
            if (transform.GetChild(1).tag == "ResourceTree") resourceGathering.AssignXP(0, woodcuttingXPReward);

            if (resourceGathering != null)
            {
                // Reset the quantity left for resource gathering
                resourceGathering.quantityLeft = originalQuantity;  // Reset the quantity for the next resource instance
                resourceGathering.isDestroyed = false; // Reset the IsDestroyed flag
            }
        }
    }
}

ResourceGathering.cs (I commented out the calls for ‘AccumulateXP()’, so you can get an idea where the problem is coming from):

using UnityEngine;
using GameDevTV.Inventories;
using RPG.Control;
using RPG.Movement;
using RPG.Skills;

// Steps to get the experience:
// 1. Declare a 'SkillExperience' variable
// 2. get the component of the variable in 'Awake()'
// 3. Split the type of XP you get, based on the tag... for trees, it's "Tree", and for Rocks, it's "ResourceRocks"
// 4. When gathering, call 'skillExperience.GainExperience()', so you get the experience you are supposed to get when gathering resources
// 5. Play and test multiple times...

namespace RPG.ResourceManager
{
    public class ResourceGathering : MonoBehaviour, IRaycastable
    {
        [SerializeField] InventoryItem resourceToAccumulate;    // the loot we are getting from the tree, rock, or whatever we're harvesting
        [SerializeField] int quantityPerHit = 1;    // how many of that loot is being acquired, per mouse click
        private float acceptanceRadius; // how close do we need to be to the resource source (tree, rock, etc...) before we can interact with it
        public int quantityLeft = 10;   // how many resources are in that source, before it's destroyed and re-instantiated

        Inventory playerInventory;  // the target inventory for our resources to go to (the player's inventory in this case)
        
        public bool isDestroyed = false;    // flag, to kill the source if it's out of resources
        bool isMovingTowardsResourceObject = false;   // flag, to ensure that we are close to the source of the resources, before being able to gather resources from it

        // skill experience link, to get the skill, and fine-tune it according to the trained skill
        SkillExperience skillExperience;
        [SerializeField] int treeWoodcuttingXP;
        [SerializeField] int rockMiningXP;

        void Awake()
        {
            skillExperience = GetComponent<SkillExperience>();
            
            playerInventory = Inventory.GetPlayerInventory();
            if (playerInventory == null) Debug.Log("Player Inventory Not Found");
            acceptanceRadius = 4.0f;
            isMovingTowardsResourceObject = false;
        }

        public int GetWoodcuttingXPReward() {
            return treeWoodcuttingXP;
        }

        public int GetMiningXPReward() {
            return rockMiningXP;
        }

        public void Gather()
        {

            Debug.Log("Gather Method Called");

            if (quantityLeft > 0)
            {
                // Play animation of whatever you're doing (mining, woodcutting, etc), and only stop when the resource source is dead
                // Find a way to kill the tree slowly, and improve the higher your level goes up (revise the defence algorithm in 'Health.cs')
                playerInventory.AddToFirstEmptySlot(resourceToAccumulate, quantityPerHit);
                quantityLeft--;
            }

            if (quantityLeft <= 0)
            {
                Destroy(gameObject);
                isDestroyed = true;
                Debug.Log(isDestroyed);
            }
        
        }

        // Assigning the XP to their respective skills:
        public void AssignXP(int miningSkillRewardXP = 0, int woodcuttingSkillRewardXP = 0)
        {

            this.rockMiningXP = miningSkillRewardXP;
            this.treeWoodcuttingXP = woodcuttingSkillRewardXP;

        }

        public void AccumulateXP()
        {

            Debug.Log("Accumulate XP Called...");

            if (gameObject.tag == "ResourceTree")
            {
                // Something is wrong here... THE SPAWNED TREE IS UNTAGGED (Solve that in 'ResourceTreeRespawner.cs'!):
                Debug.Log($"{treeWoodcuttingXP} XP Gained");
                skillExperience.GainExperience(Skill.Woodcutting, treeWoodcuttingXP);
            }

            else if (gameObject.tag == "ResourceRock")
            {
                skillExperience.GainExperience(Skill.Mining, rockMiningXP);
            }

        }

        public CursorType GetCursorType()
        {
            if (isDestroyed == true) return CursorType.None;
            else if (gameObject.tag == "ResourceRock") return CursorType.Rock;
            else return CursorType.Tree;
        }

        public bool HandleRaycast(PlayerController callingController)
        {

            // Resource Gathering Logic:

            // A. If your mouse is clicked:
            // 1. if you are far away from the tree, get closer and then cut it down ('Gather()')
            // 2. if you were already close to the tree, just cut it down already... ('Gather()')
            
            // B. If your mouse is not clicked, but when you clicked it earlier you were already moving towards the tree, and you are now close to it:
            // 1. You're close, so just cut it down already... ('Gather()')

            if (Input.GetMouseButtonDown(0)) {

                float distanceToTree = Vector3.Distance(transform.position, callingController.transform.position);

                if (distanceToTree > acceptanceRadius)
                {
                    Debug.Log("Moving towards the tree");
                    callingController.GetComponent<Mover>().MoveTo(transform.position, 1.0f);
                    isMovingTowardsResourceObject = true;
                    
                    if (isMovingTowardsResourceObject && distanceToTree <= acceptanceRadius) {

                        Debug.Log("Cutting this stupid ass tree down...");
                        Gather();
                        AccumulateXP();
                        isMovingTowardsResourceObject = false;
                        
                        }

                    return true;

                }

                else if (distanceToTree <= acceptanceRadius)
                {
                    Debug.Log("Cutting down the tree...");
                    Gather();
                    // AccumulateXP();
                    isMovingTowardsResourceObject = false;
                    return true;
                }

            }

            if (isMovingTowardsResourceObject && Vector3.Distance(transform.position, callingController.transform.position) <= acceptanceRadius)
            {
                Gather();
                // AccumulateXP();
                isMovingTowardsResourceObject = false;
                return true;
            }

            return true;

        }
    }
}

And the Woodcutting level displayers:

WoodcuttingXPDisplay.cs:

using System;
using RPG.Skills;
using UnityEngine;
using UnityEngine.UI;

namespace RPG.Skills.UI {

    public class WoodcuttingXPDisplay : MonoBehaviour {

        SkillExperience skillExperience;
        SkillStore skillStore;

        public void Awake() {

            skillExperience = GameObject.FindWithTag("Player").GetComponent<SkillExperience>();
            skillStore = GameObject.FindWithTag("Player").GetComponent<SkillStore>();

        }

        public void Update() {

            GetComponent<Text>().text = String.Format("{0:0}/{1:0}", skillExperience.GetExperience(Skill.Woodcutting), skillStore.ExperienceNeeded(Skill.Woodcutting));

        }

    }

}

And the WoodcuttingLevelDisplay.cs (this one works perfectly fine):

using UnityEngine;
using RPG.Skills;
using UnityEngine.UI;
using System;

namespace RPG.Skills.UI {

    public class WoodcuttingLevelDisplay : MonoBehaviour {

        SkillStore skillStore;

        public void Awake() {

            skillStore = GameObject.FindWithTag("Player").GetComponent<SkillStore>();
            
        }

        public void Update() {

            GetComponent<Text>().text = String.Format("{0:0}", skillStore.GetSkillLevel(Skill.Woodcutting));

        }

    }

}

Again, I apologize for any problems caused :slight_smile:

You really don’t want any if (tag == "something"). This will just be a nightmare to maintain. @Brian_Trotter helped you build this extensible system that you’re just completely disregarding.
You should use it in exactly the same way as the combat. A resource gets a Skill and a value. No tag and no long list of ‘woodcuttingXP’, ‘rockMiningXP’, etc. Then, you just need to update the players skill xp for the attached skill with the specified value. So, instead of

            if (gameObject.tag == "ResourceTree")
            {
                // Something is wrong here... THE SPAWNED TREE IS UNTAGGED (Solve that in 'ResourceTreeRespawner.cs'!):
                Debug.Log($"{treeWoodcuttingXP} XP Gained");
                skillExperience.GainExperience(Skill.Woodcutting, treeWoodcuttingXP);
            }

            else if (gameObject.tag == "ResourceRock")
            {
                skillExperience.GainExperience(Skill.Mining, rockMiningXP);
            }

            // .. etc. ..

for every possible resource you can have, you will just have

skillExperience.GainExperience(associatedSkill, experienceValue);

When you create a Tree prefab, you assign the skill and the value in the inspector. The spawner just creates the tree, it doesn’t need to be setting any values on that tree.

It should be something simple, like this

using UnityEngine;
using GameDevTV.Inventories;
using RPG.Control;
using RPG.Movement;
using RPG.Skills;

// Steps to get the experience:
// 1. Declare a 'SkillExperience' variable
// 2. get the component of the variable in 'Awake()'
// 3. Split the type of XP you get, based on the tag... for trees, it's "Tree", and for Rocks, it's "ResourceRocks"
// 4. When gathering, call 'skillExperience.GainExperience()', so you get the experience you are supposed to get when gathering resources
// 5. Play and test multiple times...

namespace RPG.ResourceManager
{
    public class ResourceGathering : MonoBehaviour, IRaycastable
    {
        [SerializeField] InventoryItem resourceToAccumulate;    // the loot we are getting from the tree, rock, or whatever we're harvesting
        [SerializeField] int quantityPerHit = 1;    // how many of that loot is being acquired, per mouse click
        private float acceptanceRadius; // how close do we need to be to the resource source (tree, rock, etc...) before we can interact with it
        public int quantityLeft = 10;   // how many resources are in that source, before it's destroyed and re-instantiated

        Inventory playerInventory;  // the target inventory for our resources to go to (the player's inventory in this case)
        
        public bool isDestroyed = false;    // flag, to kill the source if it's out of resources
        bool isMovingTowardsResourceObject = false;   // flag, to ensure that we are close to the source of the resources, before being able to gather resources from it

        // skill experience link, to get the skill, and fine-tune it according to the trained skill
        SkillExperience skillExperience;
        [SerializeField] Skill associatedSkill;
        [SerializeField] int experienceValue;
        [SerializeField] CursorType cursorType;

        void Awake()
        {
            skillExperience = GetComponent<SkillExperience>();
            
            playerInventory = Inventory.GetPlayerInventory();
            if (playerInventory == null) Debug.Log("Player Inventory Not Found");
            acceptanceRadius = 4.0f;
            isMovingTowardsResourceObject = false;
        }

        public void Gather()
        {

            Debug.Log("Gather Method Called");

            if (quantityLeft > 0)
            {
                // Play animation of whatever you're doing (mining, woodcutting, etc), and only stop when the resource source is dead
                // Find a way to kill the tree slowly, and improve the higher your level goes up (revise the defence algorithm in 'Health.cs')
                playerInventory.AddToFirstEmptySlot(resourceToAccumulate, quantityPerHit);
                quantityLeft--;
            }

            if (quantityLeft <= 0)
            {
                Destroy(gameObject);
                isDestroyed = true;
                Debug.Log(isDestroyed);
            }
        
        }

        public void AccumulateXP()
        {

            Debug.Log("Accumulate XP Called...");
            skillExperience.GainExperience(associatedSkill, experienceValue);
        }

        public CursorType GetCursorType()
        {
            if (isDestroyed == true) return CursorType.None;
            return cursorType;
        }

        public bool HandleRaycast(PlayerController callingController)
        {

            // Resource Gathering Logic:

            // A. If your mouse is clicked:
            // 1. if you are far away from the tree, get closer and then cut it down ('Gather()')
            // 2. if you were already close to the tree, just cut it down already... ('Gather()')
            
            // B. If your mouse is not clicked, but when you clicked it earlier you were already moving towards the tree, and you are now close to it:
            // 1. You're close, so just cut it down already... ('Gather()')

            if (Input.GetMouseButtonDown(0)) {

                float distanceToTree = Vector3.Distance(transform.position, callingController.transform.position);

                if (distanceToTree > acceptanceRadius)
                {
                    Debug.Log("Moving towards the tree");
                    callingController.GetComponent<Mover>().MoveTo(transform.position, 1.0f);
                    isMovingTowardsResourceObject = true;
                    
                    if (isMovingTowardsResourceObject && distanceToTree <= acceptanceRadius) {

                        Debug.Log("Cutting this stupid ass tree down...");
                        Gather();
                        AccumulateXP();
                        isMovingTowardsResourceObject = false;
                        
                        }

                    return true;

                }

                else if (distanceToTree <= acceptanceRadius)
                {
                    Debug.Log("Cutting down the tree...");
                    Gather();
                    // AccumulateXP();
                    isMovingTowardsResourceObject = false;
                    return true;
                }

            }

            if (isMovingTowardsResourceObject && Vector3.Distance(transform.position, callingController.transform.position) <= acceptanceRadius)
            {
                Gather();
                // AccumulateXP();
                isMovingTowardsResourceObject = false;
                return true;
            }

            return true;

        }
    }
}

Here I made fields for the Skill, the xp value and the cursor type (which you can now set in the inspector). I updated the bits that use those (I didn’t change HandleRaycast).

Edit
Wait. Is SkillExperience not on the player? This script goes on the resource. You won’t find the SkillExperience on the resource. You can do the sneaky I did in the crafting system and get the SkillExperience off the inventory

void Awake()
{
    playerInventory = Inventory.GetPlayerInventory();
    if (playerInventory == null) Debug.Log("Player Inventory Not Found");

    skillExperience = playerInventory.GetComponent<SkillExperience>();

    acceptanceRadius = 4.0f;
    isMovingTowardsResourceObject = false;
}

I realise it’s a little unintuitive and confusing (what does the skill experience and inventory have to do with each other?). I’m just reusing the inventory to get the skill experience because it (should be) on the same game object


As for the NRE, the only thing that can be null there is skillExperience when you ‘get’ it in Awake, make sure it’s not null. The same way you checked the playerInventory

Rest assured, I’m not doing that on purpose. I’m just following the advice both of you gave me to try and take control of my code on my own, and use whatever I have (to my best knowledge), without destroying my code. As much as I’d love to say I’m superior, I can’t fully agree there… I’m just trying my best, and if I get stuck I’ll ask questions :slight_smile:

Anyway, I’ll give this code a go and try to understand it, and if I have any further questions I’ll definitely ask

I didn’t mean to offend, I’m sorry.

The idea is that you don’t want anything that requires code changes later on. You have woodcuttingXP and rockMiningXP and later you’d want to add berryPickingXP and toeStubbingXP and every time you want to add more, you have to go in and change all the code to accomodate for that. If you make everything so it depends on inspector values instead, you can add more and more stuff without touching the code and it will just work. In this case you would have to add a new Skill enum value for BerryPicking and ToeStubbing as well as a new CursorType enum for each, but that’s it. No looking through code to see where to add another ‘if’ and missing spots and forgetting tags, etc. I personally almost never use enums. I make a simple scriptable object instead. Then I don’t even have to change the enums when I want to add something new, I just create a new asset that I can drag into the inspector.

Think of it like this; Your code should really never know about content. A tree is content. A rock is content. You should not have anything ‘tree’ or ‘rock’ in the code. These are ‘resources’. Now, you think of a way to write that without ever talking about a tree or a rock. It’s not always 100% possible, but it helps to make the code reusable later on, when you add berries and you don’t want to talk about berries. It’s still a resource and if you did the tree and rock bits right, the berries will just fall in place.

1 Like

All is good man, I understand. By all means, the suggestion you mentioned earlier didn’t work either… I absolutely loved the intuitive you have of making things work by making the code recyclable, but it still didn’t work… Any other suggestions you got? :slight_smile:

(P.S: class in 2 minutes, I’ll be back in an hour or so)

What didn’t work? Your initial issue was the NRE. That would happen because you are looking for SkillExperience on the resource. The rest was just me ranting because I have bad people skills and your solution was going to give you nightmares later on

@bixarrio alright so what I tried doing earlier was get the ‘AccumulateXP()’ to work when my player gathers a resource. If you go through my original ‘ResourceGathering.cs’ script (and btw, the ‘SkillExperience.cs’ goes on the player, not the resource), you’ll notice I called and commented out ‘AccumulateXP()’ in ‘HandleRaycast()’ multiple times, to ensure the game actually has the player go to the Resource before gathering it, and deal with multiple conditions

The NRE unfortunately still exists, although my code now is a little cleaner, thanks to you :slight_smile:

Never fully understood those tbh. All I know is they are data containers, and possibly what we use when we want to create something in the Asset menu, and that’s about it… :sweat_smile:

Where do you get these names from :stuck_out_tongue_winking_eye:

So, you’re saying the SkillExperience is not on the same game object as the Inventory?
Technically, there should only be one SkillExperience so you could, instead, do the same thing in there that we did in Inventory

// In SkillExperience.cs
public static SkillExperience GetPlayerSkillExperience()
{
    return FindObjectOfType<SkillExperience>();
}

and then use that to get the SkillExperience instead of GetComponent()

The SkillExperience is on the same game object as the inventory, but the resource gathering system isn’t

Yes, I know. So, playerInventory.GetComponent<SkillExperience>(); should give you the SkillExperience and will then not be null anymore. Yet it is. Why?

Aha! That was my mistake… I forgot to accumulate the ‘SkillExperience’ off the player, it was blindly trying to get it off the Resource Gatherer… thanks @bixarrio, apologies for my error :slight_smile:

My next step, if Brian doesn’t catch this chat yet, will be to integrate the difficulty function he mentioned when we were still developing ‘SkillFormula.cs’, the function that’s responsible for making these actions hard at the start, and they get easier as the levels increase. This function, in ‘SkillStore.cs’:

public bool SkillCheck(Skill skill, int levelRequired) {

            int skillLevel = GetSkillLevel(skill); 
            // difficulty to acquire something formula (tune as you desire) below:
            int bonus = (levelRequired - skillLevel) * 2;
            return UnityEngine.Random.Range(1, skillLevel) + bonus < levelRequired;

        }

It would be an amazing add-on for both combat and skills, to ensure the player doesn’t have it easy from the get-go (so your chances of landing a hit are low at the start, but they get better, and so is the case for resource gathering and other skills)

I’m trying to understand your HandleRaycast(). I don’t see how that could work. I see scenarios where it will work, but I also see scenarios where it doesn’t and then weird things start happening in my mind with regard to where everything will go wrong

HandleRaycast is only called on objects under the mouse, so

  • If I’m far from the tree and click it to chop it down, the player will start walking towards it. If I now move my mouse away, the player will walk up to the tree and do nothing. When I move the mouse over the tree again (not even clicking), the player will start chopping the tree.
  • If I’m far from the tree and click it to chop it down, the player will start walking towards it. If I now click somewhere else, the player will start walking somewhere else, abandoning the tree. However, as soon as I move my mouse over the tree again (not even clicking) the player will start moving towards the tree again to try and chop it down. If I did this to 90 trees, the player is going to be walking all over the place when I just move the mouse.

You also have an if saying;

  • if the player is more than 4m from the tree
    • start moving towards the tree
    • if the player is less than 4m from the tree
      • chop the tree

The second if will never execute because it is checked in the same frame as the code where you told it to move towards the tree - the player hasn’t moved yet - so it will never be closer than it was 1 line earlier when it was still more than 4m away

Looking at your ‘resource’, I would do something like this: Your ‘resource’ (tree) has a quantityPerHit that gives the player that amount when hitting the tree. Using the player’s current skill I would go one of two ways;

  • Make this the max quantity and adjust based on the player’s skill. If the skill is low, the player gets a lower quantity. You’d then probably want to store the max skill level required for max quantity. So, if you have quantityPerHit = 5; and maxSkillRequired = 10;, a player at level 5 will only get 2.5 (rounded) per hit, while a player at level 10 will get all 5. This would be amountToGive = Mathf.FloorToInt(quantityPerHit * Mathf.Clamp01(playerSkillLevel / (float)maxSkillRequired));
  • Determine a ‘drop chance’ based on the skill level. Again, you’d probably want to store a max skill level. So, if you have maxSkillRequired = 10; a player on level 1 will only have a 10% chance of getting the resource on a hit, while a player on level 5 will have a 50% chance and a player on level 10 will have a 100% chance. The check would be something like Random.value <= Mathf.Clamp01(playerSkillLevel / (float)maxSkillRequired);

Believe me when I say this, I have went through this walking-to algorithm a thousand times before I got a daytime job and tried fixing it to act properly multiple times, but I never got it to work properly. It was, honestly, a nightmare for me to even get this close, and then I tried tuning it multiple times to get it to work properly, but nothing worked well for me. Please, if you have any potential solutions that’ll make it work, share them with me :slight_smile: (I know we said I’ll try coding my own algorithms on my own, but I still do need help if I’m being fair)

You are spot-on with your predictions though… this Algorithm was a nightmare to develop.

Ooo I like this one. It’s spontaneous, has an element of surprise, and I definitely want this integrated into my systems… My question though is, where do we place/call this code? It was where I struggled with @Brian_Trotter 's skill function - Guess I’ll figure it out next :slight_smile:

Why do I have a feeling like this probability goes into a progression asset…?

In ResourceGathering. I’m going to change my ‘formula’ a little and allow for a minSkillRequired and a maxSkillRequired. If the player skill is below the minimum, they have no chance of a drop

public void Gather()
{

    Debug.Log("Gather Method Called");

    if (quantityLeft > 0)
    {
        // Play animation of whatever you're doing (mining, woodcutting, etc), and only stop when the resource source is dead
        // Find a way to kill the tree slowly, and improve the higher your level goes up (revise the defence algorithm in 'Health.cs')

        // Get the player skill level for this resource (need SkillStore for this)
        var playerSkillLevel = skillStore.GetSkillLevel(associatedSkill);
        // Determine the player drop chance based off their skill. We clamp it to between 0 and 1
        var playerDropChance = Mathf.Clamp01((playerSkillLevel - minSkillRequired) / (float)(maxSkillRequired - minSkillRequired));
        // Check if we will drop something here. If we do, add it to the inventory and reduce this resource
        if (Random.value <= playerDropChance)
        {
            playerInventory.AddToFirstEmptySlot(resourceToAccumulate, quantityPerHit);
            quantityLeft -= quantityPerHit;
        }
    }

    if (quantityLeft <= 0)
    {
        Destroy(gameObject);
        isDestroyed = true;
        Debug.Log(isDestroyed);
    }
}

Make sure minSkillRequired and maxSkillRequired does not have the same value, and that minSkillRequired is less than maxSkillRequired.

Privacy & Terms